From 57e867e64431682fd6de6391c8f07e0dbae8eb81 Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Fri, 7 Mar 2025 14:33:24 +0100 Subject: [PATCH 01/10] docs: docs for multiple packages --- apps/docs/astro.config.mjs | 5 + apps/docs/src/components/Footer.astro | 1 - apps/docs/src/components/Hero.astro | 4 +- apps/docs/src/components/SiteTitle.astro | 1 - apps/docs/src/content/docs/index.mdx | 1 - .../explainers/async-transform.mdx | 0 .../{ => svelte}/explainers/linking-tasks.mdx | 0 .../explainers/mid-run-cancellation.mdx | 0 .../explainers/task-modifiers.mdx | 13 +- .../getting-started/installation.mdx | 0 .../{ => svelte}/getting-started/usage.mdx | 0 .../getting-started/what-is-it.mdx | 0 .../docs/{ => svelte}/reference/default.mdx | 0 .../docs/{ => svelte}/reference/drop.mdx | 0 .../docs/{ => svelte}/reference/enqueue.mdx | 0 .../{ => svelte}/reference/keep-latest.mdx | 0 .../docs/{ => svelte}/reference/restart.mdx | 0 .../{ => svelte}/reference/sheepdog-utils.mdx | 0 .../{ => svelte}/reference/task-instance.mdx | 0 .../docs/{ => svelte}/reference/transform.mdx | 0 .../vanilla/explainers/async-transform.mdx | 360 ++++++++++++++++++ .../docs/vanilla/explainers/linking-tasks.mdx | 78 ++++ .../explainers/mid-run-cancellation.mdx | 305 +++++++++++++++ .../vanilla/explainers/task-modifiers.mdx | 197 ++++++++++ .../vanilla/getting-started/installation.mdx | 53 +++ .../docs/vanilla/getting-started/usage.mdx | 147 +++++++ .../vanilla/getting-started/what-is-it.mdx | 26 ++ .../docs/vanilla/reference/default.mdx | 239 ++++++++++++ .../content/docs/vanilla/reference/drop.mdx | 273 +++++++++++++ .../docs/vanilla/reference/enqueue.mdx | 273 +++++++++++++ .../docs/vanilla/reference/keep-latest.mdx | 281 ++++++++++++++ .../docs/vanilla/reference/restart.mdx | 279 ++++++++++++++ .../docs/vanilla/reference/sheepdog-utils.mdx | 30 ++ .../docs/vanilla/reference/task-instance.mdx | 108 ++++++ .../docs/vanilla/reference/transform.mdx | 43 +++ 35 files changed, 2706 insertions(+), 11 deletions(-) rename apps/docs/src/content/docs/{ => svelte}/explainers/async-transform.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/explainers/linking-tasks.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/explainers/mid-run-cancellation.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/explainers/task-modifiers.mdx (94%) rename apps/docs/src/content/docs/{ => svelte}/getting-started/installation.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/getting-started/usage.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/getting-started/what-is-it.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/reference/default.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/reference/drop.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/reference/enqueue.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/reference/keep-latest.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/reference/restart.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/reference/sheepdog-utils.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/reference/task-instance.mdx (100%) rename apps/docs/src/content/docs/{ => svelte}/reference/transform.mdx (100%) create mode 100644 apps/docs/src/content/docs/vanilla/explainers/async-transform.mdx create mode 100644 apps/docs/src/content/docs/vanilla/explainers/linking-tasks.mdx create mode 100644 apps/docs/src/content/docs/vanilla/explainers/mid-run-cancellation.mdx create mode 100644 apps/docs/src/content/docs/vanilla/explainers/task-modifiers.mdx create mode 100644 apps/docs/src/content/docs/vanilla/getting-started/installation.mdx create mode 100644 apps/docs/src/content/docs/vanilla/getting-started/usage.mdx create mode 100644 apps/docs/src/content/docs/vanilla/getting-started/what-is-it.mdx create mode 100644 apps/docs/src/content/docs/vanilla/reference/default.mdx create mode 100644 apps/docs/src/content/docs/vanilla/reference/drop.mdx create mode 100644 apps/docs/src/content/docs/vanilla/reference/enqueue.mdx create mode 100644 apps/docs/src/content/docs/vanilla/reference/keep-latest.mdx create mode 100644 apps/docs/src/content/docs/vanilla/reference/restart.mdx create mode 100644 apps/docs/src/content/docs/vanilla/reference/sheepdog-utils.mdx create mode 100644 apps/docs/src/content/docs/vanilla/reference/task-instance.mdx create mode 100644 apps/docs/src/content/docs/vanilla/reference/transform.mdx diff --git a/apps/docs/astro.config.mjs b/apps/docs/astro.config.mjs index e3fee847..30ade68d 100644 --- a/apps/docs/astro.config.mjs +++ b/apps/docs/astro.config.mjs @@ -143,4 +143,9 @@ export default defineConfig({ ], output: 'static', adapter: netlify({}), + redirects: { + '/explainers/[...all]': '/svelte/explainers/[...all]', + '/getting-started/[...all]': '/svelte/getting-started/[...all]', + '/reference/[...all]': '/svelte/reference/[...all]', + }, }); diff --git a/apps/docs/src/components/Footer.astro b/apps/docs/src/components/Footer.astro index 8054029e..4df5372a 100644 --- a/apps/docs/src/components/Footer.astro +++ b/apps/docs/src/components/Footer.astro @@ -1,5 +1,4 @@ --- -import type { Props } from '@astrojs/starlight/props'; import Default from '@astrojs/starlight/components/Footer.astro'; import MainmatterLogo from '../assets/Mainmatter.astro'; diff --git a/apps/docs/src/components/Hero.astro b/apps/docs/src/components/Hero.astro index 83d1c96a..a279a455 100644 --- a/apps/docs/src/components/Hero.astro +++ b/apps/docs/src/components/Hero.astro @@ -5,7 +5,7 @@ import type { ImageMetadata } from 'astro'; const PAGE_TITLE_ID = '_top'; const { data } = Astro.locals.starlightRoute.entry; -const { title = data.title ?? "Sheepdog", tagline, image, actions = [] } = data?.hero ?? {}; +const { tagline, image, actions = [] } = data?.hero ?? {}; const imageAttrs = { loading: 'eager' as const, @@ -82,7 +82,7 @@ if (image) { {rawHtml &&
}
-

+

@sheepdog/svelte

{tagline &&
}
{ diff --git a/apps/docs/src/components/SiteTitle.astro b/apps/docs/src/components/SiteTitle.astro index 8d640de7..197156e0 100644 --- a/apps/docs/src/components/SiteTitle.astro +++ b/apps/docs/src/components/SiteTitle.astro @@ -1,5 +1,4 @@ --- -import type { Props } from '@astrojs/starlight/props'; import Default from '@astrojs/starlight/components/SiteTitle.astro'; import BuiltBy from '../assets/BuiltBy.astro'; diff --git a/apps/docs/src/content/docs/index.mdx b/apps/docs/src/content/docs/index.mdx index 2d252e87..1a9be750 100644 --- a/apps/docs/src/content/docs/index.mdx +++ b/apps/docs/src/content/docs/index.mdx @@ -25,4 +25,3 @@ hero: import Landing from '../../components/landing.astro'; - diff --git a/apps/docs/src/content/docs/explainers/async-transform.mdx b/apps/docs/src/content/docs/svelte/explainers/async-transform.mdx similarity index 100% rename from apps/docs/src/content/docs/explainers/async-transform.mdx rename to apps/docs/src/content/docs/svelte/explainers/async-transform.mdx diff --git a/apps/docs/src/content/docs/explainers/linking-tasks.mdx b/apps/docs/src/content/docs/svelte/explainers/linking-tasks.mdx similarity index 100% rename from apps/docs/src/content/docs/explainers/linking-tasks.mdx rename to apps/docs/src/content/docs/svelte/explainers/linking-tasks.mdx diff --git a/apps/docs/src/content/docs/explainers/mid-run-cancellation.mdx b/apps/docs/src/content/docs/svelte/explainers/mid-run-cancellation.mdx similarity index 100% rename from apps/docs/src/content/docs/explainers/mid-run-cancellation.mdx rename to apps/docs/src/content/docs/svelte/explainers/mid-run-cancellation.mdx diff --git a/apps/docs/src/content/docs/explainers/task-modifiers.mdx b/apps/docs/src/content/docs/svelte/explainers/task-modifiers.mdx similarity index 94% rename from apps/docs/src/content/docs/explainers/task-modifiers.mdx rename to apps/docs/src/content/docs/svelte/explainers/task-modifiers.mdx index a4297ed8..681a51e8 100644 --- a/apps/docs/src/content/docs/explainers/task-modifiers.mdx +++ b/apps/docs/src/content/docs/svelte/explainers/task-modifiers.mdx @@ -3,7 +3,7 @@ title: Task modifiers description: How task modifiers work --- -import Timeline from '../TimelineExplainer.svelte'; +import Timeline from '../../TimelineExplainer.svelte'; In [Getting started](/getting-started/usage#task-modifiers), you've seen that you can specify a modifier on the task, either with the dot notation or the options notation. The reason you have this @@ -16,8 +16,8 @@ they behave and hopefully make it clearer to understand when to use one or the o ## Default By default, every task simply runs as soon as you perform it. This is basically like a function call -with the added benefit of having derived state and cancellability. You might've seen -this simple demonstration on our homepage; but if you didn't, allow me to introduce you to the friend that will +with the added benefit of having derived state and cancellability. You might've seen this simple +demonstration on our homepage; but if you didn't, allow me to introduce you to the friend that will guide us through discovering the task modifiers: the Timeline demo! Take your time to familiarize yourself with it: you can perform a new task by clicking on the @@ -33,8 +33,8 @@ to nuke them all! The `enqueue` task, as the name suggests, creates a queue of all the task instances after the max concurrency has been reached. This means that up until the moment max concurrency is exceeded every task instance will be executed immediately. But if you try to perform a new task instance and the -number of running instances is above the `max` level, that new task instance will be stored in a queue -and executed as soon as the number of concurrent instances drops below the `max` again. +number of running instances is above the `max` level, that new task instance will be stored in a +queue and executed as soon as the number of concurrent instances drops below the `max` again. This is the right kind of task to use when you want to be sure that every task instance is executed at some point (unless the user leaves the page) but you also don't want to run them concurrently. A @@ -83,7 +83,8 @@ the same time. ## Drop The `drop` task will simply ignore (and thus drop) every `perform` that is executed when the `max` -amount of concurrent task instances is exceeded. With a max concurrency of 1, our example will drop any other tasks that are performed while our initial task is running. +amount of concurrent task instances is exceeded. With a max concurrency of 1, our example will drop +any other tasks that are performed while our initial task is running. You can think of this kind of task as a sort of throttle; and since the function will be just tossed away you should use this modifier only when you don't care about the "execution loss". A good diff --git a/apps/docs/src/content/docs/getting-started/installation.mdx b/apps/docs/src/content/docs/svelte/getting-started/installation.mdx similarity index 100% rename from apps/docs/src/content/docs/getting-started/installation.mdx rename to apps/docs/src/content/docs/svelte/getting-started/installation.mdx diff --git a/apps/docs/src/content/docs/getting-started/usage.mdx b/apps/docs/src/content/docs/svelte/getting-started/usage.mdx similarity index 100% rename from apps/docs/src/content/docs/getting-started/usage.mdx rename to apps/docs/src/content/docs/svelte/getting-started/usage.mdx diff --git a/apps/docs/src/content/docs/getting-started/what-is-it.mdx b/apps/docs/src/content/docs/svelte/getting-started/what-is-it.mdx similarity index 100% rename from apps/docs/src/content/docs/getting-started/what-is-it.mdx rename to apps/docs/src/content/docs/svelte/getting-started/what-is-it.mdx diff --git a/apps/docs/src/content/docs/reference/default.mdx b/apps/docs/src/content/docs/svelte/reference/default.mdx similarity index 100% rename from apps/docs/src/content/docs/reference/default.mdx rename to apps/docs/src/content/docs/svelte/reference/default.mdx diff --git a/apps/docs/src/content/docs/reference/drop.mdx b/apps/docs/src/content/docs/svelte/reference/drop.mdx similarity index 100% rename from apps/docs/src/content/docs/reference/drop.mdx rename to apps/docs/src/content/docs/svelte/reference/drop.mdx diff --git a/apps/docs/src/content/docs/reference/enqueue.mdx b/apps/docs/src/content/docs/svelte/reference/enqueue.mdx similarity index 100% rename from apps/docs/src/content/docs/reference/enqueue.mdx rename to apps/docs/src/content/docs/svelte/reference/enqueue.mdx diff --git a/apps/docs/src/content/docs/reference/keep-latest.mdx b/apps/docs/src/content/docs/svelte/reference/keep-latest.mdx similarity index 100% rename from apps/docs/src/content/docs/reference/keep-latest.mdx rename to apps/docs/src/content/docs/svelte/reference/keep-latest.mdx diff --git a/apps/docs/src/content/docs/reference/restart.mdx b/apps/docs/src/content/docs/svelte/reference/restart.mdx similarity index 100% rename from apps/docs/src/content/docs/reference/restart.mdx rename to apps/docs/src/content/docs/svelte/reference/restart.mdx diff --git a/apps/docs/src/content/docs/reference/sheepdog-utils.mdx b/apps/docs/src/content/docs/svelte/reference/sheepdog-utils.mdx similarity index 100% rename from apps/docs/src/content/docs/reference/sheepdog-utils.mdx rename to apps/docs/src/content/docs/svelte/reference/sheepdog-utils.mdx diff --git a/apps/docs/src/content/docs/reference/task-instance.mdx b/apps/docs/src/content/docs/svelte/reference/task-instance.mdx similarity index 100% rename from apps/docs/src/content/docs/reference/task-instance.mdx rename to apps/docs/src/content/docs/svelte/reference/task-instance.mdx diff --git a/apps/docs/src/content/docs/reference/transform.mdx b/apps/docs/src/content/docs/svelte/reference/transform.mdx similarity index 100% rename from apps/docs/src/content/docs/reference/transform.mdx rename to apps/docs/src/content/docs/svelte/reference/transform.mdx diff --git a/apps/docs/src/content/docs/vanilla/explainers/async-transform.mdx b/apps/docs/src/content/docs/vanilla/explainers/async-transform.mdx new file mode 100644 index 00000000..0fd8fd1a --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/explainers/async-transform.mdx @@ -0,0 +1,360 @@ +--- +title: Async Transform +description: How the Async Transform works +--- + +import { LinkCard, Tabs, TabItem, Aside, Code } from '@astrojs/starlight/components'; +import BlankLink from '@components/BlankLink.astro'; + +This explainer is meant to show you how the `Async Transform` Vite plugin works so that you can be +confident in using it. If you want to know how to setup the Vite plugin please go to the following +link. + + + +## What is a Vite plugin? + +If you just want to know about what the `Async Transform` does, feel free to skip to the next +paragraph. If you're completely new to Vite plugins or just want to understand more about what they +are; here's a brief introduction. + +
+ Introduction to Vite plugins + + A Vite plugin is essentially a small bit of code with a series of methods that Vite will call + sequentially during the dev and build script. + + Every function is called at a specific moment of the build and can affect it in different ways. + + Here's an example of a simple Vite plugin that is very similar to one in + + + +In this case we handle our own custom file extension and compile that to JS but you can also use the +transform hook to change your JS. + + + +This super simple plugin eliminates every instance of `console.log()` from your code. + + + +The gist of it is: you can do a lot of cool things with a Vite plugin and we decided to build the +Async Transform to make your life (hopefully) easier! + +
+ +## How does the `Async Transform` change my code? + +The reason we are writing this explainer is because we firmly believe that if you allow your code to +be changed by some script you should fully understand how it changes it and what the final result +will look like. + +As shown in our [Mid run cancellation](/explainers/mid-run-cancellation) explainer, this Vite plugin +aims to solve a couple of problems with async code in Typescript: + +- Promises are not cancelable: once you start them there's no way to stop the code in the middle of + the function from executing. +- Generators can be "stopped" mid execution but Typescript doesn't play very well with the `yield` + keyword. + +To fix these problems, we let you write the simple and more familiar async code, and we provide a +Vite plugin to transform your async functions into generators! + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + + +As you can see we aim to touch your code the bare minimum. But I can still see the worry on your +face, so let's dive a bit more and make a couple of clarifications + +### What about my other async functions? + +If you don't want to see all of your beloved async functions be turned into generators, worry not, +because we specifically target only the async functions that are arguments of our own `task` or +`task.modifier` functions. And we were really careful with this so even if you have other functions +that are named `task` or if you rename your import everything outside the argument of that function +will be left untouched. + + + + + +```svelte + +``` + + + + + +```svelte + +``` + + + + + +and the same works even for imported functions with the same name. + + + + + +{/* prettier-ignore-start */} + +```svelte + +``` + + + + + +```svelte + +``` + + + + + +However sometimes you might want to have a single function to create multiple tasks or maybe you +want to have your tasks in a separate ts file. Well good news for you... + +## The `transform` function + +If you want to tell `@sheepdog/svelte` that a function is meant to be transformed because you will +pass that function to a task you can do so by using the `transform` function exported from +`@sheepdog/svelte/utils`. + +This function doesn't do anything at runtime but it serves to purpose: + +1. As a marker for the vite plugin to transform the function you pass to it. +2. as a "safety measure". The function will actually throw in `DEV` if you use it without the Async + Transform. This will warn you that what you expect to be a mid-run-cancellable function it is not + because you forgot to add the vite plugin. + +Here's how a transformed function will look like + + + + + +```ts +import { transform } from '@sheepdog/svelte/utils'; + +// this will be converted +const myTask = transform(async () => { + const result = await fetch('/api/my-endpoint'); + return await result.json(); +}); +``` + + + + + +```ts +// this will be converted +const myTask = async function* () { + const result = yield fetch('/api/my-endpoint'); + return yield result.json(); +}; +``` + + + + + +as you can see the function invocation was completely removed. This obviously works the same if you +rename your import. + + + + + +```ts +import { transform as sheepdogTransform } from '@sheepdog/svelte/utils'; +import { transform } from 'random-npm-library'; + +// this will remain untouched +const otherTransform = transform(async () => { + // async stuff +}); + +// this will be converted +const myTask = sheepdogTransform(async () => { + const result = await fetch('/api/my-endpoint'); + return await result.json(); +}); +``` + + + + + +```ts +import { transform } from 'random-npm-library'; + +// this will remain untouched +const otherTransform = transform(async () => { + // async stuff +}); + +// this will be converted +const myTask = async function* () { + const result = yield fetch('/api/my-endpoint'); + return yield result.json(); +}; +``` + + + + diff --git a/apps/docs/src/content/docs/vanilla/explainers/linking-tasks.mdx b/apps/docs/src/content/docs/vanilla/explainers/linking-tasks.mdx new file mode 100644 index 00000000..3040f6e2 --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/explainers/linking-tasks.mdx @@ -0,0 +1,78 @@ +--- +title: Linking Tasks +description: How to bind tasks together for simpler cancellation +--- + +### Motivation + +Breaking up asynchronous calls into bitesize chunks makes a lot of sense: it's easier to reason about, it allows us to show different states depending on what stage of the call we are in, and it helps us organize our code better. + +But the biggest issue this exposes that it is very difficult to cancel all children if the parent is cancelled. + +Imagine this scenario: we're creating a blog feed widget that will get all of the user's posts and comments and show them in a small box somewhere on the page. We might have something like this to load all of those posts and comments: + +```js +async function fetch(){ + // make first call + const user = await fetchUser() + // make second call based on first response + const posts = await fetchUserPosts(user) + // make third call based on first and second responses + return await fetchPostComments({user, posts}) +} + +async function fetchUser(){ + // returns user stuff +} + +async function fetchUserPosts(user){ + // returns user's posts +} + +async function fetchPostComments({user, posts}){ + // returns posts' comments +} +``` + +Now this is great, it's been neatly enclosed in a component so that the rest of the page doesn't need to be blocked while waiting for this widget to load. We could also include some logic to enable us to show which call is currently being run. + +But what would happen if the user navigated away or destroyed between the initial call and the final result? Our API would still receive those requests and go and do all of the work to get the data and return it but we are no longer using the returned data or even have a reference to it in our app. This kind of scenario can very easily cause data leaks that can gradually bring our app down. + +_If only there was a way to link these calls together._ + +### Solution + +Well, we heard your cries and created the `link` function. + +`Link` is one of two [SheepdogUtils](/reference/sheepdog-utils) and it enables us to link a child task to its parent so that the lifecycle of the child task is directly bound to the lifecycle of its parent. That means that if the parent is canceled, the child is automatically aborted. + +Turning our above scenario into tasks would look like this: + +```js +import { task } from "@sheepdog/svelte" + +fetch = task(async (_, { link }) => { + // make first call + const user = await link(fetchUser).perform(); + // make second call based on first response + const posts = await link(fetchUserPosts).perform(user) + // make third call based on first and second responses + return await link(fetchPostComments).perform({user, posts}) +}) + +fetchUser = task(async () => { + // returns user stuff +}) + +fetchUserPosts = task(async (user) => { + // returns user's posts +}) + +fetchPostComments = task(async ({user, posts}) => { + // returns posts' comments +}) +``` + +And now all of our child tasks are bound to the parent context, meaning if the context that the parent task lives on is destroyed, all of the other tasks will be cancelled. + +Another added benefit of this is that we already have the different loading states out of the box, we could simply see which task is running and be able to show each loading state individually. \ No newline at end of file diff --git a/apps/docs/src/content/docs/vanilla/explainers/mid-run-cancellation.mdx b/apps/docs/src/content/docs/vanilla/explainers/mid-run-cancellation.mdx new file mode 100644 index 00000000..c76352b7 --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/explainers/mid-run-cancellation.mdx @@ -0,0 +1,305 @@ +--- +title: Mid run cancellation +description: How to handle the cancellation of a task mid execution +--- + +import { LinkCard, Tabs, TabItem, Aside } from '@astrojs/starlight/components'; +import BlankLink from '@components/BlankLink.astro'; + +As we hinted in the [What is it?](/getting-started/what-is-it/) explainer, one of the advantages of +`@sheepdog/svelte` is that it allows you to really "cancel" a task. Let's look at the actual problem +and how it is solved with `@sheepdog/svelte`. + +## The problem + +Promises are the de facto way to run asynchronous code in JavaScript and (especially after the +introduction of the `async` and `await` keywords) they are quite nice to work with. + +```ts +// this automatically returns a promise +async function myStuff() { + //i can await for the execution of async code to happen + const response = await fetch('/api/my-endpoint'); + const myEndpoint = await response.json(); + + return myEndpoint.data; +} +``` + +However, they have a big problem: Once invoked there is no way to stop the execution of the code. +This can lead to performance problems in the simplest case or even bugs in more complex scenarios. + +```ts +async function fetchALotOfStuff() { + const response = await fetch('/api/very-long-list'); + const longList = await response.json(); + + // what if the user canceled the request before this step? + for (const element of longList) { + expensiveCalculation(element); + } +} +``` + +This is especially true if we are invoking those functions within a UI framework because we tend to +assign values outside of the scope of the function to reactively show them in the UI. + +```svelte + + + + +
    + {#each list as element} +
  • {element}
  • + {/each} +
+``` + +The simplest way to solve this problem is to set up a variable and check it after the fetch. + +```svelte + + + + + +
    + {#each list as element} +
  • {element}
  • + {/each} +
+``` + +This works fine but it gets tedious pretty fast, especially if you need to do it multiple times. +That's where `@sheepdog/svelte` comes into play. + +## The solution(s) + +`@sheepdog/svelte` provides you multiple tools to solve this problem (hence the parenthesized plural +in the title of this section); let's go over them one by one + + + +### Solution 1: `AbortSignal` + +The simplest but more verbose solution to this problem is to use `AbortSignal` solution: +`@sheepdog/svelte` invokes your task with a series of utils, one of which is the `AbortSignal`. +Every task has its own `AbortController` and you can cancel a single task instance by invoking the +`cancel` method on it or cancel **every** instance by invoking the `cancelAll` method on the task. + +```svelte + + + + + + +
    + {#each list as element} +
  • {element}
  • + {/each} +
+``` + +We've gained the ability to stop in-flight fetches with the `AbortSignal` without having to create a +separate `canceled` variable. That's a win, but it does not cover other async functions or library +APIs that do not support `AbortSignals`. + +### Solution 2: Async generators + +Those who don't know about might be a bit confused right now and those who know about them might be already running away in +fear, but please bear with us for a second and we will show you that generators are not really that +scary. + +A generator is a particular function in JavaScript that is able to `yield` back the execution to the +caller. The syntax to create one looks like this: + +```ts +function* ping() { + console.log('in the generator'); + const value = yield 'pong'; + console.log(`after yield: ${value}`); +} + +const generator = ping(); +const yielded = generator.next(); +// logs: "in the generator" +console.log(yielded); +// logs: { done: false, value: "pong" } +generator.next('ping'); +// logs: "after yield: ping" +``` + +I know, I told you this wouldn't be scary and for the moment I haven't kept my promise (pun +intended). But the main takeaway from this snippet of code is that generator functions have +a way to stop executing and return something to the caller and the caller has a way to communicate +something back. + +`@sheepdog/svelte` has been built to be able to accept an async generator function and, most +importantly, has been built to make the generator function work basically like a normal async +function if you replace `await` with `yield`. Let's take a look + + + + + +```ts +let value; +const myTask = task(async function* (_, { signal }) { + const response = yield fetch('/api/my-endpoint', { signal }); + value = yield response.json(); +}); +``` + + + + +```ts +let value; +const myTask = task(async (_, { signal }) => { + const response = await fetch('/api/my-endpoint', { signal }); + value = await response.json(); +}); +``` + + + + + +As you can see, the code in the two tabs changes very little but with generators +`@sheepdog/svelte` has the ability to never call `next` if the task was canceled. This means that +if you cancel the task while fetch is still in-flight the second line of the function will +**never** be called! + +There is one small detail we have hidden from you, however: `yield` doesn't work very well with +TypeScript, especially if there are multiple of them. If you try to paste that code in a `.ts` file +(or in a Svelte component with ` + + + + + +
    + {#each list as element} +
  • {element}
  • + {/each} +
+``` diff --git a/apps/docs/src/content/docs/vanilla/explainers/task-modifiers.mdx b/apps/docs/src/content/docs/vanilla/explainers/task-modifiers.mdx new file mode 100644 index 00000000..681a51e8 --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/explainers/task-modifiers.mdx @@ -0,0 +1,197 @@ +--- +title: Task modifiers +description: How task modifiers work +--- + +import Timeline from '../../TimelineExplainer.svelte'; + +In [Getting started](/getting-started/usage#task-modifiers), you've seen that you can specify a +modifier on the task, either with the dot notation or the options notation. The reason you have this +ability is so that you can specify what should happen when multiple, concurrent task instances are +running. + +In this explainer we will guide you through how each of our modifiers work, with examples of how +they behave and hopefully make it clearer to understand when to use one or the other. + +## Default + +By default, every task simply runs as soon as you perform it. This is basically like a function call +with the added benefit of having derived state and cancellability. You might've seen this simple +demonstration on our homepage; but if you didn't, allow me to introduce you to the friend that will +guide us through discovering the task modifiers: the Timeline demo! + +Take your time to familiarize yourself with it: you can perform a new task by clicking on the +`perform` button and reset the demo with the `clear timeline` button. The moment you perform a task, +the timeline will start and it will show you the state of the task. Each task will run for two +seconds and you can either click on it individually to cancel it or click on the `cancelAll` button +to nuke them all! + + + +## Enqueue + +The `enqueue` task, as the name suggests, creates a queue of all the task instances after the max +concurrency has been reached. This means that up until the moment max concurrency is exceeded every +task instance will be executed immediately. But if you try to perform a new task instance and the +number of running instances is above the `max` level, that new task instance will be stored in a +queue and executed as soon as the number of concurrent instances drops below the `max` again. + +This is the right kind of task to use when you want to be sure that every task instance is executed +at some point (unless the user leaves the page) but you also don't want to run them concurrently. A +good use case for this could be uploading a list of files to the server. You want to upload all of +your files but you don't want to send them all together because that would be too resource intensive +and also makes it harder to handle in case of an error. You can use `enqueue` to allow only `max` +file uploads at a time. + +```svelte + + + { + files = [...e.target.files]; + }} +/> + +``` + +You can play around with it in our Timeline and you may notice a new parameter available for you: +initially set at 1, you can update the `max` parameter to allow for more task instances to run at +the same time. + + + +## Drop + +The `drop` task will simply ignore (and thus drop) every `perform` that is executed when the `max` +amount of concurrent task instances is exceeded. With a max concurrency of 1, our example will drop +any other tasks that are performed while our initial task is running. + +You can think of this kind of task as a sort of throttle; and since the function will be just tossed +away you should use this modifier only when you don't care about the "execution loss". A good +example could be triggering a very expensive task that you want to make sure is only triggered once. + +```svelte + + + +``` + +I know you are waiting for it so here's your `drop` Timeline playground: + + + +## Restart + +The `restart` task will cancel the oldest task instance once the `max` is exceeded. With `max` of 1, +this basically means that every time you perform again you will cancel the last task instance. + +You can think of this kind of task as a the good ol' debounce: if you await for, let's say, 200ms +before doing any action in a `restart` task you can call `perform` repeatedly and the actual task +will be executed only once the `perform` as not been called for 200ms. This is very useful to +perform fetch requests while the user is typing without inundating your server with requests. Once +the user stops typing the last request will go through without being canceled. + +```svelte + + + { + search.perform(e.target.value); + }} +/> +``` + +And as per usual, here's your `restart` Timeline playground: + + + +## Keep Latest + +Finally, we arrive at `keepLatest`. It's a mix of `drop` with a sprinkle of `enqueue`: once the max +concurrency is exceeded it will drop every new `perform` but it will also keep the very last in the +queue to execute it when the max concurrency drop below the `max`. + +You might need this when you are performing after some very frequent event but you don't really care +about intermediate state because the last one is the important one. Let's say for example that you +are building a collaborative document app. When someone else save the document you want to receive a +notification that allow you to reload the document data with the newest additions. If there are two +or more saves while you are fetching the data you: + +1. don't want to restart the current fetch because you want to show it to the user immediately +2. don't want to drop the new task because you actually want the latest data +3. don't case about the in-between data because the last one is the actual content of the file + +That's where `keepLatest` will come in handy! + +```svelte + + + +``` + + + +## Conclusion + +Task modifiers are very powerful and one of the core features of `@sheepdog/svelte`, this explainer +aims to give you all the tools to use them at their best...and some playful time with the timeline +example! diff --git a/apps/docs/src/content/docs/vanilla/getting-started/installation.mdx b/apps/docs/src/content/docs/vanilla/getting-started/installation.mdx new file mode 100644 index 00000000..1429719f --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/getting-started/installation.mdx @@ -0,0 +1,53 @@ +--- +title: Installation +description: How to install `@sheepdog/svelte`. +--- + +import { Tabs, TabItem, Code, Aside, LinkCard } from '@astrojs/starlight/components'; + +As the name suggests `@sheepdog/svelte` is meant to be used in a `svelte` project. + +To install, run the following command with your preferred package manager. + + + + + + + + + + + + + + + + +## Setup the Async Transform + + + +The Async Transform is a Vite plugin so to set it up you need to modify your `vite.config.{js|ts}` + +```diff lang="ts" title="vite.config.ts" +import { sveltekit } from '@sveltejs/kit/vite'; ++import { asyncTransform } from '@sheepdog/svelte/vite'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig(({ mode }) => ({ + plugins: [ + sveltekit(), ++ asyncTransform(), + ], + // rest of your config +})); +``` + +And that's it! You can now use async tasks with proper mid-run cancellation! + +Remember: the Async Transform will only change your code in some very specific places, read more +about it in [the async transform explainer](/explainers/async-transform). diff --git a/apps/docs/src/content/docs/vanilla/getting-started/usage.mdx b/apps/docs/src/content/docs/vanilla/getting-started/usage.mdx new file mode 100644 index 00000000..bf4d294d --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/getting-started/usage.mdx @@ -0,0 +1,147 @@ +--- +title: Usage +description: Basic usage of `@sheepdog/svelte`. +--- + +import { Tabs, TabItem, Code, Aside, LinkCard } from '@astrojs/starlight/components'; +import BlankLink from '@components/BlankLink.astro'; + +The simplest way to use `@sheepdog/svelte` is the following + +```svelte + + + +``` + +This will run your async function when the user presses the button. _"But why not use a normal async +function?"_ I hear you ask. + +**Because the `myTask` variable that you used to call `perform` is also a svelte store that contains +a lot of useful information about your task!** + +## `Task` structure + +- `isRunning`: whether the task is currently running or not +- `last`: the last task instance, regardless of whether it errored, was canceled, or was successful +- `lastCanceled`: the last canceled task instance +- `lastErrored`: the last errored task instance +- `lastRunning`: the last running task instance, as soon as the task stops running, this will be + undefined +- `lastSuccessful`: the last successful task instance +- `performCount`: the number of times the task has been run, + +```ts +type Task = { + /** + * whether the task is currently running or not + */ + isRunning: boolean; + /** + * the last task instance, regardless of whether it errored, was canceled, or was successful + */ + last?: TaskInstance; + /** + * the last canceled task instance + */ + lastCanceled?: TaskInstance; + /** + * the last errored task instance + */ + lastErrored?: TaskInstance; + /** + * the last running task instance, as soon as the task stops running, this will be undefined + */ + lastRunning?: TaskInstance; + /** + * the last successful task instance + */ + lastSuccessful?: TaskInstance; + /** + * number of times the task has been run, + */ + performCount: number; +}; +``` + +## `TaskInstance` structure + +To make it easier to reason about, every type of task modifier utilizes the same underlying structure. You can find more detail about the structure of the `TaskInstance` in the [reference docs](/reference/task-instance). + +## Task modifiers + +`@sheepdog/svelte` allows you to specify a task modifier that changes the behavior of the perform +function. By default when you call `perform` the task will be executed immediately regardless if +there are other instances of the same task in execution. For example, you could use the `enqueue` +modifier to instruct `@sheepdog/svelte` to execute the task instances one after another (in a +queue - who would've thought) + +Here's how you can specify the modifier: + + + + ```svelte ins=".enqueue" {4} + + + + ``` + + + ```svelte ins="enqueue" {6} + + + + ``` + + + + + + +There are currently five task modifiers that you can apply to your task. + +- `default`: the default behavior, every task will run immediately +- `drop`: if other tasks are already running the new instance will be immediately canceled +- `enqueue`: every task instance is stored in a queue and executed with a + + strategy +- `keepLatest`: like drop but the last instance will actually be kept around and executed as soon as + the current running instance finishes +- `restart`: the oldest running task instance is canceled and the new one will start running + immediately diff --git a/apps/docs/src/content/docs/vanilla/getting-started/what-is-it.mdx b/apps/docs/src/content/docs/vanilla/getting-started/what-is-it.mdx new file mode 100644 index 00000000..1d512d04 --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/getting-started/what-is-it.mdx @@ -0,0 +1,26 @@ +--- +title: What is it? +description: What problem does `@sheepdog/svelte` solves. +--- + +import Mainmatter from '@assets/Mainmatter.astro'; + +`@sheepdog/svelte` is a library that aims to solve a problem that every developer will face at one +point or another: handling async and concurrent tasks. + +`@sheepdog/svelte` supplies a simple way to introduce cancellable concurrency into your app. Not +only do we provide the "cancelability" that is missing from normal Promises, `@sheepdog/svelte` also +provides a public API that allows you to observe the running state of your task without having to +set a single flag manually. + +Tasks that live on components are automatically canceled when their context is destroyed, meaning +you don't need to worry about the clean up - we've got you covered. + +Choose whether you want to keep the oldest, keep the newest or keep all instances of your task to +help boost the performance of your app and reduce unnecessary server load. + +## Credits + +The work on `@sheepdog/svelte` is sponsored by [Mainmatter](https://mainmatter.com) + + diff --git a/apps/docs/src/content/docs/vanilla/reference/default.mdx b/apps/docs/src/content/docs/vanilla/reference/default.mdx new file mode 100644 index 00000000..40249b57 --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/reference/default.mdx @@ -0,0 +1,239 @@ +--- +title: Default task +description: the task function +--- + +import { LinkCard, Tabs, TabItem, Aside } from '@astrojs/starlight/components'; + +This is the way to create a default task. + +There's no concurrency management in this task and every `perform` will be executed immediately. + +## Usage + +To specify a default task, you can either use the dot notation or the options notation. + + + + + + + +```svelte + +``` + + + + + +```svelte + +``` + + + + + +### The task store + +The return value of the task function will be a svelte store where you can access state from all the +instances running and eventually cancel them with `cancelAll`. + + + +### Passing props + +While defining a task, if the function that you pass in has some arguments, those will be required +by the `perform` function (and it will be strongly typed too). + + + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + + +### Getting the return value + +If you return something from your task you can access the return value by awaiting the `perform` +function. + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + + +### Getting the `TaskInstance` + +If you don't await the `perform` function, then you'll get back the +[task instance](/reference/task-instance) that you can use either to cancel it or to get its current +state. The `TaskInstance` is also a svelte store and you can access the current value with +`instance.get()`. + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + + + diff --git a/apps/docs/src/content/docs/vanilla/reference/drop.mdx b/apps/docs/src/content/docs/vanilla/reference/drop.mdx new file mode 100644 index 00000000..f043c158 --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/reference/drop.mdx @@ -0,0 +1,273 @@ +--- +title: Drop task +description: the Drop task function +--- + +import { LinkCard, Tabs, TabItem, Aside } from '@astrojs/starlight/components'; + +This is the way to create a droppable task. + +This will cancel any new instances of the task. You can also provide a `max` that will only drop the +task instances if the threshold is exceeded. + +## Usage + +To specify a task as droppable, you can either use the dot notation or the options notation. + + + + + +```svelte + +``` + + + + + +```svelte + +``` + + + + + +### Max concurrency + +This is how you can specify the maximum number of concurrent instances. The default is 1, here we're +setting it to 5. + + + + + +```svelte + +``` + + + + + +```svelte + +``` + + + + + +### The task store + +The return value of the task function will be a svelte store where you can access state from all the +instances running and eventually cancel them with `cancelAll`. + + + +### Passing props + +While defining a task, if the function that you pass in has some arguments, those will be required +by the `perform` function (and it will be strongly typed too). + + + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + + +### Getting the return value + +If you return something from your task you can access the return value by awaiting the `perform` +function. + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + + +### Getting the `TaskInstance` + +If you don't await the `perform` function, then you'll get back the +[task instance](/reference/task-instance) that you can use either to cancel it or to get its current +state. The `TaskInstance` is also a svelte store and you can access the current value with +`instance.get()`. + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + diff --git a/apps/docs/src/content/docs/vanilla/reference/enqueue.mdx b/apps/docs/src/content/docs/vanilla/reference/enqueue.mdx new file mode 100644 index 00000000..7cf61ef5 --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/reference/enqueue.mdx @@ -0,0 +1,273 @@ +--- +title: Enqueue task +description: the Enqueue task function +--- + +import { LinkCard, Tabs, TabItem, Aside } from '@astrojs/starlight/components'; + +This is the way to create an enqueue-able task. + +This will add all task instances to a list and each task will be run in order. You can also provide +a `max` that will dictate the number of task instances that will run at the same time. + +## Usage + +To specify a task as enqueue-able, you can either use the dot notation or the options notation. + + + + + +```svelte + +``` + + + + + +```svelte + +``` + + + + + +### Max concurrency + +This is how you can specify the maximum number of concurrent instances. The default is 1, here we're +setting it to 5. + + + + + +```svelte + +``` + + + + + +```svelte + +``` + + + + + +### The task store + +The return value of the task function will be a svelte store where you can access state from all the +instances running and eventually cancel them with `cancelAll`. + + + +### Passing props + +While defining a task, if the function that you pass in has some arguments, those will be required +by the `perform` function (and it will be strongly typed too). + + + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + + +### Getting the return value + +If you return something from your task you can access the return value by awaiting the `perform` +function. + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + + +### Getting the `TaskInstance` + +If you don't await the `perform` function, then you'll get back the +[task instance](/reference/task-instance) that you can use either to cancel it or to get its current +state. The `TaskInstance` is also a svelte store and you can access the current value with +`instance.get()`. + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + diff --git a/apps/docs/src/content/docs/vanilla/reference/keep-latest.mdx b/apps/docs/src/content/docs/vanilla/reference/keep-latest.mdx new file mode 100644 index 00000000..e91db00c --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/reference/keep-latest.mdx @@ -0,0 +1,281 @@ +--- +title: KeepLatest task +description: the KeepLatest task function +--- + +import { LinkCard, Tabs, TabItem, Aside } from '@astrojs/starlight/components'; + +This is the way to create a keepLatest task. + +This will run the initial tasks and then, if over the `max` concurrency will keep the very last +instance in a que. You can also provide a `max` that will dictate the number of task instances that +will run initially. + + + +## Usage + +To specify a task as keepLatest, you can either use the dot notation or the options notation. + + + + + +```svelte + +``` + + + + + +```svelte + +``` + + + + + +### Max concurrency + +This is how you can specify the maximum number of concurrent instances. The default is 1, here we're +setting it to 5. + + + + + + + +```svelte + +``` + + + + + +```svelte + +``` + + + + + +### The task store + +The return value of the task function will be a svelte store where you can access state from all the +instances running and eventually cancel them with `cancelAll`. + + + +### Passing props + +While defining a task, if the function that you pass in has some arguments, those will be required +by the `perform` function (and it will be strongly typed too). + + + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + + +### Getting the return value + +If you return something from your task you can access the return value by awaiting the `perform` +function. + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + + +### Getting the `TaskInstance` + +If you don't await the `perform` function, then you'll get back the +[task instance](/reference/task-instance) that you can use either to cancel it or to get its current +state. The `TaskInstance` is also a svelte store and you can access the current value with +`instance.get()`. + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + diff --git a/apps/docs/src/content/docs/vanilla/reference/restart.mdx b/apps/docs/src/content/docs/vanilla/reference/restart.mdx new file mode 100644 index 00000000..94015bc0 --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/reference/restart.mdx @@ -0,0 +1,279 @@ +--- +title: Restart task +description: the Restart task function +--- + +import { LinkCard, Tabs, TabItem, Aside } from '@astrojs/starlight/components'; + +This is the way to create a restartable task. + +This will cancel the oldest instance of the task and start a new instance of it. You can also +provide a `max` that will only restart the oldest task instance if the threshold is exceeded. + +## Usage + +To specify a task as restartable, you can either use the dot notation or the options notation. + + + + + +```svelte + +``` + + + + + +```svelte + +``` + + + + + + + +### Max concurrency + +This is how you can specify the maximum number of concurrent instances. The default is 1, here we're +setting it to 5. + + + + + +```svelte + +``` + + + + + +```svelte + +``` + + + + + +### The task store + +The return value of the task function will be a svelte store where you can access state from all the +instances running and eventually cancel them with `cancelAll`. + + + +### Passing props + +While defining a task, if the function that you pass in has some arguments, those will be required +by the `perform` function (and it will be strongly typed too). + + + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + + +### Getting the return value + +If you return something from your task you can access the return value by awaiting the `perform` +function. + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + + +### Getting the `TaskInstance` + +If you don't await the `perform` function, then you'll get back the +[task instance](/reference/task-instance) that you can use either to cancel it or to get its current +state. The `TaskInstance` is also a svelte store and you can access the current value with +`instance.get()`. + + + + + +```svelte + + + +``` + + + + + +```svelte + + + +``` + + + + diff --git a/apps/docs/src/content/docs/vanilla/reference/sheepdog-utils.mdx b/apps/docs/src/content/docs/vanilla/reference/sheepdog-utils.mdx new file mode 100644 index 00000000..1564bb12 --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/reference/sheepdog-utils.mdx @@ -0,0 +1,30 @@ +--- +title: Sheepdog Utils +description: the utilities object that is available in every task +--- + +When creating a task (of any type) there are 2 possible arguments that can be accessed, the first is any argument that is passed in when calling `.perform()`, and the second is an object that can be used to optionally enhance your task - `SheepdogUtils`. + +`SheepdogUtils` offers two properties to extend your task with: +- `signal` - the `AbortSignal` is available so that you can do any further manipulation you need based on the state of the `AbortSignal`. We won't go into too much detail about this as it is the standard `AbortSignal` interface that is simply passed back from `Sheepdog`. +- `link` - this is a `Sheepdog` specific function that allows you to bind tasks together. For more information, check out the [Linking Tasks explainer](/explainer/linking-tasks). + +```svelte + +``` + +```ts +type SheepdogUtils = { + signal: AbortSignal; + link: (task: Task) => Task; +}; +``` + + + diff --git a/apps/docs/src/content/docs/vanilla/reference/task-instance.mdx b/apps/docs/src/content/docs/vanilla/reference/task-instance.mdx new file mode 100644 index 00000000..137b8ce0 --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/reference/task-instance.mdx @@ -0,0 +1,108 @@ +--- +title: Task Instance +description: the underlying structure of every task instance +--- + +### Structure + +To standardize the approach regardless of which task modifier you're using, every task modifier will use the same underlying `TaskInstance` structure inside of a svelte store. + +A `TaskInstance` comprises of: +- `error`: if an error is thrown inside the task instance, it will be found here +- `hasStarted`: it is possible for the task instance to be in a queue, waiting to start. This property will change to `true` the instant the task instance has started +- `isCanceled`: whether the task instance was canceled +- `isError`: whether the task instance throw an error before completing +- `isRunning`: whether the task instance is currently running +- `isSuccessful`: whether the task instance completed successfully +- `value`: if the task instance completed successfully, this will be the return value + +And for those of you that prefer to read code, here is the typing of the `TaskInstance`: +```ts +type TaskInstance = { + /** + * If an error is thrown inside the task instance, it will be found here. + */ + error?: Error; + + /** + * Indicates whether the task instance has started. + */ + hasStarted: boolean; + + /** + * Indicates whether the task instance was canceled. + */ + isCanceled: boolean; + + /** + * Indicates whether the task instance threw an error before completing. + */ + isError: boolean; + + /** + * Indicates whether the task instance is currently running. + */ + isRunning: boolean; + + /** + * Indicates whether the task instance completed successfully. + */ + isSuccessful: boolean; + + /** + * If the task instance completed successfully, this will be the return value. + */ + value?: TReturn; +}; +``` + +You can access these properties just as you would with any other Svelte store: + +```svelte + + + + + {#if $lastInstance.isRunning} + Last instance is running! + {/if} + + +``` + +### Cancellation + +Each task instance is also packaged with a `cancel` function that can be used to cancel itself. + +```svelte + + + + + +``` \ No newline at end of file diff --git a/apps/docs/src/content/docs/vanilla/reference/transform.mdx b/apps/docs/src/content/docs/vanilla/reference/transform.mdx new file mode 100644 index 00000000..c2e45cc3 --- /dev/null +++ b/apps/docs/src/content/docs/vanilla/reference/transform.mdx @@ -0,0 +1,43 @@ +--- +title: Transform +description: the transform function +--- + +import { LinkCard, Tabs, TabItem, Aside } from '@astrojs/starlight/components'; + +This function allows you to mark a function that is not directly passed to the `task` function as +transformable from the Async Transform. + + + +## Usage + +You can import this function from `@sheepdog/svelte/utils` and call it with the same first argument +you would pass to the `task` function + +```ts +import { transform } from '@sheepdog/svelte/utils'; + +const myTask = transform(async (id: number) => { + const res = await fetch(`/products/${id}`); + return await res.json(); +}); +``` + +and this is really all you need to do. If you added the Async Transform vite plugin this function +will be transformed and the `transform` import will be removed + +```ts +const myTask = async function* (id: number) { + const res = yield fetch(`/products/${id}`); + return yield res.json(); +}; +``` + +If by any chance you forgot to add the vite plugin the function will just return the function as is +but it will throw an error at runtime (only in dev mode) to let you know that you need to include +the vite plugin to actually transform the function. From 39147af36469fe0f8acd02b973c0622c0712627a Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Fri, 7 Mar 2025 14:43:16 +0100 Subject: [PATCH 02/10] fix: specify all redirects --- apps/docs/astro.config.mjs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/docs/astro.config.mjs b/apps/docs/astro.config.mjs index 30ade68d..316aa75f 100644 --- a/apps/docs/astro.config.mjs +++ b/apps/docs/astro.config.mjs @@ -144,8 +144,21 @@ export default defineConfig({ output: 'static', adapter: netlify({}), redirects: { - '/explainers/[...all]': '/svelte/explainers/[...all]', - '/getting-started/[...all]': '/svelte/getting-started/[...all]', + '/explainers/async-transform': '/svelte/explainers/async-transform', + '/explainers/linking-tasks': '/svelte/explainers/linking-tasks', + '/explainers/mid-run-cancellation': '/svelte/explainers/mid-run-cancellation', + '/explainers/task-modifiers': '/svelte/explainers/task-modifiers', + '/getting-started/installation': '/svelte/getting-started/installation', + '/getting-started/usage': '/svelte/getting-started/usage', + '/getting-started/what-is-it': '/svelte/getting-started/what-is-it', '/reference/[...all]': '/svelte/reference/[...all]', + '/reference/default': '/svelte/reference/default', + '/reference/drop': '/svelte/reference/drop', + '/reference/enqueue': '/svelte/reference/enqueue', + '/reference/keep-latest': '/svelte/reference/keep-latest', + '/reference/restart': '/svelte/reference/restart', + '/reference/sheepdog-utils': '/svelte/reference/sheepdog-utils', + '/reference/task-instance': '/svelte/reference/task-instance', + '/reference/transform': '/svelte/reference/transform', }, }); From 8b7c3e365b034636c6a6f6ab886d26f07354691d Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Fri, 7 Mar 2025 15:58:39 +0100 Subject: [PATCH 03/10] docs: update landing and sidebar/title --- apps/docs/astro.config.mjs | 4 ++- apps/docs/src/components/Footer.astro | 3 +- apps/docs/src/components/Hero.astro | 43 +++++++++++++++++++++++- apps/docs/src/components/SiteTitle.astro | 7 ++-- apps/docs/src/env.d.ts | 9 +++++ apps/docs/src/route-data.ts | 24 +++++++++++++ apps/docs/src/utils/is-homepage.ts | 3 ++ apps/docs/tsconfig.json | 9 ++++- 8 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 apps/docs/src/route-data.ts create mode 100644 apps/docs/src/utils/is-homepage.ts diff --git a/apps/docs/astro.config.mjs b/apps/docs/astro.config.mjs index 316aa75f..2f66a9a3 100644 --- a/apps/docs/astro.config.mjs +++ b/apps/docs/astro.config.mjs @@ -13,17 +13,19 @@ export default defineConfig({ alias: { '@assets': path.resolve(process.cwd(), './src/assets'), '@components': path.resolve(process.cwd(), './src/components'), + '@utils': path.resolve(process.cwd(), './src/utils'), }, }, }, integrations: [ starlight({ + routeMiddleware: './src/route-data.ts', components: { SiteTitle: './src/components/SiteTitle.astro', Hero: './src/components/Hero.astro', Footer: './src/components/Footer.astro', }, - title: '@sheepdog/svelte', + title: '@sheepdog', expressiveCode: { themes: ['github-dark-default', 'github-light-default'], }, diff --git a/apps/docs/src/components/Footer.astro b/apps/docs/src/components/Footer.astro index 4df5372a..51a9cd31 100644 --- a/apps/docs/src/components/Footer.astro +++ b/apps/docs/src/components/Footer.astro @@ -1,8 +1,9 @@ --- import Default from '@astrojs/starlight/components/Footer.astro'; import MainmatterLogo from '../assets/Mainmatter.astro'; +import { is_homepage } from '@utils/is-homepage'; -const isHomepage = Astro.locals.starlightRoute.id === ''; +const isHomepage = is_homepage(Astro.locals.starlightRoute.id); --- { diff --git a/apps/docs/src/components/Hero.astro b/apps/docs/src/components/Hero.astro index a279a455..2fe2a254 100644 --- a/apps/docs/src/components/Hero.astro +++ b/apps/docs/src/components/Hero.astro @@ -65,6 +65,40 @@ if (image) { isWoofed = !isWoofed; }), ); + + const package_span = document.getElementById('package'); + + if (package_span) { + const packages = package_span?.dataset.packages?.split(',') ?? []; + + let current_package = 0; + let current_wrote = 0; + let deleting = false; + + function typewrite_package() { + if (packages[current_package]) { + let time = 100; + if (current_wrote < packages[current_package].length && !deleting) { + current_wrote++; + package_span!.innerHTML = packages[current_package].substring(0, current_wrote); + if (current_wrote === packages[current_package].length) { + deleting = true; + time = 2000; + } + } else if (current_wrote > 0) { + current_wrote--; + package_span!.innerHTML = packages[current_package].substring(0, current_wrote); + if (current_wrote === 0) { + deleting = false; + current_package = (current_package + 1) % packages.length; + } + } + setTimeout(typewrite_package, time); + } + } + + setTimeout(typewrite_package, 100); + }
@@ -82,7 +116,14 @@ if (image) { {rawHtml &&
}
-

@sheepdog/svelte

+

+ {Astro.locals.starlightRoute.siteTitle} `/${pkg}`) + .join(',')} + id="package"> +

{tagline &&
}
{ diff --git a/apps/docs/src/components/SiteTitle.astro b/apps/docs/src/components/SiteTitle.astro index 197156e0..fdadc455 100644 --- a/apps/docs/src/components/SiteTitle.astro +++ b/apps/docs/src/components/SiteTitle.astro @@ -1,8 +1,9 @@ --- import Default from '@astrojs/starlight/components/SiteTitle.astro'; import BuiltBy from '../assets/BuiltBy.astro'; +import { is_homepage } from '@utils/is-homepage.ts'; -const isHomepage = Astro.locals.starlightRoute.id === ''; +const isHomepage = is_homepage(Astro.locals.starlightRoute.id); --- { @@ -12,9 +13,7 @@ const isHomepage = Astro.locals.starlightRoute.id === ''; built by Mainmatter
) : ( - - - + ) } diff --git a/apps/docs/src/env.d.ts b/apps/docs/src/env.d.ts index 9c03f0a2..bec478a2 100644 --- a/apps/docs/src/env.d.ts +++ b/apps/docs/src/env.d.ts @@ -1,2 +1,11 @@ /// /// +declare global { + namespace App { + export interface Locals { + available_packages: string[]; + } + } +} + +export {}; diff --git a/apps/docs/src/route-data.ts b/apps/docs/src/route-data.ts new file mode 100644 index 00000000..afd6b539 --- /dev/null +++ b/apps/docs/src/route-data.ts @@ -0,0 +1,24 @@ +import { defineRouteMiddleware } from '@astrojs/starlight/route-data'; +import { is_homepage } from '@utils/is-homepage'; + +const available_packages = ['svelte', 'vanilla']; + +export const onRequest = defineRouteMiddleware((context) => { + const { sidebar, id } = context.locals.starlightRoute; + context.locals.available_packages = available_packages; + const current_package = available_packages.find((pkg) => id.startsWith(pkg)) ?? 'svelte'; + function update_sidebar(sidebar_part: typeof sidebar) { + for (let element of sidebar_part) { + if (element.type === 'group') { + update_sidebar(element.entries); + continue; + } + element.href = `/${current_package}${element.href}`; + } + } + context.locals.starlightRoute.siteTitle = is_homepage(id) + ? '@sheepdog' + : `@sheepdog/${current_package}`; + + update_sidebar(sidebar); +}); diff --git a/apps/docs/src/utils/is-homepage.ts b/apps/docs/src/utils/is-homepage.ts new file mode 100644 index 00000000..f28c8875 --- /dev/null +++ b/apps/docs/src/utils/is-homepage.ts @@ -0,0 +1,3 @@ +export function is_homepage(route: string) { + return route === 'index.mdx'; +} diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json index a3f69810..45e7b69a 100644 --- a/apps/docs/tsconfig.json +++ b/apps/docs/tsconfig.json @@ -1,3 +1,10 @@ { - "extends": "astro/tsconfigs/strict" + "extends": "astro/tsconfigs/strict", + "compilerOptions": { + "paths": { + "@utils/*": ["./src/utils/*"], + "@assets/*": ["./src/assets/*"], + "@components/*": ["./src/components/*"] + } + } } From 3e3ea95c35991d02b01505084fcebb7232ab80b8 Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Fri, 21 Mar 2025 16:08:56 +0100 Subject: [PATCH 04/10] chore: update explainers and getting started --- apps/docs/astro.config.mjs | 1 + .../vanilla/explainers/async-transform.mdx | 194 +++++++------- .../docs/vanilla/explainers/linking-tasks.mdx | 96 ++++--- .../explainers/mid-run-cancellation.mdx | 238 ++++++++---------- .../vanilla/explainers/task-modifiers.mdx | 154 +++++------- .../vanilla/getting-started/installation.mdx | 23 +- .../docs/vanilla/getting-started/usage.mdx | 106 +++++--- .../vanilla/getting-started/what-is-it.mdx | 14 +- 8 files changed, 391 insertions(+), 435 deletions(-) diff --git a/apps/docs/astro.config.mjs b/apps/docs/astro.config.mjs index 2f66a9a3..241a5e0d 100644 --- a/apps/docs/astro.config.mjs +++ b/apps/docs/astro.config.mjs @@ -62,6 +62,7 @@ export default defineConfig({ editLink: { baseUrl: 'https://github.com/mainmatter/sheepdog/edit/main', }, + // the sidebar is updated in `route-data.ts` to prepend the correct package name sidebar: [ { label: 'Getting started', diff --git a/apps/docs/src/content/docs/vanilla/explainers/async-transform.mdx b/apps/docs/src/content/docs/vanilla/explainers/async-transform.mdx index 0fd8fd1a..e9f25578 100644 --- a/apps/docs/src/content/docs/vanilla/explainers/async-transform.mdx +++ b/apps/docs/src/content/docs/vanilla/explainers/async-transform.mdx @@ -11,7 +11,7 @@ confident in using it. If you want to know how to setup the Vite plugin please g link. @@ -87,8 +87,8 @@ The reason we are writing this explainer is because we firmly believe that if yo be changed by some script you should fully understand how it changes it and what the final result will look like. -As shown in our [Mid run cancellation](/explainers/mid-run-cancellation) explainer, this Vite plugin -aims to solve a couple of problems with async code in Typescript: +As shown in our [Mid run cancellation](/vanilla/explainers/mid-run-cancellation) explainer, this +Vite plugin aims to solve a couple of problems with async code in Typescript: - Promises are not cancelable: once you start them there's no way to stop the code in the middle of the function from executing. @@ -102,46 +102,34 @@ Vite plugin to transform your async functions into generators! -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const myTask = task(async () => { + const result = await fetch('/api/my-endpoint'); + return await result.json(); +}); + +button.addEventListener('click', () => { + myTask.perform(); +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const myTask = task(async function* () { + const result = yield fetch('/api/my-endpoint'); + return yield result.json(); +}); + +button.addEventListener('click', () => { + myTask.perform(); +}); ``` @@ -163,50 +151,46 @@ will be left untouched. -```svelte - +// this will remain untouched +const otherTask = task(async () => { + // some async stuff +}); + +// this will be transformed +const myTask = sheepdogTask(async () => { + const result = await fetch('/api/my-endpoint'); + return await result.json(); +}); ``` -```svelte - +// this will be transformed +const myTask = sheepdogTask(async function* () { + const result = yield fetch('/api/my-endpoint'); + return yield result.json(); +}); ``` @@ -221,44 +205,40 @@ and the same works even for imported functions with the same name. {/* prettier-ignore-start */} -```svelte - +```ts +import { task as sheepdogTask } from '@sheepdog/vanilla'; +import { task } from 'random-npm-library'; + +// this will remain untouched +const otherTask = task(async () => { + // some async stuff +}); + +// this will be transformed +const myTask = sheepdogTask(async () => { + const result = await fetch('/api/my-endpoint'); + return await result.json(); +}); ``` -```svelte - +```ts +import { task as sheepdogTask } from '@sheepdog/vanilla'; +import { task } from 'random-npm-library'; + +// this will remain untouched +const otherTask = task(async () => { + // some async stuff +}); + +// this will be transformed +const myTask = sheepdogTask(async function* () { + const result = yield fetch('/api/my-endpoint'); + return yield result.json(); +}); ``` @@ -270,9 +250,9 @@ want to have your tasks in a separate ts file. Well good news for you... ## The `transform` function -If you want to tell `@sheepdog/svelte` that a function is meant to be transformed because you will +If you want to tell `@sheepdog/vanilla` that a function is meant to be transformed because you will pass that function to a task you can do so by using the `transform` function exported from -`@sheepdog/svelte/utils`. +`@sheepdog/vanilla/utils`. This function doesn't do anything at runtime but it serves to purpose: @@ -288,7 +268,7 @@ Here's how a transformed function will look like ```ts -import { transform } from '@sheepdog/svelte/utils'; +import { transform } from '@sheepdog/vanilla/utils'; // this will be converted const myTask = transform(async () => { @@ -321,7 +301,7 @@ rename your import. ```ts -import { transform as sheepdogTransform } from '@sheepdog/svelte/utils'; +import { transform as sheepdogTransform } from '@sheepdog/vanilla/utils'; import { transform } from 'random-npm-library'; // this will remain untouched diff --git a/apps/docs/src/content/docs/vanilla/explainers/linking-tasks.mdx b/apps/docs/src/content/docs/vanilla/explainers/linking-tasks.mdx index 3040f6e2..a7efc01a 100644 --- a/apps/docs/src/content/docs/vanilla/explainers/linking-tasks.mdx +++ b/apps/docs/src/content/docs/vanilla/explainers/linking-tasks.mdx @@ -5,38 +5,48 @@ description: How to bind tasks together for simpler cancellation ### Motivation -Breaking up asynchronous calls into bitesize chunks makes a lot of sense: it's easier to reason about, it allows us to show different states depending on what stage of the call we are in, and it helps us organize our code better. - -But the biggest issue this exposes that it is very difficult to cancel all children if the parent is cancelled. - -Imagine this scenario: we're creating a blog feed widget that will get all of the user's posts and comments and show them in a small box somewhere on the page. We might have something like this to load all of those posts and comments: - -```js -async function fetch(){ - // make first call - const user = await fetchUser() - // make second call based on first response - const posts = await fetchUserPosts(user) - // make third call based on first and second responses - return await fetchPostComments({user, posts}) +Breaking up asynchronous calls into bitesize chunks makes a lot of sense: it's easier to reason +about, it allows us to show different states depending on what stage of the call we are in, and it +helps us organize our code better. + +But the biggest issue this exposes that it is very difficult to cancel all children if the parent is +cancelled. + +Imagine this scenario: we're creating a blog feed widget that will get all of the user's posts and +comments and show them in a small box somewhere on the page. We might have something like this to +load all of those posts and comments: + +```js +async function fetch() { + // make first call + const user = await fetchUser(); + // make second call based on first response + const posts = await fetchUserPosts(user); + // make third call based on first and second responses + return await fetchPostComments({ user, posts }); } -async function fetchUser(){ - // returns user stuff +async function fetchUser() { + // returns user stuff } -async function fetchUserPosts(user){ - // returns user's posts +async function fetchUserPosts(user) { + // returns user's posts } -async function fetchPostComments({user, posts}){ - // returns posts' comments +async function fetchPostComments({ user, posts }) { + // returns posts' comments } ``` -Now this is great, it's been neatly enclosed in a component so that the rest of the page doesn't need to be blocked while waiting for this widget to load. We could also include some logic to enable us to show which call is currently being run. +Now this is great, it's been neatly enclosed in a component so that the rest of the page doesn't +need to be blocked while waiting for this widget to load. We could also include some logic to enable +us to show which call is currently being run. -But what would happen if the user navigated away or destroyed between the initial call and the final result? Our API would still receive those requests and go and do all of the work to get the data and return it but we are no longer using the returned data or even have a reference to it in our app. This kind of scenario can very easily cause data leaks that can gradually bring our app down. +But what would happen if the user navigated away or destroyed between the initial call and the final +result? Our API would still receive those requests and go and do all of the work to get the data and +return it but we are no longer using the returned data or even have a reference to it in our app. +This kind of scenario can very easily cause data leaks that can gradually bring our app down. _If only there was a way to link these calls together._ @@ -44,35 +54,39 @@ _If only there was a way to link these calls together._ Well, we heard your cries and created the `link` function. -`Link` is one of two [SheepdogUtils](/reference/sheepdog-utils) and it enables us to link a child task to its parent so that the lifecycle of the child task is directly bound to the lifecycle of its parent. That means that if the parent is canceled, the child is automatically aborted. +`Link` is one of two [SheepdogUtils](/vanilla/reference/sheepdog-utils) and it enables us to link a +child task to its parent so that the lifecycle of the child task is directly bound to the lifecycle +of its parent. That means that if the parent is canceled, the child is automatically aborted. Turning our above scenario into tasks would look like this: -```js -import { task } from "@sheepdog/svelte" +```js +import { task } from '@sheepdog/vanilla'; fetch = task(async (_, { link }) => { - // make first call - const user = await link(fetchUser).perform(); - // make second call based on first response - const posts = await link(fetchUserPosts).perform(user) - // make third call based on first and second responses - return await link(fetchPostComments).perform({user, posts}) -}) + // make first call + const user = await link(fetchUser).perform(); + // make second call based on first response + const posts = await link(fetchUserPosts).perform(user); + // make third call based on first and second responses + return await link(fetchPostComments).perform({ user, posts }); +}); fetchUser = task(async () => { - // returns user stuff -}) + // returns user stuff +}); fetchUserPosts = task(async (user) => { - // returns user's posts -}) + // returns user's posts +}); -fetchPostComments = task(async ({user, posts}) => { - // returns posts' comments -}) +fetchPostComments = task(async ({ user, posts }) => { + // returns posts' comments +}); ``` -And now all of our child tasks are bound to the parent context, meaning if the context that the parent task lives on is destroyed, all of the other tasks will be cancelled. +And now all of our child tasks are bound to the parent context, meaning if the context that the +parent task lives on is destroyed, all of the other tasks will be cancelled. -Another added benefit of this is that we already have the different loading states out of the box, we could simply see which task is running and be able to show each loading state individually. \ No newline at end of file +Another added benefit of this is that we already have the different loading states out of the box, +we could simply see which task is running and be able to show each loading state individually. diff --git a/apps/docs/src/content/docs/vanilla/explainers/mid-run-cancellation.mdx b/apps/docs/src/content/docs/vanilla/explainers/mid-run-cancellation.mdx index c76352b7..2c2abdb7 100644 --- a/apps/docs/src/content/docs/vanilla/explainers/mid-run-cancellation.mdx +++ b/apps/docs/src/content/docs/vanilla/explainers/mid-run-cancellation.mdx @@ -6,9 +6,9 @@ description: How to handle the cancellation of a task mid execution import { LinkCard, Tabs, TabItem, Aside } from '@astrojs/starlight/components'; import BlankLink from '@components/BlankLink.astro'; -As we hinted in the [What is it?](/getting-started/what-is-it/) explainer, one of the advantages of -`@sheepdog/svelte` is that it allows you to really "cancel" a task. Let's look at the actual problem -and how it is solved with `@sheepdog/svelte`. +As we hinted in the [What is it?](/vanilla/getting-started/what-is-it/) explainer, one of the +advantages of `@sheepdog/vanilla` is that it allows you to really "cancel" a task. Let's look at the +actual problem and how it is solved with `@sheepdog/vanilla`. ## The problem @@ -41,68 +41,53 @@ async function fetchALotOfStuff() { } ``` -This is especially true if we are invoking those functions within a UI framework because we tend to -assign values outside of the scope of the function to reactively show them in the UI. +This can be hard to wrap your head around especially if you directly manipulate the dom after the +fetch call. -```svelte - - - +} -
    - {#each list as element} -
  • {element}
  • - {/each} -
+button.addEventListener('click', fetchList); ``` The simplest way to solve this problem is to set up a variable and check it after the fetch. -```svelte - - - - - -
    - {#each list as element} -
  • {element}
  • - {/each} -
+} + +button.addEventListener('click', fetchList); +cancelButton.addEventListener('click', () => { + canceled = true; +}); ``` This works fine but it gets tedious pretty fast, especially if you need to do it multiple times. -That's where `@sheepdog/svelte` comes into play. +That's where `@sheepdog/vanilla` comes into play. ## The solution(s) -`@sheepdog/svelte` provides you multiple tools to solve this problem (hence the parenthesized plural -in the title of this section); let's go over them one by one +`@sheepdog/vanilla` provides you multiple tools to solve this problem (hence the parenthesized +plural in the title of this section); let's go over them one by one @@ -110,46 +95,35 @@ in the title of this section); let's go over them one by one ### Solution 1: `AbortSignal` The simplest but more verbose solution to this problem is to use `AbortSignal` solution: -`@sheepdog/svelte` invokes your task with a series of utils, one of which is the `AbortSignal`. +`@sheepdog/vanilla` invokes your task with a series of utils, one of which is the `AbortSignal`. Every task has its own `AbortController` and you can cancel a single task instance by invoking the `cancel` method on it or cancel **every** instance by invoking the `cancelAll` method on the task. -```svelte - - - - - - -
    - {#each list as element} -
  • {element}
  • - {/each} -
+```ts +import { task } from '@sheepdog/vanilla'; + +let list; +let lastInstance; + +const fetchTask = task((_, { signal }) => { + // we can pass the signal to fetch to potentially abort the in-flight request + const response = await fetch('/api/very-long-list', { signal }); + const list = await response.json(); + for (let element of list) { + document.getElementById('my-list').appendChild(element); + } +}); + +button.addEventListener('click', () => { + lastInstance = fetchTask.perform(); +}); +cancelButton.addEventListener('click', () => { + lastInstance.cancel(); +}); + +cancelAllButton.addEventListener('click', () => { + fetchTask.cancelAll(); +}); ``` We've gained the ability to stop in-flight fetches with the `AbortSignal` without having to create a @@ -185,11 +159,11 @@ generator.next('ping'); ``` I know, I told you this wouldn't be scary and for the moment I haven't kept my promise (pun -intended). But the main takeaway from this snippet of code is that generator functions have -a way to stop executing and return something to the caller and the caller has a way to communicate -something back. +intended). But the main takeaway from this snippet of code is that generator functions have a way to +stop executing and return something to the caller and the caller has a way to communicate something +back. -`@sheepdog/svelte` has been built to be able to accept an async generator function and, most +`@sheepdog/vanilla` has been built to be able to accept an async generator function and, most importantly, has been built to make the generator function work basically like a normal async function if you replace `await` with `yield`. Let's take a look @@ -220,16 +194,14 @@ const myTask = task(async (_, { signal }) => { -As you can see, the code in the two tabs changes very little but with generators -`@sheepdog/svelte` has the ability to never call `next` if the task was canceled. This means that -if you cancel the task while fetch is still in-flight the second line of the function will -**never** be called! +As you can see, the code in the two tabs changes very little but with generators `@sheepdog/vanilla` +has the ability to never call `next` if the task was canceled. This means that if you cancel the +task while fetch is still in-flight the second line of the function will **never** be called! There is one small detail we have hidden from you, however: `yield` doesn't work very well with -TypeScript, especially if there are multiple of them. If you try to paste that code in a `.ts` file -(or in a Svelte component with ` - - - - - -
    - {#each list as element} -
  • {element}
  • - {/each} -
+`@sheepdog/vanilla` will be able to cancel every task, even in the middle of an execution! + +```ts +import { task } from '@sheepdog/vanilla'; + +let list; + +const fetchTask = task((_, { signal }) => { + const response = await fetch('/api/very-long-list', { signal }); + // this line will never be executed if the task is canceled before fetch ends + const list = await response.json(); + for (let element of list) { + document.getElementById('my-list').appendChild(element); + } +}); + +let lastInstance; + +button.addEventListener('click', () => { + lastInstance = fetchTask.perform(); +}); +cancelButton.addEventListener('click', () => { + lastInstance.cancel(); +}); + +cancelAllButton.addEventListener('click', () => { + fetchTask.cancelAll(); +}); ``` diff --git a/apps/docs/src/content/docs/vanilla/explainers/task-modifiers.mdx b/apps/docs/src/content/docs/vanilla/explainers/task-modifiers.mdx index 681a51e8..5653ac24 100644 --- a/apps/docs/src/content/docs/vanilla/explainers/task-modifiers.mdx +++ b/apps/docs/src/content/docs/vanilla/explainers/task-modifiers.mdx @@ -5,10 +5,10 @@ description: How task modifiers work import Timeline from '../../TimelineExplainer.svelte'; -In [Getting started](/getting-started/usage#task-modifiers), you've seen that you can specify a -modifier on the task, either with the dot notation or the options notation. The reason you have this -ability is so that you can specify what should happen when multiple, concurrent task instances are -running. +In [Getting started](/vanilla/getting-started/usage#task-modifiers), you've seen that you can +specify a modifier on the task, either with the dot notation or the options notation. The reason you +have this ability is so that you can specify what should happen when multiple, concurrent task +instances are running. In this explainer we will guide you through how each of our modifiers work, with examples of how they behave and hopefully make it clearer to understand when to use one or the other. @@ -43,35 +43,28 @@ your files but you don't want to send them all together because that would be to and also makes it harder to handle in case of an error. You can use `enqueue` to allow only `max` file uploads at a time. -```svelte - - - { - files = [...e.target.files]; - }} -/> - +```ts +import { task, timeout } from '@sheepdog/vanilla'; +import upload from './file-upload'; + +let files = []; + +const uploadFile = task.enqueue( + async (file: File) => { + await upload(file); + }, + { max: 3 }, +); + +input.addEventListener('change', (e) => { + files = [...e.target.files]; +}); + +startUpload.addEventListener('click', () => { + for (let file of files) { + uploadFile.perform(file); + } +}); ``` You can play around with it in our Timeline and you may notice a new parameter available for you: @@ -90,23 +83,17 @@ You can think of this kind of task as a sort of throttle; and since the function away you should use this modifier only when you don't care about the "execution loss". A good example could be triggering a very expensive task that you want to make sure is only triggered once. -```svelte - - - +```ts +import { task, timeout } from '@sheepdog/vanilla'; + +const expensiveTask = task.drop(async () => { + await timeout(200); + // do very costly calculations that should only be triggered once at a time. +}); + +button.addEventListener('click', () => { + expensiveTask.perform(); +}); ``` I know you are waiting for it so here's your `drop` Timeline playground: @@ -124,21 +111,17 @@ will be executed only once the `perform` as not been called for 200ms. This is v perform fetch requests while the user is typing without inundating your server with requests. Once the user stops typing the last request will go through without being canceled. -```svelte - - - { - search.perform(e.target.value); - }} -/> +```ts +import { task, timeout } from '@sheepdog/vanilla'; + +const search = task.restart(async (query: string) => { + await timeout(200); + const res = await fetch(`/api/search?q=${query}`); +}); + +input.addEventListener('input', (e) => { + search.perform(e.target.value); +}); ``` And as per usual, here's your `restart` Timeline playground: @@ -163,35 +146,28 @@ or more saves while you are fetching the data you: That's where `keepLatest` will come in handy! -```svelte - - - +```ts +import { ws } from '$lib/websocket'; +import { task } from '@sheepdog/vanilla'; +import { renderDocument } from '$lib/renderer'; + +const loadDocument = task.keepLatest(async () => { + const res = await fetch(`/api/get-document`); + const document = await res.json(); + renderDocument(document); +}); + +loadDocument.perform(); + +ws.on('new-save', () => { + loadDocument.perform(); +}); ``` ## Conclusion -Task modifiers are very powerful and one of the core features of `@sheepdog/svelte`, this explainer +Task modifiers are very powerful and one of the core features of `@sheepdog/vanilla`, this explainer aims to give you all the tools to use them at their best...and some playful time with the timeline example! diff --git a/apps/docs/src/content/docs/vanilla/getting-started/installation.mdx b/apps/docs/src/content/docs/vanilla/getting-started/installation.mdx index 1429719f..6499d153 100644 --- a/apps/docs/src/content/docs/vanilla/getting-started/installation.mdx +++ b/apps/docs/src/content/docs/vanilla/getting-started/installation.mdx @@ -1,46 +1,43 @@ --- title: Installation -description: How to install `@sheepdog/svelte`. +description: How to install `@sheepdog/vanilla`. --- import { Tabs, TabItem, Code, Aside, LinkCard } from '@astrojs/starlight/components'; -As the name suggests `@sheepdog/svelte` is meant to be used in a `svelte` project. - To install, run the following command with your preferred package manager. - + - + - + - + ## Setup the Async Transform The Async Transform is a Vite plugin so to set it up you need to modify your `vite.config.{js|ts}` ```diff lang="ts" title="vite.config.ts" -import { sveltekit } from '@sveltejs/kit/vite'; -+import { asyncTransform } from '@sheepdog/svelte/vite'; ++import { asyncTransform } from '@sheepdog/vanilla/vite'; import { defineConfig } from 'vitest/config'; export default defineConfig(({ mode }) => ({ plugins: [ - sveltekit(), + asyncTransform(), ], // rest of your config @@ -50,4 +47,4 @@ export default defineConfig(({ mode }) => ({ And that's it! You can now use async tasks with proper mid-run cancellation! Remember: the Async Transform will only change your code in some very specific places, read more -about it in [the async transform explainer](/explainers/async-transform). +about it in [the async transform explainer](/vanilla/explainers/async-transform). diff --git a/apps/docs/src/content/docs/vanilla/getting-started/usage.mdx b/apps/docs/src/content/docs/vanilla/getting-started/usage.mdx index bf4d294d..2ea7763f 100644 --- a/apps/docs/src/content/docs/vanilla/getting-started/usage.mdx +++ b/apps/docs/src/content/docs/vanilla/getting-started/usage.mdx @@ -1,36 +1,30 @@ --- title: Usage -description: Basic usage of `@sheepdog/svelte`. +description: Basic usage of `@sheepdog/vanilla`. --- import { Tabs, TabItem, Code, Aside, LinkCard } from '@astrojs/starlight/components'; import BlankLink from '@components/BlankLink.astro'; -The simplest way to use `@sheepdog/svelte` is the following +The simplest way to use `@sheepdog/vanilla` is the following -```svelte - +const myTask = task(async () => { + // async stuff +}); - +button.addEventListener('click', () => { + myTask.perform(); +}); ``` This will run your async function when the user presses the button. _"But why not use a normal async function?"_ I hear you ask. -**Because the `myTask` variable that you used to call `perform` is also a svelte store that contains -a lot of useful information about your task!** +**Because the `myTask` variable that you used to call `perform` is also a special object that +contains a lot of useful information about your task!** ## `Task` structure @@ -43,6 +37,25 @@ a lot of useful information about your task!** - `lastSuccessful`: the last successful task instance - `performCount`: the number of times the task has been run, +it also returns two functions. + +The `on` function that has the same structure of an event listener (so you can pass a signal to it +for example) and allow you to listen for the following events: + +- `start`: emitted when the number of running tasks goes from 0 to at least 1 +- `finish`: emitted when the number of running tasks goes from to 0 +- `instance-create`: emitted when an instance of the task is created (for + [tasks with a modifier](#task-modifiers) the task might be created but not executed immediately) +- `instance-error`: emitted when an instance of the task errors out +- `instance-finish`: emitted when an instance of the task terminates, be it for an error, a cancel, + or a success +- `instance-cancel`: emitted when an instance of the task is canceled +- `instance-start`: emitted when an instance of the task actually starts +- `instance-success`: emitted when an instance of the task completes successfully + +The `destroy` function is used to clean up everything the task has allocated, and cancelling the +task + ```ts type Task = { /** @@ -73,63 +86,76 @@ type Task = { * number of times the task has been run, */ performCount: number; + /** + * a function to add a listener on the task itself, the events you can listen for are: + * + * - start + * - finish + * - instance-create + * - instance-error + * - instance-finish + * - instance-cancel + * - instance-start + * - instance-success + */ + on: (event: Events, cb: () => void, options?: AddEventListenerOptions) => void; + /** + * a function to destroy the task, this will cancel it and clean every resource allocated by the task + */ + destroy: () => void; }; ``` ## `TaskInstance` structure -To make it easier to reason about, every type of task modifier utilizes the same underlying structure. You can find more detail about the structure of the `TaskInstance` in the [reference docs](/reference/task-instance). +To make it easier to reason about, every type of task modifier utilizes the same underlying +structure. You can find more detail about the structure of the `TaskInstance` in the +[reference docs](/vanilla/reference/task-instance). ## Task modifiers -`@sheepdog/svelte` allows you to specify a task modifier that changes the behavior of the perform +`@sheepdog/vanilla` allows you to specify a task modifier that changes the behavior of the perform function. By default when you call `perform` the task will be executed immediately regardless if there are other instances of the same task in execution. For example, you could use the `enqueue` -modifier to instruct `@sheepdog/svelte` to execute the task instances one after another (in a +modifier to instruct `@sheepdog/vanilla` to execute the task instances one after another (in a queue - who would've thought) Here's how you can specify the modifier: - ```svelte ins=".enqueue" {4} - - + button.addEventListener('click', () => { + myTask.perform(); + }); ``` + - ```svelte ins="enqueue" {6} - - + button.addEventListener('click', () => { + myTask.perform(); + }); ``` diff --git a/apps/docs/src/content/docs/vanilla/getting-started/what-is-it.mdx b/apps/docs/src/content/docs/vanilla/getting-started/what-is-it.mdx index 1d512d04..e7dcfef6 100644 --- a/apps/docs/src/content/docs/vanilla/getting-started/what-is-it.mdx +++ b/apps/docs/src/content/docs/vanilla/getting-started/what-is-it.mdx @@ -1,17 +1,17 @@ --- title: What is it? -description: What problem does `@sheepdog/svelte` solves. +description: What problem does `@sheepdog/vanilla` solves. --- import Mainmatter from '@assets/Mainmatter.astro'; -`@sheepdog/svelte` is a library that aims to solve a problem that every developer will face at one +`@sheepdog/vanilla` is a library that aims to solve a problem that every developer will face at one point or another: handling async and concurrent tasks. -`@sheepdog/svelte` supplies a simple way to introduce cancellable concurrency into your app. Not -only do we provide the "cancelability" that is missing from normal Promises, `@sheepdog/svelte` also -provides a public API that allows you to observe the running state of your task without having to -set a single flag manually. +`@sheepdog/vanilla` supplies a simple way to introduce cancellable concurrency into your app. Not +only do we provide the "cancelability" that is missing from normal Promises, `@sheepdog/vanilla` +also provides a public API that allows you to observe the running state of your task without having +to set a single flag manually. Tasks that live on components are automatically canceled when their context is destroyed, meaning you don't need to worry about the clean up - we've got you covered. @@ -21,6 +21,6 @@ help boost the performance of your app and reduce unnecessary server load. ## Credits -The work on `@sheepdog/svelte` is sponsored by [Mainmatter](https://mainmatter.com) +The work on `@sheepdog/vanilla` is sponsored by [Mainmatter](https://mainmatter.com) From 60ac48f1bb85cfd13dfa6d8b807b6c533bd6de19 Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Fri, 21 Mar 2025 17:02:06 +0100 Subject: [PATCH 05/10] docs: all referece except task instance --- .../docs/vanilla/reference/default.mdx | 220 +++++++-------- .../content/docs/vanilla/reference/drop.mdx | 256 ++++++++---------- .../docs/vanilla/reference/enqueue.mdx | 256 ++++++++---------- .../docs/vanilla/reference/keep-latest.mdx | 256 ++++++++---------- .../docs/vanilla/reference/restart.mdx | 256 ++++++++---------- .../docs/vanilla/reference/sheepdog-utils.mdx | 27 +- .../docs/vanilla/reference/transform.mdx | 6 +- 7 files changed, 571 insertions(+), 706 deletions(-) diff --git a/apps/docs/src/content/docs/vanilla/reference/default.mdx b/apps/docs/src/content/docs/vanilla/reference/default.mdx index 40249b57..eaf2f147 100644 --- a/apps/docs/src/content/docs/vanilla/reference/default.mdx +++ b/apps/docs/src/content/docs/vanilla/reference/default.mdx @@ -14,52 +14,48 @@ There's no concurrency management in this task and every `perform` will be execu To specify a default task, you can either use the dot notation or the options notation. -```svelte - +const myTask = task(async () => { + // your code +}); ``` -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const myTask = task( + async () => { + // your code + }, + { kind: 'default' }, +); ``` -### The task store +### The task -The return value of the task function will be a svelte store where you can access state from all the -instances running and eventually cancel them with `cancelAll`. +The return value of the task function will be an object with getters where you can access state from +all the instances running and eventually cancel them with `cancelAll`. @@ -71,50 +67,42 @@ by the `perform` function (and it will be strongly typed too). -```svelte - +const myTask = task(async (id: string) => { + // your code +}); - +button.addEventListener('click', () => { + myTask.perform('42'); +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const myTask = task( + async (id: string) => { + // your code + }, + { kind: 'default' }, +); + +button.addEventListener('click', () => { + myTask.perform('42'); +}); ``` @@ -130,45 +118,37 @@ function. -```svelte - - - +const myTask = task(async () => { + return 42; +}); + +button.addEventListener('click', () => { + const number = await myTask.perform(); + console.log(number); // 42 +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const myTask = task( + async () => { + return 42; + }, + { kind: 'default' }, +); + +button.addEventListener('click', () => { + const number = await myTask.perform(); + console.log(number); // 42 +}); ``` @@ -178,55 +158,51 @@ function. ### Getting the `TaskInstance` If you don't await the `perform` function, then you'll get back the -[task instance](/reference/task-instance) that you can use either to cancel it or to get its current -state. The `TaskInstance` is also a svelte store and you can access the current value with -`instance.get()`. +[task instance](/vanilla/reference/task-instance) that you can use either to cancel it or to get its +current state. The `TaskInstance` is also an object with getters you can access the current value +with `instance.value` or register events on it with `instance.on`. -```svelte - - - + lastRun.cancel(); +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const myTask = task( + async () => { + // your code + }, + { kind: 'default' }, +); + +button.addEventListener('click', () => { + lastRun = myTask.perform(); + lastRun.on('success', () => { + console.log(lastRun.value); + }); + lastRun.cancel(); +}); ``` diff --git a/apps/docs/src/content/docs/vanilla/reference/drop.mdx b/apps/docs/src/content/docs/vanilla/reference/drop.mdx index f043c158..1ac46aa1 100644 --- a/apps/docs/src/content/docs/vanilla/reference/drop.mdx +++ b/apps/docs/src/content/docs/vanilla/reference/drop.mdx @@ -18,31 +18,27 @@ To specify a task as droppable, you can either use the dot notation or the optio -```svelte - +const dropTask = task.drop(async () => { + // your code +}); ``` -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const dropTask = task( + async () => { + // your code + }, + { kind: 'drop' }, +); ``` @@ -58,47 +54,43 @@ setting it to 5. -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const dropTask = task.drop( + async () => { + // your code + }, + { max: 5 }, +); ``` -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const dropTask = task( + async () => { + // your code + }, + { kind: 'drop', max: 5 }, +); ``` -### The task store +### The task -The return value of the task function will be a svelte store where you can access state from all the -instances running and eventually cancel them with `cancelAll`. +The return value of the task function will be an object with getters where you can access state from +all the instances running and eventually cancel them with `cancelAll`. @@ -110,50 +102,42 @@ by the `perform` function (and it will be strongly typed too). -```svelte - +const dropTask = task.drop(async (id: string) => { + // your code +}); - +button.addEventListener('click', () => { + dropTask.perform('42'); +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const dropTask = task( + async (id: string) => { + // your code + }, + { kind: 'drop' }, +); + +button.addEventListener('click', () => { + dropTask.perform('42'); +}); ``` @@ -169,45 +153,37 @@ function. -```svelte - - - +const dropTask = task.drop(async () => { + return 42; +}); + +button.addEventListener('click', () => { + const number = await dropTask.perform(); + console.log(number); // 42 +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const dropTask = task( + async () => { + return 42; + }, + { kind: 'drop' }, +); + +button.addEventListener('click', () => { + const number = await dropTask.perform(); + console.log(number); // 42 +}); ``` @@ -217,55 +193,51 @@ function. ### Getting the `TaskInstance` If you don't await the `perform` function, then you'll get back the -[task instance](/reference/task-instance) that you can use either to cancel it or to get its current -state. The `TaskInstance` is also a svelte store and you can access the current value with -`instance.get()`. +[task instance](/vanilla/reference/task-instance) that you can use either to cancel it or to get its +current state. The `TaskInstance` is also an object with getters you can access the current value +with `instance.value` or register events on it with `instance.on`. -```svelte - - - + lastRun.cancel(); +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const dropTask = task( + async () => { + // your code + }, + { kind: 'drop' }, +); + +button.addEventListener('click', () => { + lastRun = dropTask.perform(); + lastRun.on('success', () => { + console.log(lastRun.value); + }); + lastRun.cancel(); +}); ``` diff --git a/apps/docs/src/content/docs/vanilla/reference/enqueue.mdx b/apps/docs/src/content/docs/vanilla/reference/enqueue.mdx index 7cf61ef5..35419b6e 100644 --- a/apps/docs/src/content/docs/vanilla/reference/enqueue.mdx +++ b/apps/docs/src/content/docs/vanilla/reference/enqueue.mdx @@ -18,31 +18,27 @@ To specify a task as enqueue-able, you can either use the dot notation or the op -```svelte - +const enqueued = task.enqueue(async () => { + // your code +}); ``` -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const enqueued = task( + async () => { + // your code + }, + { kind: 'enqueue' }, +); ``` @@ -58,47 +54,43 @@ setting it to 5. -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const enqueued = task.enqueue( + async () => { + // your code + }, + { max: 5 }, +); ``` -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const enqueued = task( + async () => { + // your code + }, + { kind: 'enqueue', max: 5 }, +); ``` -### The task store +### The task -The return value of the task function will be a svelte store where you can access state from all the -instances running and eventually cancel them with `cancelAll`. +The return value of the task function will be an object with getters where you can access state from +all the instances running and eventually cancel them with `cancelAll`. @@ -110,50 +102,42 @@ by the `perform` function (and it will be strongly typed too). -```svelte - +const enqueued = task.enqueue(async (id: string) => { + // your code +}); - +button.addEventListener('click', () => { + enqueued.perform('42'); +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const enqueued = task( + async (id: string) => { + // your code + }, + { kind: 'enqueue' }, +); + +button.addEventListener('click', () => { + enqueued.perform('42'); +}); ``` @@ -169,45 +153,37 @@ function. -```svelte - - - +const enqueued = task.enqueue(async () => { + return 42; +}); + +button.addEventListener('click', () => { + const number = await enqueued.perform(); + console.log(number); // 42 +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const enqueued = task( + async () => { + return 42; + }, + { kind: 'enqueue' }, +); + +button.addEventListener('click', () => { + const number = await enqueued.perform(); + console.log(number); // 42 +}); ``` @@ -217,55 +193,51 @@ function. ### Getting the `TaskInstance` If you don't await the `perform` function, then you'll get back the -[task instance](/reference/task-instance) that you can use either to cancel it or to get its current -state. The `TaskInstance` is also a svelte store and you can access the current value with -`instance.get()`. +[task instance](/vanilla/reference/task-instance) that you can use either to cancel it or to get its +current state. The `TaskInstance` is also an object with getters you can access the current value +with `instance.value` or register events on it with `instance.on`. -```svelte - - - + lastRun.cancel(); +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const enqueued = task( + async () => { + // your code + }, + { kind: 'enqueue' }, +); + +button.addEventListener('click', () => { + lastRun = enqueued.perform(); + lastRun.on('success', () => { + console.log(lastRun.value); + }); + lastRun.cancel(); +}); ``` diff --git a/apps/docs/src/content/docs/vanilla/reference/keep-latest.mdx b/apps/docs/src/content/docs/vanilla/reference/keep-latest.mdx index e91db00c..28537df4 100644 --- a/apps/docs/src/content/docs/vanilla/reference/keep-latest.mdx +++ b/apps/docs/src/content/docs/vanilla/reference/keep-latest.mdx @@ -21,31 +21,27 @@ To specify a task as keepLatest, you can either use the dot notation or the opti -```svelte - +const latestTask = task.keepLatest(async () => { + // your code +}); ``` -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const latestTask = task( + async () => { + // your code + }, + { kind: 'keepLatest' }, +); ``` @@ -66,47 +62,43 @@ setting it to 5. -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const latestTask = task.keepLatest( + async () => { + // your code + }, + { max: 5 }, +); ``` -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const latestTask = task( + async () => { + // your code + }, + { kind: 'keepLatest', max: 5 }, +); ``` -### The task store +### The task -The return value of the task function will be a svelte store where you can access state from all the -instances running and eventually cancel them with `cancelAll`. +The return value of the task function will be an object with getters where you can access state from +all the instances running and eventually cancel them with `cancelAll`. @@ -118,50 +110,42 @@ by the `perform` function (and it will be strongly typed too). -```svelte - +const latestTask = task.keepLatest(async (id: string) => { + // your code +}); - +button.addEventListener('click', () => { + latestTask.perform('42'); +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const latestTask = task( + async (id: string) => { + // your code + }, + { kind: 'keepLatest' }, +); + +button.addEventListener('click', () => { + latestTask.perform('42'); +}); ``` @@ -177,45 +161,37 @@ function. -```svelte - - - +const latestTask = task.keepLatest(async () => { + return 42; +}); + +button.addEventListener('click', () => { + const number = await latestTask.perform(); + console.log(number); // 42 +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const latestTask = task( + async () => { + return 42; + }, + { kind: 'keepLatest' }, +); + +button.addEventListener('click', () => { + const number = await latestTask.perform(); + console.log(number); // 42 +}); ``` @@ -225,55 +201,51 @@ function. ### Getting the `TaskInstance` If you don't await the `perform` function, then you'll get back the -[task instance](/reference/task-instance) that you can use either to cancel it or to get its current -state. The `TaskInstance` is also a svelte store and you can access the current value with -`instance.get()`. +[task instance](/vanilla/reference/task-instance) that you can use either to cancel it or to get its +current state. The `TaskInstance` is also an object with getters you can access the current value +with `instance.value` or register events on it with `instance.on`. -```svelte - - - + lastRun.cancel(); +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const latestTask = task( + async () => { + // your code + }, + { kind: 'keepLatest' }, +); + +button.addEventListener('click', () => { + lastRun = latestTask.perform(); + lastRun.on('success', () => { + console.log(lastRun.value); + }); + lastRun.cancel(); +}); ``` diff --git a/apps/docs/src/content/docs/vanilla/reference/restart.mdx b/apps/docs/src/content/docs/vanilla/reference/restart.mdx index 94015bc0..5b54aaad 100644 --- a/apps/docs/src/content/docs/vanilla/reference/restart.mdx +++ b/apps/docs/src/content/docs/vanilla/reference/restart.mdx @@ -18,31 +18,27 @@ To specify a task as restartable, you can either use the dot notation or the opt -```svelte - +const restartTask = task.restart(async () => { + // your code +}); ``` -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const restartTask = task( + async () => { + // your code + }, + { kind: 'restart' }, +); ``` @@ -64,47 +60,43 @@ setting it to 5. -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const restartTask = task.restart( + async () => { + // your code + }, + { max: 5 }, +); ``` -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const restartTask = task( + async () => { + // your code + }, + { kind: 'restart', max: 5 }, +); ``` -### The task store +### The task -The return value of the task function will be a svelte store where you can access state from all the -instances running and eventually cancel them with `cancelAll`. +The return value of the task function will be an object with getters where you can access state from +all the instances running and eventually cancel them with `cancelAll`. @@ -116,50 +108,42 @@ by the `perform` function (and it will be strongly typed too). -```svelte - +const restartTask = task.restart(async (id: string) => { + // your code +}); - +button.addEventListener('click', () => { + restartTask.perform('42'); +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const restartTask = task( + async (id: string) => { + // your code + }, + { kind: 'restart' }, +); + +button.addEventListener('click', () => { + restartTask.perform('42'); +}); ``` @@ -175,45 +159,37 @@ function. -```svelte - - - +const restartTask = task.restart(async () => { + return 42; +}); + +button.addEventListener('click', () => { + const number = await restartTask.perform(); + console.log(number); // 42 +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const restartTask = task( + async () => { + return 42; + }, + { kind: 'restart' }, +); + +button.addEventListener('click', () => { + const number = await restartTask.perform(); + console.log(number); // 42 +}); ``` @@ -223,55 +199,51 @@ function. ### Getting the `TaskInstance` If you don't await the `perform` function, then you'll get back the -[task instance](/reference/task-instance) that you can use either to cancel it or to get its current -state. The `TaskInstance` is also a svelte store and you can access the current value with -`instance.get()`. +[task instance](/vanilla/reference/task-instance) that you can use either to cancel it or to get its +current state. The `TaskInstance` is also an object with getters you can access the current value +with `instance.value` or register events on it with `instance.on`. -```svelte - - - + lastRun.cancel(); +}); ``` -```svelte - - - +```ts +import { task } from '@sheepdog/vanilla'; + +const restartTask = task( + async () => { + // your code + }, + { kind: 'restart' }, +); + +button.addEventListener('click', () => { + lastRun = restartTask.perform(); + lastRun.on('success', () => { + console.log(lastRun.value); + }); + lastRun.cancel(); +}); ``` diff --git a/apps/docs/src/content/docs/vanilla/reference/sheepdog-utils.mdx b/apps/docs/src/content/docs/vanilla/reference/sheepdog-utils.mdx index 1564bb12..63d892ec 100644 --- a/apps/docs/src/content/docs/vanilla/reference/sheepdog-utils.mdx +++ b/apps/docs/src/content/docs/vanilla/reference/sheepdog-utils.mdx @@ -3,20 +3,24 @@ title: Sheepdog Utils description: the utilities object that is available in every task --- -When creating a task (of any type) there are 2 possible arguments that can be accessed, the first is any argument that is passed in when calling `.perform()`, and the second is an object that can be used to optionally enhance your task - `SheepdogUtils`. +When creating a task (of any type) there are 2 possible arguments that can be accessed, the first is +any argument that is passed in when calling `.perform()`, and the second is an object that can be +used to optionally enhance your task - `SheepdogUtils`. `SheepdogUtils` offers two properties to extend your task with: -- `signal` - the `AbortSignal` is available so that you can do any further manipulation you need based on the state of the `AbortSignal`. We won't go into too much detail about this as it is the standard `AbortSignal` interface that is simply passed back from `Sheepdog`. -- `link` - this is a `Sheepdog` specific function that allows you to bind tasks together. For more information, check out the [Linking Tasks explainer](/explainer/linking-tasks). -```svelte - +```ts +import { task } from '@sheepdog/vanilla'; + +const myTask = task(async (myArgument, { signal, link }) => { + // your code +}); ``` ```ts @@ -25,6 +29,3 @@ type SheepdogUtils = { link: (task: Task) => Task; }; ``` - - - diff --git a/apps/docs/src/content/docs/vanilla/reference/transform.mdx b/apps/docs/src/content/docs/vanilla/reference/transform.mdx index c2e45cc3..02a95d0e 100644 --- a/apps/docs/src/content/docs/vanilla/reference/transform.mdx +++ b/apps/docs/src/content/docs/vanilla/reference/transform.mdx @@ -9,18 +9,18 @@ This function allows you to mark a function that is not directly passed to the ` transformable from the Async Transform. ## Usage -You can import this function from `@sheepdog/svelte/utils` and call it with the same first argument +You can import this function from `@sheepdog/vanilla/utils` and call it with the same first argument you would pass to the `task` function ```ts -import { transform } from '@sheepdog/svelte/utils'; +import { transform } from '@sheepdog/vanilla/utils'; const myTask = transform(async (id: number) => { const res = await fetch(`/products/${id}`); From 005c33ef5406f893e81520bc16662361a1f21483 Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Fri, 28 Mar 2025 09:45:54 +0100 Subject: [PATCH 06/10] docs: convert task instance docs --- .../docs/vanilla/reference/task-instance.mdx | 108 +++++++----------- 1 file changed, 39 insertions(+), 69 deletions(-) diff --git a/apps/docs/src/content/docs/vanilla/reference/task-instance.mdx b/apps/docs/src/content/docs/vanilla/reference/task-instance.mdx index 137b8ce0..5e09d7f0 100644 --- a/apps/docs/src/content/docs/vanilla/reference/task-instance.mdx +++ b/apps/docs/src/content/docs/vanilla/reference/task-instance.mdx @@ -5,18 +5,25 @@ description: the underlying structure of every task instance ### Structure -To standardize the approach regardless of which task modifier you're using, every task modifier will use the same underlying `TaskInstance` structure inside of a svelte store. +To standardize the approach regardless of which task modifier you're using, every task modifier will +use the same underlying `TaskInstance` structure inside of a svelte store. A `TaskInstance` comprises of: + - `error`: if an error is thrown inside the task instance, it will be found here -- `hasStarted`: it is possible for the task instance to be in a queue, waiting to start. This property will change to `true` the instant the task instance has started -- `isCanceled`: whether the task instance was canceled -- `isError`: whether the task instance throw an error before completing -- `isRunning`: whether the task instance is currently running -- `isSuccessful`: whether the task instance completed successfully - `value`: if the task instance completed successfully, this will be the return value +- `on`: a function to add a listener on the task instance itself, the events you can listen for are: + - `error`: emitted by the instance when an error is thrown during the run + - `finish`: emitted by the instance when for whatever reason it finishes (either if it errors, is + being canceled or it complete successfully) + - `cancel`: emitted by the instance when the task is canceled + - `start`: emitted by the instance when the task actually starts (it could be canceled before this + event is emitted) + - `success`: emitted by the instance when the execution terminates without erroring out or being + canceled And for those of you that prefer to read code, here is the typing of the `TaskInstance`: + ```ts type TaskInstance = { /** @@ -24,85 +31,48 @@ type TaskInstance = { */ error?: Error; - /** - * Indicates whether the task instance has started. - */ - hasStarted: boolean; - - /** - * Indicates whether the task instance was canceled. - */ - isCanceled: boolean; - - /** - * Indicates whether the task instance threw an error before completing. - */ - isError: boolean; - - /** - * Indicates whether the task instance is currently running. - */ - isRunning: boolean; - /** - * Indicates whether the task instance completed successfully. + * If the task instance completed successfully, this will be the return value. */ - isSuccessful: boolean; + value?: TReturn; /** - * If the task instance completed successfully, this will be the return value. + * A function to add a listener on the task instance itself, the events you can listen for are: + * + * - error + * - finish + * - cancel + * - start + * - success */ - value?: TReturn; + on: (event: InstanceEvents, cb: () => void, options?: AddEventListenerOptions) => void; }; ``` -You can access these properties just as you would with any other Svelte store: - -```svelte - - - - - {#if $lastInstance.isRunning} - Last instance is running! - {/if} - - +```ts +type InstanceEvents = 'error' | 'finish' | 'cancel' | 'start' | 'success'; ``` ### Cancellation Each task instance is also packaged with a `cancel` function that can be used to cancel itself. -```svelte - +let lastInstance; - +button.addEventListener('click', () => { + lastInstance = myTask.perform(); +}); - -``` \ No newline at end of file +cancelLast.addEventListener('click', () => { + lastInstance?.cancel(); +}); +``` From 875d5243d33866b44a3e3fcdd6f744e34c44e332 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 28 Mar 2025 09:43:35 +0000 Subject: [PATCH 07/10] chore(deps): update dependency vite to v6.2.3 [security] --- pnpm-lock.yaml | 646 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 429 insertions(+), 217 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79f114ac..6d9205eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,13 +43,13 @@ importers: version: 0.9.4(prettier@3.5.3)(typescript@5.8.2) '@astrojs/netlify': specifier: ^6.0.0 - version: 6.2.2(@types/node@22.13.9)(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1))(encoding@0.1.13)(rollup@4.34.9)(yaml@2.5.1) + version: 6.2.2(@types/node@22.13.9)(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1))(encoding@0.1.13)(rollup@4.37.0)(yaml@2.5.1) '@astrojs/starlight': specifier: ^0.32.0 - version: 0.32.2(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1)) + version: 0.32.2(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1)) '@astrojs/svelte': specifier: ^7.0.0 - version: 7.0.5(@types/node@22.13.9)(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1))(svelte@5.22.5)(typescript@5.8.2)(yaml@2.5.1) + version: 7.0.5(@types/node@22.13.9)(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1))(svelte@5.22.5)(typescript@5.8.2)(yaml@2.5.1) '@fontsource/calistoga': specifier: ^5.0.21 version: 5.2.5 @@ -64,7 +64,7 @@ importers: version: 5.0.3(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)) astro: specifier: ^5.0.0 - version: 5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1) + version: 5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1) sharp: specifier: ^0.33.0 version: 0.33.5 @@ -98,7 +98,7 @@ importers: version: 0.3.8 vite: specifier: ^6.0.0 - version: 6.2.1(@types/node@22.13.9)(yaml@2.5.1) + version: 6.2.3(@types/node@22.13.9)(yaml@2.5.1) vitest: specifier: ^3.0.5 version: 3.0.9(@types/debug@4.1.12)(@types/node@22.13.9)(@vitest/ui@3.0.8)(happy-dom@17.3.0)(yaml@2.5.1) @@ -114,22 +114,22 @@ importers: version: 1.51.0 '@sveltejs/adapter-auto': specifier: ^5.0.0 - version: 5.0.0(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1))) + version: 5.0.0(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1))) '@sveltejs/kit': specifier: ^2.5.8 - version: 2.18.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)) + version: 2.18.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)) '@sveltejs/package': specifier: ^2.3.1 version: 2.3.10(svelte@5.22.5)(typescript@5.8.2) '@sveltejs/vite-plugin-svelte': specifier: ^5.0.1 - version: 5.0.3(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)) + version: 5.0.3(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)) '@testing-library/jest-dom': specifier: ^6.4.5 version: 6.6.3 '@testing-library/svelte': specifier: ^5.1.0 - version: 5.2.7(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1))(vitest@3.0.9) + version: 5.2.7(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1))(vitest@3.0.9) '@types/eslint': specifier: 9.6.1 version: 9.6.1 @@ -198,7 +198,7 @@ importers: version: 8.26.0(eslint@9.21.0)(typescript@5.8.2) vite: specifier: ^6.0.0 - version: 6.2.1(@types/node@22.13.9)(yaml@2.5.1) + version: 6.2.3(@types/node@22.13.9)(yaml@2.5.1) vitest: specifier: ^3.0.5 version: 3.0.9(@types/debug@4.1.12)(@types/node@22.13.9)(@vitest/ui@3.0.8)(happy-dom@17.3.0)(yaml@2.5.1) @@ -259,7 +259,7 @@ importers: version: 8.26.0(eslint@9.21.0)(typescript@5.8.2) vite: specifier: ^6.0.0 - version: 6.0.9(@types/node@22.13.9)(yaml@2.5.1) + version: 6.2.3(@types/node@22.13.9)(yaml@2.5.1) vitest: specifier: ^3.0.0 version: 3.0.9(@types/debug@4.1.12)(@types/node@22.13.9)(@vitest/ui@3.0.8)(happy-dom@17.3.0)(yaml@2.5.1) @@ -408,26 +408,26 @@ packages: '@emnapi/runtime@1.3.0': resolution: {integrity: sha512-XMBySMuNZs3DM96xcJmLW4EfGnf+uGmFNjzpehMjuX5PLB5j87ar2Zc4e3PVeZ3I5g3tYtAqskB28manlF69Zw==} - '@esbuild/aix-ppc64@0.24.2': - resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.0': - resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + '@esbuild/aix-ppc64@0.25.1': + resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.24.2': - resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.0': - resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + '@esbuild/android-arm64@0.25.1': + resolution: {integrity: sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -438,22 +438,16 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.24.2': - resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.25.0': resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.24.2': - resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + '@esbuild/android-arm@0.25.1': + resolution: {integrity: sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm] os: [android] '@esbuild/android-x64@0.25.0': @@ -462,11 +456,11 @@ packages: cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.24.2': - resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + '@esbuild/android-x64@0.25.1': + resolution: {integrity: sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==} engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] + cpu: [x64] + os: [android] '@esbuild/darwin-arm64@0.25.0': resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} @@ -474,10 +468,10 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.24.2': - resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + '@esbuild/darwin-arm64@0.25.1': + resolution: {integrity: sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.25.0': @@ -486,11 +480,11 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.24.2': - resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + '@esbuild/darwin-x64@0.25.1': + resolution: {integrity: sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==} engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] + cpu: [x64] + os: [darwin] '@esbuild/freebsd-arm64@0.25.0': resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} @@ -498,10 +492,10 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.24.2': - resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + '@esbuild/freebsd-arm64@0.25.1': + resolution: {integrity: sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.25.0': @@ -510,11 +504,11 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.24.2': - resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + '@esbuild/freebsd-x64@0.25.1': + resolution: {integrity: sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==} engines: {node: '>=18'} - cpu: [arm64] - os: [linux] + cpu: [x64] + os: [freebsd] '@esbuild/linux-arm64@0.25.0': resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} @@ -522,10 +516,10 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.24.2': - resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + '@esbuild/linux-arm64@0.25.1': + resolution: {integrity: sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==} engines: {node: '>=18'} - cpu: [arm] + cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.25.0': @@ -534,10 +528,10 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.24.2': - resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + '@esbuild/linux-arm@0.25.1': + resolution: {integrity: sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==} engines: {node: '>=18'} - cpu: [ia32] + cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.25.0': @@ -546,28 +540,28 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.25.1': + resolution: {integrity: sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.15.18': resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} engines: {node: '>=12'} cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.24.2': - resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.25.0': resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.24.2': - resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + '@esbuild/linux-loong64@0.25.1': + resolution: {integrity: sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==} engines: {node: '>=18'} - cpu: [mips64el] + cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.25.0': @@ -576,10 +570,10 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.24.2': - resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + '@esbuild/linux-mips64el@0.25.1': + resolution: {integrity: sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==} engines: {node: '>=18'} - cpu: [ppc64] + cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.25.0': @@ -588,10 +582,10 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.24.2': - resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + '@esbuild/linux-ppc64@0.25.1': + resolution: {integrity: sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==} engines: {node: '>=18'} - cpu: [riscv64] + cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.25.0': @@ -600,10 +594,10 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.24.2': - resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + '@esbuild/linux-riscv64@0.25.1': + resolution: {integrity: sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==} engines: {node: '>=18'} - cpu: [s390x] + cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.25.0': @@ -612,10 +606,10 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.24.2': - resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + '@esbuild/linux-s390x@0.25.1': + resolution: {integrity: sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==} engines: {node: '>=18'} - cpu: [x64] + cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.25.0': @@ -624,11 +618,11 @@ packages: cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.24.2': - resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + '@esbuild/linux-x64@0.25.1': + resolution: {integrity: sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==} engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] + cpu: [x64] + os: [linux] '@esbuild/netbsd-arm64@0.25.0': resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} @@ -636,10 +630,10 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.24.2': - resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + '@esbuild/netbsd-arm64@0.25.1': + resolution: {integrity: sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.25.0': @@ -648,11 +642,11 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.24.2': - resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + '@esbuild/netbsd-x64@0.25.1': + resolution: {integrity: sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==} engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] + cpu: [x64] + os: [netbsd] '@esbuild/openbsd-arm64@0.25.0': resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} @@ -660,10 +654,10 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.24.2': - resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + '@esbuild/openbsd-arm64@0.25.1': + resolution: {integrity: sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==} engines: {node: '>=18'} - cpu: [x64] + cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.25.0': @@ -672,11 +666,11 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.24.2': - resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + '@esbuild/openbsd-x64@0.25.1': + resolution: {integrity: sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==} engines: {node: '>=18'} cpu: [x64] - os: [sunos] + os: [openbsd] '@esbuild/sunos-x64@0.25.0': resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} @@ -684,11 +678,11 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.24.2': - resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + '@esbuild/sunos-x64@0.25.1': + resolution: {integrity: sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==} engines: {node: '>=18'} - cpu: [arm64] - os: [win32] + cpu: [x64] + os: [sunos] '@esbuild/win32-arm64@0.25.0': resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} @@ -696,10 +690,10 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.24.2': - resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + '@esbuild/win32-arm64@0.25.1': + resolution: {integrity: sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==} engines: {node: '>=18'} - cpu: [ia32] + cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.25.0': @@ -708,10 +702,10 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.24.2': - resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + '@esbuild/win32-ia32@0.25.1': + resolution: {integrity: sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==} engines: {node: '>=18'} - cpu: [x64] + cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.25.0': @@ -720,6 +714,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.25.1': + resolution: {integrity: sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.1': resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1307,96 +1307,196 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.37.0': + resolution: {integrity: sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.34.9': resolution: {integrity: sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.37.0': + resolution: {integrity: sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.34.9': resolution: {integrity: sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.37.0': + resolution: {integrity: sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.34.9': resolution: {integrity: sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.37.0': + resolution: {integrity: sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.34.9': resolution: {integrity: sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.37.0': + resolution: {integrity: sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.34.9': resolution: {integrity: sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.37.0': + resolution: {integrity: sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.34.9': resolution: {integrity: sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.37.0': + resolution: {integrity: sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.34.9': resolution: {integrity: sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.37.0': + resolution: {integrity: sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.34.9': resolution: {integrity: sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.37.0': + resolution: {integrity: sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.34.9': resolution: {integrity: sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.37.0': + resolution: {integrity: sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-loongarch64-gnu@4.34.9': resolution: {integrity: sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==} cpu: [loong64] os: [linux] + '@rollup/rollup-linux-loongarch64-gnu@4.37.0': + resolution: {integrity: sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.34.9': resolution: {integrity: sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.37.0': + resolution: {integrity: sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.34.9': resolution: {integrity: sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.37.0': + resolution: {integrity: sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.37.0': + resolution: {integrity: sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.34.9': resolution: {integrity: sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.37.0': + resolution: {integrity: sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.34.9': resolution: {integrity: sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.37.0': + resolution: {integrity: sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.34.9': resolution: {integrity: sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.37.0': + resolution: {integrity: sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w==} + cpu: [x64] + os: [linux] + '@rollup/rollup-win32-arm64-msvc@4.34.9': resolution: {integrity: sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.37.0': + resolution: {integrity: sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.34.9': resolution: {integrity: sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.37.0': + resolution: {integrity: sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.34.9': resolution: {integrity: sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.37.0': + resolution: {integrity: sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA==} + cpu: [x64] + os: [win32] + '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -1531,6 +1631,9 @@ packages: '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/fs-extra@9.0.13': resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} @@ -2402,13 +2505,13 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.24.2: - resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} engines: {node: '>=18'} hasBin: true - esbuild@0.25.0: - resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + esbuild@0.25.1: + resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==} engines: {node: '>=18'} hasBin: true @@ -3527,8 +3630,8 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -3891,10 +3994,6 @@ packages: resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} engines: {node: '>=4'} - postcss@8.5.1: - resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.3: resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} @@ -4193,6 +4292,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.37.0: + resolution: {integrity: sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -4746,8 +4850,8 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@6.0.9: - resolution: {integrity: sha512-MSgUxHcaXLtnBPktkbUSoQUANApKYuxZ6DrbVENlIorbhL2dZydTLaZ01tjUoE3szeFzlFk9ANOKk0xurh4MKA==} + vite@6.2.1: + resolution: {integrity: sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -4786,8 +4890,8 @@ packages: yaml: optional: true - vite@6.2.1: - resolution: {integrity: sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==} + vite@6.2.3: + resolution: {integrity: sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -5254,12 +5358,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/mdx@4.0.7(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1))': + '@astrojs/mdx@4.0.7(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1))': dependencies: '@astrojs/markdown-remark': 6.0.2 '@mdx-js/mdx': 3.1.0(acorn@8.14.1) acorn: 8.14.1 - astro: 5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1) + astro: 5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1) es-module-lexer: 1.6.0 estree-util-visit: 2.0.0 hast-util-to-html: 9.0.4 @@ -5273,14 +5377,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/netlify@6.2.2(@types/node@22.13.9)(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1))(encoding@0.1.13)(rollup@4.34.9)(yaml@2.5.1)': + '@astrojs/netlify@6.2.2(@types/node@22.13.9)(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1))(encoding@0.1.13)(rollup@4.37.0)(yaml@2.5.1)': dependencies: '@astrojs/internal-helpers': 0.6.0 '@astrojs/underscore-redirects': 0.6.0 '@netlify/blobs': 8.1.1 '@netlify/functions': 2.8.2 - '@vercel/nft': 0.29.2(encoding@0.1.13)(rollup@4.34.9) - astro: 5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1) + '@vercel/nft': 0.29.2(encoding@0.1.13)(rollup@4.37.0) + astro: 5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1) esbuild: 0.25.0 tinyglobby: 0.2.12 vite: 6.2.1(@types/node@22.13.9)(yaml@2.5.1) @@ -5310,16 +5414,16 @@ snapshots: stream-replace-string: 2.0.0 zod: 3.24.2 - '@astrojs/starlight@0.32.2(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1))': + '@astrojs/starlight@0.32.2(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1))': dependencies: - '@astrojs/mdx': 4.0.7(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1)) + '@astrojs/mdx': 4.0.7(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1)) '@astrojs/sitemap': 3.2.1 '@pagefind/default-ui': 1.3.0 '@types/hast': 3.0.4 '@types/js-yaml': 4.0.9 '@types/mdast': 4.0.4 - astro: 5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1) - astro-expressive-code: 0.40.1(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1)) + astro: 5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1) + astro-expressive-code: 0.40.1(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1)) bcp-47: 2.1.0 hast-util-from-html: 2.0.3 hast-util-select: 6.0.3 @@ -5341,10 +5445,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/svelte@7.0.5(@types/node@22.13.9)(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1))(svelte@5.22.5)(typescript@5.8.2)(yaml@2.5.1)': + '@astrojs/svelte@7.0.5(@types/node@22.13.9)(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1))(svelte@5.22.5)(typescript@5.8.2)(yaml@2.5.1)': dependencies: '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)) - astro: 5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1) + astro: 5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1) svelte: 5.22.5 svelte2tsx: 0.7.35(svelte@5.22.5)(typescript@5.8.2) typescript: 5.8.2 @@ -5453,162 +5557,162 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.24.2': - optional: true - '@esbuild/aix-ppc64@0.25.0': optional: true - '@esbuild/android-arm64@0.24.2': + '@esbuild/aix-ppc64@0.25.1': optional: true '@esbuild/android-arm64@0.25.0': optional: true - '@esbuild/android-arm@0.15.18': + '@esbuild/android-arm64@0.25.1': optional: true - '@esbuild/android-arm@0.24.2': + '@esbuild/android-arm@0.15.18': optional: true '@esbuild/android-arm@0.25.0': optional: true - '@esbuild/android-x64@0.24.2': + '@esbuild/android-arm@0.25.1': optional: true '@esbuild/android-x64@0.25.0': optional: true - '@esbuild/darwin-arm64@0.24.2': + '@esbuild/android-x64@0.25.1': optional: true '@esbuild/darwin-arm64@0.25.0': optional: true - '@esbuild/darwin-x64@0.24.2': + '@esbuild/darwin-arm64@0.25.1': optional: true '@esbuild/darwin-x64@0.25.0': optional: true - '@esbuild/freebsd-arm64@0.24.2': + '@esbuild/darwin-x64@0.25.1': optional: true '@esbuild/freebsd-arm64@0.25.0': optional: true - '@esbuild/freebsd-x64@0.24.2': + '@esbuild/freebsd-arm64@0.25.1': optional: true '@esbuild/freebsd-x64@0.25.0': optional: true - '@esbuild/linux-arm64@0.24.2': + '@esbuild/freebsd-x64@0.25.1': optional: true '@esbuild/linux-arm64@0.25.0': optional: true - '@esbuild/linux-arm@0.24.2': + '@esbuild/linux-arm64@0.25.1': optional: true '@esbuild/linux-arm@0.25.0': optional: true - '@esbuild/linux-ia32@0.24.2': + '@esbuild/linux-arm@0.25.1': optional: true '@esbuild/linux-ia32@0.25.0': optional: true - '@esbuild/linux-loong64@0.15.18': + '@esbuild/linux-ia32@0.25.1': optional: true - '@esbuild/linux-loong64@0.24.2': + '@esbuild/linux-loong64@0.15.18': optional: true '@esbuild/linux-loong64@0.25.0': optional: true - '@esbuild/linux-mips64el@0.24.2': + '@esbuild/linux-loong64@0.25.1': optional: true '@esbuild/linux-mips64el@0.25.0': optional: true - '@esbuild/linux-ppc64@0.24.2': + '@esbuild/linux-mips64el@0.25.1': optional: true '@esbuild/linux-ppc64@0.25.0': optional: true - '@esbuild/linux-riscv64@0.24.2': + '@esbuild/linux-ppc64@0.25.1': optional: true '@esbuild/linux-riscv64@0.25.0': optional: true - '@esbuild/linux-s390x@0.24.2': + '@esbuild/linux-riscv64@0.25.1': optional: true '@esbuild/linux-s390x@0.25.0': optional: true - '@esbuild/linux-x64@0.24.2': + '@esbuild/linux-s390x@0.25.1': optional: true '@esbuild/linux-x64@0.25.0': optional: true - '@esbuild/netbsd-arm64@0.24.2': + '@esbuild/linux-x64@0.25.1': optional: true '@esbuild/netbsd-arm64@0.25.0': optional: true - '@esbuild/netbsd-x64@0.24.2': + '@esbuild/netbsd-arm64@0.25.1': optional: true '@esbuild/netbsd-x64@0.25.0': optional: true - '@esbuild/openbsd-arm64@0.24.2': + '@esbuild/netbsd-x64@0.25.1': optional: true '@esbuild/openbsd-arm64@0.25.0': optional: true - '@esbuild/openbsd-x64@0.24.2': + '@esbuild/openbsd-arm64@0.25.1': optional: true '@esbuild/openbsd-x64@0.25.0': optional: true - '@esbuild/sunos-x64@0.24.2': + '@esbuild/openbsd-x64@0.25.1': optional: true '@esbuild/sunos-x64@0.25.0': optional: true - '@esbuild/win32-arm64@0.24.2': + '@esbuild/sunos-x64@0.25.1': optional: true '@esbuild/win32-arm64@0.25.0': optional: true - '@esbuild/win32-ia32@0.24.2': + '@esbuild/win32-arm64@0.25.1': optional: true '@esbuild/win32-ia32@0.25.0': optional: true - '@esbuild/win32-x64@0.24.2': + '@esbuild/win32-ia32@0.25.1': optional: true '@esbuild/win32-x64@0.25.0': optional: true + '@esbuild/win32-x64@0.25.1': + optional: true + '@eslint-community/eslint-utils@4.4.1(eslint@9.21.0)': dependencies: eslint: 9.21.0 @@ -6311,71 +6415,131 @@ snapshots: '@publint/pack@0.1.2': {} - '@rollup/pluginutils@5.1.4(rollup@4.34.9)': + '@rollup/pluginutils@5.1.4(rollup@4.37.0)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.34.9 + rollup: 4.37.0 '@rollup/rollup-android-arm-eabi@4.34.9': optional: true + '@rollup/rollup-android-arm-eabi@4.37.0': + optional: true + '@rollup/rollup-android-arm64@4.34.9': optional: true + '@rollup/rollup-android-arm64@4.37.0': + optional: true + '@rollup/rollup-darwin-arm64@4.34.9': optional: true + '@rollup/rollup-darwin-arm64@4.37.0': + optional: true + '@rollup/rollup-darwin-x64@4.34.9': optional: true + '@rollup/rollup-darwin-x64@4.37.0': + optional: true + '@rollup/rollup-freebsd-arm64@4.34.9': optional: true + '@rollup/rollup-freebsd-arm64@4.37.0': + optional: true + '@rollup/rollup-freebsd-x64@4.34.9': optional: true + '@rollup/rollup-freebsd-x64@4.37.0': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.34.9': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.37.0': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.34.9': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.37.0': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.34.9': optional: true + '@rollup/rollup-linux-arm64-gnu@4.37.0': + optional: true + '@rollup/rollup-linux-arm64-musl@4.34.9': optional: true + '@rollup/rollup-linux-arm64-musl@4.37.0': + optional: true + '@rollup/rollup-linux-loongarch64-gnu@4.34.9': optional: true + '@rollup/rollup-linux-loongarch64-gnu@4.37.0': + optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.34.9': optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.37.0': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.34.9': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.37.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.37.0': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.34.9': optional: true + '@rollup/rollup-linux-s390x-gnu@4.37.0': + optional: true + '@rollup/rollup-linux-x64-gnu@4.34.9': optional: true + '@rollup/rollup-linux-x64-gnu@4.37.0': + optional: true + '@rollup/rollup-linux-x64-musl@4.34.9': optional: true + '@rollup/rollup-linux-x64-musl@4.37.0': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.34.9': optional: true + '@rollup/rollup-win32-arm64-msvc@4.37.0': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.34.9': optional: true + '@rollup/rollup-win32-ia32-msvc@4.37.0': + optional: true + '@rollup/rollup-win32-x64-msvc@4.34.9': optional: true + '@rollup/rollup-win32-x64-msvc@4.37.0': + optional: true + '@sec-ant/readable-stream@0.4.1': {} '@shikijs/core@1.29.2': @@ -6423,14 +6587,14 @@ snapshots: dependencies: acorn: 8.14.1 - '@sveltejs/adapter-auto@5.0.0(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)))': + '@sveltejs/adapter-auto@5.0.0(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)))': dependencies: - '@sveltejs/kit': 2.18.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)) + '@sveltejs/kit': 2.18.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)) import-meta-resolve: 4.1.0 - '@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1))': + '@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)) '@types/cookie': 0.6.0 cookie: 0.6.0 devalue: 5.1.1 @@ -6443,7 +6607,7 @@ snapshots: set-cookie-parser: 2.7.1 sirv: 3.0.1 svelte: 5.22.5 - vite: 6.2.1(@types/node@22.13.9)(yaml@2.5.1) + vite: 6.2.3(@types/node@22.13.9)(yaml@2.5.1) '@sveltejs/package@2.3.10(svelte@5.22.5)(typescript@5.8.2)': dependencies: @@ -6465,6 +6629,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1))': + dependencies: + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)) + debug: 4.4.0 + svelte: 5.22.5 + vite: 6.2.3(@types/node@22.13.9)(yaml@2.5.1) + transitivePeerDependencies: + - supports-color + '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1))': dependencies: '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)) @@ -6478,6 +6651,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)))(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)) + debug: 4.4.0 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.17 + svelte: 5.22.5 + vite: 6.2.3(@types/node@22.13.9)(yaml@2.5.1) + vitefu: 1.0.5(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)) + transitivePeerDependencies: + - supports-color + '@szmarczak/http-timer@5.0.1': dependencies: defer-to-connect: 2.0.1 @@ -6503,12 +6689,12 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/svelte@5.2.7(svelte@5.22.5)(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1))(vitest@3.0.9)': + '@testing-library/svelte@5.2.7(svelte@5.22.5)(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1))(vitest@3.0.9)': dependencies: '@testing-library/dom': 10.4.0 svelte: 5.22.5 optionalDependencies: - vite: 6.2.1(@types/node@22.13.9)(yaml@2.5.1) + vite: 6.2.3(@types/node@22.13.9)(yaml@2.5.1) vitest: 3.0.9(@types/debug@4.1.12)(@types/node@22.13.9)(@vitest/ui@3.0.8)(happy-dom@17.3.0)(yaml@2.5.1) '@tootallnate/once@1.1.2': {} @@ -6538,10 +6724,12 @@ snapshots: '@types/estree-jsx@1.0.5': dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.7 '@types/estree@1.0.6': {} + '@types/estree@1.0.7': {} + '@types/fs-extra@9.0.13': dependencies: '@types/node': 22.13.9 @@ -6679,10 +6867,10 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vercel/nft@0.29.2(encoding@0.1.13)(rollup@4.34.9)': + '@vercel/nft@0.29.2(encoding@0.1.13)(rollup@4.37.0)': dependencies: '@mapbox/node-pre-gyp': 2.0.0(encoding@0.1.13) - '@rollup/pluginutils': 5.1.4(rollup@4.34.9) + '@rollup/pluginutils': 5.1.4(rollup@4.37.0) acorn: 8.14.1 acorn-import-attributes: 1.9.5(acorn@8.14.1) async-sema: 3.1.1 @@ -6723,13 +6911,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.0.9(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1))': + '@vitest/mocker@3.0.9(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1))': dependencies: '@vitest/spy': 3.0.9 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 6.2.1(@types/node@22.13.9)(yaml@2.5.1) + vite: 6.2.3(@types/node@22.13.9)(yaml@2.5.1) '@vitest/pretty-format@3.0.8': dependencies: @@ -6940,19 +7128,19 @@ snapshots: astring@1.9.0: {} - astro-expressive-code@0.40.1(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1)): + astro-expressive-code@0.40.1(astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1)): dependencies: - astro: 5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1) + astro: 5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1) rehype-expressive-code: 0.40.1 - astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.34.9)(typescript@5.8.2)(yaml@2.5.1): + astro@5.4.2(@netlify/blobs@8.1.1)(@types/node@22.13.9)(rollup@4.37.0)(typescript@5.8.2)(yaml@2.5.1): dependencies: '@astrojs/compiler': 2.10.4 '@astrojs/internal-helpers': 0.6.0 '@astrojs/markdown-remark': 6.2.0 '@astrojs/telemetry': 3.2.0 '@oslojs/encoding': 1.1.0 - '@rollup/pluginutils': 5.1.4(rollup@4.34.9) + '@rollup/pluginutils': 5.1.4(rollup@4.37.0) '@types/cookie': 0.6.0 acorn: 8.14.1 aria-query: 5.3.2 @@ -7527,34 +7715,6 @@ snapshots: esbuild-windows-64: 0.15.18 esbuild-windows-arm64: 0.15.18 - esbuild@0.24.2: - optionalDependencies: - '@esbuild/aix-ppc64': 0.24.2 - '@esbuild/android-arm': 0.24.2 - '@esbuild/android-arm64': 0.24.2 - '@esbuild/android-x64': 0.24.2 - '@esbuild/darwin-arm64': 0.24.2 - '@esbuild/darwin-x64': 0.24.2 - '@esbuild/freebsd-arm64': 0.24.2 - '@esbuild/freebsd-x64': 0.24.2 - '@esbuild/linux-arm': 0.24.2 - '@esbuild/linux-arm64': 0.24.2 - '@esbuild/linux-ia32': 0.24.2 - '@esbuild/linux-loong64': 0.24.2 - '@esbuild/linux-mips64el': 0.24.2 - '@esbuild/linux-ppc64': 0.24.2 - '@esbuild/linux-riscv64': 0.24.2 - '@esbuild/linux-s390x': 0.24.2 - '@esbuild/linux-x64': 0.24.2 - '@esbuild/netbsd-arm64': 0.24.2 - '@esbuild/netbsd-x64': 0.24.2 - '@esbuild/openbsd-arm64': 0.24.2 - '@esbuild/openbsd-x64': 0.24.2 - '@esbuild/sunos-x64': 0.24.2 - '@esbuild/win32-arm64': 0.24.2 - '@esbuild/win32-ia32': 0.24.2 - '@esbuild/win32-x64': 0.24.2 - esbuild@0.25.0: optionalDependencies: '@esbuild/aix-ppc64': 0.25.0 @@ -7583,6 +7743,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.0 '@esbuild/win32-x64': 0.25.0 + esbuild@0.25.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.1 + '@esbuild/android-arm': 0.25.1 + '@esbuild/android-arm64': 0.25.1 + '@esbuild/android-x64': 0.25.1 + '@esbuild/darwin-arm64': 0.25.1 + '@esbuild/darwin-x64': 0.25.1 + '@esbuild/freebsd-arm64': 0.25.1 + '@esbuild/freebsd-x64': 0.25.1 + '@esbuild/linux-arm': 0.25.1 + '@esbuild/linux-arm64': 0.25.1 + '@esbuild/linux-ia32': 0.25.1 + '@esbuild/linux-loong64': 0.25.1 + '@esbuild/linux-mips64el': 0.25.1 + '@esbuild/linux-ppc64': 0.25.1 + '@esbuild/linux-riscv64': 0.25.1 + '@esbuild/linux-s390x': 0.25.1 + '@esbuild/linux-x64': 0.25.1 + '@esbuild/netbsd-arm64': 0.25.1 + '@esbuild/netbsd-x64': 0.25.1 + '@esbuild/openbsd-arm64': 0.25.1 + '@esbuild/openbsd-x64': 0.25.1 + '@esbuild/sunos-x64': 0.25.1 + '@esbuild/win32-arm64': 0.25.1 + '@esbuild/win32-ia32': 0.25.1 + '@esbuild/win32-x64': 0.25.1 + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} @@ -9181,7 +9369,7 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.8: {} + nanoid@3.3.11: {} natural-compare@1.4.0: {} @@ -9517,15 +9705,9 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss@8.5.1: - dependencies: - nanoid: 3.3.8 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.3: dependencies: - nanoid: 3.3.8 + nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -9907,6 +10089,32 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.34.9 fsevents: 2.3.3 + rollup@4.37.0: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.37.0 + '@rollup/rollup-android-arm64': 4.37.0 + '@rollup/rollup-darwin-arm64': 4.37.0 + '@rollup/rollup-darwin-x64': 4.37.0 + '@rollup/rollup-freebsd-arm64': 4.37.0 + '@rollup/rollup-freebsd-x64': 4.37.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.37.0 + '@rollup/rollup-linux-arm-musleabihf': 4.37.0 + '@rollup/rollup-linux-arm64-gnu': 4.37.0 + '@rollup/rollup-linux-arm64-musl': 4.37.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.37.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.37.0 + '@rollup/rollup-linux-riscv64-gnu': 4.37.0 + '@rollup/rollup-linux-riscv64-musl': 4.37.0 + '@rollup/rollup-linux-s390x-gnu': 4.37.0 + '@rollup/rollup-linux-x64-gnu': 4.37.0 + '@rollup/rollup-linux-x64-musl': 4.37.0 + '@rollup/rollup-win32-arm64-msvc': 4.37.0 + '@rollup/rollup-win32-ia32-msvc': 4.37.0 + '@rollup/rollup-win32-x64-msvc': 4.37.0 + fsevents: 2.3.3 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -10452,7 +10660,7 @@ snapshots: debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 6.2.1(@types/node@22.13.9)(yaml@2.5.1) + vite: 6.2.3(@types/node@22.13.9)(yaml@2.5.1) transitivePeerDependencies: - '@types/node' - jiti @@ -10467,21 +10675,21 @@ snapshots: - tsx - yaml - vite@6.0.9(@types/node@22.13.9)(yaml@2.5.1): + vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1): dependencies: - esbuild: 0.24.2 - postcss: 8.5.1 + esbuild: 0.25.0 + postcss: 8.5.3 rollup: 4.34.9 optionalDependencies: '@types/node': 22.13.9 fsevents: 2.3.3 yaml: 2.5.1 - vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1): + vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1): dependencies: - esbuild: 0.25.0 + esbuild: 0.25.1 postcss: 8.5.3 - rollup: 4.34.9 + rollup: 4.37.0 optionalDependencies: '@types/node': 22.13.9 fsevents: 2.3.3 @@ -10491,6 +10699,10 @@ snapshots: optionalDependencies: vite: 6.2.1(@types/node@22.13.9)(yaml@2.5.1) + vitefu@1.0.5(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)): + optionalDependencies: + vite: 6.2.3(@types/node@22.13.9)(yaml@2.5.1) + vitefu@1.0.6(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)): optionalDependencies: vite: 6.2.1(@types/node@22.13.9)(yaml@2.5.1) @@ -10498,7 +10710,7 @@ snapshots: vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.9)(@vitest/ui@3.0.8)(happy-dom@17.3.0)(yaml@2.5.1): dependencies: '@vitest/expect': 3.0.9 - '@vitest/mocker': 3.0.9(vite@6.2.1(@types/node@22.13.9)(yaml@2.5.1)) + '@vitest/mocker': 3.0.9(vite@6.2.3(@types/node@22.13.9)(yaml@2.5.1)) '@vitest/pretty-format': 3.0.9 '@vitest/runner': 3.0.9 '@vitest/snapshot': 3.0.9 @@ -10514,7 +10726,7 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.2.1(@types/node@22.13.9)(yaml@2.5.1) + vite: 6.2.3(@types/node@22.13.9)(yaml@2.5.1) vite-node: 3.0.9(@types/node@22.13.9)(yaml@2.5.1) why-is-node-running: 2.3.0 optionalDependencies: From b54ad2552e55b213da3c737479a0863a0c7b4882 Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Fri, 28 Mar 2025 10:46:50 +0100 Subject: [PATCH 08/10] chore: add require label workflow --- .github/workflows/require-label.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/require-label.yml diff --git a/.github/workflows/require-label.yml b/.github/workflows/require-label.yml new file mode 100644 index 00000000..1111cc58 --- /dev/null +++ b/.github/workflows/require-label.yml @@ -0,0 +1,19 @@ +name: Pull Request Labels +on: + pull_request: + types: [opened, labeled, unlabeled, synchronize] +jobs: + label: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: mheap/github-action-required-labels@v5 + with: + mode: minimum + count: 1 + labels: | + documentation + internal + bug + enhancement From a80f11f47911d171d99fface4a7ec62ccce0401a Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Fri, 28 Mar 2025 10:48:44 +0100 Subject: [PATCH 09/10] fix: remove write permission --- .github/workflows/require-label.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/require-label.yml b/.github/workflows/require-label.yml index 1111cc58..0a7f27c0 100644 --- a/.github/workflows/require-label.yml +++ b/.github/workflows/require-label.yml @@ -5,8 +5,6 @@ on: jobs: label: runs-on: ubuntu-latest - permissions: - pull-requests: write steps: - uses: mheap/github-action-required-labels@v5 with: From 4107f5ce8f57b11ba4c18ae67eddf2d44adb86f8 Mon Sep 17 00:00:00 2001 From: BEERINHO Date: Fri, 28 Mar 2025 13:18:33 +0100 Subject: [PATCH 10/10] Add distinction between svelte and vanilla libraries --- apps/docs/src/components/SiteTitle.astro | 51 +++++++- apps/docs/src/components/landing.astro | 153 +++++++++++++++++++++-- apps/docs/src/content/docs/index.mdx | 16 ++- 3 files changed, 195 insertions(+), 25 deletions(-) diff --git a/apps/docs/src/components/SiteTitle.astro b/apps/docs/src/components/SiteTitle.astro index fdadc455..491ed08c 100644 --- a/apps/docs/src/components/SiteTitle.astro +++ b/apps/docs/src/components/SiteTitle.astro @@ -1,5 +1,4 @@ --- -import Default from '@astrojs/starlight/components/SiteTitle.astro'; import BuiltBy from '../assets/BuiltBy.astro'; import { is_homepage } from '@utils/is-homepage.ts'; @@ -13,14 +12,54 @@ const isHomepage = is_homepage(Astro.locals.starlightRoute.id); built by Mainmatter
) : ( - +
+