@lostgradient/weft 0.2.0 → 0.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 +47 -22
- package/dist/cli/generated/operation-client.generated.d.ts +28 -1
- package/dist/cli/generated/operation-client.generated.js +2 -0
- package/dist/cli-main.js +79 -79
- package/dist/client/handle-delegation.d.ts +4 -0
- package/dist/client/handle-delegation.js +6 -0
- package/dist/client/http-client-requests.d.ts +2 -0
- package/dist/client/http-client-requests.js +3 -0
- package/dist/client/http-client.d.ts +4 -1
- package/dist/client/http-client.js +9 -1
- package/dist/client/interface.d.ts +57 -2
- package/dist/client/local.d.ts +4 -1
- package/dist/client/local.js +7 -0
- package/dist/client/start-body.d.ts +7 -1
- package/dist/client/start-body.js +13 -4
- package/dist/core/codec/extension-codec.js +4 -2
- package/dist/core/codec/index.d.ts +1 -0
- package/dist/core/codec/index.js +1 -0
- package/dist/core/codec/serializer-registry.d.ts +122 -0
- package/dist/core/codec/serializer-registry.js +51 -0
- package/dist/core/context/index.d.ts +10 -0
- package/dist/core/context/index.js +3 -0
- package/dist/core/context/internals.d.ts +10 -0
- package/dist/core/context/internals.js +5 -1
- package/dist/core/context/run-operation.d.ts +16 -3
- package/dist/core/context/run-operation.js +16 -7
- package/dist/core/context/speculative-child.js +2 -0
- package/dist/core/context/types.d.ts +6 -0
- package/dist/core/engine/bulk-operations-purge.js +1 -0
- package/dist/core/engine/bulk-operations.js +1 -1
- package/dist/core/engine/callback-creators-bundles.js +2 -1
- package/dist/core/engine/callback-creators-core.js +2 -1
- package/dist/core/engine/construction.d.ts +1 -1
- package/dist/core/engine/construction.js +15 -3
- package/dist/core/engine/disposal.js +12 -0
- package/dist/core/engine/engine-create-types.d.ts +0 -14
- package/dist/core/engine/engine-internal-types.d.ts +17 -0
- package/dist/core/engine/engine-leak-warnings.d.ts +6 -0
- package/dist/core/engine/engine-leak-warnings.js +4 -0
- package/dist/core/engine/engine-runtime-helpers.d.ts +17 -0
- package/dist/core/engine/engine-runtime-helpers.js +26 -5
- package/dist/core/engine/errors.d.ts +74 -0
- package/dist/core/engine/errors.js +25 -1
- package/dist/core/engine/handle-result.js +1 -1
- package/dist/core/engine/handles.d.ts +89 -40
- package/dist/core/engine/handles.js +25 -27
- package/dist/core/engine/index.d.ts +122 -4
- package/dist/core/engine/index.js +82 -5
- package/dist/core/engine/inline-launch-queue.d.ts +14 -0
- package/dist/core/engine/inline-launch-queue.js +32 -7
- package/dist/core/engine/internals.d.ts +26 -10
- package/dist/core/engine/lifecycle/fork-helpers.js +1 -7
- package/dist/core/engine/lifecycle/persist.js +5 -20
- package/dist/core/engine/lifecycle/recovered-services.d.ts +45 -0
- package/dist/core/engine/lifecycle/recovered-services.js +34 -0
- package/dist/core/engine/lifecycle/resume.js +33 -5
- package/dist/core/engine/lifecycle/shared.d.ts +8 -0
- package/dist/core/engine/lifecycle/start-batch.js +23 -12
- package/dist/core/engine/lifecycle/start-commit.d.ts +47 -0
- package/dist/core/engine/lifecycle/start-commit.js +27 -0
- package/dist/core/engine/lifecycle/start-exec.d.ts +30 -2
- package/dist/core/engine/lifecycle/start-exec.js +38 -0
- package/dist/core/engine/lifecycle/start-or-signal-resolution.d.ts +79 -0
- package/dist/core/engine/lifecycle/start-or-signal-resolution.js +60 -0
- package/dist/core/engine/lifecycle/start-or-signal.d.ts +45 -0
- package/dist/core/engine/lifecycle/start-or-signal.js +141 -0
- package/dist/core/engine/lifecycle/start.d.ts +3 -3
- package/dist/core/engine/lifecycle/start.js +42 -37
- package/dist/core/engine/lifecycle.d.ts +3 -2
- package/dist/core/engine/lifecycle.js +9 -2
- package/dist/core/engine/listing.js +1 -1
- package/dist/core/engine/operations-data.d.ts +16 -0
- package/dist/core/engine/operations-data.js +6 -0
- package/dist/core/engine/operations-time.d.ts +3 -2
- package/dist/core/engine/operations-time.js +6 -1
- package/dist/core/engine/persisted-data-version.d.ts +5 -9
- package/dist/core/engine/persisted-data-version.js +4 -5
- package/dist/core/engine/schedule-handle.d.ts +45 -0
- package/dist/core/engine/schedule-handle.js +26 -0
- package/dist/core/engine/schedules.d.ts +1 -1
- package/dist/core/engine/schedules.js +7 -3
- package/dist/core/engine/second-instance-detector.d.ts +96 -0
- package/dist/core/engine/second-instance-detector.js +108 -0
- package/dist/core/engine/signals.d.ts +22 -0
- package/dist/core/engine/signals.js +15 -0
- package/dist/core/engine/termination/cleanup.d.ts +25 -0
- package/dist/core/engine/termination/cleanup.js +21 -1
- package/dist/core/engine/termination/complete.js +4 -3
- package/dist/core/engine/termination/suspend.d.ts +68 -0
- package/dist/core/engine/termination/suspend.js +41 -0
- package/dist/core/engine/termination.d.ts +4 -2
- package/dist/core/engine/termination.js +2 -0
- package/dist/core/engine/validation.js +25 -1
- package/dist/core/engine/workflow-feed.d.ts +5 -3
- package/dist/core/events/event-map.d.ts +2 -1
- package/dist/core/events/workflow-events.d.ts +23 -0
- package/dist/core/events/workflow-events.js +9 -0
- package/dist/core/inline-execution-strategy.d.ts +5 -0
- package/dist/core/inline-execution-strategy.js +2 -1
- package/dist/core/list-filter-validation.js +2 -1
- package/dist/core/start-workflow-validation.d.ts +22 -0
- package/dist/core/start-workflow-validation.js +11 -1
- package/dist/core/step-context.d.ts +10 -6
- package/dist/core/step-context.js +7 -15
- package/dist/core/types/activity.d.ts +6 -3
- package/dist/core/types/identity.d.ts +8 -1
- package/dist/core/types/launch-metadata.d.ts +33 -0
- package/dist/core/types/launch-metadata.js +0 -0
- package/dist/core/types/message-handles.d.ts +25 -0
- package/dist/core/types/options.d.ts +90 -7
- package/dist/core/types/reviews.d.ts +2 -1
- package/dist/core/types/services-resolution.d.ts +47 -0
- package/dist/core/types/services-resolution.js +0 -0
- package/dist/core/types/state.d.ts +11 -11
- package/dist/core/types/workflow-builder.d.ts +5 -4
- package/dist/core/types/workflow-context.d.ts +25 -0
- package/dist/core/types/workflow-function.d.ts +17 -0
- package/dist/core/types/workflow-snapshot.d.ts +29 -0
- package/dist/core/types/workflow-snapshot.js +0 -0
- package/dist/core/types.d.ts +3 -0
- package/dist/core/types.js +3 -0
- package/dist/core/weft-error.d.ts +46 -14
- package/dist/core/weft-error.js +12 -1
- package/dist/diagnostics/doctor.js +6 -3
- package/dist/diagnostics/format.js +2 -2
- package/dist/diagnostics/types.d.ts +1 -0
- package/dist/diagnostics/version-check.js +6 -4
- package/dist/index.d.ts +10 -5
- package/dist/index.js +11 -2
- package/dist/json-schema.js +3 -3
- package/dist/mcp/cli.js +35 -35
- package/dist/mcp/list-filter.js +2 -1
- package/dist/mcp/session.js +1 -0
- package/dist/observability/index.js +2 -2
- package/dist/server/handler.js +30 -30
- package/dist/server/index.js +33 -33
- package/dist/server/interactive-operations.js +1 -0
- package/dist/server/operations/resume-workflow.js +2 -2
- package/dist/server/operations/start-or-signal-workflow.d.ts +39 -0
- package/dist/server/operations/start-or-signal-workflow.js +140 -0
- package/dist/server/operations/start-workflow-options.d.ts +32 -0
- package/dist/server/operations/start-workflow-options.js +63 -0
- package/dist/server/operations/start-workflow.js +7 -69
- package/dist/server/operations/suspend-workflow.d.ts +13 -0
- package/dist/server/operations/suspend-workflow.js +36 -0
- package/dist/server/rest-binding.d.ts +18 -7
- package/dist/server/rest-bindings.js +12 -0
- package/dist/server/runtime/task-dispatch.js +5 -3
- package/dist/server/runtime/task-polling.d.ts +16 -2
- package/dist/server/runtime/task-polling.js +20 -5
- package/dist/server/runtime/websocket-worker.js +8 -0
- package/dist/server/serve-internals.d.ts +8 -0
- package/dist/server/serve-internals.js +4 -2
- package/dist/server/task-state.d.ts +8 -0
- package/dist/service-worker/index.js +28 -28
- package/dist/storage/capabilities.d.ts +10 -2
- package/dist/storage/capabilities.js +2 -2
- package/dist/storage/http.js +2 -2
- package/dist/storage/index.d.ts +7 -1
- package/dist/storage/indexeddb.js +1 -1
- package/dist/storage/interface.d.ts +40 -0
- package/dist/storage/interface.js +1 -1
- package/dist/storage/key-prefixes.d.ts +1 -1
- package/dist/storage/key-prefixes.js +3 -0
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon-value-mapping.d.ts +47 -0
- package/dist/storage/neon-value-mapping.js +11 -0
- package/dist/storage/neon.d.ts +108 -0
- package/dist/storage/neon.js +10 -0
- package/dist/storage/node-sqlite-loader.d.ts +71 -0
- package/dist/storage/node-sqlite-loader.js +41 -0
- package/dist/storage/node-sqlite.d.ts +1 -19
- package/dist/storage/node-sqlite.js +38 -32
- package/dist/storage/postgres-key-value-queries.d.ts +79 -0
- package/dist/storage/postgres-key-value-queries.js +63 -0
- package/dist/storage/resolve.d.ts +2 -165
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/storage-configuration.d.ts +209 -0
- package/dist/storage/storage-configuration.js +0 -0
- package/dist/storage/text-value-store.d.ts +13 -10
- package/dist/storage/turso.js +2 -2
- package/dist/storage/typed-storage.js +1 -1
- package/dist/storage/web-extension.js +1 -1
- package/dist/testing/event-loop.d.ts +36 -2
- package/dist/testing/index.d.ts +31 -1
- package/dist/testing/index.js +33 -33
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/index.js +9 -5
- package/dist/worker/long-poll.js +4 -0
- package/dist/worker/protocol-messages.d.ts +20 -0
- package/dist/worker/protocol-schemas.d.ts +32 -0
- package/dist/worker/protocol-schemas.js +8 -4
- package/dist/worker/protocol-task-result.d.ts +28 -0
- package/dist/worker/protocol-task-result.js +76 -0
- package/dist/worker/protocol.d.ts +4 -15
- package/dist/worker/protocol.js +1 -1
- package/dist/worker/registry/fair-share.d.ts +29 -0
- package/dist/worker/registry/fair-share.js +30 -0
- package/dist/worker/registry/routing.d.ts +18 -0
- package/dist/worker/registry/routing.js +14 -0
- package/dist/worker/registry/types.d.ts +7 -0
- package/dist/worker/registry.d.ts +16 -1
- package/dist/worker/registry.js +24 -36
- package/package.json +17 -4
|
@@ -30,12 +30,16 @@ function validateTaskResultBody(body) {
|
|
|
30
30
|
return Response.json({ error: "Missing required fields: operationId, status" }, { status: 400 });
|
|
31
31
|
if (status !== "completed" && status !== "failed")
|
|
32
32
|
return Response.json({ error: 'status must be "completed" or "failed"' }, { status: 400 });
|
|
33
|
+
const rawAttemptToken = body.attemptToken;
|
|
34
|
+
if (rawAttemptToken !== void 0 && (typeof rawAttemptToken !== "string" || rawAttemptToken === ""))
|
|
35
|
+
return Response.json({ error: "attemptToken must be a non-empty string when present" }, { status: 400 });
|
|
33
36
|
return {
|
|
34
37
|
operationId,
|
|
35
38
|
status,
|
|
36
39
|
workerId: typeof body.workerId === "string" ? body.workerId : void 0,
|
|
37
40
|
value: body.value,
|
|
38
|
-
error: typeof body.error === "string" ? body.error : void 0
|
|
41
|
+
error: typeof body.error === "string" ? body.error : void 0,
|
|
42
|
+
attemptToken: rawAttemptToken
|
|
39
43
|
};
|
|
40
44
|
}
|
|
41
45
|
async function applyTaskResult(context, options, result, inflightRecord) {
|
|
@@ -69,6 +73,7 @@ export function createLongPollInflightRecord(queue, task) {
|
|
|
69
73
|
visibilityTimeout,
|
|
70
74
|
retryPolicy: task.retryPolicy,
|
|
71
75
|
workflowId: task.workflowId,
|
|
76
|
+
attemptToken: crypto.randomUUID(),
|
|
72
77
|
firstQueuedAt: task.firstQueuedAt ?? task.enqueuedAt ?? now,
|
|
73
78
|
lastQueuedAt: task.lastQueuedAt ?? task.enqueuedAt ?? now,
|
|
74
79
|
lastDispatchedAt: now,
|
|
@@ -87,7 +92,10 @@ export async function markTaskClaimedByLongPollWorker(context, options, queue, t
|
|
|
87
92
|
const normalizedInflightRecord = await transitionQueuedToInflight(options.engine.storage, task.operationId, inflightRecord);
|
|
88
93
|
recordTaskQueueLatencyMetric(context.metricsCollector, normalizedInflightRecord);
|
|
89
94
|
recordTaskBacklogMetric(context.metricsCollector, context.taskQueue);
|
|
90
|
-
return
|
|
95
|
+
return {
|
|
96
|
+
workerId: normalizedInflightRecord.workerId,
|
|
97
|
+
attemptToken: inflightRecord.attemptToken
|
|
98
|
+
};
|
|
91
99
|
}
|
|
92
100
|
export async function handleTaskPollRequest(context, options, request, url, principal) {
|
|
93
101
|
if (request.method !== "GET")
|
|
@@ -105,8 +113,8 @@ export async function handleTaskPollRequest(context, options, request, url, prin
|
|
|
105
113
|
return Response.json({ error: 'At least one "activity" query parameter is required' }, { status: 400 });
|
|
106
114
|
const rawTimeout = url.searchParams.get("timeout"), timeout = rawTimeout !== null ? Math.min(Math.max(0, Number(rawTimeout)), MAX_POLL_TIMEOUT) : DEFAULT_POLL_TIMEOUT, task = await context.taskQueue.poll(queue, activities, timeout, request.signal);
|
|
107
115
|
if (task !== null) {
|
|
108
|
-
const
|
|
109
|
-
return Response.json({ ...task, workerId });
|
|
116
|
+
const claim = await markTaskClaimedByLongPollWorker(context, options, queue, task);
|
|
117
|
+
return Response.json({ ...task, workerId: claim.workerId, attemptToken: claim.attemptToken });
|
|
110
118
|
}
|
|
111
119
|
return new Response(null, { status: 204 });
|
|
112
120
|
}
|
|
@@ -125,8 +133,15 @@ export async function handleTaskResultRequest(context, options, request, url, pr
|
|
|
125
133
|
if (validated instanceof Response)
|
|
126
134
|
return validated;
|
|
127
135
|
const inflightRecord = await readInflightRecord(options.engine.storage, validated.operationId);
|
|
128
|
-
if (inflightRecord !== null && (
|
|
136
|
+
if (inflightRecord !== null && !isLongPollCompletionAuthorized(inflightRecord, validated))
|
|
129
137
|
return Response.json({ error: "Forbidden" }, { status: 403 });
|
|
130
138
|
await applyTaskResult(context, options, validated, inflightRecord);
|
|
131
139
|
return Response.json({ ok: !0 });
|
|
132
140
|
}
|
|
141
|
+
function isLongPollCompletionAuthorized(inflightRecord, validated) {
|
|
142
|
+
if (validated.workerId === void 0 || inflightRecord.workerId !== validated.workerId)
|
|
143
|
+
return !1;
|
|
144
|
+
if (inflightRecord.attemptToken !== void 0 && validated.attemptToken !== void 0 && validated.attemptToken !== inflightRecord.attemptToken)
|
|
145
|
+
return !1;
|
|
146
|
+
return !0;
|
|
147
|
+
}
|
|
@@ -120,6 +120,14 @@ function onTaskResultMessage(context, options, ws, message, cleanupWorkflowIndex
|
|
|
120
120
|
});
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
123
|
+
if (!context.registry.isAssignedToAttempt(operationId, workerId, message.attemptToken)) {
|
|
124
|
+
sendWorkerProtocolMessage(ws, {
|
|
125
|
+
type: "protocolError",
|
|
126
|
+
code: "invalid_message",
|
|
127
|
+
message: `taskResult for operation "${operationId}" rejected \u2014 stale attempt token`
|
|
128
|
+
});
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
123
131
|
context.registry.completeTask(operationId);
|
|
124
132
|
context.deadlineTracker.remove(operationId);
|
|
125
133
|
cleanupWorkflowIndex(operationId);
|
|
@@ -11,6 +11,14 @@ import type { WebSocketData } from './json-rpc-websocket-runtime.ts';
|
|
|
11
11
|
import { createServerWebSocketHandlers } from './runtime/authentication-bridge.ts';
|
|
12
12
|
import type { ServerContext } from './runtime/context.ts';
|
|
13
13
|
import { type EventBroadcastingHandle } from './runtime/event-broadcasting.ts';
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
*
|
|
17
|
+
* Warning emitted when a server starts with no authentication and no explicit
|
|
18
|
+
* `unauthenticatedAccess` policy. Exported only for internal tests (see
|
|
19
|
+
* `tests/auth-warning-filter.test.ts`) and not part of the public API surface.
|
|
20
|
+
*/
|
|
21
|
+
export declare const NO_AUTHENTICATION_WARNING = "[weft] WARNING: server started with NO authentication; all non-public operations are publicly accessible. Configure serve({ auth }) to lock down, or set unauthenticatedAccess: \"reject\" in production to fail closed.";
|
|
14
22
|
/**
|
|
15
23
|
* Clamp a user-supplied `workerReconnectGracePeriodMs` into `[0, 5_000]`.
|
|
16
24
|
* Returns the default when undefined or non-finite.
|
|
@@ -26,7 +26,9 @@ import { reconcileOrphanedRecords, scanExpiredTasks } from "./runtime/task-recon
|
|
|
26
26
|
import { isInflightRecord, withRetry } from "./runtime/websocket-worker.js";
|
|
27
27
|
import { TaskQueue } from "./task-queue.js";
|
|
28
28
|
import { createWorkflowEventFeed } from "./workflow-event-feed.js";
|
|
29
|
-
const RECONCILIATION_MULTIPLIER = 12, DEFAULT_WORKER_RECONNECT_GRACE_PERIOD_MS = 100, MAX_WORKER_RECONNECT_GRACE_PERIOD_MS = 5000, AUTHENTICATION_REQUIRED_ENVIRONMENT_VARIABLE = "WEFT_SERVER_AUTHENTICATION_REQUIRED"
|
|
29
|
+
const RECONCILIATION_MULTIPLIER = 12, DEFAULT_WORKER_RECONNECT_GRACE_PERIOD_MS = 100, MAX_WORKER_RECONNECT_GRACE_PERIOD_MS = 5000, AUTHENTICATION_REQUIRED_ENVIRONMENT_VARIABLE = "WEFT_SERVER_AUTHENTICATION_REQUIRED";
|
|
30
|
+
export const NO_AUTHENTICATION_WARNING = '[weft] WARNING: server started with NO authentication; all non-public operations are publicly accessible. Configure serve({ auth }) to lock down, or set unauthenticatedAccess: "reject" in production to fail closed.';
|
|
31
|
+
const NO_AUTHENTICATION_REJECT_ERROR = '[weft] Refusing to start server with no authentication. Configure serve({ auth }) or set unauthenticatedAccess: "allow" only for trusted local development.', NO_AUTHENTICATION_ENVIRONMENT_ERROR = "[weft] Refusing to start server with no authentication because WEFT_SERVER_AUTHENTICATION_REQUIRED requires authentication. Configure serve({ auth }) or unset WEFT_SERVER_AUTHENTICATION_REQUIRED only for trusted local development.", TRUTHY_AUTHENTICATION_REQUIREMENT_VALUES = new Set(["1", "true", "yes", "on"]), FALSY_AUTHENTICATION_REQUIREMENT_VALUES = new Set(["0", "false", "no", "off"]);
|
|
30
32
|
export function clampWorkerReconnectGracePeriod(value) {
|
|
31
33
|
if (value === void 0 || !Number.isFinite(value))
|
|
32
34
|
return DEFAULT_WORKER_RECONNECT_GRACE_PERIOD_MS;
|
|
@@ -179,7 +181,7 @@ export function restoreInflightTasks(context, options) {
|
|
|
179
181
|
continue;
|
|
180
182
|
}
|
|
181
183
|
const remaining = record.deadline - now;
|
|
182
|
-
context.registry.assignTask(record.workerId, record.operationId, remaining);
|
|
184
|
+
context.registry.assignTask(record.workerId, record.operationId, remaining, void 0, record.attemptToken);
|
|
183
185
|
context.deadlineTracker.add({ operationId: record.operationId, deadline: record.deadline });
|
|
184
186
|
const tracked = context.registry.getWorkerTasks(record.workerId).find((t) => t.operationId === record.operationId);
|
|
185
187
|
if (tracked)
|
|
@@ -67,6 +67,14 @@ export interface InflightRecord extends TaskLifecycleFields {
|
|
|
67
67
|
retryPolicy?: RetryPolicy | undefined;
|
|
68
68
|
/** Workflow that dispatched this activity. Present when the dispatch included a workflowId. */
|
|
69
69
|
workflowId?: string | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Unique, unguessable token identifying this dispatch attempt. Rotated on every
|
|
72
|
+
* (re-)dispatch because each dispatch writes a fresh InflightRecord. The
|
|
73
|
+
* long-poll completion handler rejects a result whose echoed token does not
|
|
74
|
+
* match this value. Optional for back-compatible decoding of records written
|
|
75
|
+
* before this field existed; a missing token disables the check for that record.
|
|
76
|
+
*/
|
|
77
|
+
attemptToken?: string | undefined;
|
|
70
78
|
}
|
|
71
79
|
/** Persisted record for a task in the resolved state. */
|
|
72
80
|
export interface ResolvedRecord {
|