@notionhq/apps 0.0.17 → 0.0.19
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/AGENTS.md +27 -0
- package/README.md +77 -35
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/build.js +12 -7
- package/dist/cli/emit-manifest.d.ts +1 -1
- package/dist/cli/emit-manifest.d.ts.map +1 -1
- package/dist/cli/emit-manifest.js +66 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/notion-as-code/custom-agent.d.ts +1 -0
- package/dist/notion-as-code/custom-agent.d.ts.map +1 -1
- package/dist/notion-as-code/custom-agent.js +1 -1
- package/dist/notion-as-code/database.d.ts +90 -48
- package/dist/notion-as-code/database.d.ts.map +1 -1
- package/dist/notion-as-code/database.js +135 -45
- package/dist/notion-as-code/database.test.d.ts +2 -0
- package/dist/notion-as-code/database.test.d.ts.map +1 -0
- package/dist/notion-as-code/index.d.ts +2 -2
- package/dist/notion-as-code/index.d.ts.map +1 -1
- package/dist/notion-as-code/intents.d.ts +34 -2
- package/dist/notion-as-code/intents.d.ts.map +1 -1
- package/dist/notion-as-code/page.d.ts +1 -0
- package/dist/notion-as-code/page.d.ts.map +1 -1
- package/dist/notion-as-code/page.js +1 -0
- package/dist/notion-as-code/schema.d.ts +8 -4
- package/dist/notion-as-code/schema.d.ts.map +1 -1
- package/dist/notion-as-code/views.d.ts +29 -10
- package/dist/notion-as-code/views.d.ts.map +1 -1
- package/dist/sync.d.ts +33 -15
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +12 -0
- package/dist/workflow-access-types.test.d.ts +2 -0
- package/dist/workflow-access-types.test.d.ts.map +1 -0
- package/dist/workflow-access.d.ts +68 -0
- package/dist/workflow-access.d.ts.map +1 -0
- package/dist/workflow-access.js +135 -0
- package/dist/workflow-state.d.ts +13 -0
- package/dist/workflow-state.d.ts.map +1 -0
- package/dist/workflow-state.js +107 -0
- package/dist/workflow.d.ts +65 -9
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +119 -5
- package/docs/BUILD.md +134 -4
- package/package.json +1 -1
- package/skills/notion-as-code/SKILL.md +89 -49
- package/skills/sync/SKILL.md +21 -18
- package/skills/workflow/SKILL.md +101 -17
- package/src/cli/build.test.ts +148 -64
- package/src/cli/build.ts +13 -8
- package/src/cli/emit-manifest.ts +107 -0
- package/src/index.ts +21 -1
- package/src/notion-as-code/custom-agent.ts +2 -1
- package/src/notion-as-code/database.test.ts +661 -0
- package/src/notion-as-code/database.ts +349 -127
- package/src/notion-as-code/index.ts +16 -1
- package/src/notion-as-code/intents.ts +39 -2
- package/src/notion-as-code/page.ts +2 -0
- package/src/notion-as-code/schema.ts +11 -3
- package/src/notion-as-code/views.ts +29 -10
- package/src/sync.test.ts +295 -0
- package/src/sync.ts +85 -21
- package/src/workflow-access-types.test.ts +69 -0
- package/src/workflow-access.ts +299 -0
- package/src/workflow-state.ts +152 -0
- package/src/workflow.test.ts +441 -1
- package/src/workflow.ts +230 -13
package/src/workflow.ts
CHANGED
|
@@ -6,6 +6,13 @@ import {
|
|
|
6
6
|
type WorkflowConnectionRequirement,
|
|
7
7
|
type WorkflowConnections,
|
|
8
8
|
} from "./connections.js";
|
|
9
|
+
import {
|
|
10
|
+
hydrateWorkflowAccess,
|
|
11
|
+
normalizeWorkflowAccess,
|
|
12
|
+
type WorkflowAccess,
|
|
13
|
+
type WorkflowAccessDeclarations,
|
|
14
|
+
type WorkflowAccessRequirements,
|
|
15
|
+
} from "./workflow-access.js";
|
|
9
16
|
import { createHash } from "node:crypto";
|
|
10
17
|
import { readFile } from "node:fs/promises";
|
|
11
18
|
import { join } from "node:path";
|
|
@@ -17,6 +24,7 @@ import { writeOutput } from "./output.js";
|
|
|
17
24
|
import { resolveRuntimeInput } from "./runtime-input.js";
|
|
18
25
|
import { readRunMetadata, type RunMetadata } from "./runtime-metadata.js";
|
|
19
26
|
import { createWorkflowTriggers } from "./triggers.generated.js";
|
|
27
|
+
import { createWorkflowStepState, type WorkflowState } from "./workflow-state.js";
|
|
20
28
|
import type {
|
|
21
29
|
WorkflowEventMap,
|
|
22
30
|
WorkflowTrigger,
|
|
@@ -29,6 +37,60 @@ type HandlerOptions = {
|
|
|
29
37
|
concreteOutput?: true;
|
|
30
38
|
};
|
|
31
39
|
|
|
40
|
+
type WorkflowWaitDurationValues = {
|
|
41
|
+
milliseconds?: number;
|
|
42
|
+
seconds?: number;
|
|
43
|
+
minutes?: number;
|
|
44
|
+
hours?: number;
|
|
45
|
+
days?: number;
|
|
46
|
+
weeks?: number;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type WorkflowWaitDuration = {
|
|
50
|
+
[Unit in keyof WorkflowWaitDurationValues]-?: Required<Pick<WorkflowWaitDurationValues, Unit>> &
|
|
51
|
+
WorkflowWaitDurationValues;
|
|
52
|
+
}[keyof WorkflowWaitDurationValues];
|
|
53
|
+
|
|
54
|
+
type WorkflowWaitKey = string | string[];
|
|
55
|
+
|
|
56
|
+
export type WorkflowWaitUntilOptions =
|
|
57
|
+
| {
|
|
58
|
+
at: Date;
|
|
59
|
+
key?: WorkflowWaitKey;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
after: WorkflowWaitDuration;
|
|
63
|
+
key?: WorkflowWaitKey;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type WorkflowWaitUntilResult = {
|
|
67
|
+
status: "waiting";
|
|
68
|
+
wait: {
|
|
69
|
+
type: "until";
|
|
70
|
+
name: string;
|
|
71
|
+
resumeAtMs: number;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
type WorkflowHandlerResult = { status: "success" } | WorkflowWaitUntilResult;
|
|
76
|
+
|
|
77
|
+
const MAX_WORKFLOW_WAIT_MS = 7 * 24 * 60 * 60 * 1_000;
|
|
78
|
+
|
|
79
|
+
export type {
|
|
80
|
+
WorkflowAccess,
|
|
81
|
+
WorkflowAccessAgentLevel,
|
|
82
|
+
WorkflowAccessBindings,
|
|
83
|
+
WorkflowAccessDeclaration,
|
|
84
|
+
WorkflowAccessDeclarations,
|
|
85
|
+
WorkflowAccessLevel,
|
|
86
|
+
WorkflowAccessLevelFor,
|
|
87
|
+
WorkflowAccessResource,
|
|
88
|
+
WorkflowAccessResourceLevel,
|
|
89
|
+
WorkflowAccessResourceType,
|
|
90
|
+
WorkflowAccessRequirement,
|
|
91
|
+
WorkflowAccessRequirements,
|
|
92
|
+
WorkflowAccessValue,
|
|
93
|
+
} from "./workflow-access.js";
|
|
32
94
|
export type WorkflowEvent = WorkflowEventMap[keyof WorkflowEventMap];
|
|
33
95
|
|
|
34
96
|
export type WorkflowEventForTrigger<T extends WorkflowTrigger> = WorkflowEventMap[T["type"]];
|
|
@@ -52,6 +114,7 @@ export {
|
|
|
52
114
|
export type WorkflowConfiguration<
|
|
53
115
|
TTriggers extends readonly [WorkflowTrigger, ...WorkflowTrigger[]],
|
|
54
116
|
TConnections extends WorkflowConnectionDeclarations = WorkflowConnectionDeclarations,
|
|
117
|
+
TAccess extends WorkflowAccessDeclarations = WorkflowAccessDeclarations,
|
|
55
118
|
> = {
|
|
56
119
|
/**
|
|
57
120
|
* A human-readable name for the workflow, shown in the UI when viewing workflows
|
|
@@ -70,6 +133,13 @@ export type WorkflowConfiguration<
|
|
|
70
133
|
*/
|
|
71
134
|
triggers: TTriggers;
|
|
72
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Notion-as-Code resources whose access is configured during cloud deployment.
|
|
138
|
+
* Deployment reconciles grants through the existing Notion-module permissions
|
|
139
|
+
* and supplies named runtime bindings on `context.access`.
|
|
140
|
+
*/
|
|
141
|
+
access?: TAccess;
|
|
142
|
+
|
|
73
143
|
/**
|
|
74
144
|
* Connections that must be set up before this workflow can run.
|
|
75
145
|
*/
|
|
@@ -77,7 +147,7 @@ export type WorkflowConfiguration<
|
|
|
77
147
|
|
|
78
148
|
handler: (
|
|
79
149
|
event: WorkflowEventForTriggers<TTriggers>,
|
|
80
|
-
context: WorkflowContext<TConnections>,
|
|
150
|
+
context: WorkflowContext<TConnections, TAccess>,
|
|
81
151
|
) => Promise<void> | void;
|
|
82
152
|
};
|
|
83
153
|
|
|
@@ -85,7 +155,8 @@ export type WorkflowConfiguration<
|
|
|
85
155
|
export type WorkflowTriggerConfiguration<
|
|
86
156
|
TTriggers extends readonly [WorkflowTrigger, ...WorkflowTrigger[]],
|
|
87
157
|
TConnections extends WorkflowConnectionDeclarations,
|
|
88
|
-
|
|
158
|
+
TAccess extends WorkflowAccessDeclarations = WorkflowAccessDeclarations,
|
|
159
|
+
> = Omit<WorkflowConfiguration<TTriggers, TConnections, TAccess>, "triggers"> & {
|
|
89
160
|
triggers: (context: { triggers: WorkflowTriggerCreators<NoInfer<TConnections>> }) => TTriggers;
|
|
90
161
|
};
|
|
91
162
|
|
|
@@ -107,11 +178,12 @@ export type Workflow<
|
|
|
107
178
|
description: string;
|
|
108
179
|
triggers: TTriggers;
|
|
109
180
|
connections?: readonly WorkflowConnectionRequirement[];
|
|
181
|
+
access?: WorkflowAccessRequirements;
|
|
110
182
|
};
|
|
111
183
|
handler: (
|
|
112
184
|
event: WorkflowEventForTriggers<TTriggers>,
|
|
113
185
|
options?: HandlerOptions,
|
|
114
|
-
) => Promise<
|
|
186
|
+
) => Promise<WorkflowHandlerResult | undefined>;
|
|
115
187
|
};
|
|
116
188
|
|
|
117
189
|
/**
|
|
@@ -146,20 +218,26 @@ export function workflow<
|
|
|
146
218
|
...WorkflowTriggerForConnections<NoInfer<TConnections>>[],
|
|
147
219
|
],
|
|
148
220
|
const TConnections extends WorkflowConnectionDeclarations = Record<never, never>,
|
|
149
|
-
|
|
221
|
+
const TAccess extends WorkflowAccessDeclarations = Record<never, never>,
|
|
222
|
+
>(
|
|
223
|
+
configuration: WorkflowTriggerConfiguration<TTriggers, TConnections, TAccess>,
|
|
224
|
+
): Workflow<TTriggers>;
|
|
150
225
|
export function workflow<
|
|
151
226
|
const TTriggers extends readonly [WorkflowTrigger, ...WorkflowTrigger[]],
|
|
152
227
|
const TConnections extends WorkflowConnectionDeclarations = Record<never, never>,
|
|
153
|
-
|
|
228
|
+
const TAccess extends WorkflowAccessDeclarations = Record<never, never>,
|
|
229
|
+
>(configuration: WorkflowConfiguration<TTriggers, TConnections, TAccess>): Workflow<TTriggers>;
|
|
154
230
|
export function workflow<
|
|
155
231
|
const TTriggers extends readonly [WorkflowTrigger, ...WorkflowTrigger[]],
|
|
156
232
|
const TConnections extends WorkflowConnectionDeclarations = Record<never, never>,
|
|
233
|
+
const TAccess extends WorkflowAccessDeclarations = Record<never, never>,
|
|
157
234
|
>(
|
|
158
235
|
configuration:
|
|
159
|
-
| WorkflowConfiguration<TTriggers, TConnections>
|
|
160
|
-
| WorkflowTriggerConfiguration<TTriggers, TConnections>,
|
|
236
|
+
| WorkflowConfiguration<TTriggers, TConnections, TAccess>
|
|
237
|
+
| WorkflowTriggerConfiguration<TTriggers, TConnections, TAccess>,
|
|
161
238
|
): Workflow<TTriggers> {
|
|
162
239
|
const requirements = normalizeConnectionRequirements(configuration.connections ?? {});
|
|
240
|
+
const accessRequirements = normalizeWorkflowAccess(configuration.access);
|
|
163
241
|
const connectionDeclarations =
|
|
164
242
|
configuration.connections === undefined
|
|
165
243
|
? undefined
|
|
@@ -177,23 +255,27 @@ export function workflow<
|
|
|
177
255
|
description: configuration.description,
|
|
178
256
|
triggers,
|
|
179
257
|
...(configuration.connections === undefined ? {} : { connections: requirements }),
|
|
258
|
+
...(configuration.access === undefined ? {} : { access: accessRequirements }),
|
|
180
259
|
},
|
|
181
260
|
async handler(
|
|
182
261
|
event: WorkflowEventForTriggers<TTriggers>,
|
|
183
262
|
options?: HandlerOptions,
|
|
184
|
-
): Promise<
|
|
263
|
+
): Promise<WorkflowHandlerResult | undefined> {
|
|
185
264
|
try {
|
|
186
265
|
event = await resolveRuntimeInput(event);
|
|
187
266
|
const runMetadata = readRunMetadata();
|
|
188
267
|
const baseContext = createCapabilityContext();
|
|
189
|
-
const
|
|
268
|
+
const step = createStep(runMetadata);
|
|
269
|
+
const capabilityContext: WorkflowContext<TConnections, TAccess> = {
|
|
190
270
|
...baseContext,
|
|
271
|
+
access: hydrateWorkflowAccess<TAccess>(accessRequirements),
|
|
191
272
|
connections: createWorkflowConnections(
|
|
192
273
|
baseContext.notion,
|
|
193
274
|
connectionDeclarations,
|
|
194
275
|
),
|
|
195
276
|
...runMetadata,
|
|
196
|
-
step
|
|
277
|
+
step,
|
|
278
|
+
wait: createWait(step),
|
|
197
279
|
};
|
|
198
280
|
await configuration.handler(event, capabilityContext);
|
|
199
281
|
|
|
@@ -203,6 +285,18 @@ export function workflow<
|
|
|
203
285
|
|
|
204
286
|
writeOutput({ _tag: "success", value: { status: "success" } });
|
|
205
287
|
} catch (err) {
|
|
288
|
+
if (err instanceof WorkflowWaitInterrupt) {
|
|
289
|
+
if (options?.concreteOutput) {
|
|
290
|
+
return err.result;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
writeOutput({
|
|
294
|
+
_tag: "wait",
|
|
295
|
+
wait: err.result.wait,
|
|
296
|
+
});
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
206
300
|
const error = new ExecutionError(err);
|
|
207
301
|
|
|
208
302
|
if (!options?.concreteOutput) {
|
|
@@ -229,6 +323,13 @@ export type StepContext = {
|
|
|
229
323
|
* Use this to make side effects idempotent when the workflow is retried.
|
|
230
324
|
*/
|
|
231
325
|
id: string;
|
|
326
|
+
/**
|
|
327
|
+
* Durable key-value state shared by every run of this workflow.
|
|
328
|
+
*
|
|
329
|
+
* State is exposed on the step context because every operation participates
|
|
330
|
+
* in the active step's durability and replay boundary.
|
|
331
|
+
*/
|
|
332
|
+
state: WorkflowState;
|
|
232
333
|
};
|
|
233
334
|
|
|
234
335
|
type WorkflowStepResult<T> = T extends void ? null : T;
|
|
@@ -252,10 +353,21 @@ type WorkflowStep = {
|
|
|
252
353
|
): Promise<WorkflowStepResult<T>>;
|
|
253
354
|
};
|
|
254
355
|
|
|
356
|
+
type WorkflowWait = {
|
|
357
|
+
/**
|
|
358
|
+
* Suspend the workflow until a relative duration has elapsed or an absolute
|
|
359
|
+
* date has been reached.
|
|
360
|
+
*/
|
|
361
|
+
until(name: string, options: WorkflowWaitUntilOptions): Promise<void>;
|
|
362
|
+
};
|
|
363
|
+
|
|
255
364
|
/** Context passed to a workflow handler. */
|
|
256
365
|
export type WorkflowContext<
|
|
257
366
|
TConnections extends WorkflowConnectionDeclarations = WorkflowConnectionDeclarations,
|
|
367
|
+
TAccess extends WorkflowAccessDeclarations = WorkflowAccessDeclarations,
|
|
258
368
|
> = CapabilityContext & {
|
|
369
|
+
/** Resolved NaC bindings for this invocation; UI-granted resources are not added here. */
|
|
370
|
+
readonly access: WorkflowAccess<TAccess>;
|
|
259
371
|
connections: WorkflowConnections<TConnections>;
|
|
260
372
|
} & RunMetadata & {
|
|
261
373
|
/**
|
|
@@ -279,6 +391,8 @@ export type WorkflowContext<
|
|
|
279
391
|
* ```
|
|
280
392
|
*/
|
|
281
393
|
step: WorkflowStep;
|
|
394
|
+
/** Suspend this workflow without consuming compute resources. */
|
|
395
|
+
wait: WorkflowWait;
|
|
282
396
|
};
|
|
283
397
|
|
|
284
398
|
type StepCompletionType = "success" | "failure";
|
|
@@ -314,7 +428,10 @@ function writeStepEvent(
|
|
|
314
428
|
);
|
|
315
429
|
}
|
|
316
430
|
|
|
317
|
-
function createStep(
|
|
431
|
+
function createStep(
|
|
432
|
+
metadata: Pick<RunMetadata, "runGroupId" | "workerId">,
|
|
433
|
+
): WorkflowContext["step"] {
|
|
434
|
+
const { runGroupId } = metadata;
|
|
318
435
|
const stepNameByKey = new Map<string, string>();
|
|
319
436
|
|
|
320
437
|
async function step<T>(
|
|
@@ -338,7 +455,9 @@ function createStep({ runGroupId }: Pick<RunMetadata, "runGroupId">): WorkflowCo
|
|
|
338
455
|
}
|
|
339
456
|
stepNameByKey.set(key, name);
|
|
340
457
|
|
|
341
|
-
const
|
|
458
|
+
const id = `${runGroupId}:${key}`;
|
|
459
|
+
const stepState = createWorkflowStepState(metadata, id);
|
|
460
|
+
const context: StepContext = { id, state: stepState.state };
|
|
342
461
|
const event = { id: context.id, name, key };
|
|
343
462
|
const startedAt = performance.now();
|
|
344
463
|
|
|
@@ -367,7 +486,7 @@ function createStep({ runGroupId }: Pick<RunMetadata, "runGroupId">): WorkflowCo
|
|
|
367
486
|
return checkpoint.value as WorkflowStepResult<T>;
|
|
368
487
|
}
|
|
369
488
|
|
|
370
|
-
const value = normalizeWorkflowStepResult(await fn(context));
|
|
489
|
+
const value = normalizeWorkflowStepResult(await stepState.run(() => fn(context)));
|
|
371
490
|
writeStepEvent("success", { ...event, value }, startedAt);
|
|
372
491
|
writeStepEvent(
|
|
373
492
|
"completed",
|
|
@@ -407,6 +526,104 @@ function createStep({ runGroupId }: Pick<RunMetadata, "runGroupId">): WorkflowCo
|
|
|
407
526
|
return step;
|
|
408
527
|
}
|
|
409
528
|
|
|
529
|
+
class WorkflowWaitInterrupt extends Error {
|
|
530
|
+
constructor(readonly result: WorkflowWaitUntilResult) {
|
|
531
|
+
super(`Workflow is waiting until ${new Date(result.wait.resumeAtMs).toISOString()}`);
|
|
532
|
+
this.name = "WorkflowWaitInterrupt";
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function createWait(step: WorkflowStep): WorkflowWait {
|
|
537
|
+
return {
|
|
538
|
+
async until(name, options) {
|
|
539
|
+
const keyInput = options.key ?? name;
|
|
540
|
+
const keySegments = [
|
|
541
|
+
"wait",
|
|
542
|
+
"until",
|
|
543
|
+
...(typeof keyInput === "string" ? [keyInput] : keyInput),
|
|
544
|
+
];
|
|
545
|
+
const resumeAtMs = await step(name, { key: keySegments }, () =>
|
|
546
|
+
resolveWaitUntilMs(options),
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
if (Date.now() >= resumeAtMs) {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
throw new WorkflowWaitInterrupt({
|
|
554
|
+
status: "waiting",
|
|
555
|
+
wait: {
|
|
556
|
+
type: "until",
|
|
557
|
+
name,
|
|
558
|
+
resumeAtMs,
|
|
559
|
+
},
|
|
560
|
+
});
|
|
561
|
+
},
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function resolveWaitUntilMs(options: WorkflowWaitUntilOptions): number {
|
|
566
|
+
const now = Date.now();
|
|
567
|
+
if ("at" in options) {
|
|
568
|
+
const resumeAtMs = options.at.getTime();
|
|
569
|
+
if (!Number.isFinite(resumeAtMs)) {
|
|
570
|
+
throw new Error("Workflow wait date must be valid.");
|
|
571
|
+
}
|
|
572
|
+
assertWaitFitsCheckpointRetention(resumeAtMs - now);
|
|
573
|
+
return resumeAtMs;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const durationMs = workflowWaitDurationMs(options.after);
|
|
577
|
+
assertWaitFitsCheckpointRetention(durationMs);
|
|
578
|
+
return now + durationMs;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function workflowWaitDurationMs(duration: WorkflowWaitDuration): number {
|
|
582
|
+
const entries = Object.entries(duration);
|
|
583
|
+
if (entries.length === 0) {
|
|
584
|
+
throw new Error("Workflow wait duration must specify at least one unit.");
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
let durationMs = 0;
|
|
588
|
+
for (const [unit, value] of entries) {
|
|
589
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
590
|
+
throw new Error("Workflow wait duration values must be positive finite integers.");
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
durationMs += value * waitDurationUnitMultiplier(unit);
|
|
594
|
+
if (!Number.isSafeInteger(durationMs)) {
|
|
595
|
+
throw new Error("Workflow wait duration is too large.");
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return durationMs;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function assertWaitFitsCheckpointRetention(durationMs: number): void {
|
|
603
|
+
if (durationMs >= MAX_WORKFLOW_WAIT_MS) {
|
|
604
|
+
throw new Error("Workflow waits must be shorter than 7 days.");
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function waitDurationUnitMultiplier(unit: string): number {
|
|
609
|
+
switch (unit) {
|
|
610
|
+
case "milliseconds":
|
|
611
|
+
return 1;
|
|
612
|
+
case "seconds":
|
|
613
|
+
return 1_000;
|
|
614
|
+
case "minutes":
|
|
615
|
+
return 60_000;
|
|
616
|
+
case "hours":
|
|
617
|
+
return 3_600_000;
|
|
618
|
+
case "days":
|
|
619
|
+
return 86_400_000;
|
|
620
|
+
case "weeks":
|
|
621
|
+
return 604_800_000;
|
|
622
|
+
default:
|
|
623
|
+
throw new Error(`Unsupported workflow wait duration unit: ${unit}`);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
410
627
|
function createWorkflowStepKey(segments: readonly unknown[]): string {
|
|
411
628
|
if (segments.length === 0) {
|
|
412
629
|
throw new Error("Workflow step keys must contain at least one segment.");
|