@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.
Files changed (70) hide show
  1. package/README.md +3 -8
  2. package/dist/cli/generated/operation-catalog.snapshot.json +977 -77
  3. package/dist/cli/generated/operation-client.generated.d.ts +14 -21
  4. package/dist/core/aggregate-validation.d.ts +2 -5
  5. package/dist/core/aggregate-validation.js +2 -10
  6. package/dist/core/atomic-state.js +21 -24
  7. package/dist/core/engine/schedules.js +4 -1
  8. package/dist/core/inline-execution-strategy.js +8 -46
  9. package/dist/core/list-filter-validation.d.ts +2 -5
  10. package/dist/core/list-filter-validation.js +2 -10
  11. package/dist/core/types/schedules.d.ts +13 -31
  12. package/dist/core/validation-issues.d.ts +13 -0
  13. package/dist/core/validation-issues.js +9 -0
  14. package/dist/http.js +1 -1
  15. package/dist/indexeddb.js +1 -1
  16. package/dist/mcp/session.js +2 -10
  17. package/dist/mcp/tools.js +2 -10
  18. package/dist/mcp/tuple-list-equality.d.ts +1 -0
  19. package/dist/mcp/tuple-list-equality.js +12 -0
  20. package/dist/server/operation-catalog/types.d.ts +1 -1
  21. package/dist/server/operation-registry.d.ts +2 -31
  22. package/dist/server/operations/bulk-filter-helpers.d.ts +17 -17
  23. package/dist/server/operations/bulk-filter-helpers.js +16 -37
  24. package/dist/server/operations/bulk-mutate-workflow-tags.d.ts +27 -27
  25. package/dist/server/operations/bulk-signal-workflows.d.ts +26 -26
  26. package/dist/server/operations/create-schedule.js +7 -19
  27. package/dist/server/operations/event-stream-contracts.d.ts +20 -0
  28. package/dist/server/operations/event-stream-contracts.js +67 -0
  29. package/dist/server/operations/fleet-events-sse.js +21 -50
  30. package/dist/server/operations/fleet-events-subscription.js +8 -12
  31. package/dist/server/operations/list-workflows.d.ts +43 -43
  32. package/dist/server/operations/list-workflows.js +9 -30
  33. package/dist/server/operations/purge-workflows.d.ts +11 -11
  34. package/dist/server/operations/schedule-rest-body.d.ts +11 -0
  35. package/dist/server/operations/schedule-rest-body.js +28 -0
  36. package/dist/server/operations/update-schedule.js +7 -19
  37. package/dist/server/operations/workflow-events-sse.js +29 -67
  38. package/dist/server/operations/workflow-events-subscription.js +9 -27
  39. package/dist/server/runtime/task-polling.js +3 -5
  40. package/dist/server/task-state.d.ts +2 -8
  41. package/dist/server/task-state.js +10 -7
  42. package/dist/storage/bun-sql.js +52 -49
  43. package/dist/storage/conditional-batch-body.d.ts +7 -0
  44. package/dist/storage/conditional-batch-body.js +14 -0
  45. package/dist/storage/lmdb.js +1 -1
  46. package/dist/storage/memory.js +1 -1
  47. package/dist/storage/neon.js +2 -2
  48. package/dist/storage/node-sqlite-loader.d.ts +2 -2
  49. package/dist/storage/node-sqlite.js +32 -17
  50. package/dist/storage/postgres.js +1 -1
  51. package/dist/storage/resolve.js +1 -1
  52. package/dist/storage/scoped-storage.js +1 -1
  53. package/dist/storage/turso.js +1 -1
  54. package/dist/version.d.ts +1 -1
  55. package/dist/version.js +1 -1
  56. package/dist/web-extension.js +1 -1
  57. package/dist/worker/execute-with-interceptors.d.ts +1 -1
  58. package/dist/worker/execute-with-interceptors.js +2 -2
  59. package/dist/worker/index.js +5 -5
  60. package/dist/worker/long-poll.js +3 -4
  61. package/dist/worker/protocol-messages.d.ts +11 -19
  62. package/dist/worker/protocol-schemas.d.ts +8 -8
  63. package/dist/worker/protocol-schemas.js +4 -4
  64. package/dist/worker/protocol-task-result.d.ts +1 -0
  65. package/dist/worker/protocol-task-result.js +1 -3
  66. package/dist/worker/protocol.js +1 -1
  67. package/dist/worker/registry/types.d.ts +2 -7
  68. package/dist/worker/registry.d.ts +3 -9
  69. package/dist/worker/registry.js +2 -5
  70. 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"), fleetEventEnvelopeSchema = z.object({
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, replayComplete = Promise.withResolvers();
88
- let closed = !1;
89
- const close = async () => {
90
- if (closed)
91
- return;
92
- closed = !0;
93
- controller.abort();
94
- }, subscribeOptions = {
95
- fromCursor: input.lastEventId ?? input.fromCursor ?? INITIAL_CURSOR,
96
- signal: controller.signal,
97
- replayLimit: MAX_FLEET_SSE_REPLAY_EVENTS,
98
- filterEnvelope: (envelope) => matchesFleetEventFilter(envelope, input),
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 = isClosableFleetEventsIterable(output) ? () => output.close() : async () => {
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
- ...isReplayAwareFleetEventsIterable(output) ? { ready: output.replayComplete } : {},
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: async () => {
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 { FailureCategory, PaginatedResult, SearchAttributeValue, WorkflowStatus, WorkflowSummary } from '../../core/types.ts';
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.ZodCustom<WorkflowStatus, WorkflowStatus>, z.ZodArray<z.ZodCustom<WorkflowStatus, WorkflowStatus>>]>>;
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.$strip>>;
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.$strip>>;
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.$strip>>;
40
- failureCategory: z.ZodOptional<z.ZodUnion<readonly [z.ZodCustom<FailureCategory, FailureCategory>, z.ZodArray<z.ZodCustom<FailureCategory, FailureCategory>>]>>;
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
- }, z.core.$strip>;
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?: WorkflowStatus | WorkflowStatus[] | undefined;
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?: FailureCategory | FailureCategory[] | undefined;
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 workflowStatusSchema = z.custom((value) => typeof value === "string"), failureCategorySchema = z.custom((value) => typeof value === "string"), searchAttributeValueSchema = z.custom((value) => {
12
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
13
- return !0;
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: timeRangeSchema.optional(),
39
- updatedAt: timeRangeSchema.optional(),
40
- executionDeadline: timeRangeSchema.optional(),
41
- failureCategory: z.union([failureCategorySchema, z.array(failureCategorySchema)]).optional(),
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: string;
15
- value?: import("../../index.ts").SearchAttributeValue | undefined;
16
- gt?: import("../../core/types.ts").AttributeFilterScalarValue | undefined;
17
- lt?: import("../../core/types.ts").AttributeFilterScalarValue | undefined;
18
- gte?: import("../../core/types.ts").AttributeFilterScalarValue | undefined;
19
- lte?: import("../../core/types.ts").AttributeFilterScalarValue | undefined;
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 { readRestJsonBody } from "../rest-body.js";
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
- let body;
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
- cronExpression: Object.hasOwn(record, "cronExpression") ? record.cronExpression : void 0,
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"), workflowEventEnvelopeSchema = z.object({
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 controller = new AbortController, replayComplete = Promise.withResolvers();
114
- let closed = !1;
115
- const close = async () => {
116
- if (closed)
117
- return;
118
- closed = !0;
119
- controller.abort();
120
- lease?.release();
121
- }, fromCursor = input.lastEventId ?? input.fromCursor ?? INITIAL_CURSOR, subscribeOptions = {
122
- workflowId: input.workflowId,
123
- selector: input.selector,
124
- fromCursor,
125
- signal: controller.signal,
126
- replayLimit: MAX_WORKFLOW_SSE_REPLAY_EVENTS,
127
- onReplayComplete: () => replayComplete.resolve(),
128
- createReplayLimitError: (count, limit) => invalidParamsFault(`Workflow event replay window is ${count} events; maximum is ${limit}. Supply a more recent fromCursor.`)
129
- };
130
- let source;
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 = isClosableWorkflowEventsIterable(output) ? () => output.close() : async () => {
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
- ...isReplayAwareWorkflowEventsIterable(output) ? { ready: output.replayComplete } : {},
119
+ ...isReplayAwareClosableIterable(output) ? { ready: output.replayComplete } : {},
158
120
  signal: request.signal
159
121
  }), {
160
122
  status: 200,