@executablemd/durable-streams 0.3.1 → 0.4.1

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.
@@ -21,9 +21,6 @@ import { all as effectionAll, ensure, race as effectionRace, spawn, suspend, use
21
21
  import { DurableCtx } from "./context.js";
22
22
  import { ephemeral } from "./ephemeral.js";
23
23
  import { deserializeError, serializeError } from "./serialize.js";
24
- // ---------------------------------------------------------------------------
25
- // Internal: wrap a child workflow with DurableContext + Close emission
26
- // ---------------------------------------------------------------------------
27
24
  /**
28
25
  * Run a child workflow within a spawned scope, setting up its own
29
26
  * DurableContext and emitting a Close event when it terminates.
@@ -121,9 +118,6 @@ function* runDurableChild(childWorkflow, childId, parentCtx) {
121
118
  throw error;
122
119
  }
123
120
  }
124
- // ---------------------------------------------------------------------------
125
- // durableSpawn — spawn a single durable child, returns Task<T>
126
- // ---------------------------------------------------------------------------
127
121
  /**
128
122
  * Spawn a durable child workflow.
129
123
  *
@@ -147,9 +141,6 @@ export function durableSpawn(childWorkflow) {
147
141
  return yield* spawn(() => runDurableChild(childWorkflow, childId, ctx));
148
142
  })());
149
143
  }
150
- // ---------------------------------------------------------------------------
151
- // durableAll — fork/join, wait for all children
152
- // ---------------------------------------------------------------------------
153
144
  /**
154
145
  * Run multiple durable workflows concurrently and wait for all to complete.
155
146
  *
@@ -187,9 +178,6 @@ export function durableAll(workflows) {
187
178
  return yield* effectionAll(childOps);
188
179
  })());
189
180
  }
190
- // ---------------------------------------------------------------------------
191
- // durableRace — first child to complete wins, others cancelled
192
- // ---------------------------------------------------------------------------
193
181
  /**
194
182
  * Race multiple durable workflows. The first to complete wins;
195
183
  * remaining children are cancelled.
package/esm/divergence.js CHANGED
@@ -21,9 +21,6 @@
21
21
  */
22
22
  import { createApi } from "effection/experimental";
23
23
  import { ContinuePastCloseDivergenceError, DivergenceError } from "./errors.js";
24
- // ---------------------------------------------------------------------------
25
- // Default policy (strict — all divergences are fatal)
26
- // ---------------------------------------------------------------------------
27
24
  /** The default (strict) decide function. */
28
25
  function defaultDecide(info) {
29
26
  if (info.kind === "description-mismatch") {
@@ -39,9 +36,6 @@ function defaultDecide(info) {
39
36
  };
40
37
  }
41
38
  }
42
- // ---------------------------------------------------------------------------
43
- // The Divergence API instance
44
- // ---------------------------------------------------------------------------
45
39
  /**
46
40
  * The Divergence API.
47
41
  *
package/esm/each.js CHANGED
@@ -16,9 +16,6 @@
16
16
  import { ensure } from "effection";
17
17
  import { createDurableOperation } from "./effect.js";
18
18
  import { ephemeral } from "./ephemeral.js";
19
- // ---------------------------------------------------------------------------
20
- // Internal state
21
- // ---------------------------------------------------------------------------
22
19
  /** Sentinel for source exhaustion. Not exported — cannot collide with JSON. */
23
20
  const DONE = Symbol("durableEach.done");
24
21
  /** Type guard for the DONE sentinel. */
@@ -46,9 +43,6 @@ function isDone(value) {
46
43
  * support for context that spans across ephemeral() boundaries.
47
44
  */
48
45
  let activeState = null;
49
- // ---------------------------------------------------------------------------
50
- // durableEachFetch — shared helper for fetching one item
51
- // ---------------------------------------------------------------------------
52
46
  /**
53
47
  * Fetch a single item from the source (or replay it from the journal).
54
48
  *
@@ -66,14 +60,12 @@ let activeState = null;
66
60
  function durableEachFetch(name, source) {
67
61
  return (function* () {
68
62
  const result = (yield createDurableOperation({ type: "each", name }, () => source.next()));
69
- if ("done" in result)
63
+ if ("done" in result) {
70
64
  return DONE;
65
+ }
71
66
  return result.value;
72
67
  })();
73
68
  }
74
- // ---------------------------------------------------------------------------
75
- // durableEach — initial fetch + returns synchronous iterable
76
- // ---------------------------------------------------------------------------
77
69
  /**
78
70
  * Durable iteration over a DurableSource (internal implementation).
79
71
  *
@@ -148,9 +140,6 @@ function* _durableEachOp(name, source) {
148
140
  function _durableEach(name, source) {
149
141
  return ephemeral(_durableEachOp(name, source));
150
142
  }
151
- // ---------------------------------------------------------------------------
152
- // durableEach.next — static method to advance iteration
153
- // ---------------------------------------------------------------------------
154
143
  /**
155
144
  * Advance the current durable iteration.
156
145
  *
@@ -171,9 +160,6 @@ function* _durableEachNext() {
171
160
  state.current = yield* durableEachFetch(state.name, state.source);
172
161
  state.advanced = true;
173
162
  }
174
- // ---------------------------------------------------------------------------
175
- // Public API — durableEach with static .next() method
176
- // ---------------------------------------------------------------------------
177
163
  /**
178
164
  * Durable iteration over a DurableSource.
179
165
  *
package/esm/effect.js CHANGED
@@ -117,9 +117,6 @@ function checkReplay(desc, resolve, routine, ctx) {
117
117
  } // end replay block
118
118
  return { path: "live" };
119
119
  }
120
- // ---------------------------------------------------------------------------
121
- // createDurableEffect — callback-based (Executor pattern)
122
- // ---------------------------------------------------------------------------
123
120
  /**
124
121
  * Creates a DurableEffect using a callback-based executor.
125
122
  *
@@ -138,16 +135,18 @@ export function createDurableEffect(desc, execute) {
138
135
  enter(resolve, routine) {
139
136
  const ctx = routine.scope.expect(DurableCtx);
140
137
  const replay = checkReplay(desc, resolve, routine, ctx);
141
- if (replay.path === "replayed")
138
+ if (replay.path === "replayed") {
142
139
  return replay.teardown;
140
+ }
143
141
  // ── LIVE PATH ──
144
142
  let settled = false;
145
143
  let tornDown = false;
146
144
  let teardown = () => { };
147
145
  /** Persist a Yield event then resume the generator. */
148
146
  function persistAndResolve(result) {
149
- if (settled)
147
+ if (settled) {
150
148
  return;
149
+ }
151
150
  settled = true;
152
151
  const event = {
153
152
  type: "yield",
@@ -209,9 +208,6 @@ export function createDurableEffect(desc, execute) {
209
208
  },
210
209
  };
211
210
  }
212
- // ---------------------------------------------------------------------------
213
- // createDurableOperation — Operation-based (structured concurrency)
214
- // ---------------------------------------------------------------------------
215
211
  /**
216
212
  * Creates a DurableEffect from an Operation-returning function.
217
213
  *
@@ -233,8 +229,9 @@ export function createDurableOperation(desc, execute) {
233
229
  enter(resolve, routine) {
234
230
  const ctx = routine.scope.expect(DurableCtx);
235
231
  const replay = checkReplay(desc, resolve, routine, ctx);
236
- if (replay.path === "replayed")
232
+ if (replay.path === "replayed") {
237
233
  return replay.teardown;
234
+ }
238
235
  // ── LIVE PATH ──
239
236
  // Run the entire execute → capture → persist → resolve sequence
240
237
  // as a structured operation in the routine's scope.
@@ -31,9 +31,6 @@
31
31
  * See replay-guard-spec.md for the full design.
32
32
  */
33
33
  import { createApi } from "effection/experimental";
34
- // ---------------------------------------------------------------------------
35
- // Default implementation (pass-through)
36
- // ---------------------------------------------------------------------------
37
34
  /**
38
35
  * Default check — no-op. Events pass through without observation.
39
36
  */
@@ -48,9 +45,6 @@ function* defaultCheck(_event) {
48
45
  function defaultDecide(_event) {
49
46
  return { outcome: "replay" };
50
47
  }
51
- // ---------------------------------------------------------------------------
52
- // The ReplayGuard API instance
53
- // ---------------------------------------------------------------------------
54
48
  /**
55
49
  * The ReplayGuard API.
56
50
  *
@@ -47,8 +47,9 @@ export class ReplayIndex {
47
47
  * or undefined if the cursor is past the end or replay is disabled.
48
48
  */
49
49
  peekYield(coroutineId) {
50
- if (this.disabled.has(coroutineId))
50
+ if (this.disabled.has(coroutineId)) {
51
51
  return undefined;
52
+ }
52
53
  const list = this.yields.get(coroutineId);
53
54
  const cursor = this.cursors.get(coroutineId) ?? 0;
54
55
  return list?.[cursor];
@@ -64,14 +65,16 @@ export class ReplayIndex {
64
65
  }
65
66
  /** Returns true if a Close event exists for this coroutine (and replay is not disabled). */
66
67
  hasClose(coroutineId) {
67
- if (this.disabled.has(coroutineId))
68
+ if (this.disabled.has(coroutineId)) {
68
69
  return false;
70
+ }
69
71
  return this.closes.has(coroutineId);
70
72
  }
71
73
  /** Returns the Close event for this coroutine, or undefined. */
72
74
  getClose(coroutineId) {
73
- if (this.disabled.has(coroutineId))
75
+ if (this.disabled.has(coroutineId)) {
74
76
  return undefined;
77
+ }
75
78
  return this.closes.get(coroutineId);
76
79
  }
77
80
  /**
@@ -82,11 +85,13 @@ export class ReplayIndex {
82
85
  */
83
86
  hasAnyUnconsumedYields() {
84
87
  for (const [coroutineId, entries] of this.yields.entries()) {
85
- if (this.disabled.has(coroutineId))
88
+ if (this.disabled.has(coroutineId)) {
86
89
  continue;
90
+ }
87
91
  const cursor = this.cursors.get(coroutineId) ?? 0;
88
- if (cursor < entries.length)
92
+ if (cursor < entries.length) {
89
93
  return true;
94
+ }
90
95
  }
91
96
  return false;
92
97
  }
@@ -102,10 +107,12 @@ export class ReplayIndex {
102
107
  */
103
108
  firstUnconsumed() {
104
109
  for (const [coroutineId, entries] of this.yields.entries()) {
105
- if (this.disabled.has(coroutineId))
110
+ if (this.disabled.has(coroutineId)) {
106
111
  continue;
107
- if (this.closes.has(coroutineId))
112
+ }
113
+ if (this.closes.has(coroutineId)) {
108
114
  continue;
115
+ }
109
116
  const cursor = this.cursors.get(coroutineId) ?? 0;
110
117
  if (cursor < entries.length) {
111
118
  return { coroutineId, cursor, totalYields: entries.length };
@@ -121,8 +128,9 @@ export class ReplayIndex {
121
128
  * Returns false if replay is disabled (run-live mode).
122
129
  */
123
130
  isFullyReplayed(coroutineId) {
124
- if (this.disabled.has(coroutineId))
131
+ if (this.disabled.has(coroutineId)) {
125
132
  return false;
133
+ }
126
134
  return this.peekYield(coroutineId) === undefined && this.hasClose(coroutineId);
127
135
  }
128
136
  /** Returns the total number of yield entries for this coroutine. */
package/esm/serialize.js CHANGED
@@ -6,9 +6,6 @@
6
6
  * - Effection Result ({ ok: true, value } | { ok: false, error })
7
7
  * - Error ↔ SerializedError
8
8
  */
9
- // ---------------------------------------------------------------------------
10
- // Error serialization
11
- // ---------------------------------------------------------------------------
12
9
  /** Serialize an Error to a JSON-safe SerializedError. */
13
10
  export function serializeError(error) {
14
11
  return {
@@ -20,15 +17,14 @@ export function serializeError(error) {
20
17
  /** Deserialize a SerializedError back to an Error. */
21
18
  export function deserializeError(se) {
22
19
  const error = new Error(se.message);
23
- if (se.name)
20
+ if (se.name) {
24
21
  error.name = se.name;
25
- if (se.stack)
22
+ }
23
+ if (se.stack) {
26
24
  error.stack = se.stack;
25
+ }
27
26
  return error;
28
27
  }
29
- // ---------------------------------------------------------------------------
30
- // Result conversion: Protocol ↔ Effection
31
- // ---------------------------------------------------------------------------
32
28
  /**
33
29
  * Convert a protocol Result to an Effection Result.
34
30
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@executablemd/durable-streams",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "Durable, replayable event streams for executable.md.",
5
5
  "homepage": "https://executable.md",
6
6
  "repository": {