@lostgradient/weft 0.14.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/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/engine/schedules.js +4 -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/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/http.js +1 -1
- 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/operation-catalog/types.d.ts +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-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/schedule-rest-body.d.ts +11 -0
- package/dist/server/operations/schedule-rest-body.js +28 -0
- 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/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/lmdb.js +1 -1
- package/dist/storage/memory.js +1 -1
- 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
|
@@ -4,6 +4,12 @@ import { EVENTS_READ_EVENT_TYPES } from "../runtime/client-visible-events.js";
|
|
|
4
4
|
import {
|
|
5
5
|
decodeCursor
|
|
6
6
|
} from "../workflow-event-feed.js";
|
|
7
|
+
import {
|
|
8
|
+
createReplayAwareClosableIterable,
|
|
9
|
+
fleetEventEnvelopeSchema,
|
|
10
|
+
isClosableAsyncIterable,
|
|
11
|
+
isReplayAwareClosableIterable
|
|
12
|
+
} from "./event-stream-contracts.js";
|
|
7
13
|
import { invalidParamsFault } from "./operation-helpers.js";
|
|
8
14
|
import {
|
|
9
15
|
createEventEnvelopeSSEStream,
|
|
@@ -19,14 +25,7 @@ const INITIAL_CURSOR = "-1", MAX_FLEET_SSE_REPLAY_EVENTS = 1000, cursorSchema =
|
|
|
19
25
|
kind: fleetEventKindSchema.optional(),
|
|
20
26
|
fromCursor: cursorSchema.optional(),
|
|
21
27
|
lastEventId: cursorSchema.optional()
|
|
22
|
-
}), fleetEventsSseOutputSchema = z.custom(isAsyncIterable, "Expected async iterable fleet event stream")
|
|
23
|
-
kind: z.string(),
|
|
24
|
-
workflowId: z.string().optional(),
|
|
25
|
-
sequence: z.number(),
|
|
26
|
-
cursor: z.string(),
|
|
27
|
-
emittedAtMs: z.number(),
|
|
28
|
-
payload: z.unknown()
|
|
29
|
-
});
|
|
28
|
+
}), fleetEventsSseOutputSchema = z.custom(isAsyncIterable, "Expected async iterable fleet event stream");
|
|
30
29
|
export const fleetEventsSseOperation = defineOperation({
|
|
31
30
|
name: "weft.events.sse",
|
|
32
31
|
mcpExposable: !1,
|
|
@@ -67,12 +66,6 @@ function isAsyncIterable(value) {
|
|
|
67
66
|
return !1;
|
|
68
67
|
return typeof value[Symbol.asyncIterator] === "function";
|
|
69
68
|
}
|
|
70
|
-
function isClosableFleetEventsIterable(value) {
|
|
71
|
-
return "close" in value && typeof value.close === "function";
|
|
72
|
-
}
|
|
73
|
-
function isReplayAwareFleetEventsIterable(value) {
|
|
74
|
-
return isClosableFleetEventsIterable(value) && "replayComplete" in value && value.replayComplete instanceof Promise;
|
|
75
|
-
}
|
|
76
69
|
function matchesFleetEventFilter(envelope, input) {
|
|
77
70
|
if (input.workflowId !== void 0 && envelope.workflowId !== input.workflowId)
|
|
78
71
|
return !1;
|
|
@@ -84,49 +77,27 @@ function createFleetEventsIterable(input, context) {
|
|
|
84
77
|
const feed = context.fleetEventFeed;
|
|
85
78
|
if (feed === void 0)
|
|
86
79
|
throw unsupportedEventStreamContextFault("fleet event SSE requires a fleet event feed");
|
|
87
|
-
const controller = new AbortController,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
onReplayComplete: () => replayComplete.resolve(),
|
|
100
|
-
createReplayLimitError: (count, limit) => invalidParamsFault(`Fleet event replay window is ${count} matching events; maximum is ${limit}. Supply a more recent fromCursor.`)
|
|
101
|
-
};
|
|
102
|
-
let source;
|
|
103
|
-
try {
|
|
104
|
-
source = feed.subscribe(subscribeOptions);
|
|
105
|
-
} catch (error) {
|
|
106
|
-
close();
|
|
107
|
-
throw error;
|
|
108
|
-
}
|
|
109
|
-
return {
|
|
110
|
-
async* [Symbol.asyncIterator]() {
|
|
111
|
-
try {
|
|
112
|
-
for await (const envelope of source)
|
|
113
|
-
yield envelope;
|
|
114
|
-
} finally {
|
|
115
|
-
await close();
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
replayComplete: replayComplete.promise,
|
|
119
|
-
close
|
|
120
|
-
};
|
|
80
|
+
const controller = new AbortController, fromCursor = input.lastEventId ?? input.fromCursor ?? INITIAL_CURSOR;
|
|
81
|
+
return createReplayAwareClosableIterable((onReplayComplete) => {
|
|
82
|
+
const subscribeOptions = {
|
|
83
|
+
fromCursor,
|
|
84
|
+
signal: controller.signal,
|
|
85
|
+
replayLimit: MAX_FLEET_SSE_REPLAY_EVENTS,
|
|
86
|
+
filterEnvelope: (envelope) => matchesFleetEventFilter(envelope, input),
|
|
87
|
+
onReplayComplete,
|
|
88
|
+
createReplayLimitError: (count, limit) => invalidParamsFault(`Fleet event replay window is ${count} matching events; maximum is ${limit}. Supply a more recent fromCursor.`)
|
|
89
|
+
};
|
|
90
|
+
return feed.subscribe(subscribeOptions);
|
|
91
|
+
}, { close: () => controller.abort() });
|
|
121
92
|
}
|
|
122
93
|
function shapeFleetEventsSseSuccess(output, request) {
|
|
123
|
-
const close =
|
|
94
|
+
const close = isClosableAsyncIterable(output) ? () => output.close() : async () => {
|
|
124
95
|
return;
|
|
125
96
|
};
|
|
126
97
|
return new Response(createEventEnvelopeSSEStream({
|
|
127
98
|
iterable: output,
|
|
128
99
|
close,
|
|
129
|
-
...
|
|
100
|
+
...isReplayAwareClosableIterable(output) ? { ready: output.replayComplete } : {},
|
|
130
101
|
signal: request.signal
|
|
131
102
|
}), {
|
|
132
103
|
status: 200,
|
|
@@ -3,6 +3,10 @@ import { raiseFault } from "../operation-catalog/raise-fault.js";
|
|
|
3
3
|
import { defineOperation } from "../operation-registry.js";
|
|
4
4
|
import { EVENTS_READ_EVENT_TYPES } from "../runtime/client-visible-events.js";
|
|
5
5
|
import { decodeCursor } from "../workflow-event-feed.js";
|
|
6
|
+
import {
|
|
7
|
+
createReplayAwareClosableIterable,
|
|
8
|
+
fleetEventEnvelopeSchema
|
|
9
|
+
} from "./event-stream-contracts.js";
|
|
6
10
|
import { invalidParamsFault } from "./operation-helpers.js";
|
|
7
11
|
const INITIAL_SUBSCRIPTION_CURSOR = "-1", MAX_FLEET_SUBSCRIPTION_REPLAY_EVENTS = 1000, fleetEventKindSchema = z.enum(EVENTS_READ_EVENT_TYPES), fleetCursorSchema = z.string().regex(/^(?:-1|\d+)$/, "Invalid cursor").refine((cursor) => decodeCursor(cursor) !== null, {
|
|
8
12
|
message: "Invalid cursor"
|
|
@@ -13,13 +17,6 @@ const INITIAL_SUBSCRIPTION_CURSOR = "-1", MAX_FLEET_SUBSCRIPTION_REPLAY_EVENTS =
|
|
|
13
17
|
}), fleetEventsSubscriptionEnvelope = z.object({
|
|
14
18
|
subscriptionId: z.string(),
|
|
15
19
|
cursor: z.string()
|
|
16
|
-
}), fleetEventEnvelopeSchema = z.object({
|
|
17
|
-
kind: z.string(),
|
|
18
|
-
workflowId: z.string().optional(),
|
|
19
|
-
sequence: z.number(),
|
|
20
|
-
cursor: z.string(),
|
|
21
|
-
emittedAtMs: z.number(),
|
|
22
|
-
payload: z.unknown()
|
|
23
20
|
});
|
|
24
21
|
export const fleetEventsSubscriptionOperation = defineOperation({
|
|
25
22
|
name: "weft.events.subscribe",
|
|
@@ -44,19 +41,18 @@ export const fleetEventsSubscriptionOperation = defineOperation({
|
|
|
44
41
|
const startingCursor = input.fromCursor ?? INITIAL_SUBSCRIPTION_CURSOR;
|
|
45
42
|
if (decodeCursor(startingCursor) === null)
|
|
46
43
|
raiseFault(fleetEventsSubscriptionOperation, invalidParamsFault("Invalid cursor"));
|
|
47
|
-
const fleetFeed = getFleetEventFeed(engine, transport), controller = new AbortController, iterable = fleetFeed.subscribe({
|
|
44
|
+
const fleetFeed = getFleetEventFeed(engine, transport), controller = new AbortController, iterable = createReplayAwareClosableIterable((onReplayComplete) => fleetFeed.subscribe({
|
|
48
45
|
...input.fromCursor === void 0 ? {} : { fromCursor: input.fromCursor },
|
|
49
46
|
signal: controller.signal,
|
|
50
47
|
replayLimit: MAX_FLEET_SUBSCRIPTION_REPLAY_EVENTS,
|
|
51
48
|
filterEnvelope: (envelope) => matchesFleetEventFilter(envelope, input),
|
|
49
|
+
onReplayComplete,
|
|
52
50
|
createReplayLimitError: (count, limit) => fleetReplayLimitFault(count, limit)
|
|
53
|
-
});
|
|
51
|
+
}), { close: () => controller.abort() });
|
|
54
52
|
return {
|
|
55
53
|
envelope: { subscriptionId: `sub_${crypto.randomUUID()}`, cursor: startingCursor },
|
|
56
54
|
iterable,
|
|
57
|
-
close:
|
|
58
|
-
controller.abort();
|
|
59
|
-
}
|
|
55
|
+
close: () => iterable.close()
|
|
60
56
|
};
|
|
61
57
|
}
|
|
62
58
|
});
|
|
@@ -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;
|
|
@@ -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,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,
|