yieldstar 0.4.6 → 0.5.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 CHANGED
@@ -4,7 +4,7 @@ JavaScript‑native distributed workflows.
4
4
 
5
5
  Write workflows as async generator functions that yield steps. Use first‑class primitives for retries, delays, and polling. .
6
6
 
7
- Currently supports running on Bun and Sqlite. Postgres support will be added next.
7
+ The resident-process runtime runs on Bun and Node with native SQLite connectors.
8
8
 
9
9
  ## Install
10
10
 
@@ -16,14 +16,14 @@ bun add yieldstar
16
16
 
17
17
  Depending on how you run workflows, also install:
18
18
 
19
- - Local (recommended): `@yieldstar/bun-worker-invoker` and `@yieldstar/bun-sqlite-runtime`
20
- - HTTP server (Bun): `@yieldstar/bun-http-server` and `@yieldstar/bun-worker-invoker`
19
+ - Local (recommended): `@yieldstar/worker-invoker` and `@yieldstar/sqlite-runtime`
20
+ - HTTP server: `@yieldstar/http-server` and `@yieldstar/worker-invoker`
21
21
 
22
22
  Note: `@yieldstar/test-utils` is an internal testing helper used by this repository. For local application use, prefer the SQLite runtime.
23
23
 
24
24
  ## Quick Start: Local (SQLite)
25
25
 
26
- Run a workflow locally with persistence and timers using a Bun worker and the SQLite runtime.
26
+ Run a workflow locally with persistence and timers using a subprocess worker and the SQLite runtime. The example uses the Bun connector; Node applications use `@yieldstar/sqlite-runtime/node` with otherwise identical runtime wiring.
27
27
 
28
28
  ```ts
29
29
  // router.ts
@@ -43,8 +43,8 @@ export type Router = typeof router;
43
43
  // worker.ts
44
44
  import pino from "pino";
45
45
  import { WorkflowRunner } from "@yieldstar/core";
46
- import { createWorkflowWorker } from "@yieldstar/bun-worker-invoker";
47
- import { SqliteHeapClient, SqliteTimersClient, SqliteTaskQueueClient, SqliteSchedulerClient, createSqliteDb } from "@yieldstar/bun-sqlite-runtime";
46
+ import { createWorkflowWorker } from "@yieldstar/worker-invoker";
47
+ import { SqliteHeapClient, SqliteTimersClient, SqliteTaskQueueClient, SqliteSchedulerClient, createSqliteDb } from "@yieldstar/sqlite-runtime/bun";
48
48
  import { router } from "./router";
49
49
 
50
50
  const logger = pino();
@@ -66,9 +66,9 @@ createWorkflowWorker(runner, logger).listen();
66
66
  ```ts
67
67
  // app.ts
68
68
  import pino from "pino";
69
- import { createWorkflowInvoker } from "@yieldstar/bun-worker-invoker";
69
+ import { createWorkflowInvoker } from "@yieldstar/worker-invoker";
70
70
  import { createLocalSdk } from "yieldstar";
71
- import { SqliteEventLoop, createSqliteDb } from "@yieldstar/bun-sqlite-runtime";
71
+ import { SqliteEventLoop, createSqliteDb } from "@yieldstar/sqlite-runtime/bun";
72
72
 
73
73
  const logger = pino();
74
74
  const workerPath = new URL("./worker.ts", import.meta.url).href;
@@ -83,11 +83,13 @@ const result = await sdk.triggerAndWait({ workflowId: "simple-workflow" });
83
83
  console.log(result); // 2
84
84
  ```
85
85
 
86
+ TypeScript worker paths require Node 22.6 or newer for built-in type stripping. Use compiled JavaScript workers on older Node versions.
87
+
86
88
  ## Pick Your Runtime
87
89
 
88
90
  ### Option A: Local Worker + SQLite (single process)
89
91
 
90
- Use a Bun worker to execute steps and a SQLite‑backed runtime for persistence, timers, and a task queue.
92
+ Use a subprocess worker to execute steps and a SQLite-backed runtime for persistence, timers, and a task queue.
91
93
 
92
94
  1) Define workflows and a router:
93
95
 
@@ -109,10 +111,10 @@ export const router = createWorkflowRouter({ "dynamic-workflow": dynamic });
109
111
  // worker.ts
110
112
  import pino from "pino";
111
113
  import { WorkflowRunner } from "@yieldstar/core";
112
- import { createWorkflowWorker } from "@yieldstar/bun-worker-invoker";
113
- import { SqliteHeapClient, SqliteTimersClient, SqliteTaskQueueClient, SqliteSchedulerClient } from "@yieldstar/bun-sqlite-runtime";
114
+ import { createWorkflowWorker } from "@yieldstar/worker-invoker";
115
+ import { SqliteHeapClient, SqliteTimersClient, SqliteTaskQueueClient, SqliteSchedulerClient } from "@yieldstar/sqlite-runtime/bun";
114
116
  import { router } from "./router";
115
- import { createSqliteDb } from "@yieldstar/bun-sqlite-runtime";
117
+ import { createSqliteDb } from "@yieldstar/sqlite-runtime/bun";
116
118
 
117
119
  const logger = pino();
118
120
  const db = createSqliteDb({ path: "./.db/local.sqlite" });
@@ -135,9 +137,9 @@ createWorkflowWorker(runner, logger).listen();
135
137
  ```ts
136
138
  // app.ts
137
139
  import pino from "pino";
138
- import { createWorkflowInvoker } from "@yieldstar/bun-worker-invoker";
140
+ import { createWorkflowInvoker } from "@yieldstar/worker-invoker";
139
141
  import { createLocalSdk } from "yieldstar";
140
- import { SqliteEventLoop, createSqliteDb } from "@yieldstar/bun-sqlite-runtime";
142
+ import { SqliteEventLoop, createSqliteDb } from "@yieldstar/sqlite-runtime/bun";
141
143
 
142
144
  const logger = pino();
143
145
  const workerPath = new URL("./worker.ts", import.meta.url).href;
@@ -162,9 +164,9 @@ Server:
162
164
 
163
165
  ```ts
164
166
  import pino from "pino";
165
- import { createRoutes, createMiddleware } from "@yieldstar/bun-http-server";
166
- import { createWorkflowInvoker } from "@yieldstar/bun-worker-invoker";
167
- import { SqliteEventLoop, createSqliteDb } from "@yieldstar/bun-sqlite-runtime";
167
+ import { createRoutes, createMiddleware } from "@yieldstar/http-server";
168
+ import { createWorkflowInvoker } from "@yieldstar/worker-invoker";
169
+ import { SqliteEventLoop, createSqliteDb } from "@yieldstar/sqlite-runtime/bun";
168
170
  import { router } from "./router";
169
171
 
170
172
  const logger = pino();
@@ -370,7 +372,7 @@ Use `Router` to type your SDKs so `workflowId` and `params` are enforced.
370
372
  For repository development we use an in‑memory runtime driven by `@yieldstar/test-utils` to `triggerAndWait` in tests. This package is internal; application users should test against the same runtime they use in production where possible. Example (internal):
371
373
 
372
374
  ```ts
373
- import { test, expect } from "bun:test";
375
+ import { test, expect } from "vitest";
374
376
  import { createTestSdkFactory } from "@yieldstar/test-utils";
375
377
  import { createWorkflow } from "yieldstar";
376
378
 
@@ -405,4 +407,4 @@ MIT
405
407
 
406
408
  ## Author
407
409
 
408
- [Daniel Grant](danielgrant.co)
410
+ [Daniel Grant](https://danielgrant.co)
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/exports/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,YAAY,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAkB1D,MAAM,MAAM,UAAU,CACpB,MAAM,SAAS,WAAW,EAC1B,MAAM,EACN,OAAO,SAAS,YAAY,IAC1B,CACF,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,MAAM,EAAE,MAAM,KACX,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAEjC,wBAAgB,QAAQ,CACtB,MAAM,SAAS,WAAW,EAC1B,MAAM,EACN,OAAO,SAAS,YAAY,EAE5B,UAAU,EAAE,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,GAC9C,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAuM5C;AAED,eAAO,MAAM,cAAc,iBAAW,CAAC"}
1
+ {"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/exports/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,YAAY,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAmB1D,MAAM,MAAM,UAAU,CACpB,MAAM,SAAS,WAAW,EAC1B,MAAM,EACN,OAAO,SAAS,YAAY,IAC1B,CACF,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,MAAM,EAAE,MAAM,KACX,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAEjC,wBAAgB,QAAQ,CACtB,MAAM,SAAS,WAAW,EAC1B,MAAM,EACN,OAAO,SAAS,YAAY,EAE5B,UAAU,EAAE,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,GAC9C,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CA0M5C;AAED,eAAO,MAAM,cAAc,iBAAW,CAAC"}
package/dist/index.d.ts CHANGED
@@ -4,4 +4,7 @@ export { createWorkflowRouter } from "./exports/router";
4
4
  export { RetryableError } from "./exports/errors";
5
5
  export { createWorkflow, workflow } from "./exports/workflow";
6
6
  export type { WorkflowFn } from "./exports/workflow";
7
+ export { defineStore } from "./internal/step-runner";
8
+ export type { WorkflowStore } from "./internal/step-runner";
9
+ export type { Draft, RuntimeStore, StandardSchemaV1, StoreDefinition, StoreDeleteFromResult, StoreKey, StorePath, StoreSelector, StoreSnapshot, StoreState, StoreUpdateFromResult, StoreUpdateResult, StoreVersion, WorkflowEvent, } from "@yieldstar/core";
7
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9D,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9D,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EACV,KAAK,EACL,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,QAAQ,EACR,SAAS,EACT,aAAa,EACb,aAAa,EACb,UAAU,EACV,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,EACZ,aAAa,GACd,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- // @bun
2
1
  // src/exports/sdk-local.ts
3
2
  import { nanoid } from "nanoid";
4
3
  function createLocalSdk(invoker) {
@@ -89,16 +88,15 @@ var isIterable = (i) => {
89
88
  return typeof i === "object" && i != null && Symbol.iterator in i;
90
89
  };
91
90
  function getCallSiteHash(stepFn) {
92
- const originalStack = stepFn.stack;
93
- Error.captureStackTrace(stepFn, getCallSiteHash);
94
- const stack = stepFn.stack;
95
- const stepFnCallSite = stack.split(")")[1];
96
- if (originalStack === undefined) {
97
- delete stepFn.stack;
98
- } else {
99
- stepFn.stack = originalStack;
91
+ const holder = {};
92
+ Error.captureStackTrace(holder, stepFn);
93
+ const frames = (holder.stack ?? "").split(`
94
+ `).filter((line) => /^\s*at\s/.test(line));
95
+ const callSite = frames[0]?.trim();
96
+ if (!callSite) {
97
+ throw new Error("Could not derive a cache key from the call site. Pass an explicit key to this step.");
100
98
  }
101
- return createHash("sha1").update(stepFnCallSite).digest("hex");
99
+ return createHash("sha1").update(callSite).digest("hex");
102
100
  }
103
101
 
104
102
  // src/internal/serialise.ts
@@ -106,6 +104,7 @@ import {
106
104
  StepDelay,
107
105
  StepError,
108
106
  StepResult,
107
+ StepStoreWait,
109
108
  WorkflowResult
110
109
  } from "@yieldstar/core";
111
110
  import { isErrorLike, serializeError, deserializeError as deserializeError2 } from "serialize-error";
@@ -134,6 +133,8 @@ function deserializeStepResponse(jsonString) {
134
133
  return new StepDelay(stepResponse.resumeIn);
135
134
  case "step-result":
136
135
  return new StepResult(stepResponse.result);
136
+ case "store-wait":
137
+ return new StepStoreWait;
137
138
  case "workflow-result":
138
139
  return new WorkflowResult(stepResponse.result);
139
140
  default:
@@ -143,24 +144,34 @@ function deserializeStepResponse(jsonString) {
143
144
 
144
145
  // src/internal/step-runner.ts
145
146
  import {
147
+ cloneStoreState,
148
+ defineStore,
146
149
  StepKey,
147
150
  StepError as StepError2,
148
151
  StepResult as StepResult2,
149
152
  StepDelay as StepDelay2,
150
- StepCacheCheck
153
+ StepCacheCheck,
154
+ StepStoreWait as StepStoreWait2,
155
+ trackStoreSelector
151
156
  } from "@yieldstar/core";
152
- var stepRunner = { run, delay, poll };
153
- async function* run(arg1, arg2) {
154
- let key = null;
155
- let fn;
156
- if (typeof arg1 === "string") {
157
- key = arg1;
158
- fn = arg2;
159
- } else {
160
- fn = arg1;
157
+ function createStepRunner(params) {
158
+ const { event, storeClient } = params;
159
+ function store(definition, params2) {
160
+ const id = params2?.id ?? event.executionId;
161
+ const key = `store:${definition.name}:${id}`;
162
+ return storeStep({
163
+ key,
164
+ definition,
165
+ id,
166
+ event,
167
+ storeClient,
168
+ initial: params2?.initial
169
+ });
161
170
  }
162
- if (!key)
163
- key = getCallSiteHash(run);
171
+ return { run, delay, poll, store };
172
+ }
173
+ async function* storeStep(params) {
174
+ const { key, definition, id, event, storeClient, initial } = params;
164
175
  yield new StepKey(key);
165
176
  const cached = yield new StepCacheCheck;
166
177
  if (cached) {
@@ -168,25 +179,111 @@ async function* run(arg1, arg2) {
168
179
  if (cached instanceof StepError2) {
169
180
  throw cached.err;
170
181
  }
171
- return cached.result;
182
+ return createWorkflowStore({
183
+ definition,
184
+ id,
185
+ event,
186
+ storeClient
187
+ });
172
188
  }
173
189
  try {
174
- const result = await fn();
175
- yield new StepResult2(result);
176
- return result;
190
+ await storeClient.getOrCreateStore({
191
+ definition,
192
+ id,
193
+ initial
194
+ });
195
+ yield new StepResult2({ storeName: definition.name, storeId: id });
196
+ return createWorkflowStore({
197
+ definition,
198
+ id,
199
+ event,
200
+ storeClient
201
+ });
177
202
  } catch (err) {
178
- if (err instanceof RetryableError) {
179
- yield new StepError2(err, {
180
- maxAttempts: err.maxAttempts,
181
- retryInterval: err.retryInterval
203
+ yield new StepError2(err);
204
+ throw err;
205
+ }
206
+ }
207
+ function createWorkflowStore(params) {
208
+ const { definition, id, event, storeClient } = params;
209
+ const key = { storeName: definition.name, storeId: id };
210
+ return {
211
+ definition,
212
+ id,
213
+ key,
214
+ get(stepKey) {
215
+ return durableStep(stepKey ?? getCallSiteHash(this.get), async () => await storeClient.getStore({ definition, id }));
216
+ },
217
+ select(stepKey, selector) {
218
+ return durableStep(stepKey, async () => {
219
+ const snapshot = await storeClient.getStore({ definition, id });
220
+ return selector(cloneStoreState(snapshot.state));
221
+ });
222
+ },
223
+ update(stepKey, updater) {
224
+ return durableStep(stepKey, async () => await storeClient.updateStore({
225
+ definition,
226
+ id,
227
+ updater,
228
+ stepId: { executionId: event.executionId, stepKey }
229
+ }));
230
+ },
231
+ updateFrom(stepKey, snapshot, updater) {
232
+ return durableStep(stepKey, async () => await storeClient.updateStoreFrom({
233
+ definition,
234
+ id,
235
+ snapshot,
236
+ updater,
237
+ stepId: { executionId: event.executionId, stepKey }
238
+ }));
239
+ },
240
+ deleteFrom(stepKey, snapshot) {
241
+ return durableStep(stepKey, () => storeClient.deleteStoreFrom({
242
+ definition,
243
+ id,
244
+ snapshot,
245
+ stepId: { executionId: event.executionId, stepKey }
246
+ }));
247
+ },
248
+ when(arg1, arg2) {
249
+ const stepKey = typeof arg1 === "string" ? arg1 : getCallSiteHash(this.when);
250
+ const selector = typeof arg1 === "string" ? arg2 : arg1;
251
+ return whenStep({
252
+ definition,
253
+ id,
254
+ event,
255
+ storeClient,
256
+ stepKey,
257
+ selector
258
+ });
259
+ },
260
+ take(stepKey, selector, claim) {
261
+ return takeStep({
262
+ definition,
263
+ id,
264
+ event,
265
+ storeClient,
266
+ stepKey,
267
+ selector,
268
+ claim
182
269
  });
183
- } else {
184
- yield new StepError2(err);
185
270
  }
186
- throw err;
271
+ };
272
+ }
273
+ function run(arg1, arg2) {
274
+ let key = null;
275
+ let fn;
276
+ if (typeof arg1 === "string") {
277
+ key = arg1;
278
+ fn = arg2;
279
+ } else {
280
+ fn = arg1;
187
281
  }
282
+ if (!key)
283
+ key = getCallSiteHash(run);
284
+ return durableStep(key, fn);
188
285
  }
189
- async function* delay(arg1, arg2) {
286
+ function delay(arg1, arg2) {
190
287
  let key = null;
191
288
  let retryInterval;
192
289
  if (typeof arg1 === "string") {
@@ -197,6 +294,9 @@ async function* delay(arg1, arg2) {
197
294
  }
198
295
  if (!key)
199
296
  key = getCallSiteHash(delay);
297
+ return delayStep(key, retryInterval);
298
+ }
299
+ async function* delayStep(key, retryInterval) {
200
300
  yield new StepKey(key);
201
301
  const cached = yield new StepCacheCheck;
202
302
  if (cached) {
@@ -205,7 +305,7 @@ async function* delay(arg1, arg2) {
205
305
  yield new StepDelay2(Date.now() + retryInterval);
206
306
  }
207
307
  }
208
- async function* poll(arg1, arg2, arg3) {
308
+ function poll(arg1, arg2, arg3) {
209
309
  let key = null;
210
310
  let opts;
211
311
  let predicate;
@@ -227,7 +327,107 @@ async function* poll(arg1, arg2, arg3) {
227
327
  });
228
328
  }
229
329
  };
230
- yield* run(key, task);
330
+ return run(key, task);
331
+ }
332
+ async function* durableStep(key, fn) {
333
+ yield new StepKey(key);
334
+ const cached = yield new StepCacheCheck;
335
+ if (cached) {
336
+ yield cached;
337
+ if (cached instanceof StepError2) {
338
+ throw cached.err;
339
+ }
340
+ return cached.result;
341
+ }
342
+ try {
343
+ const result = await fn();
344
+ yield new StepResult2(result);
345
+ return result;
346
+ } catch (err) {
347
+ if (err instanceof RetryableError) {
348
+ yield new StepError2(err, {
349
+ maxAttempts: err.maxAttempts,
350
+ retryInterval: err.retryInterval
351
+ });
352
+ } else {
353
+ yield new StepError2(err);
354
+ }
355
+ throw err;
356
+ }
357
+ }
358
+ async function* whenStep(params) {
359
+ const { definition, id, event, storeClient, stepKey, selector } = params;
360
+ yield new StepKey(stepKey);
361
+ const cached = yield new StepCacheCheck;
362
+ if (cached) {
363
+ yield cached;
364
+ if (!(cached instanceof StepResult2)) {
365
+ throw new Error("Store wait step cache must resolve to a step result");
366
+ }
367
+ return cached.result;
368
+ }
369
+ const snapshot = await storeClient.getStore({ definition, id });
370
+ const { result, readPaths } = trackStoreSelector(cloneStoreState(snapshot.state), selector);
371
+ if (result !== undefined && result !== null && result !== false) {
372
+ const clonedResult = cloneStoreState(result);
373
+ yield new StepResult2(clonedResult);
374
+ return clonedResult;
375
+ }
376
+ await storeClient.registerWaiter({
377
+ workflowId: event.workflowId,
378
+ executionId: event.executionId,
379
+ stepKey,
380
+ event,
381
+ storeName: definition.name,
382
+ storeId: id,
383
+ instanceId: snapshot.instanceId,
384
+ sinceVersion: snapshot.version,
385
+ readPaths
386
+ });
387
+ yield new StepStoreWait2;
388
+ throw new Error("Store wait yielded control unexpectedly");
389
+ }
390
+ async function* takeStep(params) {
391
+ const { definition, id, event, storeClient, stepKey, selector, claim } = params;
392
+ yield new StepKey(stepKey);
393
+ const cached = yield new StepCacheCheck;
394
+ if (cached) {
395
+ yield cached;
396
+ if (!(cached instanceof StepResult2)) {
397
+ throw new Error("Store take step cache must resolve to a step result");
398
+ }
399
+ return cached.result;
400
+ }
401
+ let outcome;
402
+ try {
403
+ outcome = await storeClient.takeFromStore({
404
+ definition,
405
+ id,
406
+ selector,
407
+ claim,
408
+ stepId: { executionId: event.executionId, stepKey }
409
+ });
410
+ } catch (err) {
411
+ yield new StepError2(err);
412
+ throw err;
413
+ }
414
+ if (outcome.matched) {
415
+ yield new StepResult2(outcome.selected);
416
+ return outcome.selected;
417
+ }
418
+ await storeClient.registerWaiter({
419
+ workflowId: event.workflowId,
420
+ executionId: event.executionId,
421
+ stepKey,
422
+ event,
423
+ storeName: definition.name,
424
+ storeId: id,
425
+ instanceId: outcome.instanceId,
426
+ sinceVersion: outcome.version,
427
+ readPaths: outcome.readPaths
428
+ });
429
+ yield new StepStoreWait2;
430
+ throw new Error("Store take yielded control unexpectedly");
231
431
  }
232
432
 
233
433
  // src/exports/workflow.ts
@@ -239,10 +439,12 @@ import {
239
439
  StepDelay as StepDelay3,
240
440
  StepCacheCheck as StepCacheCheck2,
241
441
  StepInvalid,
442
+ StepStoreWait as StepStoreWait3,
242
443
  WorkflowResult as WorkflowResult2
243
444
  } from "@yieldstar/core";
244
445
  function workflow(workflowFn) {
245
- return async function* workflowGenerator({ event, heapClient, logger }) {
446
+ return async function* workflowGenerator({ event, heapClient, storeClient, logger }) {
447
+ const stepRunner = createStepRunner({ event, storeClient });
246
448
  const workflowIterator = workflowFn(stepRunner, event, logger);
247
449
  const { executionId } = event;
248
450
  let iteratorResult = null;
@@ -303,12 +505,13 @@ function workflow(workflowFn) {
303
505
  stepResponse = new StepInvalid;
304
506
  }
305
507
  const needsRetry = !cached?.meta.done && stepResponse instanceof StepError3 && stepResponse.maxAttempts > stepAttempt + 1;
508
+ const needsWake = !cached?.meta.done && stepResponse instanceof StepStoreWait3;
306
509
  if (!cached) {
307
510
  await heapClient.writeStep({
308
511
  executionId,
309
512
  stepKey,
310
513
  stepAttempt,
311
- stepDone: !needsRetry,
514
+ stepDone: !needsRetry && !needsWake,
312
515
  stepResponseJson: serializeStepResponse(stepResponse)
313
516
  });
314
517
  }
@@ -341,6 +544,7 @@ function workflow(workflowFn) {
341
544
  var createWorkflow = workflow;
342
545
  export {
343
546
  workflow,
547
+ defineStore,
344
548
  createWorkflowRouter,
345
549
  createWorkflow,
346
550
  createLocalSdk,
@@ -1 +1 @@
1
- {"version":3,"file":"serialise.d.ts","sourceRoot":"","sources":["../../src/internal/serialise.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,YAAY,EAEb,MAAM,iBAAiB,CAAC;AAGzB,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAShE;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAuBxE"}
1
+ {"version":3,"file":"serialise.d.ts","sourceRoot":"","sources":["../../src/internal/serialise.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,YAAY,EAGb,MAAM,iBAAiB,CAAC;AAGzB,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAShE;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAyBxE"}
@@ -1,4 +1,5 @@
1
- import { StepResponse, StepError, StepResult } from "@yieldstar/core";
1
+ import { defineStore, type Draft, type StandardSchemaV1, StepResponse, StepError, StepResult, type StoreClient, type StoreDefinition, type StoreDeleteFromResult, type StoreKey, type StoreSelector, type StoreSnapshot, type StoreState, type StoreUpdateFromResult, type StoreUpdateResult } from "@yieldstar/core";
2
+ import type { WorkflowEvent } from "@yieldstar/core";
2
3
  /**
3
4
  * @description A library of step generators, each of which:
4
5
  * @yields a StepResponse to workflow consumers
@@ -7,12 +8,33 @@ import { StepResponse, StepError, StepResult } from "@yieldstar/core";
7
8
  * @throws any error caught when running the user-defined function,
8
9
  * once retries have been exhausted, or if there are no retry semantics
9
10
  */
10
- export declare const stepRunner: {
11
+ export { defineStore };
12
+ export type WorkflowStore<T> = {
13
+ readonly definition: StoreDefinition<any>;
14
+ readonly id: string;
15
+ readonly key: StoreKey;
16
+ get(key?: string): AsyncGenerator<StepResponse, StoreSnapshot<T>>;
17
+ select<R>(key: string, selector: StoreSelector<T, R>): AsyncGenerator<StepResponse, R>;
18
+ update(key: string, updater: (draft: Draft<T>) => void | T): AsyncGenerator<StepResponse, StoreUpdateResult<T>>;
19
+ updateFrom(key: string, snapshot: StoreSnapshot<T>, updater: (draft: Draft<T>) => void | T): AsyncGenerator<StepResponse, StoreUpdateFromResult<T>>;
20
+ deleteFrom(key: string, snapshot: StoreSnapshot<T>): AsyncGenerator<StepResponse, StoreDeleteFromResult>;
21
+ when<R>(selector: StoreSelector<T, R | undefined | null | false>): AsyncGenerator<StepResponse, NonNullable<R>>;
22
+ when<R>(key: string, selector: StoreSelector<T, R | undefined | null | false>): AsyncGenerator<StepResponse, NonNullable<R>>;
23
+ take<R>(key: string, selector: StoreSelector<T, R | undefined | null | false>, claim: (draft: Draft<T>, selected: NonNullable<R>) => void): AsyncGenerator<StepResponse, NonNullable<R>>;
24
+ };
25
+ export type StepRunner = ReturnType<typeof createStepRunner>;
26
+ export declare function createStepRunner(params: {
27
+ event: WorkflowEvent;
28
+ storeClient: StoreClient;
29
+ }): {
11
30
  run: typeof run;
12
31
  delay: typeof delay;
13
32
  poll: typeof poll;
33
+ store: <Schema extends StandardSchemaV1>(definition: StoreDefinition<Schema>, params?: {
34
+ id?: string;
35
+ initial?: StoreState<Schema> | (() => StoreState<Schema> | Promise<StoreState<Schema>>);
36
+ }) => AsyncGenerator<StepResponse, WorkflowStore<StoreState<Schema>>>;
14
37
  };
15
- export type StepRunner = typeof stepRunner;
16
38
  declare function run<T extends any>(fn: () => T | Promise<T>): AsyncGenerator<StepResponse, T, StepResult | StepError>;
17
39
  declare function run<T extends any>(key: string, fn: () => T | Promise<T>): AsyncGenerator<StepResponse, T, StepResult | StepError>;
18
40
  declare function delay(retryInterval: number): any;
@@ -24,5 +46,4 @@ type PollOpts = {
24
46
  type PollPredicate = () => boolean | Promise<boolean>;
25
47
  declare function poll(opts: PollOpts, predicate: PollPredicate): any;
26
48
  declare function poll(key: string, opts: PollOpts, predicate: PollPredicate): any;
27
- export {};
28
49
  //# sourceMappingURL=step-runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"step-runner.d.ts","sourceRoot":"","sources":["../../src/internal/step-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAEZ,SAAS,EACT,UAAU,EAGX,MAAM,iBAAiB,CAAC;AAIzB;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU;;;;CAAuB,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC;AAE3C,iBAAS,GAAG,CAAC,CAAC,SAAS,GAAG,EACxB,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,cAAc,CAAC,YAAY,EAAE,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,CAAC;AAE3D,iBAAS,GAAG,CAAC,CAAC,SAAS,GAAG,EACxB,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,cAAc,CAAC,YAAY,EAAE,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,CAAC;AAiD3D,iBAAS,KAAK,CAAC,aAAa,EAAE,MAAM,GAAG,GAAG,CAAC;AAC3C,iBAAS,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,GAAG,CAAC;AA6BxD,KAAK,QAAQ,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC;AAC/D,KAAK,aAAa,GAAG,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtD,iBAAS,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAAG,GAAG,CAAC;AAC7D,iBAAS,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAAG,GAAG,CAAC"}
1
+ {"version":3,"file":"step-runner.d.ts","sourceRoot":"","sources":["../../src/internal/step-runner.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,14 @@
1
1
  export declare const isIterable: <T>(i: T | Iterable<T>) => i is Iterable<T>;
2
+ /**
3
+ * Derives a replay-stable cache key from the call site of a keyless step.
4
+ *
5
+ * IMPORTANT: `stepFn` must be the synchronously-executing function that the
6
+ * user called directly (e.g. the `step.run` wrapper) – NOT an async generator
7
+ * body. Async generator bodies don't run until the first `.next()`, by which
8
+ * point the user's frame is no longer on the stack. `Error.captureStackTrace`
9
+ * omits every frame above (and including) `stepFn`, so the first captured
10
+ * frame is the user's call site (file:line:column), which is deterministic
11
+ * across executions of the same workflow code.
12
+ */
2
13
  export declare function getCallSiteHash(stepFn: Function): string;
3
14
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/internal/utils.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAG,CAAC,IAAI,QAAQ,CAAC,CAAC,CAIjE,CAAC;AAGF,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,UAe/C"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/internal/utils.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAG,CAAC,IAAI,QAAQ,CAAC,CAAC,CAIjE,CAAC;AAGF;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAkBxD"}
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "yieldstar",
3
- "version": "0.4.6",
3
+ "version": "0.5.0",
4
4
  "license": "MIT",
5
5
  "author": "Daniel Grant",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/notationlabs/yieldstar.git",
9
+ "directory": "packages/yieldstar"
10
+ },
6
11
  "type": "module",
7
12
  "main": "dist/index.js",
8
13
  "types": "dist/index.d.ts",
@@ -10,12 +15,12 @@
10
15
  "nanoid": "^5.1.5",
11
16
  "pino": "^9.9.0",
12
17
  "serialize-error": "^11.0.3",
13
- "@yieldstar/core": "0.4.6"
18
+ "@yieldstar/core": "0.5.0"
14
19
  },
15
20
  "devDependencies": {
16
21
  "@types/node": "^22.18.0"
17
22
  },
18
23
  "scripts": {
19
- "build": "bun build src/index.ts --outfile dist/index.js --target bun --packages=external"
24
+ "build": "bun build src/index.ts --outfile dist/index.js --target node --packages=external"
20
25
  }
21
26
  }