@temelj/workflow 0.0.0-alpha.1 → 0.0.0-alpha.2
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/dist/bundle.d.mts +29 -0
- package/dist/bundle.mjs +28 -0
- package/dist/client/create.d.mts +10 -2
- package/dist/client/create.mjs +5 -3
- package/dist/client/facades.mjs +6 -3
- package/dist/client/run-handle.d.mts +1 -0
- package/dist/client/run-handle.mjs +3 -1
- package/dist/cloudflare/cloudflare.d.mts +12 -0
- package/dist/cloudflare/codec.d.mts +16 -0
- package/dist/cloudflare/codec.mjs +25 -0
- package/dist/cloudflare/entrypoint.d.mts +12 -0
- package/dist/cloudflare/entrypoint.mjs +18 -0
- package/dist/cloudflare/execution.d.mts +13 -0
- package/dist/cloudflare/execution.mjs +28 -0
- package/dist/cloudflare/host.d.mts +19 -0
- package/dist/cloudflare/host.mjs +24 -0
- package/dist/cloudflare/mod.d.mts +8 -0
- package/dist/cloudflare/mod.mjs +7 -0
- package/dist/cloudflare/name.d.mts +5 -0
- package/dist/cloudflare/name.mjs +22 -0
- package/dist/cloudflare/registry.d.mts +24 -0
- package/dist/cloudflare/registry.mjs +26 -0
- package/dist/cloudflare/runtime.d.mts +30 -0
- package/dist/cloudflare/runtime.mjs +226 -0
- package/dist/cloudflare/step.d.mts +22 -0
- package/dist/cloudflare/step.mjs +160 -0
- package/dist/cloudflare/types.d.mts +87 -0
- package/dist/cloudflare/types.mjs +1 -0
- package/dist/engine/inspection-api.mjs +2 -1
- package/dist/engine/inspection.mjs +7 -5
- package/dist/engine/message.mjs +16 -13
- package/dist/engine/observation.mjs +1 -3
- package/dist/engine/run-data.mjs +2 -1
- package/dist/engine/run-service.mjs +2 -1
- package/dist/engine/start-api.mjs +2 -1
- package/dist/engine/state.mjs +2 -1
- package/dist/engine/stream.d.mts +1 -0
- package/dist/engine/stream.mjs +1 -0
- package/dist/engine/task.mjs +2 -1
- package/dist/errors/definition.d.mts +1 -0
- package/dist/errors/definition.mjs +3 -0
- package/dist/errors/details.mjs +2 -1
- package/dist/history/replay-projection.mjs +1 -3
- package/dist/mod.d.mts +2 -1
- package/dist/mod.mjs +2 -1
- package/dist/otel.mjs +4 -2
- package/dist/runtime.d.mts +1 -0
- package/dist/runtime.mjs +7 -3
- package/dist/store/cleanup.mjs +3 -26
- package/dist/store/message-index.mjs +2 -1
- package/dist/store/query.mjs +5 -3
- package/dist/store/run.mjs +9 -6
- package/dist/store/schedule.mjs +5 -3
- package/dist/store/step-attempt.mjs +5 -3
- package/dist/store-keys.d.mts +2 -1
- package/dist/store-keys.mjs +24 -1
- package/dist/types/common.d.mts +1 -0
- package/dist/types/common.mjs +1 -0
- package/dist/types/runtime.d.mts +10 -2
- package/dist/worker/error.d.mts +1 -0
- package/dist/worker/error.mjs +1 -0
- package/dist/worker.d.mts +1 -0
- package/dist/worker.mjs +1 -0
- package/package.json +15 -10
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Registry, WorkflowDefinitionConfig, WorkflowImplementation } from "./types/definition.mjs";
|
|
2
|
+
import { Schema } from "@temelj/standard-schema";
|
|
3
|
+
//#region src/bundle.d.ts
|
|
4
|
+
interface WorkflowImplementationLike {
|
|
5
|
+
readonly kind: "implementation";
|
|
6
|
+
readonly definition: {
|
|
7
|
+
readonly kind: "definition";
|
|
8
|
+
readonly config: WorkflowDefinitionConfig<Schema | undefined>;
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly version?: string;
|
|
11
|
+
};
|
|
12
|
+
readonly config: WorkflowDefinitionConfig<Schema | undefined>;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly version?: string;
|
|
15
|
+
readonly handler: (...args: never[]) => unknown;
|
|
16
|
+
}
|
|
17
|
+
/** Workflow value accepted by an immutable workflow bundle. */
|
|
18
|
+
type WorkflowBundleEntry = WorkflowImplementationLike | ((...args: never[]) => unknown);
|
|
19
|
+
/** Immutable collection of workflow implementations shared by workflow hosts. */
|
|
20
|
+
interface WorkflowBundle {
|
|
21
|
+
readonly kind: "workflow-bundle";
|
|
22
|
+
readonly workflows: readonly WorkflowImplementation<unknown, unknown>[];
|
|
23
|
+
}
|
|
24
|
+
/** Defines an immutable workflow collection for local and deployed workflow hosts. */
|
|
25
|
+
declare function defineWorkflowBundle(entries: readonly WorkflowBundleEntry[]): WorkflowBundle;
|
|
26
|
+
/** Creates an independent mutable registry populated from a workflow bundle. */
|
|
27
|
+
declare function createWorkflowBundleRegistry(bundle: WorkflowBundle): Registry;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { WorkflowBundle, WorkflowBundleEntry, createWorkflowBundleRegistry, defineWorkflowBundle };
|
package/dist/bundle.mjs
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { WorkflowDefinitionError } from "./errors/definition.mjs";
|
|
2
|
+
import "./errors/mod.mjs";
|
|
3
|
+
import { getCompiledWorkflowDefinition } from "./compiled-function.mjs";
|
|
4
|
+
import { Registry } from "./registry.mjs";
|
|
5
|
+
//#region src/bundle.ts
|
|
6
|
+
/** Defines an immutable workflow collection for local and deployed workflow hosts. */
|
|
7
|
+
function defineWorkflowBundle(entries) {
|
|
8
|
+
const registry = new Registry();
|
|
9
|
+
for (const entry of entries) registry.register(workflowImplementationFromBundleEntry(entry));
|
|
10
|
+
return Object.freeze({
|
|
11
|
+
kind: "workflow-bundle",
|
|
12
|
+
workflows: Object.freeze([...registry.entries()])
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/** Creates an independent mutable registry populated from a workflow bundle. */
|
|
16
|
+
function createWorkflowBundleRegistry(bundle) {
|
|
17
|
+
const registry = new Registry();
|
|
18
|
+
for (const workflow of bundle.workflows) registry.register(workflow);
|
|
19
|
+
return registry;
|
|
20
|
+
}
|
|
21
|
+
function workflowImplementationFromBundleEntry(entry) {
|
|
22
|
+
if (typeof entry !== "function") return entry;
|
|
23
|
+
const implementation = getCompiledWorkflowDefinition(entry);
|
|
24
|
+
if (implementation === void 0) WorkflowDefinitionError.compiledMetadataMissing();
|
|
25
|
+
return implementation;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { createWorkflowBundleRegistry, defineWorkflowBundle };
|
package/dist/client/create.d.mts
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import { RegistryLike, WorkflowDefinition, WorkflowHandler, WorkflowImplementation } from "../types/definition.mjs";
|
|
2
|
+
import { WorkflowBundle } from "../bundle.mjs";
|
|
2
3
|
import { CreateWorkflowEngineOptions } from "../types/engine-options.mjs";
|
|
3
4
|
import { WorkflowClientEngine, WorkflowWorkerEngine } from "../types/engine.mjs";
|
|
4
5
|
import { WorkflowClient as WorkflowClient$1, WorkflowClientAdminApi, WorkflowClientHooksApi, WorkflowClientLocksApi, WorkflowClientMessagesApi, WorkflowClientRunsApi, WorkflowClientSchedulesApi, WorkflowClientStreamsApi, WorkflowClientWorkersApi, WorkflowRunnable } from "../types/client.mjs";
|
|
5
6
|
//#region src/client/create.d.ts
|
|
6
7
|
type WorkflowClientEngineOptions = Omit<CreateWorkflowEngineOptions, "storage" | "store">;
|
|
7
|
-
interface
|
|
8
|
+
interface CreateWorkflowClientBaseOptions extends WorkflowClientEngineOptions {
|
|
8
9
|
readonly engine?: WorkflowClientEngine;
|
|
9
10
|
readonly workerEngine?: WorkflowWorkerEngine;
|
|
10
|
-
readonly registry?: RegistryLike;
|
|
11
11
|
}
|
|
12
|
+
/** Options for creating a workflow client with either a registry or an immutable bundle. */
|
|
13
|
+
type CreateWorkflowClientOptions = CreateWorkflowClientBaseOptions & ({
|
|
14
|
+
readonly registry?: RegistryLike;
|
|
15
|
+
readonly workflows?: never;
|
|
16
|
+
} | {
|
|
17
|
+
readonly registry?: never;
|
|
18
|
+
readonly workflows?: WorkflowBundle;
|
|
19
|
+
});
|
|
12
20
|
declare function createWorkflowClient(options?: CreateWorkflowClientOptions): WorkflowClient$1;
|
|
13
21
|
/** Workflow client facade implementation. */
|
|
14
22
|
declare class WorkflowClient implements WorkflowClient$1 {
|
package/dist/client/create.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { Registry } from "../registry.mjs";
|
|
2
|
+
import { createWorkflowBundleRegistry } from "../bundle.mjs";
|
|
1
3
|
import { createWorkflowClientAdminApi, createWorkflowClientHooksApi, createWorkflowClientLocksApi, createWorkflowClientMessagesApi, createWorkflowClientRunsApi, createWorkflowClientSchedulesApi, createWorkflowClientStreamsApi, createWorkflowClientWorkersApi } from "./facades.mjs";
|
|
2
4
|
import { createWorkflowEngine } from "../engine/create.mjs";
|
|
3
|
-
import { Registry } from "../registry.mjs";
|
|
4
5
|
//#region src/client/create.ts
|
|
5
6
|
function createWorkflowClient(options = {}) {
|
|
6
7
|
return new WorkflowClient(options);
|
|
@@ -20,14 +21,15 @@ var WorkflowClient = class {
|
|
|
20
21
|
#now;
|
|
21
22
|
#registry;
|
|
22
23
|
constructor(options = {}) {
|
|
23
|
-
const { engine, workerEngine, registry, now, ...engineOptions } = options;
|
|
24
|
+
const { engine, workerEngine, registry, workflows, now, ...engineOptions } = options;
|
|
25
|
+
if (registry !== void 0 && workflows !== void 0) throw new TypeError("Workflow client options cannot include both registry and workflows");
|
|
24
26
|
this.#now = now ?? Temporal.Now.instant;
|
|
25
27
|
this.#engine = engine ?? createWorkflowEngine({
|
|
26
28
|
...engineOptions,
|
|
27
29
|
now: this.#now
|
|
28
30
|
});
|
|
29
31
|
this.#workerEngine = workerEngine ?? workflowWorkerEngineFromCandidate(this.#engine);
|
|
30
|
-
this.#registry = registry ?? new Registry();
|
|
32
|
+
this.#registry = registry ?? (workflows === void 0 ? new Registry() : createWorkflowBundleRegistry(workflows));
|
|
31
33
|
this.runs = createWorkflowClientRunsApi(() => this.#engine, this.#now);
|
|
32
34
|
this.admin = createWorkflowClientAdminApi(() => this.#engine, this.#now);
|
|
33
35
|
this.schedules = createWorkflowClientSchedulesApi(() => this.#engine, this.#now);
|
package/dist/client/facades.mjs
CHANGED
|
@@ -120,13 +120,16 @@ function createWorkflowClientMessagesApi(getEngine) {
|
|
|
120
120
|
function createWorkflowClientWorkersApi(getEngine, getRegistry, now) {
|
|
121
121
|
return {
|
|
122
122
|
create: (options) => {
|
|
123
|
-
|
|
123
|
+
const engine = requireWorkflowWorkerEngine(getEngine());
|
|
124
|
+
return new WorkflowWorker(workerOptionsFromClientOptions(engine, getRegistry(), now, options));
|
|
124
125
|
},
|
|
125
126
|
processRun: async (runId, options) => {
|
|
126
|
-
|
|
127
|
+
const engine = requireWorkflowWorkerEngine(getEngine());
|
|
128
|
+
await new WorkflowWorker(workerOptionsFromClientOptions(engine, getRegistry(), now, workerOptionsFromWakeOptions(options))).processRun(runId);
|
|
127
129
|
},
|
|
128
130
|
wakeDueRuns: async (options) => {
|
|
129
|
-
const
|
|
131
|
+
const engine = requireWorkflowWorkerEngine(getEngine());
|
|
132
|
+
const worker = new WorkflowWorker(workerOptionsFromClientOptions(engine, getRegistry(), now, workerOptionsFromWakeOptions(options)));
|
|
130
133
|
const filters = options === void 0 ? {} : options;
|
|
131
134
|
const maxRuns = resolvePositiveSafeIntegerOption(options?.maxRuns, "Workflow worker maxRuns") ?? 1;
|
|
132
135
|
let processedRuns = 0;
|
|
@@ -11,6 +11,7 @@ import { WorkflowRunHandleReader, WorkflowRunMaintenance, WorkflowStreamReader }
|
|
|
11
11
|
import { WorkflowRunHandle as WorkflowRunHandle$1 } from "../types/client.mjs";
|
|
12
12
|
import "../mod.mjs";
|
|
13
13
|
import { StorageValue } from "@temelj/storage";
|
|
14
|
+
import "temporal-polyfill/global";
|
|
14
15
|
//#region src/client/run-handle.d.ts
|
|
15
16
|
type WorkflowRunHandleEngine = WorkflowRunHandleReader & WorkflowStreamReader & WorkflowRunMaintenance;
|
|
16
17
|
declare function createWorkflowRunHandle<TOutput>(engine: WorkflowRunHandleEngine, definition: WorkflowDefinition<unknown, TOutput>, runId: RunId, now: () => Temporal.Instant): WorkflowRunHandle$1<TOutput>;
|
|
@@ -4,6 +4,7 @@ import "../errors/mod.mjs";
|
|
|
4
4
|
import { workflowErrorFromRecord } from "../errors/records.mjs";
|
|
5
5
|
import { durationBetween } from "../temporal.mjs";
|
|
6
6
|
import { nonNegativeTimerDelay, sleep } from "../timer.mjs";
|
|
7
|
+
import "temporal-polyfill/global";
|
|
7
8
|
//#region src/client/run-handle.ts
|
|
8
9
|
const DEFAULT_RESULT_POLL_INTERVAL = Temporal.Duration.from({ milliseconds: 100 });
|
|
9
10
|
function createWorkflowRunHandle(engine, definition, runId, now) {
|
|
@@ -59,7 +60,8 @@ var WorkflowRunHandle = class {
|
|
|
59
60
|
if (result.kind === "failed") throw workflowErrorFromRecord(result.error);
|
|
60
61
|
let sleepDelay = pollInterval;
|
|
61
62
|
if (timeout !== void 0 && deadlineAt !== void 0) {
|
|
62
|
-
const
|
|
63
|
+
const nowInstant = this.#now();
|
|
64
|
+
const remainingDelay = durationTotal(durationBetween(nowInstant, deadlineAt));
|
|
63
65
|
if (remainingDelay <= 0) WorkflowResultTimeoutError.timedOut(this.runId, timeout);
|
|
64
66
|
sleepDelay = Math.min(sleepDelay, remainingDelay);
|
|
65
67
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/cloudflare/cloudflare.d.ts
|
|
2
|
+
declare module "cloudflare:workers" {
|
|
3
|
+
export abstract class WorkflowEntrypoint<TEnv = unknown, TPayload = unknown> {
|
|
4
|
+
protected readonly env: TEnv;
|
|
5
|
+
abstract run(event: unknown, step: unknown): Promise<unknown>;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
declare module "cloudflare:workflows" {
|
|
9
|
+
export class NonRetryableError extends Error {
|
|
10
|
+
constructor(message: string, name?: string);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region src/cloudflare/codec.d.ts
|
|
3
|
+
declare const cloudflareWorkflowEnvelopeSchema: z.ZodObject<{
|
|
4
|
+
kind: z.ZodLiteral<"temelj-workflow">;
|
|
5
|
+
version: z.ZodLiteral<1>;
|
|
6
|
+
workflowName: z.ZodString;
|
|
7
|
+
workflowVersion: z.ZodOptional<z.ZodString>;
|
|
8
|
+
input: z.ZodString;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
/** Serialized payload dispatched through the generic Cloudflare entrypoint. */
|
|
11
|
+
type CloudflareWorkflowEnvelope = z.infer<typeof cloudflareWorkflowEnvelopeSchema>;
|
|
12
|
+
declare function encodeCloudflareWorkflowValue(value: unknown): string;
|
|
13
|
+
declare function decodeCloudflareWorkflowValue(value: unknown, label: string): unknown;
|
|
14
|
+
declare function parseCloudflareWorkflowEnvelope(value: unknown): CloudflareWorkflowEnvelope;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { CloudflareWorkflowEnvelope, decodeCloudflareWorkflowValue, encodeCloudflareWorkflowValue, parseCloudflareWorkflowEnvelope };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createSuperJsonStorageCodec } from "@temelj/storage";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/cloudflare/codec.ts
|
|
4
|
+
const cloudflareWorkflowCodec = createSuperJsonStorageCodec({ format: "string" });
|
|
5
|
+
const cloudflareWorkflowEnvelopeSchema = z.object({
|
|
6
|
+
kind: z.literal("temelj-workflow"),
|
|
7
|
+
version: z.literal(1),
|
|
8
|
+
workflowName: z.string().min(1),
|
|
9
|
+
workflowVersion: z.string().min(1).optional(),
|
|
10
|
+
input: z.string()
|
|
11
|
+
});
|
|
12
|
+
function encodeCloudflareWorkflowValue(value) {
|
|
13
|
+
return cloudflareWorkflowCodec.encode(value);
|
|
14
|
+
}
|
|
15
|
+
function decodeCloudflareWorkflowValue(value, label) {
|
|
16
|
+
if (typeof value !== "string") throw new TypeError(`${label} must be an encoded string`);
|
|
17
|
+
return cloudflareWorkflowCodec.decode(value);
|
|
18
|
+
}
|
|
19
|
+
function parseCloudflareWorkflowEnvelope(value) {
|
|
20
|
+
const parsed = cloudflareWorkflowEnvelopeSchema.safeParse(value);
|
|
21
|
+
if (!parsed.success) throw new TypeError("Cloudflare Workflow payload is not a valid Temelj workflow envelope");
|
|
22
|
+
return parsed.data;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { decodeCloudflareWorkflowValue, encodeCloudflareWorkflowValue, parseCloudflareWorkflowEnvelope };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RegistryLike } from "../types/definition.mjs";
|
|
2
|
+
import { Logger } from "@temelj/log";
|
|
3
|
+
import { WorkflowEntrypoint } from "cloudflare:workers";
|
|
4
|
+
//#region src/cloudflare/entrypoint.d.ts
|
|
5
|
+
interface CreateCloudflareWorkflowEntrypointOptions {
|
|
6
|
+
readonly registry: RegistryLike;
|
|
7
|
+
readonly logger?: Logger;
|
|
8
|
+
}
|
|
9
|
+
/** Creates a Cloudflare Workflow entrypoint class that dispatches through a Temelj registry. */
|
|
10
|
+
declare function createCloudflareWorkflowEntrypoint<TEnv = unknown>(options: CreateCloudflareWorkflowEntrypointOptions): typeof WorkflowEntrypoint<TEnv, unknown>;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { CreateCloudflareWorkflowEntrypointOptions, createCloudflareWorkflowEntrypoint };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { runCloudflareWorkflow } from "./execution.mjs";
|
|
2
|
+
import { WorkflowEntrypoint } from "cloudflare:workers";
|
|
3
|
+
import { NonRetryableError } from "cloudflare:workflows";
|
|
4
|
+
//#region src/cloudflare/entrypoint.ts
|
|
5
|
+
/** Creates a Cloudflare Workflow entrypoint class that dispatches through a Temelj registry. */
|
|
6
|
+
function createCloudflareWorkflowEntrypoint(options) {
|
|
7
|
+
return class TemeljCloudflareWorkflowEntrypoint extends WorkflowEntrypoint {
|
|
8
|
+
async run(event, step) {
|
|
9
|
+
return await runCloudflareWorkflow({
|
|
10
|
+
registry: options.registry,
|
|
11
|
+
...options.logger === void 0 ? {} : { logger: options.logger },
|
|
12
|
+
nonRetryableError: NonRetryableError
|
|
13
|
+
}, event, step);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { createCloudflareWorkflowEntrypoint };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { RegistryLike } from "../types/definition.mjs";
|
|
2
|
+
import { CloudflareNonRetryableErrorConstructor, CloudflareWorkflowEvent, CloudflareWorkflowStep } from "./types.mjs";
|
|
3
|
+
import { Logger } from "@temelj/log";
|
|
4
|
+
//#region src/cloudflare/execution.d.ts
|
|
5
|
+
interface RunCloudflareWorkflowOptions {
|
|
6
|
+
readonly registry: RegistryLike;
|
|
7
|
+
readonly logger?: Logger;
|
|
8
|
+
readonly nonRetryableError?: CloudflareNonRetryableErrorConstructor;
|
|
9
|
+
}
|
|
10
|
+
/** Dispatches one Cloudflare Workflow invocation to a registered Temelj implementation. */
|
|
11
|
+
declare function runCloudflareWorkflow(options: RunCloudflareWorkflowOptions, event: CloudflareWorkflowEvent, step: CloudflareWorkflowStep): Promise<string>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { RunCloudflareWorkflowOptions, runCloudflareWorkflow };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { WorkflowCapabilityError } from "../errors/capability.mjs";
|
|
2
|
+
import "../errors/mod.mjs";
|
|
3
|
+
import { parseWorkflowInput } from "../definition.mjs";
|
|
4
|
+
import { decodeCloudflareWorkflowValue, encodeCloudflareWorkflowValue, parseCloudflareWorkflowEnvelope } from "./codec.mjs";
|
|
5
|
+
import { resolveCloudflareWorkflowImplementation } from "./registry.mjs";
|
|
6
|
+
import { createCloudflareWorkflowContext } from "./step.mjs";
|
|
7
|
+
//#region src/cloudflare/execution.ts
|
|
8
|
+
/** Dispatches one Cloudflare Workflow invocation to a registered Temelj implementation. */
|
|
9
|
+
async function runCloudflareWorkflow(options, event, step) {
|
|
10
|
+
const envelope = parseCloudflareWorkflowEnvelope(event.payload);
|
|
11
|
+
const implementation = resolveCloudflareWorkflowImplementation(options.registry, envelope.workflowName, envelope.workflowVersion);
|
|
12
|
+
if (implementation.config.retry !== void 0) WorkflowCapabilityError.unsupported("Cloudflare workflow-level retry configuration");
|
|
13
|
+
const rawInput = decodeCloudflareWorkflowValue(envelope.input, "Cloudflare Workflow input");
|
|
14
|
+
const input = await parseWorkflowInput(implementation.definition, rawInput);
|
|
15
|
+
const context = createCloudflareWorkflowContext({
|
|
16
|
+
input,
|
|
17
|
+
step,
|
|
18
|
+
runId: event.instanceId,
|
|
19
|
+
workflowName: implementation.name,
|
|
20
|
+
...implementation.version === void 0 ? {} : { workflowVersion: implementation.version },
|
|
21
|
+
createdAt: Temporal.Instant.fromEpochMilliseconds(event.timestamp.getTime()),
|
|
22
|
+
...options.logger === void 0 ? {} : { logger: options.logger },
|
|
23
|
+
...options.nonRetryableError === void 0 ? {} : { nonRetryableError: options.nonRetryableError }
|
|
24
|
+
});
|
|
25
|
+
return encodeCloudflareWorkflowValue(await implementation.handler(context));
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { runCloudflareWorkflow };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { WorkflowBundle } from "../bundle.mjs";
|
|
2
|
+
import { createCloudflareWorkflowEntrypoint } from "./entrypoint.mjs";
|
|
3
|
+
import { CloudflareWorkflowBinding } from "./types.mjs";
|
|
4
|
+
import { CloudflareWorkflowRuntime, CreateCloudflareWorkflowRuntimeOptions } from "./runtime.mjs";
|
|
5
|
+
import { Logger } from "@temelj/log";
|
|
6
|
+
//#region src/cloudflare/host.d.ts
|
|
7
|
+
interface CreateCloudflareWorkflowHostOptions {
|
|
8
|
+
readonly workflows: WorkflowBundle;
|
|
9
|
+
readonly logger?: Logger;
|
|
10
|
+
}
|
|
11
|
+
type CreateCloudflareWorkflowHostRuntimeOptions = Omit<CreateCloudflareWorkflowRuntimeOptions, "binding" | "registration" | "registry">;
|
|
12
|
+
interface CloudflareWorkflowHost<TEnv> {
|
|
13
|
+
readonly Entrypoint: ReturnType<typeof createCloudflareWorkflowEntrypoint<TEnv>>;
|
|
14
|
+
createRuntime(binding: CloudflareWorkflowBinding, options?: CreateCloudflareWorkflowHostRuntimeOptions): CloudflareWorkflowRuntime;
|
|
15
|
+
}
|
|
16
|
+
/** Creates a Cloudflare host backed by one immutable workflow bundle. */
|
|
17
|
+
declare function createCloudflareWorkflowHost<TEnv = unknown>(options: CreateCloudflareWorkflowHostOptions): CloudflareWorkflowHost<TEnv>;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { CloudflareWorkflowHost, CreateCloudflareWorkflowHostOptions, CreateCloudflareWorkflowHostRuntimeOptions, createCloudflareWorkflowHost };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createWorkflowBundleRegistry } from "../bundle.mjs";
|
|
2
|
+
import { createCloudflareWorkflowEntrypoint } from "./entrypoint.mjs";
|
|
3
|
+
import { createCloudflareWorkflowRuntime } from "./runtime.mjs";
|
|
4
|
+
//#region src/cloudflare/host.ts
|
|
5
|
+
/** Creates a Cloudflare host backed by one immutable workflow bundle. */
|
|
6
|
+
function createCloudflareWorkflowHost(options) {
|
|
7
|
+
const registry = createWorkflowBundleRegistry(options.workflows);
|
|
8
|
+
return {
|
|
9
|
+
Entrypoint: createCloudflareWorkflowEntrypoint({
|
|
10
|
+
registry,
|
|
11
|
+
...options.logger === void 0 ? {} : { logger: options.logger }
|
|
12
|
+
}),
|
|
13
|
+
createRuntime(binding, runtimeOptions = {}) {
|
|
14
|
+
return createCloudflareWorkflowRuntime({
|
|
15
|
+
...runtimeOptions,
|
|
16
|
+
binding,
|
|
17
|
+
registry,
|
|
18
|
+
registration: "static"
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { createCloudflareWorkflowHost };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CreateCloudflareWorkflowEntrypointOptions, createCloudflareWorkflowEntrypoint } from "./entrypoint.mjs";
|
|
2
|
+
import { CloudflareNonRetryableErrorConstructor, CloudflareWorkflowBinding, CloudflareWorkflowEvent, CloudflareWorkflowInstance, CloudflareWorkflowInstanceCreateOptions, CloudflareWorkflowInstanceStatus, CloudflareWorkflowStep, CloudflareWorkflowStepConfig, CloudflareWorkflowStepContext, CloudflareWorkflowStepEvent } from "./types.mjs";
|
|
3
|
+
import { RunCloudflareWorkflowOptions, runCloudflareWorkflow } from "./execution.mjs";
|
|
4
|
+
import { CloudflareWorkflowMessagesApi, CloudflareWorkflowRuntime, CloudflareWorkflowRuntimeRunsApi, CreateCloudflareWorkflowRuntimeOptions, createCloudflareWorkflowRuntime } from "./runtime.mjs";
|
|
5
|
+
import { CloudflareWorkflowHost, CreateCloudflareWorkflowHostOptions, CreateCloudflareWorkflowHostRuntimeOptions, createCloudflareWorkflowHost } from "./host.mjs";
|
|
6
|
+
import { createCloudflareWorkflowRegistry, resolveCloudflareWorkflowImplementation } from "./registry.mjs";
|
|
7
|
+
import "temporal-polyfill/global";
|
|
8
|
+
export { type CloudflareNonRetryableErrorConstructor, type CloudflareWorkflowBinding, type CloudflareWorkflowEvent, CloudflareWorkflowHost, type CloudflareWorkflowInstance, type CloudflareWorkflowInstanceCreateOptions, type CloudflareWorkflowInstanceStatus, CloudflareWorkflowMessagesApi, CloudflareWorkflowRuntime, CloudflareWorkflowRuntimeRunsApi, type CloudflareWorkflowStep, type CloudflareWorkflowStepConfig, type CloudflareWorkflowStepContext, type CloudflareWorkflowStepEvent, CreateCloudflareWorkflowEntrypointOptions, CreateCloudflareWorkflowHostOptions, CreateCloudflareWorkflowHostRuntimeOptions, CreateCloudflareWorkflowRuntimeOptions, RunCloudflareWorkflowOptions, createCloudflareWorkflowEntrypoint, createCloudflareWorkflowHost, createCloudflareWorkflowRegistry, createCloudflareWorkflowRuntime, resolveCloudflareWorkflowImplementation, runCloudflareWorkflow };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createCloudflareWorkflowRegistry, resolveCloudflareWorkflowImplementation } from "./registry.mjs";
|
|
2
|
+
import { runCloudflareWorkflow } from "./execution.mjs";
|
|
3
|
+
import { createCloudflareWorkflowEntrypoint } from "./entrypoint.mjs";
|
|
4
|
+
import { createCloudflareWorkflowRuntime } from "./runtime.mjs";
|
|
5
|
+
import { createCloudflareWorkflowHost } from "./host.mjs";
|
|
6
|
+
import "temporal-polyfill/global";
|
|
7
|
+
export { createCloudflareWorkflowEntrypoint, createCloudflareWorkflowHost, createCloudflareWorkflowRegistry, createCloudflareWorkflowRuntime, resolveCloudflareWorkflowImplementation, runCloudflareWorkflow };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
//#region src/cloudflare/name.d.ts
|
|
2
|
+
declare function makeCloudflareWorkflowStepName(value: string): string;
|
|
3
|
+
declare function makeCloudflareWorkflowEventType(value: string): string;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { makeCloudflareWorkflowEventType, makeCloudflareWorkflowStepName };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/cloudflare/name.ts
|
|
2
|
+
const CLOUDFLARE_NAME_CHARACTERS = /[^a-zA-Z0-9_-]+/g;
|
|
3
|
+
function makeCloudflareWorkflowStepName(value) {
|
|
4
|
+
return makeCloudflareName(value, 256, "step");
|
|
5
|
+
}
|
|
6
|
+
function makeCloudflareWorkflowEventType(value) {
|
|
7
|
+
return makeCloudflareName(value, 100, "event");
|
|
8
|
+
}
|
|
9
|
+
function makeCloudflareName(value, maximumLength, fallback) {
|
|
10
|
+
const normalized = value.replace(CLOUDFLARE_NAME_CHARACTERS, "-").replace(/^-+/, "");
|
|
11
|
+
return `${(normalized === "" ? fallback : normalized).slice(0, maximumLength - 16)}-${fnv1a(value)}`.slice(0, maximumLength);
|
|
12
|
+
}
|
|
13
|
+
function fnv1a(value) {
|
|
14
|
+
let hash = 14695981039346656037n;
|
|
15
|
+
for (let index = 0; index < value.length; index++) {
|
|
16
|
+
hash ^= BigInt(value.charCodeAt(index));
|
|
17
|
+
hash = BigInt.asUintN(64, hash * 1099511628211n);
|
|
18
|
+
}
|
|
19
|
+
return hash.toString(36).padStart(13, "0");
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { makeCloudflareWorkflowEventType, makeCloudflareWorkflowStepName };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RegistryLike, WorkflowDefinitionConfig, WorkflowImplementation } from "../types/definition.mjs";
|
|
2
|
+
import { CompiledWorkflowFunction } from "../types/client.mjs";
|
|
3
|
+
import { Registry } from "../registry.mjs";
|
|
4
|
+
import { Schema } from "@temelj/standard-schema";
|
|
5
|
+
//#region src/cloudflare/registry.d.ts
|
|
6
|
+
interface CloudflareWorkflowImplementationLike {
|
|
7
|
+
readonly kind: "implementation";
|
|
8
|
+
readonly definition: {
|
|
9
|
+
readonly kind: "definition";
|
|
10
|
+
readonly config: WorkflowDefinitionConfig<Schema | undefined>;
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly version?: string;
|
|
13
|
+
};
|
|
14
|
+
readonly config: WorkflowDefinitionConfig<Schema | undefined>;
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly version?: string;
|
|
17
|
+
readonly handler: (...args: never[]) => unknown;
|
|
18
|
+
}
|
|
19
|
+
type CloudflareWorkflowRegistryEntry = CloudflareWorkflowImplementationLike | CompiledWorkflowFunction<readonly unknown[], unknown>;
|
|
20
|
+
/** Creates a registry shared by the Cloudflare entrypoint and runtime facade. */
|
|
21
|
+
declare function createCloudflareWorkflowRegistry(entries?: readonly CloudflareWorkflowRegistryEntry[]): Registry;
|
|
22
|
+
declare function resolveCloudflareWorkflowImplementation(registry: RegistryLike, name: string, version?: string): WorkflowImplementation<unknown, unknown>;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { createCloudflareWorkflowRegistry, resolveCloudflareWorkflowImplementation };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { WorkflowDefinitionError } from "../errors/definition.mjs";
|
|
2
|
+
import "../errors/mod.mjs";
|
|
3
|
+
import { getCompiledWorkflowDefinition } from "../compiled-function.mjs";
|
|
4
|
+
import { Registry } from "../registry.mjs";
|
|
5
|
+
//#region src/cloudflare/registry.ts
|
|
6
|
+
/** Creates a registry shared by the Cloudflare entrypoint and runtime facade. */
|
|
7
|
+
function createCloudflareWorkflowRegistry(entries = []) {
|
|
8
|
+
const registry = new Registry();
|
|
9
|
+
for (const entry of entries) {
|
|
10
|
+
if (typeof entry === "function") {
|
|
11
|
+
const implementation = getCompiledWorkflowDefinition(entry);
|
|
12
|
+
if (implementation === void 0) WorkflowDefinitionError.compiledMetadataMissing();
|
|
13
|
+
registry.register(implementation);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
registry.register(entry);
|
|
17
|
+
}
|
|
18
|
+
return registry;
|
|
19
|
+
}
|
|
20
|
+
function resolveCloudflareWorkflowImplementation(registry, name, version) {
|
|
21
|
+
const implementation = version === void 0 ? registry.latest(name) : registry.getByName(name, version);
|
|
22
|
+
if (implementation === void 0) throw new TypeError(`Cloudflare Workflow implementation is not registered: ${version === void 0 ? name : `${name}@${version}`}`);
|
|
23
|
+
return implementation;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { createCloudflareWorkflowRegistry, resolveCloudflareWorkflowImplementation };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { RunId } from "../types/run-id.mjs";
|
|
2
|
+
import { RegistryLike, WorkflowStartTarget } from "../types/definition.mjs";
|
|
3
|
+
import { MessageChannel, SendMessageChannelOptions, SendMessageInput } from "../types/message.mjs";
|
|
4
|
+
import "../types/run.mjs";
|
|
5
|
+
import { WorkflowRunHandle } from "../types/client.mjs";
|
|
6
|
+
import { WorkflowRuntime, WorkflowRuntimeRunsApi } from "../types/runtime.mjs";
|
|
7
|
+
import { CloudflareWorkflowBinding } from "./types.mjs";
|
|
8
|
+
//#region src/cloudflare/runtime.d.ts
|
|
9
|
+
interface CreateCloudflareWorkflowRuntimeOptions {
|
|
10
|
+
readonly binding: CloudflareWorkflowBinding;
|
|
11
|
+
readonly registry?: RegistryLike;
|
|
12
|
+
readonly resultPollInterval?: Temporal.Duration;
|
|
13
|
+
/** Controls whether workflows absent from the initial registry may be registered at runtime. */
|
|
14
|
+
readonly registration?: "dynamic" | "static";
|
|
15
|
+
}
|
|
16
|
+
interface CloudflareWorkflowRuntimeRunsApi extends WorkflowRuntimeRunsApi {
|
|
17
|
+
get<TInput, TOutput, TRawInput = TInput>(workflow: WorkflowStartTarget<TInput, TOutput, TRawInput>, runId: RunId): Promise<WorkflowRunHandle<TOutput>>;
|
|
18
|
+
}
|
|
19
|
+
interface CloudflareWorkflowMessagesApi {
|
|
20
|
+
send(runId: RunId, options: SendMessageInput): Promise<void>;
|
|
21
|
+
send<TPayload, TKey = void>(runId: RunId, channel: MessageChannel<TPayload, TKey>, options?: SendMessageChannelOptions<TPayload, TKey>): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
interface CloudflareWorkflowRuntime extends WorkflowRuntime {
|
|
24
|
+
readonly runs: CloudflareWorkflowRuntimeRunsApi;
|
|
25
|
+
readonly messages: CloudflareWorkflowMessagesApi;
|
|
26
|
+
}
|
|
27
|
+
/** Creates a runtime facade over a generic Cloudflare Workflow binding. */
|
|
28
|
+
declare function createCloudflareWorkflowRuntime(options: CreateCloudflareWorkflowRuntimeOptions): CloudflareWorkflowRuntime;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { CloudflareWorkflowMessagesApi, CloudflareWorkflowRuntime, CloudflareWorkflowRuntimeRunsApi, CreateCloudflareWorkflowRuntimeOptions, createCloudflareWorkflowRuntime };
|