@lostgradient/weft 0.13.0 → 0.15.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 +3 -8
- package/dist/cli/generated/operation-catalog.snapshot.json +977 -77
- package/dist/cli/generated/operation-client.generated.d.ts +14 -21
- package/dist/cli/help-text.d.ts +3 -3
- package/dist/cli/help-text.js +4 -4
- package/dist/cli/operation-catalog-snapshot.d.ts +1 -1
- package/dist/cli/schedule.js +1 -3
- package/dist/cli/serve-registrations.d.ts +3 -3
- package/dist/cli/serve-registrations.js +2 -2
- package/dist/cli/shutdown.d.ts +11 -0
- package/dist/cli/shutdown.js +25 -0
- package/dist/cli/validate.d.ts +1 -1
- package/dist/cli/version-check.d.ts +1 -1
- package/dist/cli/version-check.js +1 -1
- package/dist/cli-main.js +10 -29
- package/dist/client/http-client-storage.js +7 -35
- package/dist/client/http-request.d.ts +8 -9
- package/dist/client/http-request.js +3 -25
- package/dist/core/aggregate-validation.d.ts +2 -5
- package/dist/core/aggregate-validation.js +2 -10
- package/dist/core/atomic-state.js +21 -24
- package/dist/core/context/parallel-operations.d.ts +0 -2
- package/dist/core/context/parallel-operations.js +0 -7
- package/dist/core/engine/callback-checkpoint-persistence.d.ts +2 -0
- package/dist/core/engine/callback-checkpoint-persistence.js +6 -3
- package/dist/core/engine/callback-creators.js +4 -22
- package/dist/core/engine/checkpoint-io.d.ts +1 -1
- package/dist/core/engine/operations-coordination.js +1 -1
- package/dist/core/engine/parallel-dispatch.d.ts +1 -1
- package/dist/core/engine/parallel-dispatch.js +1 -1
- package/dist/core/engine/review-list-entries.d.ts +1 -1
- package/dist/core/engine/review-list-entries.js +6 -30
- package/dist/core/engine/schedules.js +4 -1
- package/dist/core/engine/timeline-coordinator-detail.d.ts +1 -1
- package/dist/core/inline-execution-strategy.js +8 -46
- package/dist/core/list-filter-validation.d.ts +2 -5
- package/dist/core/list-filter-validation.js +2 -10
- package/dist/core/review/index.d.ts +87 -0
- package/dist/core/review/index.js +29 -0
- package/dist/core/types/schedules.d.ts +13 -31
- package/dist/core/validation-issues.d.ts +13 -0
- package/dist/core/validation-issues.js +9 -0
- package/dist/diagnostics/validate.d.ts +9 -30
- package/dist/diagnostics/validate.js +83 -21
- package/dist/diagnostics/version-check.d.ts +2 -2
- package/dist/http.js +2 -2
- package/dist/indexeddb.js +1 -1
- package/dist/mcp/session.js +2 -10
- package/dist/mcp/tools.js +2 -10
- package/dist/mcp/tuple-list-equality.d.ts +1 -0
- package/dist/mcp/tuple-list-equality.js +12 -0
- package/dist/server/openapi-error-responses.js +0 -14
- package/dist/server/operation-catalog/index.d.ts +1 -1
- package/dist/server/operation-catalog/raise-fault.d.ts +2 -1
- package/dist/server/operation-catalog/types.d.ts +3 -2
- package/dist/server/operation-fault.d.ts +1 -4
- package/dist/server/operation-fault.js +1 -1
- package/dist/server/operation-registry.d.ts +2 -31
- package/dist/server/operations/bulk-filter-helpers.d.ts +17 -17
- package/dist/server/operations/bulk-filter-helpers.js +16 -37
- package/dist/server/operations/bulk-mutate-workflow-tags.d.ts +27 -27
- package/dist/server/operations/bulk-signal-workflows.d.ts +26 -26
- package/dist/server/operations/create-schedule.js +7 -19
- package/dist/server/operations/event-stream-contracts.d.ts +20 -0
- package/dist/server/operations/event-stream-contracts.js +67 -0
- package/dist/server/operations/fleet-events-sse.js +21 -50
- package/dist/server/operations/fleet-events-subscription.js +8 -12
- package/dist/server/operations/list-reviews.js +2 -19
- package/dist/server/operations/list-workflows.d.ts +43 -43
- package/dist/server/operations/list-workflows.js +9 -30
- package/dist/server/operations/purge-workflows.d.ts +11 -11
- package/dist/server/operations/recover-all.js +1 -10
- package/dist/server/operations/schedule-rest-body.d.ts +11 -0
- package/dist/server/operations/schedule-rest-body.js +28 -0
- package/dist/server/operations/single-workflow-control-operation.d.ts +2 -1
- package/dist/server/operations/update-schedule.js +7 -19
- package/dist/server/operations/workflow-events-sse.js +29 -67
- package/dist/server/operations/workflow-events-subscription.js +9 -27
- package/dist/server/runtime/task-polling.js +3 -5
- package/dist/server/task-state.d.ts +2 -8
- package/dist/server/task-state.js +10 -7
- package/dist/storage/bounded-ndjson-response.d.ts +5 -0
- package/dist/storage/bounded-ndjson-response.js +33 -0
- package/dist/storage/bun-sql.js +52 -49
- package/dist/storage/conditional-batch-body.d.ts +7 -0
- package/dist/storage/conditional-batch-body.js +14 -0
- package/dist/storage/http.js +5 -37
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon.d.ts +3 -16
- package/dist/storage/neon.js +2 -2
- package/dist/storage/node-sqlite-loader.d.ts +2 -2
- package/dist/storage/node-sqlite.js +32 -17
- package/dist/storage/postgres.js +1 -1
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/turso.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/web-extension.js +1 -1
- package/dist/worker/execute-with-interceptors.d.ts +1 -1
- package/dist/worker/execute-with-interceptors.js +2 -2
- package/dist/worker/index.js +5 -5
- package/dist/worker/long-poll.js +3 -4
- package/dist/worker/protocol-messages.d.ts +11 -19
- package/dist/worker/protocol-schemas.d.ts +8 -8
- package/dist/worker/protocol-schemas.js +4 -4
- package/dist/worker/protocol-task-result.d.ts +1 -0
- package/dist/worker/protocol-task-result.js +1 -3
- package/dist/worker/protocol.js +1 -1
- package/dist/worker/registry/types.d.ts +2 -7
- package/dist/worker/registry.d.ts +3 -9
- package/dist/worker/registry.js +2 -5
- package/package.json +1 -1
|
@@ -1,85 +1,85 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import type {
|
|
2
|
+
import type { PaginatedResult, WorkflowSummary } from '../../core/types.ts';
|
|
3
3
|
import type { UnknownRestBinding } from '../rest-bindings.ts';
|
|
4
4
|
declare const listWorkflowsInput: z.ZodObject<{
|
|
5
|
-
status: z.ZodOptional<z.ZodUnion<readonly [z.
|
|
5
|
+
status: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
6
6
|
type: z.ZodOptional<z.ZodString>;
|
|
7
|
-
scheduleId: z.ZodOptional<z.ZodString>;
|
|
8
|
-
parentWorkflowId: z.ZodOptional<z.ZodString>;
|
|
9
|
-
parentWorkflowExecutionToken: z.ZodOptional<z.ZodString>;
|
|
10
|
-
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
11
|
-
attributes: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
12
|
-
key: z.ZodString;
|
|
13
|
-
value: z.ZodOptional<z.ZodCustom<SearchAttributeValue, SearchAttributeValue>>;
|
|
14
|
-
gt: z.ZodOptional<z.ZodCustom<SearchAttributeValue, SearchAttributeValue>>;
|
|
15
|
-
lt: z.ZodOptional<z.ZodCustom<SearchAttributeValue, SearchAttributeValue>>;
|
|
16
|
-
gte: z.ZodOptional<z.ZodCustom<SearchAttributeValue, SearchAttributeValue>>;
|
|
17
|
-
lte: z.ZodOptional<z.ZodCustom<SearchAttributeValue, SearchAttributeValue>>;
|
|
18
|
-
}, z.core.$strip>>>;
|
|
19
|
-
limit: z.ZodOptional<z.ZodNumber>;
|
|
20
|
-
offset: z.ZodOptional<z.ZodNumber>;
|
|
21
7
|
idPrefix: z.ZodOptional<z.ZodString>;
|
|
22
8
|
createdAt: z.ZodOptional<z.ZodObject<{
|
|
23
9
|
gte: z.ZodOptional<z.ZodNumber>;
|
|
24
|
-
gt: z.ZodOptional<z.ZodNumber>;
|
|
25
10
|
lte: z.ZodOptional<z.ZodNumber>;
|
|
11
|
+
gt: z.ZodOptional<z.ZodNumber>;
|
|
26
12
|
lt: z.ZodOptional<z.ZodNumber>;
|
|
27
|
-
}, z.core.$
|
|
13
|
+
}, z.core.$strict>>;
|
|
28
14
|
updatedAt: z.ZodOptional<z.ZodObject<{
|
|
29
15
|
gte: z.ZodOptional<z.ZodNumber>;
|
|
30
|
-
gt: z.ZodOptional<z.ZodNumber>;
|
|
31
16
|
lte: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
gt: z.ZodOptional<z.ZodNumber>;
|
|
32
18
|
lt: z.ZodOptional<z.ZodNumber>;
|
|
33
|
-
}, z.core.$
|
|
19
|
+
}, z.core.$strict>>;
|
|
34
20
|
executionDeadline: z.ZodOptional<z.ZodObject<{
|
|
35
21
|
gte: z.ZodOptional<z.ZodNumber>;
|
|
36
|
-
gt: z.ZodOptional<z.ZodNumber>;
|
|
37
22
|
lte: z.ZodOptional<z.ZodNumber>;
|
|
23
|
+
gt: z.ZodOptional<z.ZodNumber>;
|
|
38
24
|
lt: z.ZodOptional<z.ZodNumber>;
|
|
39
|
-
}, z.core.$
|
|
40
|
-
failureCategory: z.ZodOptional<z.ZodUnion<readonly [z.
|
|
25
|
+
}, z.core.$strict>>;
|
|
26
|
+
failureCategory: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
41
27
|
include: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
42
|
-
|
|
28
|
+
scheduleId: z.ZodOptional<z.ZodString>;
|
|
29
|
+
parentWorkflowId: z.ZodOptional<z.ZodString>;
|
|
30
|
+
parentWorkflowExecutionToken: z.ZodOptional<z.ZodString>;
|
|
31
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
32
|
+
attributes: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
33
|
+
key: z.ZodUnion<readonly [z.ZodString, z.ZodAny]>;
|
|
34
|
+
value: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
35
|
+
gt: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
36
|
+
lt: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
37
|
+
gte: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
38
|
+
lte: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
39
|
+
}, z.core.$strip>>>;
|
|
40
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
offset: z.ZodOptional<z.ZodNumber>;
|
|
42
|
+
}, z.core.$strict>;
|
|
43
43
|
export type ListWorkflowsInput = z.infer<typeof listWorkflowsInput>;
|
|
44
44
|
export type ListWorkflowsOutput = PaginatedResult<WorkflowSummary>;
|
|
45
45
|
export declare const listWorkflowsOperation: import("../operation-catalog.ts").OperationDefinition<{
|
|
46
|
-
status?:
|
|
46
|
+
status?: string | string[] | undefined;
|
|
47
47
|
type?: string | undefined;
|
|
48
|
-
scheduleId?: string | undefined;
|
|
49
|
-
parentWorkflowId?: string | undefined;
|
|
50
|
-
parentWorkflowExecutionToken?: string | undefined;
|
|
51
|
-
tags?: string[] | undefined;
|
|
52
|
-
attributes?: {
|
|
53
|
-
key: string;
|
|
54
|
-
value?: SearchAttributeValue | undefined;
|
|
55
|
-
gt?: SearchAttributeValue | undefined;
|
|
56
|
-
lt?: SearchAttributeValue | undefined;
|
|
57
|
-
gte?: SearchAttributeValue | undefined;
|
|
58
|
-
lte?: SearchAttributeValue | undefined;
|
|
59
|
-
}[] | undefined;
|
|
60
|
-
limit?: number | undefined;
|
|
61
|
-
offset?: number | undefined;
|
|
62
48
|
idPrefix?: string | undefined;
|
|
63
49
|
createdAt?: {
|
|
64
50
|
gte?: number | undefined;
|
|
65
|
-
gt?: number | undefined;
|
|
66
51
|
lte?: number | undefined;
|
|
52
|
+
gt?: number | undefined;
|
|
67
53
|
lt?: number | undefined;
|
|
68
54
|
} | undefined;
|
|
69
55
|
updatedAt?: {
|
|
70
56
|
gte?: number | undefined;
|
|
71
|
-
gt?: number | undefined;
|
|
72
57
|
lte?: number | undefined;
|
|
58
|
+
gt?: number | undefined;
|
|
73
59
|
lt?: number | undefined;
|
|
74
60
|
} | undefined;
|
|
75
61
|
executionDeadline?: {
|
|
76
62
|
gte?: number | undefined;
|
|
77
|
-
gt?: number | undefined;
|
|
78
63
|
lte?: number | undefined;
|
|
64
|
+
gt?: number | undefined;
|
|
79
65
|
lt?: number | undefined;
|
|
80
66
|
} | undefined;
|
|
81
|
-
failureCategory?:
|
|
67
|
+
failureCategory?: string | string[] | undefined;
|
|
82
68
|
include?: string | string[] | undefined;
|
|
69
|
+
scheduleId?: string | undefined;
|
|
70
|
+
parentWorkflowId?: string | undefined;
|
|
71
|
+
parentWorkflowExecutionToken?: string | undefined;
|
|
72
|
+
tags?: string[] | undefined;
|
|
73
|
+
attributes?: {
|
|
74
|
+
key: any;
|
|
75
|
+
value?: unknown;
|
|
76
|
+
gt?: unknown;
|
|
77
|
+
lt?: unknown;
|
|
78
|
+
gte?: unknown;
|
|
79
|
+
lte?: unknown;
|
|
80
|
+
}[] | undefined;
|
|
81
|
+
limit?: number | undefined;
|
|
82
|
+
offset?: number | undefined;
|
|
83
83
|
}, ListWorkflowsOutput, unknown>;
|
|
84
84
|
export declare const listWorkflowsRestBinding: UnknownRestBinding;
|
|
85
85
|
export {};
|
|
@@ -2,45 +2,24 @@ import { z } from "zod";
|
|
|
2
2
|
import { WorkflowListScanCapExceededError } from "../../core/engine/workflow-indexes.js";
|
|
3
3
|
import {
|
|
4
4
|
ListFilterValidationError,
|
|
5
|
+
listFilterObjectSchema,
|
|
5
6
|
normalizeListFilter
|
|
6
7
|
} from "../../core/list-filter-validation.js";
|
|
7
8
|
import { coerceStartWorkflowTags } from "../../core/start-workflow-validation.js";
|
|
8
9
|
import { defineOperation } from "../operation-registry.js";
|
|
9
10
|
import { extractListFilterFromQuery } from "./list-filter-query-extractor.js";
|
|
10
11
|
import { shapeRestFault } from "./operation-helpers.js";
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
15
|
-
}), attributeFilterSchema = z.object({
|
|
16
|
-
key: z.string().min(1),
|
|
17
|
-
value: searchAttributeValueSchema.optional(),
|
|
18
|
-
gt: searchAttributeValueSchema.optional(),
|
|
19
|
-
lt: searchAttributeValueSchema.optional(),
|
|
20
|
-
gte: searchAttributeValueSchema.optional(),
|
|
21
|
-
lte: searchAttributeValueSchema.optional()
|
|
22
|
-
}), timeRangeSchema = z.object({
|
|
23
|
-
gte: z.number().optional(),
|
|
24
|
-
gt: z.number().optional(),
|
|
25
|
-
lte: z.number().optional(),
|
|
26
|
-
lt: z.number().optional()
|
|
27
|
-
}), listIncludeSchema = z.union([z.string(), z.array(z.string()).min(1)]).refine((value) => Array.isArray(value) ? value.every((entry) => entry === "failureCategory") : value === "failureCategory", { message: 'include must be "failureCategory"' }), listWorkflowsInput = z.object({
|
|
28
|
-
status: z.union([workflowStatusSchema, z.array(workflowStatusSchema)]).optional(),
|
|
12
|
+
const listIncludeSchema = z.union([z.string(), z.array(z.string()).min(1)]).refine((value) => Array.isArray(value) ? value.every((entry) => entry === "failureCategory") : value === "failureCategory", { message: 'include must be "failureCategory"' }), listWorkflowsInput = z.object({
|
|
13
|
+
...listFilterObjectSchema.shape,
|
|
14
|
+
status: z.union([z.string(), z.array(z.string())]).optional(),
|
|
29
15
|
type: z.string().optional(),
|
|
30
|
-
scheduleId: z.string().min(1).optional(),
|
|
31
|
-
parentWorkflowId: z.string().min(1).optional(),
|
|
32
|
-
parentWorkflowExecutionToken: z.string().min(1).optional(),
|
|
33
|
-
tags: z.array(z.string()).optional(),
|
|
34
|
-
attributes: z.array(attributeFilterSchema).optional(),
|
|
35
|
-
limit: z.number().int().min(1).max(1000).optional(),
|
|
36
|
-
offset: z.number().int().min(0).optional(),
|
|
37
16
|
idPrefix: z.string().optional(),
|
|
38
|
-
createdAt:
|
|
39
|
-
updatedAt:
|
|
40
|
-
executionDeadline:
|
|
41
|
-
failureCategory: z.union([
|
|
17
|
+
createdAt: z.object(listFilterObjectSchema.shape.createdAt.unwrap().shape).strict().optional(),
|
|
18
|
+
updatedAt: z.object(listFilterObjectSchema.shape.updatedAt.unwrap().shape).strict().optional(),
|
|
19
|
+
executionDeadline: z.object(listFilterObjectSchema.shape.executionDeadline.unwrap().shape).strict().optional(),
|
|
20
|
+
failureCategory: z.union([z.string(), z.array(z.string())]).optional(),
|
|
42
21
|
include: listIncludeSchema.optional()
|
|
43
|
-
}), listWorkflowsOutput = z.unknown();
|
|
22
|
+
}).strict(), listWorkflowsOutput = z.unknown();
|
|
44
23
|
export const listWorkflowsOperation = defineOperation({
|
|
45
24
|
name: "weft.workflows.list",
|
|
46
25
|
mcpExposable: !1,
|
|
@@ -11,34 +11,34 @@ export declare const purgeWorkflowsOperation: import("../operation-catalog.ts").
|
|
|
11
11
|
parentWorkflowExecutionToken?: string | undefined;
|
|
12
12
|
tags?: string[] | undefined;
|
|
13
13
|
attributes?: {
|
|
14
|
-
key:
|
|
15
|
-
value?:
|
|
16
|
-
gt?:
|
|
17
|
-
lt?:
|
|
18
|
-
gte?:
|
|
19
|
-
lte?:
|
|
14
|
+
key: any;
|
|
15
|
+
value?: unknown;
|
|
16
|
+
gt?: unknown;
|
|
17
|
+
lt?: unknown;
|
|
18
|
+
gte?: unknown;
|
|
19
|
+
lte?: unknown;
|
|
20
20
|
}[] | undefined;
|
|
21
|
-
limit?: number | undefined;
|
|
22
21
|
offset?: number | undefined;
|
|
23
22
|
idPrefix?: string | undefined;
|
|
24
|
-
failureCategory?: import("../../index.ts").FailureCategory | import("../../index.ts").FailureCategory[] | undefined;
|
|
25
23
|
createdAt?: {
|
|
26
24
|
gte?: number | undefined;
|
|
27
|
-
gt?: number | undefined;
|
|
28
25
|
lte?: number | undefined;
|
|
26
|
+
gt?: number | undefined;
|
|
29
27
|
lt?: number | undefined;
|
|
30
28
|
} | undefined;
|
|
31
29
|
updatedAt?: {
|
|
32
30
|
gte?: number | undefined;
|
|
33
|
-
gt?: number | undefined;
|
|
34
31
|
lte?: number | undefined;
|
|
32
|
+
gt?: number | undefined;
|
|
35
33
|
lt?: number | undefined;
|
|
36
34
|
} | undefined;
|
|
37
35
|
executionDeadline?: {
|
|
38
36
|
gte?: number | undefined;
|
|
39
|
-
gt?: number | undefined;
|
|
40
37
|
lte?: number | undefined;
|
|
38
|
+
gt?: number | undefined;
|
|
41
39
|
lt?: number | undefined;
|
|
42
40
|
} | undefined;
|
|
41
|
+
failureCategory?: import("../../index.ts").FailureCategory | import("../../index.ts").FailureCategory[] | undefined;
|
|
42
|
+
limit?: number | undefined;
|
|
43
43
|
}, PurgeResult, unknown>;
|
|
44
44
|
export declare const purgeWorkflowsRestBinding: UnknownRestBinding;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { WorkflowTypeNotRegisteredForRecoveryError } from "../../core/engine.js";
|
|
3
|
-
import { shapeRestFaultBody } from "../operation-fault.js";
|
|
4
3
|
import { defineOperation } from "../operation-registry.js";
|
|
5
4
|
import { shapeRestFault } from "./operation-helpers.js";
|
|
6
5
|
const recoverAllInput = z.object({}), recoverAllOutput = z.object({
|
|
@@ -54,13 +53,5 @@ export const recoverAllOperation = defineOperation({
|
|
|
54
53
|
function shapeRecoverAllFault(fault) {
|
|
55
54
|
if (fault.code !== "Conflict" || fault.data.missingTypes === void 0)
|
|
56
55
|
return shapeRestFault(fault);
|
|
57
|
-
return
|
|
58
|
-
...shapeRestFaultBody(fault, "workflow_type_not_registered_for_recovery"),
|
|
59
|
-
missingTypes: fault.data.missingTypes,
|
|
60
|
-
missingWorkflowCount: fault.data.missingWorkflowCount,
|
|
61
|
-
samplesTruncated: fault.data.samplesTruncated
|
|
62
|
-
}), {
|
|
63
|
-
status: 409,
|
|
64
|
-
headers: { "Content-Type": "application/json" }
|
|
65
|
-
});
|
|
56
|
+
return shapeRestFault(fault, { message: "workflow_type_not_registered_for_recovery" });
|
|
66
57
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RestInputContext } from '../rest-binding.ts';
|
|
2
|
+
export type SharedScheduleRestFields = {
|
|
3
|
+
readonly cronExpression: unknown;
|
|
4
|
+
readonly every: unknown;
|
|
5
|
+
readonly description: unknown;
|
|
6
|
+
readonly overlap: unknown;
|
|
7
|
+
readonly backfill: unknown;
|
|
8
|
+
readonly jitter: unknown;
|
|
9
|
+
};
|
|
10
|
+
export declare function parseScheduleRestBodyRequestRecord(request: Request, context?: RestInputContext): Promise<Record<string, unknown>>;
|
|
11
|
+
export declare function extractSharedScheduleRestFields(record: Record<string, unknown>): SharedScheduleRestFields;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readRestJsonBody } from "../rest-body.js";
|
|
2
|
+
import { invalidParamsFault, isOperationFault } from "./operation-helpers.js";
|
|
3
|
+
function isJsonObjectLikeRecord(value) {
|
|
4
|
+
return typeof value === "object" && value !== null;
|
|
5
|
+
}
|
|
6
|
+
export async function parseScheduleRestBodyRequestRecord(request, context) {
|
|
7
|
+
let body;
|
|
8
|
+
try {
|
|
9
|
+
body = await readRestJsonBody(request, context);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
if (isOperationFault(error))
|
|
12
|
+
throw error;
|
|
13
|
+
throw invalidParamsFault("Invalid JSON body");
|
|
14
|
+
}
|
|
15
|
+
if (!isJsonObjectLikeRecord(body))
|
|
16
|
+
throw invalidParamsFault("Request body must be a JSON object");
|
|
17
|
+
return body;
|
|
18
|
+
}
|
|
19
|
+
export function extractSharedScheduleRestFields(record) {
|
|
20
|
+
return {
|
|
21
|
+
cronExpression: Object.hasOwn(record, "cronExpression") ? record.cronExpression : void 0,
|
|
22
|
+
every: Object.hasOwn(record, "every") ? record.every : void 0,
|
|
23
|
+
description: record.description,
|
|
24
|
+
overlap: record.overlap,
|
|
25
|
+
backfill: record.backfill,
|
|
26
|
+
jitter: record.jitter
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { z } from 'zod';
|
|
2
2
|
import type { Engine } from '../../core/engine.ts';
|
|
3
|
+
import type { FaultCode } from '../../core/fault-code.ts';
|
|
3
4
|
import type { OperationDefinition } from '../operation-catalog.ts';
|
|
4
|
-
import type {
|
|
5
|
+
import type { OperationFault } from '../operation-fault.ts';
|
|
5
6
|
type SingleWorkflowControlInput = {
|
|
6
7
|
readonly workflowId: string;
|
|
7
8
|
};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { normalizeScheduleUpdateOptions } from "../../core/engine/validation/schedule.js";
|
|
3
3
|
import { defineOperation } from "../operation-registry.js";
|
|
4
|
-
import {
|
|
5
|
-
import { invalidParamsFault, isOperationFault, shapeRestFault } from "./operation-helpers.js";
|
|
4
|
+
import { invalidParamsFault, shapeRestFault } from "./operation-helpers.js";
|
|
6
5
|
import { mapScheduleErrorToFault, validateScheduleInputCadence } from "./schedule-faults.js";
|
|
6
|
+
import {
|
|
7
|
+
extractSharedScheduleRestFields,
|
|
8
|
+
parseScheduleRestBodyRequestRecord
|
|
9
|
+
} from "./schedule-rest-body.js";
|
|
7
10
|
const updateScheduleInput = z.object({
|
|
8
11
|
scheduleId: z.string().min(1),
|
|
9
12
|
cronExpression: z.unknown().optional().describe("Cron expression. Supply exactly one of cronExpression or every; runtime validation requires a non-empty string."),
|
|
@@ -100,25 +103,10 @@ export const updateScheduleOperation = defineOperation({
|
|
|
100
103
|
jitter: { kind: "body-field", bodyField: "jitter" }
|
|
101
104
|
},
|
|
102
105
|
extractInput: async (request, pathParams, context) => {
|
|
103
|
-
|
|
104
|
-
try {
|
|
105
|
-
body = await readRestJsonBody(request, context);
|
|
106
|
-
} catch (error) {
|
|
107
|
-
if (isOperationFault(error))
|
|
108
|
-
throw error;
|
|
109
|
-
throw invalidParamsFault("Invalid JSON body");
|
|
110
|
-
}
|
|
111
|
-
if (typeof body !== "object" || body === null)
|
|
112
|
-
throw invalidParamsFault("Request body must be a JSON object");
|
|
113
|
-
const record = body;
|
|
106
|
+
const record = await parseScheduleRestBodyRequestRecord(request, context);
|
|
114
107
|
return {
|
|
115
108
|
scheduleId: pathParams.id ?? "",
|
|
116
|
-
|
|
117
|
-
every: Object.hasOwn(record, "every") ? record.every : void 0,
|
|
118
|
-
description: record.description,
|
|
119
|
-
overlap: record.overlap,
|
|
120
|
-
backfill: record.backfill,
|
|
121
|
-
jitter: record.jitter
|
|
109
|
+
...extractSharedScheduleRestFields(record)
|
|
122
110
|
};
|
|
123
111
|
},
|
|
124
112
|
success: { kind: "empty", status: 204 },
|
|
@@ -4,6 +4,13 @@ import { isAuthenticated } from "../principal.js";
|
|
|
4
4
|
import {
|
|
5
5
|
decodeCursor
|
|
6
6
|
} from "../workflow-event-feed.js";
|
|
7
|
+
import {
|
|
8
|
+
createReplayAwareClosableIterable,
|
|
9
|
+
isClosableAsyncIterable,
|
|
10
|
+
isReplayAwareClosableIterable,
|
|
11
|
+
workflowEventEnvelopeSchema,
|
|
12
|
+
workflowEventParameterizedAccess
|
|
13
|
+
} from "./event-stream-contracts.js";
|
|
7
14
|
import { invalidParamsFault, shapeRestFault } from "./operation-helpers.js";
|
|
8
15
|
import {
|
|
9
16
|
createEventEnvelopeSSEStream,
|
|
@@ -19,15 +26,7 @@ const INITIAL_CURSOR = "-1", MAX_WORKFLOW_SSE_REPLAY_EVENTS = 1000, MAXIMUM_WORK
|
|
|
19
26
|
selector: z.enum(["events", "tokens"]).default("events"),
|
|
20
27
|
fromCursor: cursorSchema.optional(),
|
|
21
28
|
lastEventId: cursorSchema.optional()
|
|
22
|
-
}), workflowEventsSseOutputSchema = z.custom(isAsyncIterable, "Expected async iterable workflow event stream")
|
|
23
|
-
kind: z.string(),
|
|
24
|
-
workflowId: z.string(),
|
|
25
|
-
selector: z.enum(["events", "tokens"]),
|
|
26
|
-
sequence: z.number(),
|
|
27
|
-
cursor: z.string(),
|
|
28
|
-
emittedAtMs: z.number(),
|
|
29
|
-
payload: z.unknown()
|
|
30
|
-
});
|
|
29
|
+
}), workflowEventsSseOutputSchema = z.custom(isAsyncIterable, "Expected async iterable workflow event stream");
|
|
31
30
|
export const workflowEventsSseOperation = defineOperation({
|
|
32
31
|
name: "weft.workflows.events.sse",
|
|
33
32
|
mcpExposable: !1,
|
|
@@ -39,20 +38,7 @@ export const workflowEventsSseOperation = defineOperation({
|
|
|
39
38
|
inputSchema: workflowEventsSseInputSchema,
|
|
40
39
|
outputSchema: workflowEventsSseOutputSchema,
|
|
41
40
|
eventSchema: workflowEventEnvelopeSchema,
|
|
42
|
-
parameterizedAccess:
|
|
43
|
-
discriminator: "selector",
|
|
44
|
-
defaultValue: "events",
|
|
45
|
-
variants: [
|
|
46
|
-
{
|
|
47
|
-
value: "events",
|
|
48
|
-
access: { kind: "scoped", scopes: { kind: "anyOf", scopes: ["events:read"] } }
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
value: "tokens",
|
|
52
|
-
access: { kind: "scoped", scopes: { kind: "anyOf", scopes: ["streams:read"] } }
|
|
53
|
-
}
|
|
54
|
-
]
|
|
55
|
-
},
|
|
41
|
+
parameterizedAccess: workflowEventParameterizedAccess,
|
|
56
42
|
producibleFaults: ["InvalidParams", "UnsupportedTransport"],
|
|
57
43
|
access: { kind: "authenticated" },
|
|
58
44
|
authorize: async ({ input, principal }) => {
|
|
@@ -97,12 +83,6 @@ function isAsyncIterable(value) {
|
|
|
97
83
|
return !1;
|
|
98
84
|
return typeof value[Symbol.asyncIterator] === "function";
|
|
99
85
|
}
|
|
100
|
-
function isClosableWorkflowEventsIterable(value) {
|
|
101
|
-
return "close" in value && typeof value.close === "function";
|
|
102
|
-
}
|
|
103
|
-
function isReplayAwareWorkflowEventsIterable(value) {
|
|
104
|
-
return isClosableWorkflowEventsIterable(value) && "replayComplete" in value && value.replayComplete instanceof Promise;
|
|
105
|
-
}
|
|
106
86
|
function createWorkflowEventsIterable(input, context) {
|
|
107
87
|
const feed = context.workflowEventFeed;
|
|
108
88
|
if (feed === void 0)
|
|
@@ -110,51 +90,33 @@ function createWorkflowEventsIterable(input, context) {
|
|
|
110
90
|
const lease = context.acquireWorkflowStreamConnection?.(input.workflowId);
|
|
111
91
|
if (lease === null)
|
|
112
92
|
throw maximumWorkflowStreamsFault();
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
source = feed.subscribe(subscribeOptions);
|
|
133
|
-
} catch (error) {
|
|
134
|
-
close();
|
|
135
|
-
throw error;
|
|
136
|
-
}
|
|
137
|
-
return {
|
|
138
|
-
async* [Symbol.asyncIterator]() {
|
|
139
|
-
try {
|
|
140
|
-
for await (const envelope of source)
|
|
141
|
-
yield envelope;
|
|
142
|
-
} finally {
|
|
143
|
-
await close();
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
replayComplete: replayComplete.promise,
|
|
147
|
-
close
|
|
148
|
-
};
|
|
93
|
+
const fromCursor = input.lastEventId ?? input.fromCursor ?? INITIAL_CURSOR, controller = new AbortController;
|
|
94
|
+
return createReplayAwareClosableIterable((onReplayComplete) => {
|
|
95
|
+
const subscribeOptions = {
|
|
96
|
+
workflowId: input.workflowId,
|
|
97
|
+
selector: input.selector,
|
|
98
|
+
fromCursor,
|
|
99
|
+
signal: controller.signal,
|
|
100
|
+
replayLimit: MAX_WORKFLOW_SSE_REPLAY_EVENTS,
|
|
101
|
+
onReplayComplete,
|
|
102
|
+
createReplayLimitError: (count, limit) => invalidParamsFault(`Workflow event replay window is ${count} events; maximum is ${limit}. Supply a more recent fromCursor.`)
|
|
103
|
+
};
|
|
104
|
+
return feed.subscribe(subscribeOptions);
|
|
105
|
+
}, {
|
|
106
|
+
close: () => {
|
|
107
|
+
controller.abort();
|
|
108
|
+
lease?.release();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
149
111
|
}
|
|
150
112
|
function shapeWorkflowEventsSseSuccess(output, request) {
|
|
151
|
-
const close =
|
|
113
|
+
const close = isClosableAsyncIterable(output) ? () => output.close() : async () => {
|
|
152
114
|
return;
|
|
153
115
|
};
|
|
154
116
|
return new Response(createEventEnvelopeSSEStream({
|
|
155
117
|
iterable: output,
|
|
156
118
|
close,
|
|
157
|
-
...
|
|
119
|
+
...isReplayAwareClosableIterable(output) ? { ready: output.replayComplete } : {},
|
|
158
120
|
signal: request.signal
|
|
159
121
|
}), {
|
|
160
122
|
status: 200,
|
|
@@ -4,6 +4,11 @@ import { isAuthenticated } from "../principal.js";
|
|
|
4
4
|
import {
|
|
5
5
|
decodeCursor
|
|
6
6
|
} from "../workflow-event-feed.js";
|
|
7
|
+
import {
|
|
8
|
+
createReplayAwareClosableIterable,
|
|
9
|
+
workflowEventEnvelopeSchema,
|
|
10
|
+
workflowEventParameterizedAccess
|
|
11
|
+
} from "./event-stream-contracts.js";
|
|
7
12
|
import { invalidParamsFault } from "./operation-helpers.js";
|
|
8
13
|
const INITIAL_SUBSCRIPTION_CURSOR = "-1", MAX_WORKFLOW_SUBSCRIPTION_REPLAY_EVENTS = 1000, workflowEventsSubscriptionInput = z.object({
|
|
9
14
|
workflowId: z.string().min(1),
|
|
@@ -24,30 +29,9 @@ export const workflowEventsSubscriptionOperation = defineOperation({
|
|
|
24
29
|
inputSchema: workflowEventsSubscriptionInput,
|
|
25
30
|
outputSchema: workflowEventsSubscriptionEnvelope,
|
|
26
31
|
producibleFaults: ["InvalidParams"],
|
|
27
|
-
eventSchema:
|
|
28
|
-
kind: z.string(),
|
|
29
|
-
workflowId: z.string(),
|
|
30
|
-
selector: z.enum(["events", "tokens"]),
|
|
31
|
-
sequence: z.number(),
|
|
32
|
-
cursor: z.string(),
|
|
33
|
-
emittedAtMs: z.number(),
|
|
34
|
-
payload: z.unknown()
|
|
35
|
-
}),
|
|
32
|
+
eventSchema: workflowEventEnvelopeSchema,
|
|
36
33
|
access: { kind: "authenticated" },
|
|
37
|
-
parameterizedAccess:
|
|
38
|
-
discriminator: "selector",
|
|
39
|
-
defaultValue: "events",
|
|
40
|
-
variants: [
|
|
41
|
-
{
|
|
42
|
-
value: "events",
|
|
43
|
-
access: { kind: "scoped", scopes: { kind: "anyOf", scopes: ["events:read"] } }
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
value: "tokens",
|
|
47
|
-
access: { kind: "scoped", scopes: { kind: "anyOf", scopes: ["streams:read"] } }
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
},
|
|
34
|
+
parameterizedAccess: workflowEventParameterizedAccess,
|
|
51
35
|
authorize: async ({ input, principal }) => {
|
|
52
36
|
const requiredScope = workflowSubscriptionScope(input.selector);
|
|
53
37
|
if (!isAuthenticated(principal))
|
|
@@ -71,13 +55,11 @@ export const workflowEventsSubscriptionOperation = defineOperation({
|
|
|
71
55
|
signal: controller.signal,
|
|
72
56
|
replayLimit: MAX_WORKFLOW_SUBSCRIPTION_REPLAY_EVENTS,
|
|
73
57
|
createReplayLimitError: (count, limit) => invalidParamsFault(`Workflow event replay window is ${count} events; maximum is ${limit}. Supply a more recent fromCursor.`)
|
|
74
|
-
}, iterable = feed.subscribe(subscribeOptions);
|
|
58
|
+
}, iterable = createReplayAwareClosableIterable((onReplayComplete) => feed.subscribe({ ...subscribeOptions, onReplayComplete }), { close: () => controller.abort() });
|
|
75
59
|
return {
|
|
76
60
|
envelope: { subscriptionId: `sub_${crypto.randomUUID()}`, cursor: startingCursor },
|
|
77
61
|
iterable,
|
|
78
|
-
close:
|
|
79
|
-
controller.abort();
|
|
80
|
-
}
|
|
62
|
+
close: () => iterable.close()
|
|
81
63
|
};
|
|
82
64
|
}
|
|
83
65
|
});
|
|
@@ -33,8 +33,8 @@ function validateTaskResultBody(body) {
|
|
|
33
33
|
if (status !== "completed" && status !== "failed")
|
|
34
34
|
return Response.json({ error: 'status must be "completed" or "failed"' }, { status: 400 });
|
|
35
35
|
const rawAttemptToken = body.attemptToken;
|
|
36
|
-
if (
|
|
37
|
-
return Response.json({ error: "attemptToken must be a non-empty string
|
|
36
|
+
if (typeof rawAttemptToken !== "string" || rawAttemptToken === "")
|
|
37
|
+
return Response.json({ error: "attemptToken must be a non-empty string" }, { status: 400 });
|
|
38
38
|
return {
|
|
39
39
|
operationId,
|
|
40
40
|
status,
|
|
@@ -190,7 +190,5 @@ export async function handleTaskResultRequest(context, options, request, url, pr
|
|
|
190
190
|
function isLongPollCompletionAuthorized(inflightRecord, validated) {
|
|
191
191
|
if (validated.workerId === void 0 || inflightRecord.workerId !== validated.workerId)
|
|
192
192
|
return !1;
|
|
193
|
-
|
|
194
|
-
return !1;
|
|
195
|
-
return !0;
|
|
193
|
+
return validated.attemptToken === inflightRecord.attemptToken;
|
|
196
194
|
}
|
|
@@ -73,14 +73,8 @@ export interface InflightRecord extends TaskLifecycleFields {
|
|
|
73
73
|
workflowId?: string | undefined;
|
|
74
74
|
/** Durable token for the workflow run that dispatched this activity, when known. */
|
|
75
75
|
workflowExecutionToken?: string | undefined;
|
|
76
|
-
/**
|
|
77
|
-
|
|
78
|
-
* (re-)dispatch because each dispatch writes a fresh InflightRecord. The
|
|
79
|
-
* long-poll completion handler rejects a result whose echoed token does not
|
|
80
|
-
* match this value. Optional for back-compatible decoding of records written
|
|
81
|
-
* before this field existed; a missing token disables the check for that record.
|
|
82
|
-
*/
|
|
83
|
-
attemptToken?: string | undefined;
|
|
76
|
+
/** Unique, unguessable token identifying this dispatch attempt. */
|
|
77
|
+
attemptToken: string;
|
|
84
78
|
}
|
|
85
79
|
/** Persisted record for a task in the resolved state. */
|
|
86
80
|
export interface ResolvedRecord {
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { decode, encode } from "../core/codec.js";
|
|
2
2
|
import { KEYS } from "../storage/interface.js";
|
|
3
3
|
import { buildResolvedRecord } from "./task-resolved-record.js";
|
|
4
|
-
|
|
4
|
+
async function readTaskStateSnapshot(storage, operationId) {
|
|
5
5
|
const [inflight, queued, resolved] = await Promise.all([
|
|
6
6
|
storage.get(KEYS.operationInflight(operationId)),
|
|
7
7
|
storage.get(KEYS.operationQueued(operationId)),
|
|
8
8
|
storage.get(KEYS.operationResolved(operationId))
|
|
9
9
|
]);
|
|
10
|
+
return { inflight, queued, resolved };
|
|
11
|
+
}
|
|
12
|
+
export async function getTaskState(storage, operationId) {
|
|
13
|
+
const { inflight, queued, resolved } = await readTaskStateSnapshot(storage, operationId);
|
|
10
14
|
if (inflight !== null)
|
|
11
15
|
return "inflight";
|
|
12
16
|
if (queued !== null)
|
|
@@ -16,11 +20,7 @@ export async function getTaskState(storage, operationId) {
|
|
|
16
20
|
return null;
|
|
17
21
|
}
|
|
18
22
|
export async function getExclusiveTaskState(storage, operationId) {
|
|
19
|
-
const
|
|
20
|
-
storage.get(KEYS.operationInflight(operationId)),
|
|
21
|
-
storage.get(KEYS.operationQueued(operationId)),
|
|
22
|
-
storage.get(KEYS.operationResolved(operationId))
|
|
23
|
-
]), states = [];
|
|
23
|
+
const { inflight, queued, resolved } = await readTaskStateSnapshot(storage, operationId), states = [];
|
|
24
24
|
if (inflight !== null)
|
|
25
25
|
states.push("inflight");
|
|
26
26
|
if (queued !== null)
|
|
@@ -37,11 +37,14 @@ export function isQueuedRecord(value) {
|
|
|
37
37
|
const record = value;
|
|
38
38
|
return typeof record.operationId === "string" && typeof record.activityName === "string" && typeof record.queue === "string" && typeof record.attempt === "number" && typeof record.visibilityTimeout === "number" && typeof record.queuedAt === "number";
|
|
39
39
|
}
|
|
40
|
+
function isNonEmptyString(value) {
|
|
41
|
+
return typeof value === "string" && value.length > 0;
|
|
42
|
+
}
|
|
40
43
|
export function isInflightRecord(value) {
|
|
41
44
|
if (value === null || typeof value !== "object")
|
|
42
45
|
return !1;
|
|
43
46
|
const record = value;
|
|
44
|
-
return typeof record.operationId === "string" && typeof record.activityName === "string" && typeof record.queue === "string" && typeof record.attempt === "number" && typeof record.visibilityTimeout === "number" && typeof record.workerId === "string" && typeof record.deadline === "number";
|
|
47
|
+
return typeof record.operationId === "string" && typeof record.activityName === "string" && typeof record.queue === "string" && typeof record.attempt === "number" && typeof record.visibilityTimeout === "number" && typeof record.workerId === "string" && typeof record.deadline === "number" && isNonEmptyString(record.attemptToken);
|
|
45
48
|
}
|
|
46
49
|
export function isResolvedRecord(value) {
|
|
47
50
|
if (value === null || typeof value !== "object")
|