@sheepdog/svelte
Advanced tools
Comparing version 0.8.0 to 0.9.0
@@ -40,8 +40,38 @@ export declare const handlers: { | ||
export type Task<TArgs = unknown, TReturn = unknown> = ReturnType<typeof createTask<TArgs, TReturn>>; | ||
type TaskAdapter<TReturn = unknown> = { | ||
type TaskAdapter<TReturn = unknown, TModifier = object> = { | ||
/** | ||
* Callback called to register an onDestroy callback | ||
*/ | ||
onDestroy: (fn: () => void) => void; | ||
/** | ||
* Callback called when the instance is manually canceled | ||
*/ | ||
onInstanceCancel: (instance_id: string) => void; | ||
/** | ||
* Callback called when `perform` is called regardless if the handler is | ||
* immediately executed or not | ||
*/ | ||
onInstanceCreate: (instance_id: string) => void; | ||
/** | ||
* Callback called when the instance is actually executed by the handler | ||
*/ | ||
onInstanceStart: (instance_id: string) => void; | ||
/** | ||
* Callback called if the instance completes successfully | ||
*/ | ||
onInstanceComplete: (instance_id: string, new_value: TReturn) => void; | ||
/** | ||
* Callback called when the instance errors out | ||
*/ | ||
onError: (instance_id: string, error: unknown | undefined) => void; | ||
/** | ||
* Callback called with the promise-like returned from perform. It allows you to | ||
* modify the value (like appending a subscribe or create a signal out of it) | ||
* before finally being returned to the user | ||
*/ | ||
returnModifier?: (instance_id: string, returned_value: Promise<TReturn> & { | ||
cancel(): void; | ||
}) => Promise<TReturn> & { | ||
cancel(): void; | ||
} & TModifier; | ||
}; | ||
@@ -51,8 +81,8 @@ export declare class CancelationError extends Error { | ||
} | ||
export declare function createTask<TArgs = unknown, TReturn = unknown>(adapter: TaskAdapter<TReturn>, gen_or_fun: (args: TArgs, utils: SheepdogUtils) => Promise<TReturn> | AsyncGenerator<unknown, TReturn, unknown>, options?: TaskOptions): { | ||
export declare function createTask<TArgs = unknown, TReturn = unknown, TModifier = object>(adapter: TaskAdapter<TReturn, TModifier>, gen_or_fun: (args: TArgs, utils: SheepdogUtils) => Promise<TReturn> | AsyncGenerator<unknown, TReturn, unknown>, options?: TaskOptions): { | ||
cancelAll(): void; | ||
perform(...args: undefined extends TArgs ? [] : [TArgs]): Promise<TReturn> & { | ||
cancel(): void; | ||
}; | ||
} & TModifier; | ||
}; | ||
export {}; |
@@ -37,2 +37,3 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ | ||
abort_controller.controller.abort(); | ||
abort_controller.controller.signal.removeEventListener('abort', abort_controller.listener); | ||
}); | ||
@@ -58,6 +59,7 @@ }, | ||
abort_controller.signal.addEventListener('abort', cancel_linked_and_update_store); | ||
abort_controllers.add({ | ||
const set_element = { | ||
controller: abort_controller, | ||
listener: cancel_linked_and_update_store, | ||
}); | ||
}; | ||
abort_controllers.add(set_element); | ||
function link(task) { | ||
@@ -74,2 +76,4 @@ const old_perform = task.perform; | ||
} | ||
// we always create the instance because it's also used by the returnModifier | ||
adapter.onInstanceCreate(instance_id); | ||
handler(() => { | ||
@@ -96,2 +100,4 @@ adapter.onInstanceStart(instance_id); | ||
const last_result = next_val.value; | ||
set_element.controller.signal.removeEventListener('abort', set_element.listener); | ||
abort_controllers.delete(set_element); | ||
adapter.onInstanceComplete(instance_id, last_result); | ||
@@ -102,2 +108,4 @@ resolve(next_val.value); | ||
else if (!abort_controller.signal.aborted) { | ||
set_element.controller.signal.removeEventListener('abort', set_element.listener); | ||
abort_controllers.delete(set_element); | ||
adapter.onInstanceComplete(instance_id, gen_or_value); | ||
@@ -115,9 +123,12 @@ resolve(gen_or_value); | ||
}, { promise, abort_controller }); | ||
return Object.assign(promise, { | ||
// we default to just return the same value if returnModifier is null | ||
const modifier = adapter.returnModifier ?? | ||
((_, ret) => ret); | ||
return modifier(instance_id, Object.assign(promise, { | ||
cancel() { | ||
abort_controller.abort(); | ||
}, | ||
}); | ||
})); | ||
}, | ||
}; | ||
} |
@@ -0,3 +1,3 @@ | ||
import type { HandlerType, HandlersMap, SheepdogUtils, TaskOptions } from './core'; | ||
import { CancelationError } from './core'; | ||
import type { SheepdogUtils, TaskOptions, HandlerType, HandlersMap } from './core'; | ||
export type { SheepdogUtils, TaskOptions }; | ||
@@ -7,8 +7,10 @@ export { CancelationError }; | ||
export type TaskInstance<TReturn = undefined> = { | ||
error?: undefined | unknown; | ||
error?: unknown; | ||
hasStarted: boolean; | ||
isCanceled: boolean; | ||
isError: boolean; | ||
isFinished: boolean; | ||
isRunning: boolean; | ||
isSuccessful: boolean; | ||
value?: undefined | TReturn; | ||
value?: TReturn; | ||
}; | ||
@@ -19,2 +21,4 @@ declare function _task<TArgs = unknown, TReturn = undefined>(gen_or_fun: (args: TArgs, utils: SheepdogUtils) => Promise<TReturn> | AsyncGenerator<unknown, TReturn, unknown>, options?: TaskOptions): { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<TaskInstance<TReturn>> & { | ||
get: () => TaskInstance<TReturn>; | ||
}; | ||
@@ -21,0 +25,0 @@ } & { |
import { onDestroy } from 'svelte'; | ||
import { createTask, handlers, CancelationError } from './core'; | ||
import { writable } from 'svelte/store'; | ||
import { CancelationError, createTask, handlers } from './core'; | ||
import { writable_with_get } from './internal/helpers'; | ||
export { CancelationError }; | ||
@@ -17,5 +18,2 @@ function _task(gen_or_fun, options) { | ||
return result.update((old) => { | ||
if (!instance) { | ||
return old; | ||
} | ||
if (new_instance) { | ||
@@ -41,3 +39,3 @@ old.performCount++; | ||
old.last = instance; | ||
old.isRunning = [...instances.values()].some((val) => val.isRunning); | ||
old.isRunning = [...instances.values()].some((val) => val.get().isRunning); | ||
return old; | ||
@@ -54,7 +52,17 @@ }); | ||
if (instance) { | ||
instance.error = error; | ||
instance.isRunning = false; | ||
instance.isError = true; | ||
instance.update((instance) => { | ||
instance.error = error; | ||
instance.isError = true; | ||
instance.isFinished = true; | ||
instance.isRunning = false; | ||
return instance; | ||
}); | ||
// we delete after a microtask to avoid returnModifier | ||
// not founding the instance in case of a syncronous | ||
// cancellation (for example with drop) | ||
queueMicrotask(() => { | ||
instances.delete(instance_id); | ||
}); | ||
updateResult(instance.get()); | ||
} | ||
updateResult(instance); | ||
}, | ||
@@ -64,26 +72,69 @@ onInstanceCancel(instance_id) { | ||
if (instance) { | ||
instance.isRunning = false; | ||
instance.isCanceled = true; | ||
instance.update((instance) => { | ||
instance.isCanceled = true; | ||
instance.isFinished = true; | ||
instance.isRunning = false; | ||
return instance; | ||
}); | ||
// we delete after a microtask to avoid returnModifier | ||
// not founding the instance in case of a syncronous | ||
// cancellation (for example with drop) | ||
queueMicrotask(() => { | ||
instances.delete(instance_id); | ||
}); | ||
updateResult(instance.get()); | ||
} | ||
updateResult(instance); | ||
}, | ||
onInstanceStart(instance_id) { | ||
const instance = { | ||
isRunning: true, | ||
onInstanceCreate(instance_id) { | ||
const instance_value = { | ||
hasStarted: false, | ||
isCanceled: false, | ||
isError: false, | ||
isFinished: false, | ||
isRunning: false, | ||
isSuccessful: false, | ||
}; | ||
const instance = writable_with_get(instance_value); | ||
instances.set(instance_id, instance); | ||
updateResult(instance, true); | ||
updateResult(instance_value); | ||
}, | ||
onInstanceStart(instance_id) { | ||
const instance = instances.get(instance_id); | ||
if (instance) { | ||
instance.update((instance) => { | ||
instance.hasStarted = true; | ||
instance.isRunning = true; | ||
return instance; | ||
}); | ||
updateResult(instance.get(), true); | ||
} | ||
}, | ||
onInstanceComplete(instance_id, last_result) { | ||
const instance = instances.get(instance_id); | ||
if (instance) { | ||
instance.isRunning = false; | ||
instance.isSuccessful = true; | ||
instance.value = last_result; | ||
instance.update((instance) => { | ||
instance.isFinished = true; | ||
instance.isRunning = false; | ||
instance.isSuccessful = true; | ||
instance.value = last_result; | ||
return instance; | ||
}); | ||
// we delete after a microtask to avoid returnModifier | ||
// not founding the instance in case of a synchronous | ||
// cancellation (for example with drop) | ||
queueMicrotask(() => { | ||
instances.delete(instance_id); | ||
}); | ||
updateResult(instance.get()); | ||
} | ||
updateResult(instance); | ||
}, | ||
returnModifier(instance_id, returned_value) { | ||
const instance = instances.get(instance_id); | ||
if (!instance) | ||
throw new Error('Return modifier has been called before the instance was created'); | ||
return Object.assign(returned_value, { | ||
subscribe: instance.subscribe, | ||
get: instance.get, | ||
}); | ||
}, | ||
}, gen_or_fun, options); | ||
@@ -90,0 +141,0 @@ return Object.assign(actual_task, { |
@@ -12,2 +12,4 @@ import { SvelteComponent } from "svelte"; | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -37,2 +39,4 @@ } & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -58,2 +62,12 @@ } & { | ||
}; | ||
default_instances?: Array<ReturnType<(args_0: number) => Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}>>; | ||
options_instances?: Array<ReturnType<(args_0: number) => Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}>>; | ||
}; | ||
@@ -75,2 +89,4 @@ events: { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -100,2 +116,4 @@ } & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -121,3 +139,13 @@ } & { | ||
}; | ||
get default_instances(): (Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
})[]; | ||
get options_instances(): (Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
})[]; | ||
} | ||
export {}; |
@@ -13,2 +13,4 @@ import { SvelteComponent } from "svelte"; | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -38,2 +40,4 @@ } & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -59,2 +63,12 @@ } & { | ||
}; | ||
default_instances?: Array<ReturnType<(args_0: number) => Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}>>; | ||
options_instances?: Array<ReturnType<(args_0: number) => Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}>>; | ||
}; | ||
@@ -76,2 +90,4 @@ events: { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -101,2 +117,4 @@ } & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -122,3 +140,13 @@ } & { | ||
}; | ||
get default_instances(): (Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
})[]; | ||
get options_instances(): (Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
})[]; | ||
} | ||
export {}; |
@@ -13,2 +13,4 @@ import { SvelteComponent } from "svelte"; | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -38,2 +40,4 @@ } & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -59,2 +63,12 @@ } & { | ||
}; | ||
default_instances?: Array<ReturnType<(args_0: number) => Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}>>; | ||
options_instances?: Array<ReturnType<(args_0: number) => Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}>>; | ||
}; | ||
@@ -76,2 +90,4 @@ events: { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -101,2 +117,4 @@ } & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -122,3 +140,13 @@ } & { | ||
}; | ||
get default_instances(): (Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
})[]; | ||
get options_instances(): (Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
})[]; | ||
} | ||
export {}; |
@@ -13,2 +13,4 @@ import { SvelteComponent } from "svelte"; | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -38,2 +40,4 @@ } & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -59,2 +63,12 @@ } & { | ||
}; | ||
default_instances?: Array<ReturnType<(args_0: number) => Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}>>; | ||
options_instances?: Array<ReturnType<(args_0: number) => Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}>>; | ||
}; | ||
@@ -76,2 +90,4 @@ events: { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -101,2 +117,4 @@ } & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -122,3 +140,13 @@ } & { | ||
}; | ||
get default_instances(): (Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
})[]; | ||
get options_instances(): (Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
})[]; | ||
} | ||
export {}; |
@@ -13,2 +13,4 @@ import { SvelteComponent } from "svelte"; | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -38,2 +40,4 @@ } & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -59,2 +63,12 @@ } & { | ||
}; | ||
default_instances?: Array<ReturnType<(args_0: number) => Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}>>; | ||
options_instances?: Array<ReturnType<(args_0: number) => Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}>>; | ||
}; | ||
@@ -76,2 +90,4 @@ events: { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -101,2 +117,4 @@ } & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
}; | ||
@@ -122,3 +140,13 @@ } & { | ||
}; | ||
get default_instances(): (Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
})[]; | ||
get options_instances(): (Promise<unknown> & { | ||
cancel(): void; | ||
} & import("svelte/store").Readable<import("../../task").TaskInstance<unknown>> & { | ||
get: () => import("../../task").TaskInstance<unknown>; | ||
})[]; | ||
} | ||
export {}; |
import { asyncTransform } from '../../vite'; | ||
import { readdirSync } from 'node:fs'; | ||
import { readdirSync, existsSync } from 'node:fs'; | ||
import { readFile, writeFile } from 'node:fs/promises'; | ||
@@ -8,10 +8,14 @@ const dir = readdirSync('./src/lib/tests/expected-transforms', { | ||
}); | ||
const [, , force] = process.argv; | ||
const plugin = asyncTransform(); | ||
for (const file of dir) { | ||
if (file.isFile() && file.name === 'code.js') { | ||
readFile(`${file.path}/${file.name}`).then(async (code) => { | ||
if (file.isFile() && | ||
file.name === 'code.js' && | ||
(!!force || !existsSync(`${file.parentPath}/transform.js`))) { | ||
console.log('generating expected for', file.parentPath); | ||
readFile(`${file.parentPath}/${file.name}`).then(async (code) => { | ||
// @ts-expect-error we don't have the correct `this` here but we are not using it | ||
const result = await plugin.transform(code, 'code.js'); | ||
if (result) { | ||
await writeFile(`${file.path}/transform.js`, result.code); | ||
await writeFile(`${file.parentPath}/transform.js`, result.code); | ||
} | ||
@@ -18,0 +22,0 @@ }); |
@@ -181,3 +181,6 @@ import { walk } from 'zimmerframe'; | ||
state.task_fn_name.then((name) => { | ||
if (node.callee.type === 'Identifier' && node.callee.name === name) { | ||
if ((node.callee.type === 'Identifier' && node.callee.name === name) || | ||
(node.callee.type === 'MemberExpression' && | ||
node.callee.object.type === 'Identifier' && | ||
node.callee.object.name === name)) { | ||
const task_arg = node.arguments[0]; | ||
@@ -184,0 +187,0 @@ if (task_arg && task_arg.type === 'ArrowFunctionExpression' && task_arg.async) { |
{ | ||
"name": "@sheepdog/svelte", | ||
"version": "0.8.0", | ||
"version": "0.9.0", | ||
"repository": { | ||
@@ -45,3 +45,3 @@ "type": "git", | ||
"@testing-library/svelte": "^5.1.0", | ||
"@types/eslint": "8.56.10", | ||
"@types/eslint": "9.6.0", | ||
"@types/eslint-config-prettier": "^6.11.3", | ||
@@ -76,4 +76,4 @@ "@types/eslint__js": "^8.42.3", | ||
"volta": { | ||
"node": "20.15.1", | ||
"pnpm": "9.5.0" | ||
"node": "20.16.0", | ||
"pnpm": "9.6.0" | ||
}, | ||
@@ -92,2 +92,3 @@ "scripts": { | ||
"generate-expected": "tsm ./src/lib/tests/expected-transforms/generate-expected.ts", | ||
"generate-expected-force": "tsm ./src/lib/tests/expected-transforms/generate-expected.ts -f", | ||
"test": "npm run test:unit", | ||
@@ -94,0 +95,0 @@ "test:integration": "playwright test", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
114311
94
2047