lightflow-engine 0.2.8 → 0.2.10
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/dist/compat/metadata.js +4 -3
- package/dist/compat/workflow.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +29 -20
- package/package.json +1 -1
package/dist/compat/metadata.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Vercel Workflow compat: getWorkflowMetadata() returns { workflowRunId }.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { getCurrent } from "../index.js";
|
|
5
5
|
export function getWorkflowMetadata() {
|
|
6
|
-
|
|
6
|
+
const ctx = getCurrent();
|
|
7
|
+
if (!ctx)
|
|
7
8
|
throw new Error("getWorkflowMetadata() outside a workflow");
|
|
8
|
-
return { workflowRunId:
|
|
9
|
+
return { workflowRunId: ctx.runId };
|
|
9
10
|
}
|
package/dist/compat/workflow.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { createHash } from "node:crypto";
|
|
11
11
|
import { step, registerStep, } from "../index.js";
|
|
12
|
-
import {
|
|
12
|
+
import { getCurrent } from "../index.js";
|
|
13
13
|
export { FatalError, CancelledError, sleep } from "../index.js";
|
|
14
14
|
export { getWorkflowMetadata } from "../compat/metadata.js";
|
|
15
15
|
// Vercel exports the step-aware fetch under both names; entry-agents
|
|
@@ -23,7 +23,7 @@ export { workflowFetch } from "../compat/fetch.js";
|
|
|
23
23
|
* terminal done marker.
|
|
24
24
|
*/
|
|
25
25
|
export function getWritable() {
|
|
26
|
-
const ctx =
|
|
26
|
+
const ctx = getCurrent();
|
|
27
27
|
if (!ctx)
|
|
28
28
|
throw new Error("getWritable() outside a workflow");
|
|
29
29
|
const underlying = {
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -39,19 +39,28 @@ export function registerWorkflow(id, fn) {
|
|
|
39
39
|
export function registerStep(id, fn) {
|
|
40
40
|
steps.set(id, fn);
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
// Cross-bundle execution context: Next.js may execute the engine and workflow
|
|
43
|
+
// code from different module instances of this file. `current` must be visible
|
|
44
|
+
// across all of them, so it lives on globalThis and is read via getCurrent().
|
|
45
|
+
export function getCurrent() {
|
|
46
|
+
return globalThis.__lightflowCurrent ?? null;
|
|
47
|
+
}
|
|
48
|
+
function setCurrent(v) {
|
|
49
|
+
globalThis.__lightflowCurrent = v;
|
|
50
|
+
}
|
|
51
|
+
// Rewrite plain assignments on `current` below to setCurrent(...)
|
|
43
52
|
export function getWorkflowMetadata() {
|
|
44
|
-
if (!
|
|
53
|
+
if (!getCurrent())
|
|
45
54
|
throw new Error("getWorkflowMetadata() outside a workflow");
|
|
46
|
-
return { runId:
|
|
55
|
+
return { runId: getCurrent().runId };
|
|
47
56
|
}
|
|
48
57
|
/* ------------------------------------------------------------------ */
|
|
49
58
|
/* The step id: derived from call order, so replay is deterministic */
|
|
50
59
|
/* ------------------------------------------------------------------ */
|
|
51
60
|
function nextStepKey() {
|
|
52
|
-
if (!
|
|
61
|
+
if (!getCurrent())
|
|
53
62
|
throw new Error("steps can only be called inside a workflow");
|
|
54
|
-
return `step:${
|
|
63
|
+
return `step:${getCurrent().stepCalls}`;
|
|
55
64
|
}
|
|
56
65
|
/* ------------------------------------------------------------------ */
|
|
57
66
|
/* Public API inside a workflow */
|
|
@@ -61,9 +70,9 @@ function nextStepKey() {
|
|
|
61
70
|
* the function body is NOT re-executed.
|
|
62
71
|
*/
|
|
63
72
|
export async function step(fn) {
|
|
64
|
-
if (!
|
|
73
|
+
if (!getCurrent())
|
|
65
74
|
return fn(); // plain call outside a workflow
|
|
66
|
-
const ctx =
|
|
75
|
+
const ctx = getCurrent();
|
|
67
76
|
const key = nextStepKey();
|
|
68
77
|
ctx.stepCalls += 1;
|
|
69
78
|
// Replay: memoized result for this call position? (O(1) map lookup)
|
|
@@ -154,11 +163,11 @@ async function maybeSnapshot(ctx) {
|
|
|
154
163
|
}
|
|
155
164
|
/** Durable sleep. Accepts milliseconds or an absolute Date. */
|
|
156
165
|
export async function sleep(until) {
|
|
157
|
-
if (!
|
|
166
|
+
if (!getCurrent()) {
|
|
158
167
|
const ms = until instanceof Date ? until.getTime() - Date.now() : until;
|
|
159
168
|
return new Promise((r) => setTimeout(r, Math.max(0, ms)));
|
|
160
169
|
}
|
|
161
|
-
const ctx =
|
|
170
|
+
const ctx = getCurrent();
|
|
162
171
|
const wakeAt = until instanceof Date ? until.getTime() : ctx.now() + until;
|
|
163
172
|
const key = `sleep:${ctx.sleepCalls}`;
|
|
164
173
|
ctx.sleepCalls += 1;
|
|
@@ -183,8 +192,8 @@ export class SuspendSignal {
|
|
|
183
192
|
}
|
|
184
193
|
/** Ordered output stream for a run. Chunks are persisted and replayable. */
|
|
185
194
|
export function getWritable() {
|
|
186
|
-
const ctx =
|
|
187
|
-
if (!
|
|
195
|
+
const ctx = getCurrent();
|
|
196
|
+
if (!getCurrent())
|
|
188
197
|
throw new Error("getWritable() outside a workflow");
|
|
189
198
|
return {
|
|
190
199
|
async write(chunk) {
|
|
@@ -368,8 +377,8 @@ export class Engine {
|
|
|
368
377
|
return p;
|
|
369
378
|
},
|
|
370
379
|
};
|
|
371
|
-
const prev =
|
|
372
|
-
|
|
380
|
+
const prev = getCurrent();
|
|
381
|
+
setCurrent(ctx);
|
|
373
382
|
try {
|
|
374
383
|
const output = await fn(...args);
|
|
375
384
|
await Promise.all(ctx.inflight);
|
|
@@ -397,7 +406,7 @@ export class Engine {
|
|
|
397
406
|
return fail(err instanceof Error ? err.message : String(err));
|
|
398
407
|
}
|
|
399
408
|
finally {
|
|
400
|
-
|
|
409
|
+
setCurrent(prev);
|
|
401
410
|
await claimer.releaseRun?.(runId);
|
|
402
411
|
}
|
|
403
412
|
}
|
|
@@ -524,21 +533,21 @@ export class CancelledError extends Error {
|
|
|
524
533
|
export function defineHook() {
|
|
525
534
|
return {
|
|
526
535
|
async create() {
|
|
527
|
-
if (!
|
|
536
|
+
if (!getCurrent())
|
|
528
537
|
throw new Error("hooks only inside a workflow");
|
|
529
538
|
const token = `hook_${randomUUID().replace(/-/g, "").slice(0, 20)}`;
|
|
530
|
-
const key = `hook:${
|
|
531
|
-
|
|
532
|
-
await
|
|
539
|
+
const key = `hook:${getCurrent().hookCalls}`;
|
|
540
|
+
getCurrent().hookCalls += 1;
|
|
541
|
+
await getCurrent().store.createHook?.(getCurrent().runId, token, key);
|
|
533
542
|
return { token };
|
|
534
543
|
},
|
|
535
544
|
};
|
|
536
545
|
}
|
|
537
546
|
/** Await a previously created hook until an external caller resolves it. */
|
|
538
547
|
export async function hookResult(token) {
|
|
539
|
-
if (!
|
|
548
|
+
if (!getCurrent())
|
|
540
549
|
throw new Error("hooks only inside a workflow");
|
|
541
|
-
const existing = await
|
|
550
|
+
const existing = await getCurrent().store.getHook?.(token);
|
|
542
551
|
if (existing && existing.payload?.resolved) {
|
|
543
552
|
return existing.payload.value;
|
|
544
553
|
}
|
package/package.json
CHANGED