@bluelibs/runner 5.2.0 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -10
- package/dist/browser/index.cjs +149 -59
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.mjs +149 -60
- package/dist/browser/index.mjs.map +1 -1
- package/dist/edge/index.cjs +149 -59
- package/dist/edge/index.cjs.map +1 -1
- package/dist/edge/index.mjs +149 -60
- package/dist/edge/index.mjs.map +1 -1
- package/dist/node/node.cjs +384 -201
- package/dist/node/node.cjs.map +1 -1
- package/dist/node/node.mjs +382 -202
- package/dist/node/node.mjs.map +1 -1
- package/dist/types/definers/builders/error/fluent-builder.interface.d.ts +6 -0
- package/dist/types/definers/builders/error/index.d.ts +10 -1
- package/dist/types/definers/builders/error/types.d.ts +2 -0
- package/dist/types/definers/defineError.d.ts +5 -3
- package/dist/types/defs.d.ts +2 -0
- package/dist/types/globals/resources/tunnel/protocol.d.ts +3 -0
- package/dist/types/models/Store.d.ts +5 -3
- package/dist/types/models/StoreRegistry.d.ts +5 -3
- package/dist/types/node/durable/core/DurableResource.d.ts +23 -9
- package/dist/types/node/durable/core/DurableService.d.ts +15 -9
- package/dist/types/node/durable/core/interfaces/service.d.ts +27 -19
- package/dist/types/node/durable/core/interfaces/store.d.ts +1 -1
- package/dist/types/node/durable/core/managers/ExecutionManager.d.ts +34 -4
- package/dist/types/node/durable/core/managers/ScheduleManager.d.ts +5 -3
- package/dist/types/node/durable/core/managers/TaskRegistry.d.ts +5 -5
- package/dist/types/node/durable/core/managers/WaitManager.d.ts +1 -1
- package/dist/types/node/durable/index.d.ts +1 -0
- package/dist/types/node/durable/tags/durableWorkflow.tag.d.ts +14 -0
- package/dist/types/node/node.d.ts +2 -1
- package/dist/types/public.d.ts +4 -1
- package/dist/types/testing.d.ts +0 -1
- package/dist/types/types/error.d.ts +22 -1
- package/dist/types/types/resource.d.ts +2 -4
- package/dist/types/types/symbols.d.ts +2 -2
- package/dist/types/types/tagged.d.ts +18 -0
- package/dist/universal/index.cjs +149 -59
- package/dist/universal/index.cjs.map +1 -1
- package/dist/universal/index.mjs +149 -60
- package/dist/universal/index.mjs.map +1 -1
- package/package.json +2 -2
- package/readmes/AI.md +25 -9
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { DefaultErrorType, IErrorMeta, IValidationSchema, IErrorHelper } from "../../../defs";
|
|
2
2
|
export interface ErrorFluentBuilder<TData extends DefaultErrorType = DefaultErrorType> {
|
|
3
3
|
id: string;
|
|
4
|
+
httpCode(code: number): ErrorFluentBuilder<TData>;
|
|
4
5
|
serialize(fn: (data: TData) => string): ErrorFluentBuilder<TData>;
|
|
5
6
|
parse(fn: (raw: string) => TData): ErrorFluentBuilder<TData>;
|
|
6
7
|
dataSchema(schema: IValidationSchema<TData>): ErrorFluentBuilder<TData>;
|
|
7
8
|
build(): IErrorHelper<TData>;
|
|
8
9
|
format(fn: (data: TData) => string): ErrorFluentBuilder<TData>;
|
|
10
|
+
/**
|
|
11
|
+
* Attach remediation advice that explains how to fix this error.
|
|
12
|
+
* Appears in the stringified error after the main message.
|
|
13
|
+
*/
|
|
14
|
+
remediation(advice: string | ((data: TData) => string)): ErrorFluentBuilder<TData>;
|
|
9
15
|
meta<TNewMeta extends IErrorMeta>(m: TNewMeta): ErrorFluentBuilder<TData>;
|
|
10
16
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DefaultErrorType } from "../../../defs";
|
|
2
2
|
import type { ErrorFluentBuilder } from "./fluent-builder.interface";
|
|
3
|
+
import { RunnerError } from "../../defineError";
|
|
3
4
|
export * from "./fluent-builder.interface";
|
|
4
5
|
export * from "./fluent-builder";
|
|
5
6
|
export * from "./types";
|
|
@@ -8,4 +9,12 @@ export * from "./utils";
|
|
|
8
9
|
* Entry point for creating an error builder.
|
|
9
10
|
*/
|
|
10
11
|
export declare function errorBuilder<TData extends DefaultErrorType = DefaultErrorType>(id: string): ErrorFluentBuilder<TData>;
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Check if an error is any Runner error (not just a specific one).
|
|
14
|
+
* @param error - The error to check
|
|
15
|
+
* @returns true if the error is a RunnerError instance
|
|
16
|
+
*/
|
|
17
|
+
declare function isRunnerError(error: unknown): error is RunnerError;
|
|
18
|
+
export declare const error: typeof errorBuilder & {
|
|
19
|
+
is: typeof isRunnerError;
|
|
20
|
+
};
|
|
@@ -6,7 +6,9 @@ import type { DefaultErrorType, IErrorMeta, IValidationSchema } from "../../../d
|
|
|
6
6
|
export type BuilderState<TData extends DefaultErrorType> = Readonly<{
|
|
7
7
|
id: string;
|
|
8
8
|
filePath: string;
|
|
9
|
+
httpCode?: number;
|
|
9
10
|
format?: (data: TData) => string;
|
|
11
|
+
remediation?: string | ((data: TData) => string);
|
|
10
12
|
serialize?: (data: TData) => string;
|
|
11
13
|
parse?: (raw: string) => TData;
|
|
12
14
|
dataSchema?: IValidationSchema<TData>;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { DefaultErrorType, IErrorDefinition, IErrorHelper, IErrorDefinitionFinal } from "../types/error";
|
|
2
2
|
import { symbolError, symbolFilePath, symbolOptionalDependency } from "../types/symbols";
|
|
3
|
-
declare class RunnerError<TData extends DefaultErrorType = DefaultErrorType> extends Error {
|
|
3
|
+
export declare class RunnerError<TData extends DefaultErrorType = DefaultErrorType> extends Error {
|
|
4
4
|
readonly id: string;
|
|
5
5
|
readonly data: TData;
|
|
6
|
-
|
|
6
|
+
readonly httpCode?: number;
|
|
7
|
+
readonly remediation?: string;
|
|
8
|
+
constructor(id: string, message: string, data: TData, httpCode?: number, remediation?: string);
|
|
7
9
|
}
|
|
8
10
|
export declare class ErrorHelper<TData extends DefaultErrorType = DefaultErrorType> implements IErrorHelper<TData> {
|
|
9
11
|
private readonly definition;
|
|
@@ -11,6 +13,7 @@ export declare class ErrorHelper<TData extends DefaultErrorType = DefaultErrorTy
|
|
|
11
13
|
[symbolFilePath]: string;
|
|
12
14
|
constructor(definition: IErrorDefinitionFinal<TData>, filePath: string);
|
|
13
15
|
get id(): string;
|
|
16
|
+
get httpCode(): number | undefined;
|
|
14
17
|
throw(data: TData): never;
|
|
15
18
|
is(error: unknown): error is RunnerError<TData>;
|
|
16
19
|
optional(): {
|
|
@@ -24,4 +27,3 @@ export declare class ErrorHelper<TData extends DefaultErrorType = DefaultErrorTy
|
|
|
24
27
|
* @returns
|
|
25
28
|
*/
|
|
26
29
|
export declare function defineError<TData extends DefaultErrorType = DefaultErrorType>(definition: IErrorDefinition<TData>, filePath?: string): ErrorHelper<TData>;
|
|
27
|
-
export {};
|
package/dist/types/defs.d.ts
CHANGED
|
@@ -28,5 +28,7 @@ export * from "./types/runner";
|
|
|
28
28
|
export * from "./types/asyncContext";
|
|
29
29
|
export * from "./types/error";
|
|
30
30
|
export * from "./types/contracts";
|
|
31
|
+
export * from "./types/tagged";
|
|
32
|
+
export * from "./types/inputFile";
|
|
31
33
|
export type { ICacheInstance } from "./globals/middleware/cache.middleware";
|
|
32
34
|
export * from "./types/storeTypes";
|
|
@@ -3,6 +3,7 @@ export interface ProtocolErrorShape {
|
|
|
3
3
|
code: string;
|
|
4
4
|
message: string;
|
|
5
5
|
details?: unknown;
|
|
6
|
+
httpCode?: number;
|
|
6
7
|
id?: string;
|
|
7
8
|
data?: unknown;
|
|
8
9
|
}
|
|
@@ -33,11 +34,13 @@ export interface EventRequest {
|
|
|
33
34
|
export declare class TunnelError extends Error {
|
|
34
35
|
readonly code: string;
|
|
35
36
|
readonly details?: unknown;
|
|
37
|
+
readonly httpCode?: number;
|
|
36
38
|
readonly id?: string;
|
|
37
39
|
readonly data?: unknown;
|
|
38
40
|
constructor(code: string, message: string, details?: unknown, extras?: {
|
|
39
41
|
id?: string;
|
|
40
42
|
data?: unknown;
|
|
43
|
+
httpCode?: number;
|
|
41
44
|
});
|
|
42
45
|
}
|
|
43
46
|
export declare function toTunnelError(input: unknown, fallbackMessage?: string): TunnelError;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IResource, RegisterableItems, ITag, ITaskMiddleware, IResourceMiddleware } from "../defs";
|
|
1
|
+
import { IResource, RegisterableItems, ITag, AnyTask, ITaskMiddleware, IResourceMiddleware, TaggedTask, TaggedResource, AnyResource } from "../defs";
|
|
2
2
|
import { EventManager } from "./EventManager";
|
|
3
3
|
import { Logger } from "./Logger";
|
|
4
4
|
import { ResourceStoreElementType, TaskStoreElementType, EventStoreElementType } from "../types/storeTypes";
|
|
@@ -66,11 +66,13 @@ export declare class Store {
|
|
|
66
66
|
* @param tag - The tag to filter by.
|
|
67
67
|
* @returns The tasks with the given tag.
|
|
68
68
|
*/
|
|
69
|
-
getTasksWithTag
|
|
69
|
+
getTasksWithTag<TTag extends ITag<any, any, any>>(tag: TTag): TaggedTask<TTag>[];
|
|
70
|
+
getTasksWithTag(tag: string): AnyTask[];
|
|
70
71
|
/**
|
|
71
72
|
* Returns all resources with the given tag.
|
|
72
73
|
* @param tag - The tag to filter by.
|
|
73
74
|
* @returns The resources with the given tag.
|
|
74
75
|
*/
|
|
75
|
-
getResourcesWithTag
|
|
76
|
+
getResourcesWithTag<TTag extends ITag<any, any, any>>(tag: TTag): TaggedResource<TTag>[];
|
|
77
|
+
getResourcesWithTag(tag: string): AnyResource[];
|
|
76
78
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IResource, ITask, IResourceWithConfig, RegisterableItems, ITaskMiddleware, IResourceMiddleware, IEvent, ITag, IHook } from "../defs";
|
|
1
|
+
import { IResource, ITask, AnyTask, IResourceWithConfig, RegisterableItems, ITaskMiddleware, IResourceMiddleware, IEvent, ITag, IHook, TaggedTask, TaggedResource, AnyResource } from "../defs";
|
|
2
2
|
import { TaskStoreElementType, TaskMiddlewareStoreElementType, ResourceMiddlewareStoreElementType, ResourceStoreElementType, EventStoreElementType, HookStoreElementType } from "../defs";
|
|
3
3
|
import { StoreValidator } from "./StoreValidator";
|
|
4
4
|
import { Store } from "./Store";
|
|
@@ -39,8 +39,10 @@ export declare class StoreRegistry {
|
|
|
39
39
|
*/
|
|
40
40
|
buildEventEmissionGraph(): IDependentNode[];
|
|
41
41
|
private setupBlankNodes;
|
|
42
|
-
getTasksWithTag
|
|
43
|
-
|
|
42
|
+
getTasksWithTag<TTag extends ITag<any, any, any>>(tag: TTag): TaggedTask<TTag>[];
|
|
43
|
+
getTasksWithTag(tag: string): AnyTask[];
|
|
44
|
+
getResourcesWithTag<TTag extends ITag<any, any, any>>(tag: TTag): TaggedResource<TTag>[];
|
|
45
|
+
getResourcesWithTag(tag: string): AnyResource[];
|
|
44
46
|
/**
|
|
45
47
|
* Used to fetch the value cloned, and if we're dealing with an override, we need to extend the previous value.
|
|
46
48
|
*/
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import type { Store } from "../../../models/Store";
|
|
3
3
|
import type { IEventDefinition } from "../../../types/event";
|
|
4
|
-
import type { ITask } from "../../../types/task";
|
|
4
|
+
import type { AnyTask, ITask } from "../../../types/task";
|
|
5
5
|
import type { IDurableContext } from "./interfaces/context";
|
|
6
|
-
import type {
|
|
6
|
+
import type { DurableStartAndWaitResult, ExecuteOptions, IDurableService, ScheduleOptions } from "./interfaces/service";
|
|
7
7
|
import type { Schedule } from "./types";
|
|
8
8
|
import type { IDurableStore } from "./interfaces/store";
|
|
9
9
|
import { DurableOperator } from "./DurableOperator";
|
|
@@ -11,7 +11,11 @@ import { type DurableFlowShape } from "./flowShape";
|
|
|
11
11
|
export interface DurableResourceConfig {
|
|
12
12
|
worker?: boolean;
|
|
13
13
|
}
|
|
14
|
-
export interface IDurableResource extends Pick<IDurableService, "
|
|
14
|
+
export interface IDurableResource extends Pick<IDurableService, "cancelExecution" | "wait" | "schedule" | "ensureSchedule" | "pauseSchedule" | "resumeSchedule" | "getSchedule" | "listSchedules" | "updateSchedule" | "removeSchedule" | "recover" | "signal"> {
|
|
15
|
+
start<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input?: TInput, options?: ExecuteOptions): Promise<string>;
|
|
16
|
+
start(task: string, input?: unknown, options?: ExecuteOptions): Promise<string>;
|
|
17
|
+
startAndWait<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input?: TInput, options?: ExecuteOptions): Promise<DurableStartAndWaitResult<TResult>>;
|
|
18
|
+
startAndWait<TResult = unknown>(task: string, input?: unknown, options?: ExecuteOptions): Promise<DurableStartAndWaitResult<TResult>>;
|
|
15
19
|
/**
|
|
16
20
|
* Reads the durable context for the currently running workflow execution.
|
|
17
21
|
* Throws if called outside of a durable execution.
|
|
@@ -34,10 +38,14 @@ export interface IDurableResource extends Pick<IDurableService, "startExecution"
|
|
|
34
38
|
* (steps/audit/history and operator actions where supported by the store).
|
|
35
39
|
*/
|
|
36
40
|
readonly operator: DurableOperator;
|
|
41
|
+
/**
|
|
42
|
+
* Returns all tasks tagged as durable workflows in the current runtime.
|
|
43
|
+
*/
|
|
44
|
+
getWorkflows(): AnyTask[];
|
|
37
45
|
}
|
|
38
46
|
/**
|
|
39
47
|
* A Runner-facing wrapper around `DurableService` that exposes a per-instance
|
|
40
|
-
* context store and the public durable API (`
|
|
48
|
+
* context store and the public durable API (`start`, `startAndWait`, `signal`, `wait`, etc.).
|
|
41
49
|
*
|
|
42
50
|
* This enables tasks to depend on a specific durable instance and call
|
|
43
51
|
* `durable.use()` to access the per-execution durable context.
|
|
@@ -52,17 +60,23 @@ export declare class DurableResource implements IDurableResource {
|
|
|
52
60
|
get operator(): DurableOperator;
|
|
53
61
|
use(): IDurableContext;
|
|
54
62
|
describe<TInput>(task: ITask<TInput, any, any, any, any, any>, input?: TInput): Promise<DurableFlowShape>;
|
|
63
|
+
getWorkflows(): AnyTask[];
|
|
55
64
|
private injectRecorderIntoDurableDeps;
|
|
56
|
-
|
|
65
|
+
start<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input?: TInput, options?: ExecuteOptions): Promise<string>;
|
|
66
|
+
start(task: string, input?: unknown, options?: ExecuteOptions): Promise<string>;
|
|
57
67
|
cancelExecution(executionId: string, reason?: string): Promise<void>;
|
|
58
68
|
wait<TResult>(executionId: string, options?: {
|
|
59
69
|
timeout?: number;
|
|
60
70
|
waitPollIntervalMs?: number;
|
|
61
71
|
}): Promise<TResult>;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
schedule<TInput>(task:
|
|
65
|
-
|
|
72
|
+
startAndWait<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input?: TInput, options?: ExecuteOptions): Promise<DurableStartAndWaitResult<TResult>>;
|
|
73
|
+
startAndWait<TResult = unknown>(task: string, input?: unknown, options?: ExecuteOptions): Promise<DurableStartAndWaitResult<TResult>>;
|
|
74
|
+
schedule<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input: TInput | undefined, options: ScheduleOptions): Promise<string>;
|
|
75
|
+
schedule(task: string, input: unknown, options: ScheduleOptions): Promise<string>;
|
|
76
|
+
ensureSchedule<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input: TInput | undefined, options: ScheduleOptions & {
|
|
77
|
+
id: string;
|
|
78
|
+
}): Promise<string>;
|
|
79
|
+
ensureSchedule(task: string, input: unknown, options: ScheduleOptions & {
|
|
66
80
|
id: string;
|
|
67
81
|
}): Promise<string>;
|
|
68
82
|
pauseSchedule(scheduleId: string): Promise<void>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { NoopEventBus } from "../bus/NoopEventBus";
|
|
2
|
-
import type {
|
|
2
|
+
import type { DurableStartAndWaitResult, DurableServiceConfig, ExecuteOptions, IDurableService, ScheduleOptions } from "./interfaces/service";
|
|
3
3
|
import { type Schedule } from "./types";
|
|
4
4
|
import type { IEventDefinition } from "../../../types/event";
|
|
5
|
+
import type { ITask } from "../../../types/task";
|
|
5
6
|
import { ExecutionManager, PollingManager } from "./managers";
|
|
6
7
|
export { DurableExecutionError } from "./utils";
|
|
7
8
|
/**
|
|
@@ -32,22 +33,27 @@ export declare class DurableService implements IDurableService {
|
|
|
32
33
|
/** Unique worker ID for distributed timer coordination */
|
|
33
34
|
private readonly workerId;
|
|
34
35
|
constructor(config: DurableServiceConfig);
|
|
35
|
-
registerTask<TInput, TResult>(task:
|
|
36
|
-
findTask(taskId: string):
|
|
37
|
-
|
|
36
|
+
registerTask<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>): void;
|
|
37
|
+
findTask(taskId: string): ITask<any, Promise<any>, any, any, any, any> | undefined;
|
|
38
|
+
start<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input?: TInput, options?: ExecuteOptions): Promise<string>;
|
|
39
|
+
start(task: string, input?: unknown, options?: ExecuteOptions): Promise<string>;
|
|
40
|
+
start(): void;
|
|
38
41
|
cancelExecution(executionId: string, reason?: string): Promise<void>;
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
startAndWait<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input?: TInput, options?: ExecuteOptions): Promise<DurableStartAndWaitResult<TResult>>;
|
|
43
|
+
startAndWait<TResult = unknown>(task: string, input?: unknown, options?: ExecuteOptions): Promise<DurableStartAndWaitResult<TResult>>;
|
|
41
44
|
wait<TResult>(executionId: string, options?: {
|
|
42
45
|
timeout?: number;
|
|
43
46
|
waitPollIntervalMs?: number;
|
|
44
47
|
}): Promise<TResult>;
|
|
45
|
-
schedule<TInput>(task:
|
|
46
|
-
|
|
48
|
+
schedule<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input: TInput | undefined, options: ScheduleOptions): Promise<string>;
|
|
49
|
+
schedule(task: string, input: unknown, options: ScheduleOptions): Promise<string>;
|
|
50
|
+
ensureSchedule<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input: TInput | undefined, options: ScheduleOptions & {
|
|
51
|
+
id: string;
|
|
52
|
+
}): Promise<string>;
|
|
53
|
+
ensureSchedule(task: string, input: unknown, options: ScheduleOptions & {
|
|
47
54
|
id: string;
|
|
48
55
|
}): Promise<string>;
|
|
49
56
|
recover(): Promise<void>;
|
|
50
|
-
start(): void;
|
|
51
57
|
stop(): Promise<void>;
|
|
52
58
|
pauseSchedule(id: string): Promise<void>;
|
|
53
59
|
resumeSchedule(id: string): Promise<void>;
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import type { IEventDefinition } from "../../../../types/event";
|
|
2
|
+
import type { ITask } from "../../../../types/task";
|
|
2
3
|
import type { IDurableStore } from "./store";
|
|
3
4
|
import type { IDurableQueue } from "./queue";
|
|
4
5
|
import type { IEventBus } from "./bus";
|
|
5
6
|
import type { IDurableContext } from "./context";
|
|
6
7
|
import type { Schedule } from "../types";
|
|
7
8
|
import type { DurableAuditEmitter } from "../audit";
|
|
8
|
-
export interface DurableTask<TInput = unknown, TResult = unknown> {
|
|
9
|
-
id: string;
|
|
10
|
-
run(input: TInput, ...args: any[]): Promise<TResult>;
|
|
11
|
-
}
|
|
12
9
|
export interface ITaskExecutor {
|
|
13
|
-
run<TInput, TResult>(task:
|
|
10
|
+
run<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input?: TInput): Promise<TResult>;
|
|
14
11
|
}
|
|
15
12
|
export interface ScheduleConfig<TInput = unknown> {
|
|
16
13
|
id: string;
|
|
17
|
-
task:
|
|
14
|
+
task: ITask<TInput, Promise<any>, any, any, any, any> | string;
|
|
18
15
|
cron?: string;
|
|
19
16
|
interval?: number;
|
|
20
17
|
input: TInput;
|
|
@@ -51,7 +48,7 @@ export interface DurableServiceConfig {
|
|
|
51
48
|
* Resolves tasks by id for resuming/recovering executions.
|
|
52
49
|
* Useful in Runner environments where tasks are registered in the Store registry.
|
|
53
50
|
*/
|
|
54
|
-
taskResolver?: (taskId: string) =>
|
|
51
|
+
taskResolver?: (taskId: string) => ITask<any, Promise<any>, any, any, any, any> | undefined;
|
|
55
52
|
audit?: {
|
|
56
53
|
enabled?: boolean;
|
|
57
54
|
emitter?: DurableAuditEmitter;
|
|
@@ -66,7 +63,7 @@ export interface DurableServiceConfig {
|
|
|
66
63
|
maxAttempts?: number;
|
|
67
64
|
timeout?: number;
|
|
68
65
|
/**
|
|
69
|
-
* When a queue is configured, `
|
|
66
|
+
* When a queue is configured, `start()` persists the execution and then enqueues it.
|
|
70
67
|
* If enqueue fails (eg. broker outage), the execution would otherwise remain "pending" forever.
|
|
71
68
|
*
|
|
72
69
|
* This delay arms a small store-backed timer as a failsafe so workers can retry resuming it
|
|
@@ -75,7 +72,7 @@ export interface DurableServiceConfig {
|
|
|
75
72
|
kickoffFailsafeDelayMs?: number;
|
|
76
73
|
};
|
|
77
74
|
schedules?: ScheduleConfig[];
|
|
78
|
-
tasks?: Array<
|
|
75
|
+
tasks?: Array<ITask<any, Promise<any>, any, any, any, any>>;
|
|
79
76
|
}
|
|
80
77
|
export interface ExecuteOptions {
|
|
81
78
|
timeout?: number;
|
|
@@ -94,8 +91,15 @@ export interface ScheduleOptions {
|
|
|
94
91
|
cron?: string;
|
|
95
92
|
interval?: number;
|
|
96
93
|
}
|
|
94
|
+
export interface DurableStartAndWaitResult<TResult = unknown> {
|
|
95
|
+
durable: {
|
|
96
|
+
executionId: string;
|
|
97
|
+
};
|
|
98
|
+
data: TResult;
|
|
99
|
+
}
|
|
97
100
|
export interface IDurableService {
|
|
98
|
-
|
|
101
|
+
start<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input?: TInput, options?: ExecuteOptions): Promise<string>;
|
|
102
|
+
start(task: string, input?: unknown, options?: ExecuteOptions): Promise<string>;
|
|
99
103
|
/**
|
|
100
104
|
* Request cancellation for an execution.
|
|
101
105
|
* Cancellation is cooperative: it marks the execution as cancelled and unblocks waiters,
|
|
@@ -106,24 +110,28 @@ export interface IDurableService {
|
|
|
106
110
|
timeout?: number;
|
|
107
111
|
waitPollIntervalMs?: number;
|
|
108
112
|
}): Promise<TResult>;
|
|
109
|
-
execute<TInput, TResult>(task: DurableTask<TInput, TResult>, input?: TInput, options?: ExecuteOptions): Promise<TResult>;
|
|
110
113
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* This mirrors the runtime contract where `wait()`/`execute()` treat
|
|
115
|
-
* "completed without result" as an error.
|
|
114
|
+
* Starts a workflow and waits for completion.
|
|
115
|
+
* Returns the started execution id together with the workflow result payload.
|
|
116
116
|
*/
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
startAndWait<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input?: TInput, options?: ExecuteOptions): Promise<DurableStartAndWaitResult<TResult>>;
|
|
118
|
+
startAndWait<TResult = unknown>(task: string, input?: unknown, options?: ExecuteOptions): Promise<DurableStartAndWaitResult<TResult>>;
|
|
119
|
+
schedule<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input: TInput | undefined, options: ScheduleOptions): Promise<string>;
|
|
120
|
+
schedule(task: string, input: unknown, options: ScheduleOptions): Promise<string>;
|
|
119
121
|
/**
|
|
120
122
|
* Idempotently create (or update) a recurring schedule (cron/interval) with a stable id.
|
|
121
123
|
* Safe to call concurrently from multiple processes.
|
|
122
124
|
*/
|
|
123
|
-
ensureSchedule<TInput>(task:
|
|
125
|
+
ensureSchedule<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>, input: TInput | undefined, options: ScheduleOptions & {
|
|
126
|
+
id: string;
|
|
127
|
+
}): Promise<string>;
|
|
128
|
+
ensureSchedule(task: string, input: unknown, options: ScheduleOptions & {
|
|
124
129
|
id: string;
|
|
125
130
|
}): Promise<string>;
|
|
126
131
|
recover(): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Starts the durable polling loop (timers/schedules processing).
|
|
134
|
+
*/
|
|
127
135
|
start(): void;
|
|
128
136
|
stop(): Promise<void>;
|
|
129
137
|
pauseSchedule(scheduleId: string): Promise<void>;
|
|
@@ -13,7 +13,7 @@ export interface IDurableStore {
|
|
|
13
13
|
listIncompleteExecutions(): Promise<Execution[]>;
|
|
14
14
|
/**
|
|
15
15
|
* Optional execution-level idempotency mapping.
|
|
16
|
-
* If supported, allows `
|
|
16
|
+
* If supported, allows `start(..., { idempotencyKey })` to dedupe workflow starts.
|
|
17
17
|
*/
|
|
18
18
|
getExecutionIdByIdempotencyKey?(params: {
|
|
19
19
|
taskId: string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { IDurableStore } from "../interfaces/store";
|
|
2
2
|
import type { IDurableQueue } from "../interfaces/queue";
|
|
3
3
|
import type { IEventBus } from "../interfaces/bus";
|
|
4
|
-
import type {
|
|
4
|
+
import type { DurableStartAndWaitResult, DurableServiceConfig, ExecuteOptions, ITaskExecutor } from "../interfaces/service";
|
|
5
|
+
import type { ITask } from "../../../../types/task";
|
|
5
6
|
import { type Execution } from "../types";
|
|
6
7
|
import type { TaskRegistry } from "./TaskRegistry";
|
|
7
8
|
import type { AuditLogger } from "./AuditLogger";
|
|
@@ -37,12 +38,41 @@ export declare class ExecutionManager {
|
|
|
37
38
|
private readonly auditLogger;
|
|
38
39
|
private readonly waitManager;
|
|
39
40
|
constructor(config: ExecutionManagerConfig, taskRegistry: TaskRegistry, auditLogger: AuditLogger, waitManager: WaitManager);
|
|
40
|
-
|
|
41
|
+
start(taskRef: string | ITask<any, Promise<any>, any, any, any, any>, input?: unknown, options?: ExecuteOptions): Promise<string>;
|
|
42
|
+
/**
|
|
43
|
+
* Start a workflow with deduplication: if the same (taskId, idempotencyKey)
|
|
44
|
+
* was already started, returns the existing executionId instead of creating
|
|
45
|
+
* a duplicate. Uses a distributed lock to prevent concurrent races.
|
|
46
|
+
*/
|
|
47
|
+
private startWithIdempotencyKey;
|
|
48
|
+
private assertCanExecute;
|
|
49
|
+
private assertStoreSupportsIdempotency;
|
|
50
|
+
/**
|
|
51
|
+
* Acquires a distributed lock around the idempotency check-and-set,
|
|
52
|
+
* falling back to lock-free operation when the store has no locking support.
|
|
53
|
+
*/
|
|
54
|
+
private withIdempotencyLock;
|
|
55
|
+
/**
|
|
56
|
+
* Recovery path: `setExecutionIdByIdempotencyKey` returned false (another
|
|
57
|
+
* writer won the race), so we re-read the mapping to get their executionId.
|
|
58
|
+
*/
|
|
59
|
+
private resolveRacedIdempotencyKey;
|
|
60
|
+
/**
|
|
61
|
+
* Creates a new execution record, persists it to the store, and logs an
|
|
62
|
+
* audit entry. Returns the executionId.
|
|
63
|
+
*/
|
|
64
|
+
private persistNewExecution;
|
|
65
|
+
/**
|
|
66
|
+
* Kicks off an execution with a failsafe timer for queue mode.
|
|
67
|
+
* If the queue enqueue succeeds, the timer is cleaned up immediately.
|
|
68
|
+
* If enqueue fails, the timer remains so the polling loop can retry later.
|
|
69
|
+
*/
|
|
70
|
+
private kickoffWithFailsafe;
|
|
41
71
|
cancelExecution(executionId: string, reason?: string): Promise<void>;
|
|
42
|
-
|
|
43
|
-
executeStrict<TInput, TResult>(task: undefined extends TResult ? never : DurableTask<TInput, TResult>, input?: TInput, options?: ExecuteOptions): Promise<TResult>;
|
|
72
|
+
startAndWait(taskRef: string | ITask<any, Promise<any>, any, any, any, any>, input?: unknown, options?: ExecuteOptions): Promise<DurableStartAndWaitResult<unknown>>;
|
|
44
73
|
processExecution(executionId: string): Promise<void>;
|
|
45
74
|
kickoffExecution(executionId: string): Promise<void>;
|
|
46
75
|
notifyExecutionFinished(execution: Execution): Promise<void>;
|
|
47
76
|
private runExecutionAttempt;
|
|
77
|
+
private resolveTaskReference;
|
|
48
78
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { IDurableStore } from "../interfaces/store";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ScheduleOptions } from "../interfaces/service";
|
|
3
3
|
import { type Schedule } from "../types";
|
|
4
4
|
import type { TaskRegistry } from "./TaskRegistry";
|
|
5
|
+
import type { ITask } from "../../../../types/task";
|
|
5
6
|
/**
|
|
6
7
|
* Creates and maintains durable schedules.
|
|
7
8
|
*
|
|
@@ -13,10 +14,10 @@ export declare class ScheduleManager {
|
|
|
13
14
|
private readonly store;
|
|
14
15
|
private readonly taskRegistry;
|
|
15
16
|
constructor(store: IDurableStore, taskRegistry: TaskRegistry);
|
|
16
|
-
ensureSchedule
|
|
17
|
+
ensureSchedule(taskRef: string | ITask<any, Promise<any>, any, any, any, any>, input: unknown, options: ScheduleOptions & {
|
|
17
18
|
id: string;
|
|
18
19
|
}): Promise<string>;
|
|
19
|
-
schedule
|
|
20
|
+
schedule(taskRef: string | ITask<any, Promise<any>, any, any, any, any>, input: unknown, options: ScheduleOptions): Promise<string>;
|
|
20
21
|
reschedule(schedule: Schedule, options?: {
|
|
21
22
|
lastRunAt?: Date;
|
|
22
23
|
}): Promise<void>;
|
|
@@ -30,4 +31,5 @@ export declare class ScheduleManager {
|
|
|
30
31
|
input?: unknown;
|
|
31
32
|
}): Promise<void>;
|
|
32
33
|
remove(id: string): Promise<void>;
|
|
34
|
+
private resolveTaskReference;
|
|
33
35
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ITask } from "../../../../types/task";
|
|
2
2
|
/**
|
|
3
3
|
* In-memory durable task registry.
|
|
4
4
|
*
|
|
5
5
|
* Durable executions persist only a `taskId` in the store, so the runtime needs a way
|
|
6
|
-
* to resolve `taskId ->
|
|
6
|
+
* to resolve `taskId -> ITask` when resuming. This registry holds tasks that
|
|
7
7
|
* were registered on the current process and optionally delegates to an external
|
|
8
8
|
* resolver for tasks defined elsewhere (useful for modular apps).
|
|
9
9
|
*/
|
|
10
10
|
export declare class TaskRegistry {
|
|
11
11
|
private readonly externalResolver?;
|
|
12
12
|
private readonly tasks;
|
|
13
|
-
constructor(externalResolver?: ((taskId: string) =>
|
|
14
|
-
register<TInput, TResult>(task:
|
|
15
|
-
find(taskId: string):
|
|
13
|
+
constructor(externalResolver?: ((taskId: string) => ITask<any, Promise<any>, any, any, any, any> | undefined) | undefined);
|
|
14
|
+
register<TInput, TResult>(task: ITask<TInput, Promise<TResult>, any, any, any, any>): void;
|
|
15
|
+
find(taskId: string): ITask<any, Promise<any>, any, any, any, any> | undefined;
|
|
16
16
|
}
|
|
@@ -12,7 +12,7 @@ export interface WaitConfig {
|
|
|
12
12
|
* - otherwise (or on bus issues) fall back to polling the store
|
|
13
13
|
*
|
|
14
14
|
* The durable store remains the source of truth; this manager is purely a convenience layer
|
|
15
|
-
* for callers that want `await durable.wait(...)` / `await durable.
|
|
15
|
+
* for callers that want `await durable.wait(...)` / `await durable.startAndWait(...)`.
|
|
16
16
|
*/
|
|
17
17
|
export declare class WaitManager {
|
|
18
18
|
private readonly store;
|
|
@@ -28,6 +28,7 @@ export { NoopEventBus } from "./bus/NoopEventBus";
|
|
|
28
28
|
export { RedisEventBus } from "./bus/RedisEventBus";
|
|
29
29
|
export { createDurableTestSetup, waitUntil } from "./test-utils";
|
|
30
30
|
export type { DurableTestSetup, DurableTestSetupOptions } from "./test-utils";
|
|
31
|
+
export { durableWorkflowTag, type DurableWorkflowTagConfig, } from "./tags/durableWorkflow.tag";
|
|
31
32
|
export { memoryDurableResource } from "./resources/memoryDurableResource";
|
|
32
33
|
export type { MemoryDurableResourceConfig } from "./resources/memoryDurableResource";
|
|
33
34
|
export { redisDurableResource } from "./resources/redisDurableResource";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface DurableWorkflowTagConfig {
|
|
2
|
+
/**
|
|
3
|
+
* Optional domain/category to group workflows (eg. "orders", "billing").
|
|
4
|
+
*/
|
|
5
|
+
category?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Optional metadata for dashboards/tooling.
|
|
8
|
+
*/
|
|
9
|
+
metadata?: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Marks a task as a durable workflow for runtime discovery.
|
|
13
|
+
*/
|
|
14
|
+
export declare const durableWorkflowTag: import("../..").ITag<DurableWorkflowTagConfig, void, void>;
|
|
@@ -4,7 +4,8 @@ import type { SerializerLike } from "../serializer";
|
|
|
4
4
|
export { nodeExposure } from "./exposure";
|
|
5
5
|
export { hasExposureContext, useExposureContext, } from "./exposure/requestContext";
|
|
6
6
|
export type * from "./exposure/resourceTypes";
|
|
7
|
-
export { createNodeFile } from "./files";
|
|
7
|
+
export { createNodeFile, NodeInputFile } from "./files";
|
|
8
|
+
export type { NodeReadable } from "./files";
|
|
8
9
|
export { readInputFileToBuffer, writeInputFileToPath } from "./files";
|
|
9
10
|
export { createHttpSmartClient, createHttpMixedClient } from "./http";
|
|
10
11
|
export type { HttpSmartClient, HttpSmartClientAuthConfig, HttpSmartClientConfig, MixedHttpClient, MixedHttpClientAuthConfig, MixedHttpClientConfig, Readable, } from "./http";
|
package/dist/types/public.d.ts
CHANGED
|
@@ -193,7 +193,9 @@ export declare const r: Readonly<{
|
|
|
193
193
|
tag: typeof import("./definers/builders/tag").tagBuilder;
|
|
194
194
|
override: typeof overrideBuilder;
|
|
195
195
|
asyncContext: typeof import("./definers/builders/asyncContext").asyncContextBuilder;
|
|
196
|
-
error: typeof import("./definers/builders/error").errorBuilder
|
|
196
|
+
error: typeof import("./definers/builders/error").errorBuilder & {
|
|
197
|
+
is: (error: unknown) => error is import("./public").RunnerError;
|
|
198
|
+
};
|
|
197
199
|
middleware: Readonly<{
|
|
198
200
|
task: typeof import("./definers/builders/middleware").taskMiddlewareBuilder;
|
|
199
201
|
resource: typeof import("./definers/builders/middleware").resourceMiddlewareBuilder;
|
|
@@ -204,6 +206,7 @@ export * from "./models";
|
|
|
204
206
|
export * from "./globals/types";
|
|
205
207
|
export * as Errors from "./errors";
|
|
206
208
|
export { PlatformAdapter, setPlatform } from "./platform";
|
|
209
|
+
export { RunnerError } from "./definers/defineError";
|
|
207
210
|
export * from "./http-client";
|
|
208
211
|
export * from "./http-fetch-tunnel.resource";
|
|
209
212
|
export { Serializer, SymbolPolicy, SymbolPolicyErrorMessage, } from "./serializer";
|
package/dist/types/testing.d.ts
CHANGED
|
@@ -12,7 +12,6 @@ export interface TestFacade {
|
|
|
12
12
|
* Helper to create a minimal test harness resource that wraps a root app (or any registerable)
|
|
13
13
|
* and exposes convenient testing utilities while running the full ecosystem
|
|
14
14
|
* (registration, overrides, middleware, events) without modifying the core API.
|
|
15
|
-
* @deprecated Use `run` instead with your testResource, as it provides the necessary toolkit.
|
|
16
15
|
*/
|
|
17
16
|
export declare function createTestResource(root: RegisterableItems, options?: {
|
|
18
17
|
overrides?: Array<IResource | ITask | ITaskMiddleware | IResourceMiddleware | IResourceWithConfig>;
|
|
@@ -6,9 +6,16 @@ export type ErrorReference = string | IErrorHelper<any>;
|
|
|
6
6
|
export type ThrowsList = ReadonlyArray<ErrorReference>;
|
|
7
7
|
export interface IErrorDefinition<TData extends DefaultErrorType = DefaultErrorType> {
|
|
8
8
|
id: string;
|
|
9
|
+
httpCode?: number;
|
|
9
10
|
serialize?: (data: TData) => string;
|
|
10
11
|
parse?: (data: string) => TData;
|
|
11
12
|
format?: (data: TData) => string;
|
|
13
|
+
/**
|
|
14
|
+
* Optional advice on how to fix the error. Appears in the stringified
|
|
15
|
+
* error message after the main message. Can be a static string or a
|
|
16
|
+
* function that receives the error data and returns a string.
|
|
17
|
+
*/
|
|
18
|
+
remediation?: string | ((data: TData) => string);
|
|
12
19
|
/**
|
|
13
20
|
* Validate error data on throw(). If provided, data is parsed first.
|
|
14
21
|
*/
|
|
@@ -17,8 +24,20 @@ export interface IErrorDefinition<TData extends DefaultErrorType = DefaultErrorT
|
|
|
17
24
|
}
|
|
18
25
|
export interface IErrorDefinitionFinal<TData extends DefaultErrorType> extends IErrorDefinition<TData> {
|
|
19
26
|
format: (data: TData) => string;
|
|
27
|
+
httpCode?: number;
|
|
28
|
+
remediation?: string | ((data: TData) => string);
|
|
20
29
|
}
|
|
21
30
|
export type DefaultErrorType = Record<string, unknown>;
|
|
31
|
+
/**
|
|
32
|
+
* Runtime error shape thrown by r.error()/defineError() helpers.
|
|
33
|
+
* Consumers can use helper.is(error) to narrow unknown values to this type.
|
|
34
|
+
*/
|
|
35
|
+
export interface IRunnerError<TData extends DefaultErrorType = DefaultErrorType> extends Error {
|
|
36
|
+
id: string;
|
|
37
|
+
httpCode?: number;
|
|
38
|
+
data: TData;
|
|
39
|
+
remediation?: string;
|
|
40
|
+
}
|
|
22
41
|
/**
|
|
23
42
|
* Runtime helper returned by defineError()/r.error().
|
|
24
43
|
* Contains helpers to throw typed errors and perform type-safe checks.
|
|
@@ -26,10 +45,12 @@ export type DefaultErrorType = Record<string, unknown>;
|
|
|
26
45
|
export interface IErrorHelper<TData extends DefaultErrorType = DefaultErrorType> {
|
|
27
46
|
/** Unique id for registration and DI */
|
|
28
47
|
id: string;
|
|
48
|
+
/** Optional HTTP status code associated with this error helper */
|
|
49
|
+
httpCode?: number;
|
|
29
50
|
/** Throw a typed error with the given data */
|
|
30
51
|
throw(data: TData): never;
|
|
31
52
|
/** Type guard for checking if an unknown error is this error */
|
|
32
|
-
is(error: unknown):
|
|
53
|
+
is(error: unknown): error is IRunnerError<TData>;
|
|
33
54
|
/** Brand symbol for runtime detection */
|
|
34
55
|
[symbolError]: true;
|
|
35
56
|
/** Return an optional dependency wrapper for this error */
|