@smthrs/plan 0.0.0-stage → 1.0.0-rc.3

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 (199) hide show
  1. package/CHANGELOG.md +183 -0
  2. package/LICENSE +21 -0
  3. package/README.md +228 -2
  4. package/dist/cjs/CachePolicy.d.ts +131 -0
  5. package/dist/cjs/CachePolicy.d.ts.map +1 -0
  6. package/dist/cjs/CachePolicy.js +50 -0
  7. package/dist/cjs/CachePolicy.js.map +7 -0
  8. package/dist/cjs/Effects.d.ts +240 -0
  9. package/dist/cjs/Effects.d.ts.map +1 -0
  10. package/dist/cjs/Effects.js +76 -0
  11. package/dist/cjs/Effects.js.map +7 -0
  12. package/dist/cjs/FileSet.d.ts +288 -0
  13. package/dist/cjs/FileSet.d.ts.map +1 -0
  14. package/dist/cjs/FileSet.js +191 -0
  15. package/dist/cjs/FileSet.js.map +7 -0
  16. package/dist/cjs/GraphBuildError.d.ts +122 -0
  17. package/dist/cjs/GraphBuildError.d.ts.map +1 -0
  18. package/dist/cjs/GraphBuildError.js +71 -0
  19. package/dist/cjs/GraphBuildError.js.map +7 -0
  20. package/dist/cjs/KeyMaterial.d.ts +102 -0
  21. package/dist/cjs/KeyMaterial.d.ts.map +1 -0
  22. package/dist/cjs/KeyMaterial.js +64 -0
  23. package/dist/cjs/KeyMaterial.js.map +7 -0
  24. package/dist/cjs/Node.d.ts +606 -0
  25. package/dist/cjs/Node.d.ts.map +1 -0
  26. package/dist/cjs/Node.js +207 -0
  27. package/dist/cjs/Node.js.map +7 -0
  28. package/dist/cjs/Placement.d.ts +117 -0
  29. package/dist/cjs/Placement.d.ts.map +1 -0
  30. package/dist/cjs/Placement.js +46 -0
  31. package/dist/cjs/Placement.js.map +7 -0
  32. package/dist/cjs/Plan.d.ts +440 -0
  33. package/dist/cjs/Plan.d.ts.map +1 -0
  34. package/dist/cjs/Plan.js +508 -0
  35. package/dist/cjs/Plan.js.map +7 -0
  36. package/dist/cjs/PlanDiff.d.ts +58 -0
  37. package/dist/cjs/PlanDiff.d.ts.map +1 -0
  38. package/dist/cjs/PlanDiff.js +124 -0
  39. package/dist/cjs/PlanDiff.js.map +7 -0
  40. package/dist/cjs/Planned.d.ts +121 -0
  41. package/dist/cjs/Planned.d.ts.map +1 -0
  42. package/dist/cjs/Planned.js +72 -0
  43. package/dist/cjs/Planned.js.map +7 -0
  44. package/dist/cjs/Repetition.d.ts +31 -0
  45. package/dist/cjs/Repetition.d.ts.map +1 -0
  46. package/dist/cjs/Repetition.js +36 -0
  47. package/dist/cjs/Repetition.js.map +7 -0
  48. package/dist/cjs/Scheduling.d.ts +89 -0
  49. package/dist/cjs/Scheduling.d.ts.map +1 -0
  50. package/dist/cjs/Scheduling.js +76 -0
  51. package/dist/cjs/Scheduling.js.map +7 -0
  52. package/dist/cjs/StepKey.d.ts +333 -0
  53. package/dist/cjs/StepKey.d.ts.map +1 -0
  54. package/dist/cjs/StepKey.js +311 -0
  55. package/dist/cjs/StepKey.js.map +7 -0
  56. package/dist/cjs/index.d.ts +91 -0
  57. package/dist/cjs/index.d.ts.map +1 -0
  58. package/dist/cjs/index.js +59 -0
  59. package/dist/cjs/index.js.map +7 -0
  60. package/dist/cjs/internal/ConflictAnnotation.d.ts +59 -0
  61. package/dist/cjs/internal/ConflictAnnotation.d.ts.map +1 -0
  62. package/dist/cjs/internal/ConflictAnnotation.js +247 -0
  63. package/dist/cjs/internal/ConflictAnnotation.js.map +7 -0
  64. package/dist/cjs/internal/EffectCandidates.d.ts +19 -0
  65. package/dist/cjs/internal/EffectCandidates.d.ts.map +1 -0
  66. package/dist/cjs/internal/EffectCandidates.js +92 -0
  67. package/dist/cjs/internal/EffectCandidates.js.map +7 -0
  68. package/dist/cjs/internal/JsonMirror.d.ts +34 -0
  69. package/dist/cjs/internal/JsonMirror.d.ts.map +1 -0
  70. package/dist/cjs/internal/JsonMirror.js +195 -0
  71. package/dist/cjs/internal/JsonMirror.js.map +7 -0
  72. package/dist/cjs/internal/effects.d.ts +245 -0
  73. package/dist/cjs/internal/effects.d.ts.map +1 -0
  74. package/dist/cjs/internal/effects.js +290 -0
  75. package/dist/cjs/internal/effects.js.map +7 -0
  76. package/dist/cjs/internal/node.d.ts +526 -0
  77. package/dist/cjs/internal/node.d.ts.map +1 -0
  78. package/dist/cjs/internal/node.js +312 -0
  79. package/dist/cjs/internal/node.js.map +7 -0
  80. package/dist/cjs/package.json +1 -0
  81. package/dist/cjs/test/PlanFixtures.d.ts +127 -0
  82. package/dist/cjs/test/PlanFixtures.d.ts.map +1 -0
  83. package/dist/cjs/test/PlanFixtures.js +59 -0
  84. package/dist/cjs/test/PlanFixtures.js.map +7 -0
  85. package/dist/esm/CachePolicy.d.ts +131 -0
  86. package/dist/esm/CachePolicy.d.ts.map +1 -0
  87. package/dist/esm/CachePolicy.js +117 -0
  88. package/dist/esm/CachePolicy.js.map +1 -0
  89. package/dist/esm/Effects.d.ts +240 -0
  90. package/dist/esm/Effects.d.ts.map +1 -0
  91. package/dist/esm/Effects.js +167 -0
  92. package/dist/esm/Effects.js.map +1 -0
  93. package/dist/esm/FileSet.d.ts +288 -0
  94. package/dist/esm/FileSet.d.ts.map +1 -0
  95. package/dist/esm/FileSet.js +324 -0
  96. package/dist/esm/FileSet.js.map +1 -0
  97. package/dist/esm/GraphBuildError.d.ts +122 -0
  98. package/dist/esm/GraphBuildError.d.ts.map +1 -0
  99. package/dist/esm/GraphBuildError.js +146 -0
  100. package/dist/esm/GraphBuildError.js.map +1 -0
  101. package/dist/esm/KeyMaterial.d.ts +102 -0
  102. package/dist/esm/KeyMaterial.d.ts.map +1 -0
  103. package/dist/esm/KeyMaterial.js +85 -0
  104. package/dist/esm/KeyMaterial.js.map +1 -0
  105. package/dist/esm/Node.d.ts +606 -0
  106. package/dist/esm/Node.d.ts.map +1 -0
  107. package/dist/esm/Node.js +521 -0
  108. package/dist/esm/Node.js.map +1 -0
  109. package/dist/esm/Placement.d.ts +117 -0
  110. package/dist/esm/Placement.d.ts.map +1 -0
  111. package/dist/esm/Placement.js +65 -0
  112. package/dist/esm/Placement.js.map +1 -0
  113. package/dist/esm/Plan.d.ts +440 -0
  114. package/dist/esm/Plan.d.ts.map +1 -0
  115. package/dist/esm/Plan.js +673 -0
  116. package/dist/esm/Plan.js.map +1 -0
  117. package/dist/esm/PlanDiff.d.ts +58 -0
  118. package/dist/esm/PlanDiff.d.ts.map +1 -0
  119. package/dist/esm/PlanDiff.js +159 -0
  120. package/dist/esm/PlanDiff.js.map +1 -0
  121. package/dist/esm/Planned.d.ts +121 -0
  122. package/dist/esm/Planned.d.ts.map +1 -0
  123. package/dist/esm/Planned.js +147 -0
  124. package/dist/esm/Planned.js.map +1 -0
  125. package/dist/esm/Repetition.d.ts +31 -0
  126. package/dist/esm/Repetition.d.ts.map +1 -0
  127. package/dist/esm/Repetition.js +24 -0
  128. package/dist/esm/Repetition.js.map +1 -0
  129. package/dist/esm/Scheduling.d.ts +89 -0
  130. package/dist/esm/Scheduling.d.ts.map +1 -0
  131. package/dist/esm/Scheduling.js +82 -0
  132. package/dist/esm/Scheduling.js.map +1 -0
  133. package/dist/esm/StepKey.d.ts +333 -0
  134. package/dist/esm/StepKey.d.ts.map +1 -0
  135. package/dist/esm/StepKey.js +478 -0
  136. package/dist/esm/StepKey.js.map +1 -0
  137. package/dist/esm/index.d.ts +91 -0
  138. package/dist/esm/index.d.ts.map +1 -0
  139. package/dist/esm/index.js +91 -0
  140. package/dist/esm/index.js.map +1 -0
  141. package/dist/esm/internal/ConflictAnnotation.d.ts +59 -0
  142. package/dist/esm/internal/ConflictAnnotation.d.ts.map +1 -0
  143. package/dist/esm/internal/ConflictAnnotation.js +332 -0
  144. package/dist/esm/internal/ConflictAnnotation.js.map +1 -0
  145. package/dist/esm/internal/EffectCandidates.d.ts +19 -0
  146. package/dist/esm/internal/EffectCandidates.d.ts.map +1 -0
  147. package/dist/esm/internal/EffectCandidates.js +86 -0
  148. package/dist/esm/internal/EffectCandidates.js.map +1 -0
  149. package/dist/esm/internal/JsonMirror.d.ts +34 -0
  150. package/dist/esm/internal/JsonMirror.d.ts.map +1 -0
  151. package/dist/esm/internal/JsonMirror.js +204 -0
  152. package/dist/esm/internal/JsonMirror.js.map +1 -0
  153. package/dist/esm/internal/effects.d.ts +245 -0
  154. package/dist/esm/internal/effects.d.ts.map +1 -0
  155. package/dist/esm/internal/effects.js +459 -0
  156. package/dist/esm/internal/effects.js.map +1 -0
  157. package/dist/esm/internal/node.d.ts +526 -0
  158. package/dist/esm/internal/node.d.ts.map +1 -0
  159. package/dist/esm/internal/node.js +564 -0
  160. package/dist/esm/internal/node.js.map +1 -0
  161. package/dist/esm/test/PlanFixtures.d.ts +127 -0
  162. package/dist/esm/test/PlanFixtures.d.ts.map +1 -0
  163. package/dist/esm/test/PlanFixtures.js +60 -0
  164. package/dist/esm/test/PlanFixtures.js.map +1 -0
  165. package/docs/README.md +165 -0
  166. package/docs/api.md +1475 -0
  167. package/docs/concepts/authoring-ast.md +203 -0
  168. package/docs/concepts/effects-and-conflicts.md +135 -0
  169. package/docs/concepts/plan-value.md +168 -0
  170. package/docs/concepts/step-keys.md +179 -0
  171. package/docs/guides/author-a-node-graph.md +182 -0
  172. package/docs/guides/compile-a-plan.md +146 -0
  173. package/docs/guides/declare-file-effects.md +143 -0
  174. package/docs/guides/diff-two-plans.md +101 -0
  175. package/docs/installation.md +71 -0
  176. package/docs/quickstart.md +128 -0
  177. package/docs/testing.md +73 -0
  178. package/docs/troubleshooting.md +314 -0
  179. package/package.json +254 -3
  180. package/src/CachePolicy.ts +143 -0
  181. package/src/Effects.ts +284 -0
  182. package/src/FileSet.ts +411 -0
  183. package/src/GraphBuildError.ts +158 -0
  184. package/src/KeyMaterial.ts +106 -0
  185. package/src/Node.ts +871 -0
  186. package/src/Placement.ts +99 -0
  187. package/src/Plan.ts +868 -0
  188. package/src/PlanDiff.ts +199 -0
  189. package/src/Planned.ts +208 -0
  190. package/src/Repetition.ts +33 -0
  191. package/src/Scheduling.ts +154 -0
  192. package/src/StepKey.ts +710 -0
  193. package/src/index.ts +103 -0
  194. package/src/internal/ConflictAnnotation.ts +374 -0
  195. package/src/internal/EffectCandidates.ts +89 -0
  196. package/src/internal/JsonMirror.ts +255 -0
  197. package/src/internal/effects.ts +543 -0
  198. package/src/internal/node.ts +858 -0
  199. package/src/test/PlanFixtures.ts +76 -0
@@ -0,0 +1,564 @@
1
+ /**
2
+ * The node AST, and the side tables the functions hang off.
3
+ *
4
+ * Two invariants shape every declaration here. The AST is **closure-free**
5
+ * and keeps payloads as inert JSON mirrors, so a valid plan can be shipped,
6
+ * stored, diffed, and rendered without the process that built it; control flow is
7
+ * **structure**, so a branch stores both arms as ASTs rather than a function
8
+ * that would have to be run to find out what comes next.
9
+ *
10
+ * The functions an author does write — a mapper, a continuation, a branch
11
+ * predicate — live in `WeakMap`s keyed by the AST node they belong to, and the
12
+ * AST keeps only a {@link FunctionIdentity} digest of their source. The digest
13
+ * is what enters content identity (https://smithers.sh/docs/concepts/content-addressing); the
14
+ * `WeakMap` is what the run reaches for when it has the real value in hand, and
15
+ * it drops with the AST it is keyed by.
16
+ *
17
+ * Adapted from `@smthrs/core` (`packages/smithers/flows/core`)
18
+ * `internal/node.ts`. `Dynamic` does not come across — a model call is an
19
+ * ordinary action here — and neither do annotations, which are `Context`
20
+ * values and would break serializability. Scheduling priority is the one thing that does cross, as
21
+ * the plain JSON field {@link Scheduled.priority} rather than as an
22
+ * annotation, because the scheduler has to read it out of a stored plan.
23
+ *
24
+ * @since 0.1.0
25
+ */
26
+ import { isRecord } from "@smthrs/canonical/Record";
27
+ import { digestSync } from "@smthrs/crypto";
28
+ import * as Identity from "@smthrs/crypto/Identity";
29
+ import { identity } from "effect/Function";
30
+ import { pipeArguments } from "effect/Pipeable";
31
+ import * as Schema from "effect/Schema";
32
+ import { jsonMirror } from "./JsonMirror.js";
33
+ /**
34
+ * The runtime type identifier every node carries.
35
+ *
36
+ * @since 0.1.0
37
+ * @private
38
+ * @slop
39
+ */
40
+ export const TypeId = "~@smthrs/plan/Node";
41
+ /**
42
+ * How a flow call joins the caller's plan: `inline` splices the callee's body
43
+ * in, `boundary` makes it one child execution, and `handoff` names the next
44
+ * trampoline round. Why the child call is a mode rather than a second AST tag
45
+ * is in `docs/concepts/authoring-ast.md`.
46
+ *
47
+ * @since 0.1.0
48
+ * @private
49
+ * @slop
50
+ */
51
+ export const CallMode = Schema.Literals(["inline", "boundary", "handoff"]);
52
+ /**
53
+ * The algorithms a {@link FunctionIdentity} may name. The tag is versioned so a
54
+ * change to identity semantics re-keys everything derived from one, rather than
55
+ * colliding with it.
56
+ *
57
+ * @since 0.1.0
58
+ * @private
59
+ * @slop
60
+ */
61
+ export const IdentityAlgorithm = Schema.Literals([
62
+ "sha256-source-ephemeral/v4",
63
+ "sha256-source-captures/v5",
64
+ "static-node/v1"
65
+ ]);
66
+ /**
67
+ * Brands an operation with every inert value it closes over, so its identity
68
+ * covers what it computes with and not only its source.
69
+ *
70
+ * `@smthrs/crypto` owns the captures, nested metadata, and freezing contract.
71
+ *
72
+ * @since 0.1.0
73
+ * @private
74
+ */
75
+ export const capture = Identity.capture;
76
+ // A bundled runtime and a dynamically loaded flow may contain separate
77
+ // compatible package copies. Share only live object metadata: JSON round trips
78
+ // still lose executable bindings, and structural lookalikes remain inert.
79
+ const stateKey = Symbol.for("@smthrs/plan/Node/live/v1");
80
+ const globals = globalThis;
81
+ const liveState = globals[stateKey] ??= {
82
+ references: new WeakSet(),
83
+ nodes: new WeakSet(),
84
+ operations: new WeakMap(),
85
+ predicates: new WeakMap(),
86
+ filters: new WeakMap(),
87
+ declarations: new WeakMap()
88
+ };
89
+ /** The AST references minted by this module; structural lookalikes are data. */
90
+ const plannedReferences = liveState.references;
91
+ const makePlannedReference = (reference) => {
92
+ const value = Object.freeze({
93
+ _tag: "PlannedReference",
94
+ node: reference.node,
95
+ path: Object.freeze([...reference.path])
96
+ });
97
+ plannedReferences.add(value);
98
+ return value;
99
+ };
100
+ /**
101
+ * Reads an AST-owned planned reference without trusting its public data shape.
102
+ *
103
+ * @since 1.0.0
104
+ * @private
105
+ */
106
+ export const plannedReference = (value) => typeof value === "object" && value !== null && plannedReferences.has(value)
107
+ ? value
108
+ : undefined;
109
+ const operations = liveState.operations;
110
+ const predicates = liveState.predicates;
111
+ const filters = liveState.filters;
112
+ const declarations = liveState.declarations;
113
+ /**
114
+ * The JSON mirror of an AST payload. A planned value becomes an AST-owned
115
+ * {@link PlannedReference}, so the AST stays inert JSON.
116
+ *
117
+ * @private
118
+ */
119
+ const payloadMirror = (input) => jsonMirror(input, (_value, reference) => makePlannedReference(reference));
120
+ /**
121
+ * Digests a function's exact source as UTF-8 with SHA-256. Exact source matters:
122
+ * whitespace inside a string literal is behavior, and normalizing it before
123
+ * hashing can make different functions share an identity. Unless its inert
124
+ * captures were declared with {@link capture}, the digest also includes
125
+ * process-local, per-function entropy: JavaScript cannot inspect a closure, so
126
+ * source-only identity would permit incorrect cache hits.
127
+ *
128
+ * @since 0.1.0
129
+ * @private
130
+ * @slop
131
+ */
132
+ export const functionIdentity = Identity.functionIdentity;
133
+ /**
134
+ * The prototype every node shares, so `pipe` is one function rather than one
135
+ * closure per node.
136
+ *
137
+ * @since 0.1.0
138
+ * @private
139
+ * @slop
140
+ */
141
+ export const NodeProto = {
142
+ [TypeId]: {
143
+ _A: identity,
144
+ _E: identity,
145
+ _R: identity
146
+ },
147
+ pipe() {
148
+ // eslint-disable-next-line prefer-rest-params -- `pipeArguments` takes the arguments object itself.
149
+ return pipeArguments(this, arguments);
150
+ }
151
+ };
152
+ /**
153
+ * Every node {@link makeNode} built, so a node this module made is recognized
154
+ * in constant time without walking its AST again.
155
+ */
156
+ const liveNodes = liveState.nodes;
157
+ /** Recognizes the algorithms {@link IdentityAlgorithm} declares, and nothing else. */
158
+ const isIdentityAlgorithm = Schema.is(IdentityAlgorithm);
159
+ /** Recognizes the modes {@link CallMode} declares, and nothing else. */
160
+ const isCallMode = Schema.is(CallMode);
161
+ /** Recognizes the modes {@link RaceMode} names, and nothing else. @private */
162
+ const isRaceMode = (value) => value === "race" || value === "any" || value === "quorum";
163
+ /**
164
+ * Whether `count` is a successes target a {@link Race} of `members` can reach:
165
+ * a quorum needs between one and every member, and the other modes need one.
166
+ *
167
+ * @since 1.0.0
168
+ * @private
169
+ */
170
+ export const isRaceCount = (mode, count, members) => members > 0 && Number.isSafeInteger(count) &&
171
+ (mode === "quorum" ? count >= 1 && count <= members : count === 1);
172
+ /** @private */
173
+ const isFunctionIdentity = (value) => isRecord(value) && value._tag === "FunctionIdentity" && isIdentityAlgorithm(value.algorithm) &&
174
+ typeof value.digest === "string";
175
+ /**
176
+ * Checks whether a value has the shape of a {@link NodeAst}: what a JSON round
177
+ * trip of a genuine AST produces, and nothing looser. Every variant's own
178
+ * fields are checked, and every child is walked. The walk is an explicit stack
179
+ * rather than recursion, so an arbitrarily deep AST cannot overflow the native
180
+ * stack, and it tracks the ancestors of the position it has reached, so a
181
+ * cyclic object is refused rather than walked forever while a shared sub-AST
182
+ * still passes.
183
+ *
184
+ * @since 1.0.0
185
+ * @private
186
+ */
187
+ export const isNodeAst = (value) => {
188
+ const ancestors = new Set();
189
+ const pending = [{ enter: value }];
190
+ while (pending.length > 0) {
191
+ const visit = pending.pop();
192
+ if ("exit" in visit) {
193
+ ancestors.delete(visit.exit);
194
+ continue;
195
+ }
196
+ const ast = visit.enter;
197
+ if (!isRecord(ast) || ancestors.has(ast))
198
+ return false;
199
+ if (ast.priority !== undefined && !Number.isSafeInteger(ast.priority))
200
+ return false;
201
+ const children = [];
202
+ switch (ast._tag) {
203
+ case "Succeed":
204
+ case "Fail":
205
+ break;
206
+ case "All": {
207
+ if (!isRecord(ast.nodes) || Array.isArray(ast.nodes))
208
+ return false;
209
+ for (const child of Object.values(ast.nodes))
210
+ children.push(child);
211
+ break;
212
+ }
213
+ case "Race": {
214
+ if (!isRecord(ast.nodes) || Array.isArray(ast.nodes))
215
+ return false;
216
+ const members = Object.values(ast.nodes);
217
+ if (!isRaceMode(ast.mode) || !isRaceCount(ast.mode, ast.count, members.length))
218
+ return false;
219
+ for (const child of members)
220
+ children.push(child);
221
+ break;
222
+ }
223
+ case "Map": {
224
+ if (!isFunctionIdentity(ast.mapper))
225
+ return false;
226
+ children.push(ast.first);
227
+ break;
228
+ }
229
+ case "AndThen": {
230
+ if (!isFunctionIdentity(ast.continuation))
231
+ return false;
232
+ children.push(ast.first);
233
+ if (ast.next !== undefined)
234
+ children.push(ast.next);
235
+ break;
236
+ }
237
+ case "Branch": {
238
+ if (typeof ast.subject !== "string" || !isFunctionIdentity(ast.predicate))
239
+ return false;
240
+ children.push(ast.first, ast.then, ast.else);
241
+ break;
242
+ }
243
+ case "Catch": {
244
+ if (typeof ast.subject !== "string")
245
+ return false;
246
+ if (ast.filterIdentity !== undefined && !isFunctionIdentity(ast.filterIdentity))
247
+ return false;
248
+ children.push(ast.protected, ast.failure);
249
+ break;
250
+ }
251
+ case "FlowCall": {
252
+ if (typeof ast.flow !== "string" || !isCallMode(ast.mode))
253
+ return false;
254
+ break;
255
+ }
256
+ case "ActionCall": {
257
+ if (typeof ast.action !== "string")
258
+ return false;
259
+ break;
260
+ }
261
+ default:
262
+ return false;
263
+ }
264
+ if (children.length > 0) {
265
+ ancestors.add(ast);
266
+ pending.push({ exit: ast });
267
+ for (const child of children)
268
+ pending.push({ enter: child });
269
+ }
270
+ }
271
+ return true;
272
+ };
273
+ /**
274
+ * Checks whether a value is a node.
275
+ *
276
+ * A node this module built is recognized by registration. A node that crossed
277
+ * a serialization boundary, an object whose prototype is {@link NodeProto} and
278
+ * whose own `ast` data property passes {@link isNodeAst}, is recognized by that
279
+ * shape: `@smthrs/flow` hands a rehydrated AST back as a node, and the side
280
+ * tables are all a round trip loses. The {@link TypeId} marker is an exported
281
+ * string any object can carry, so it counts for nothing on its own, and an
282
+ * `ast` that is missing, malformed, cyclic, or an accessor is refused because
283
+ * every combinator reads it as trusted topology. A proxy is judged by the
284
+ * shape it forwards.
285
+ *
286
+ * @since 1.0.0
287
+ * @private
288
+ */
289
+ export const isNode = (value) => {
290
+ if (typeof value !== "object" || value === null)
291
+ return false;
292
+ if (liveNodes.has(value))
293
+ return true;
294
+ if (Object.getPrototypeOf(value) !== NodeProto)
295
+ return false;
296
+ const ast = Object.getOwnPropertyDescriptor(value, "ast");
297
+ return ast !== undefined && "value" in ast && isNodeAst(ast.value);
298
+ };
299
+ /**
300
+ * Wraps an AST as a node.
301
+ *
302
+ * @since 0.1.0
303
+ * @private
304
+ * @slop
305
+ */
306
+ export const makeNode = (ast) => {
307
+ const node = Object.assign(Object.create(NodeProto), { ast });
308
+ liveNodes.add(node);
309
+ return node;
310
+ };
311
+ /**
312
+ * Constructs a {@link Succeed}.
313
+ *
314
+ * @since 0.1.0
315
+ * @private
316
+ * @slop
317
+ */
318
+ export const succeed = (input) => ({ _tag: "Succeed", value: payloadMirror(input) });
319
+ /**
320
+ * Constructs a {@link Fail}.
321
+ *
322
+ * @since 1.0.0
323
+ * @private
324
+ * @slop
325
+ */
326
+ export const fail = (error) => ({ _tag: "Fail", error: payloadMirror(error) });
327
+ /**
328
+ * Constructs an {@link All}.
329
+ *
330
+ * @since 0.1.0
331
+ * @private
332
+ * @slop
333
+ */
334
+ export const all = (nodes) => ({ _tag: "All", nodes });
335
+ /**
336
+ * Constructs a {@link Race}.
337
+ *
338
+ * @since 1.0.0
339
+ * @private
340
+ */
341
+ export const race = (mode, count, nodes) => ({
342
+ _tag: "Race",
343
+ mode,
344
+ count,
345
+ nodes
346
+ });
347
+ /**
348
+ * Constructs a {@link Map}, filing the mapper under the AST it belongs to.
349
+ *
350
+ * @since 0.1.0
351
+ * @private
352
+ * @slop
353
+ */
354
+ export const map = (first, operation, source) => {
355
+ const ast = { _tag: "Map", first, mapper: functionIdentity(source) };
356
+ operations.set(ast, operation);
357
+ return ast;
358
+ };
359
+ /**
360
+ * Constructs an {@link AndThen} from a builder. The builder is evaluated once
361
+ * against a planned value when the graph is built, never here.
362
+ *
363
+ * @since 0.1.0
364
+ * @private
365
+ * @slop
366
+ */
367
+ export const andThen = (first, operation, source) => {
368
+ const ast = { _tag: "AndThen", first, continuation: functionIdentity(source) };
369
+ operations.set(ast, operation);
370
+ return ast;
371
+ };
372
+ /**
373
+ * Constructs an {@link AndThen} from a node the author supplied directly. There
374
+ * is no function to digest, so the continuation carries a fixed marker.
375
+ *
376
+ * @since 0.1.0
377
+ * @private
378
+ * @slop
379
+ */
380
+ export const andThenNode = (first, next) => ({
381
+ _tag: "AndThen",
382
+ first,
383
+ continuation: { _tag: "FunctionIdentity", algorithm: "static-node/v1", digest: digestSync("static-node") },
384
+ next
385
+ });
386
+ /**
387
+ * Constructs a {@link Branch}. Both arms arrive already evaluated, because a
388
+ * branch that had to be run to reveal its topology would not be a plan.
389
+ *
390
+ * @since 0.1.0
391
+ * @private
392
+ * @slop
393
+ */
394
+ export const branch = (subject, first, predicate, source, then, otherwise) => {
395
+ const ast = {
396
+ _tag: "Branch",
397
+ subject,
398
+ first,
399
+ predicate: functionIdentity(source),
400
+ then,
401
+ else: otherwise
402
+ };
403
+ predicates.set(ast, predicate);
404
+ return ast;
405
+ };
406
+ /**
407
+ * JSON Schema does not describe arbitrary checks, declaration parsers, or
408
+ * suspended schema factories. Include every function reachable from the
409
+ * schema AST, preserving declared captures and treating opaque wrappers as
410
+ * ephemeral. The iterative walk also covers nested checks and cyclic ASTs.
411
+ *
412
+ * @private
413
+ */
414
+ const filterIdentity = (filter) => {
415
+ const identities = [];
416
+ const pending = [["$", filter.ast]];
417
+ const seen = new WeakSet();
418
+ while (pending.length > 0) {
419
+ const [path, current] = pending.pop();
420
+ if (typeof current === "function") {
421
+ identities.push([path, functionIdentity(current)]);
422
+ }
423
+ else if (typeof current === "object" && current !== null && !seen.has(current)) {
424
+ seen.add(current);
425
+ const descriptors = Object.getOwnPropertyDescriptors(current);
426
+ for (const key of Object.keys(descriptors).sort()) {
427
+ const descriptor = descriptors[key];
428
+ if ("value" in descriptor)
429
+ pending.push([`${path}/${JSON.stringify(key)}`, descriptor.value]);
430
+ else {
431
+ if (descriptor.get !== undefined)
432
+ pending.push([`${path}/${JSON.stringify(key)}/get`, descriptor.get]);
433
+ if (descriptor.set !== undefined)
434
+ pending.push([`${path}/${JSON.stringify(key)}/set`, descriptor.set]);
435
+ }
436
+ }
437
+ }
438
+ }
439
+ return {
440
+ _tag: "FunctionIdentity",
441
+ algorithm: identities.some(([, identity]) => identity.algorithm === "sha256-source-ephemeral/v4")
442
+ ? "sha256-source-ephemeral/v4"
443
+ : "sha256-source-captures/v5",
444
+ digest: digestSync(JSON.stringify(identities))
445
+ };
446
+ };
447
+ /**
448
+ * Constructs a {@link Catch} whose failure topology is already evaluated.
449
+ *
450
+ * @since 0.1.0
451
+ * @private
452
+ * @slop
453
+ */
454
+ export const catch_ = (subject, protectedAst, failure, filter) => {
455
+ const ast = {
456
+ _tag: "Catch",
457
+ subject,
458
+ protected: protectedAst,
459
+ failure,
460
+ ...(filter === undefined
461
+ ? {}
462
+ : {
463
+ filter: Schema.toJsonSchemaDocument(filter, { onExcessProperty: "error" }),
464
+ filterIdentity: filterIdentity(filter)
465
+ })
466
+ };
467
+ if (filter !== undefined)
468
+ filters.set(ast, filter);
469
+ return ast;
470
+ };
471
+ /**
472
+ * Constructs a {@link FlowCall}, filing the flow declaration beside it.
473
+ *
474
+ * @since 0.1.0
475
+ * @private
476
+ * @slop
477
+ */
478
+ export const flowCall = (declaration, flow, mode, payload) => {
479
+ const ast = { _tag: "FlowCall", flow, mode, payload: payloadMirror(payload) };
480
+ declarations.set(ast, declaration);
481
+ return ast;
482
+ };
483
+ /**
484
+ * Constructs an {@link ActionCall}, filing the action declaration beside
485
+ * it.
486
+ *
487
+ * @since 0.1.0
488
+ * @private
489
+ * @slop
490
+ */
491
+ export const actionCall = (declaration, action, payload) => {
492
+ const ast = { _tag: "ActionCall", action, payload: payloadMirror(payload) };
493
+ declarations.set(ast, declaration);
494
+ return ast;
495
+ };
496
+ /**
497
+ * The deferred function of a map or a continuation.
498
+ *
499
+ * @since 0.1.0
500
+ * @private
501
+ * @slop
502
+ */
503
+ export const operation = (ast) => operations.get(ast);
504
+ /**
505
+ * The run-time predicate of a branch.
506
+ *
507
+ * @since 0.1.0
508
+ * @private
509
+ * @slop
510
+ */
511
+ export const predicate = (ast) => predicates.get(ast);
512
+ /**
513
+ * The optional error schema filter of a catch node.
514
+ *
515
+ * @since 0.1.0
516
+ * @private
517
+ * @slop
518
+ */
519
+ export const filter = (ast) => filters.get(ast);
520
+ /**
521
+ * The flow or action declaration a call node names.
522
+ *
523
+ * @since 0.1.0
524
+ * @private
525
+ * @slop
526
+ */
527
+ export const declaration = (ast) => declarations.get(ast);
528
+ /**
529
+ * Copies an AST with a scheduling priority attached.
530
+ *
531
+ * The copy is shallow and the side tables are re-filed against it, because a
532
+ * function, a predicate, a filter schema, and a declaration are keyed by the
533
+ * exact AST object they belong to: a copy that left them behind would silently
534
+ * lose the continuation the graph walk has to evaluate.
535
+ *
536
+ * @since 0.1.0
537
+ * @private
538
+ * @slop
539
+ */
540
+ export const withPriority = (ast, priority) => {
541
+ const prioritized = { ...ast, priority };
542
+ if (ast._tag === "AndThen" || ast._tag === "Map") {
543
+ const deferred = operations.get(ast);
544
+ if (deferred !== undefined)
545
+ operations.set(prioritized, deferred);
546
+ }
547
+ if (ast._tag === "Branch") {
548
+ const decision = predicates.get(ast);
549
+ if (decision !== undefined)
550
+ predicates.set(prioritized, decision);
551
+ }
552
+ if (ast._tag === "Catch") {
553
+ const schema = filters.get(ast);
554
+ if (schema !== undefined)
555
+ filters.set(prioritized, schema);
556
+ }
557
+ if (ast._tag === "ActionCall" || ast._tag === "FlowCall") {
558
+ const target = declarations.get(ast);
559
+ if (target !== undefined)
560
+ declarations.set(prioritized, target);
561
+ }
562
+ return prioritized;
563
+ };
564
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../../src/internal/node.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The node AST, and the side tables the functions hang off.\n *\n * Two invariants shape every declaration here. The AST is **closure-free**\n * and keeps payloads as inert JSON mirrors, so a valid plan can be shipped,\n * stored, diffed, and rendered without the process that built it; control flow is\n * **structure**, so a branch stores both arms as ASTs rather than a function\n * that would have to be run to find out what comes next.\n *\n * The functions an author does write — a mapper, a continuation, a branch\n * predicate — live in `WeakMap`s keyed by the AST node they belong to, and the\n * AST keeps only a {@link FunctionIdentity} digest of their source. The digest\n * is what enters content identity (https://smithers.sh/docs/concepts/content-addressing); the\n * `WeakMap` is what the run reaches for when it has the real value in hand, and\n * it drops with the AST it is keyed by.\n *\n * Adapted from `@smthrs/core` (`packages/smithers/flows/core`)\n * `internal/node.ts`. `Dynamic` does not come across — a model call is an\n * ordinary action here — and neither do annotations, which are `Context`\n * values and would break serializability. Scheduling priority is the one thing that does cross, as\n * the plain JSON field {@link Scheduled.priority} rather than as an\n * annotation, because the scheduler has to read it out of a stored plan.\n *\n * @since 0.1.0\n */\n\nimport { isRecord } from \"@smthrs/canonical/Record\"\nimport { digestSync } from \"@smthrs/crypto\"\nimport * as Identity from \"@smthrs/crypto/Identity\"\nimport { identity } from \"effect/Function\"\nimport { pipeArguments } from \"effect/Pipeable\"\nimport * as Schema from \"effect/Schema\"\nimport type { Node } from \"../Node.ts\"\nimport type * as Planned from \"../Planned.ts\"\nimport { jsonMirror } from \"./JsonMirror.ts\"\n\n/**\n * The runtime type identifier every node carries.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const TypeId = \"~@smthrs/plan/Node\" as const\n\n/**\n * The type-level form of {@link TypeId}.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport type TypeId = typeof TypeId\n\n/**\n * What every AST variant carries besides its own shape: an optional scheduling\n * priority.\n *\n * A priority is JSON, not a `Context` annotation, which is why it can live in\n * the AST at all: the whole point of dropping core's annotations was to keep a\n * plan shippable, storable, and diffable. It is a scheduling HINT — the plan\n * compiler copies it onto the node draft and the scheduler orders ready work by\n * it, while key material never reads it, so raising a priority reorders work\n * without re-keying a single step.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport interface Scheduled {\n readonly priority?: number | undefined\n}\n\n/**\n * A constant, known before anything runs.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport interface Succeed extends Scheduled {\n readonly _tag: \"Succeed\"\n readonly value: unknown\n}\n\n/**\n * A constant typed failure, known before anything runs.\n *\n * The error is an inert JSON mirror for the same reason {@link Succeed.value}\n * is: a stored plan has to carry what the node produces without the process\n * that built it.\n *\n * @since 1.0.0\n * @private\n * @slop\n */\nexport interface Fail extends Scheduled {\n readonly _tag: \"Fail\"\n readonly error: unknown\n}\n\n/**\n * Independent children, combined by name. Width is fixed here, at plan time:\n * there is no dynamic fan-out.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport interface All extends Scheduled {\n readonly _tag: \"All\"\n readonly nodes: Readonly<Record<string, NodeAst>>\n}\n\n/**\n * How a {@link Race} settles: `race` on the first member to settle, `any` on\n * the first member to succeed, and `quorum` once `count` members succeed.\n *\n * @since 1.0.0\n * @private\n */\nexport type RaceMode = \"race\" | \"any\" | \"quorum\"\n\n/**\n * Independent children that settle early: the join decides as soon as its\n * {@link RaceMode} can, and the members it did not wait for are interrupted.\n * `count` is the successes a quorum needs, and 1 for `race` and `any`.\n *\n * @since 1.0.0\n * @private\n */\nexport interface Race extends Scheduled {\n readonly _tag: \"Race\"\n readonly mode: RaceMode\n readonly count: number\n readonly nodes: Readonly<Record<string, NodeAst>>\n}\n\n/**\n * A deferred pure transformation of an upstream result. `map` transforms; it\n * never decides.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport interface Map extends Scheduled {\n readonly _tag: \"Map\"\n readonly first: NodeAst\n readonly mapper: FunctionIdentity\n}\n\n/**\n * A continuation after an upstream node. `next` is present when the author\n * supplied a node directly instead of a builder, in which case the topology is\n * already known and nothing has to be evaluated to reveal it.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport interface AndThen extends Scheduled {\n readonly _tag: \"AndThen\"\n readonly first: NodeAst\n readonly continuation: FunctionIdentity\n readonly next?: NodeAst | undefined\n}\n\n/**\n * A decision whose arms are static topology. The predicate runs at run time on\n * the real value; both arms are already ASTs, so the exit condition and the\n * handoff site are visible before anything runs.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport interface Branch extends Scheduled {\n readonly _tag: \"Branch\"\n readonly subject: string\n readonly first: NodeAst\n readonly predicate: FunctionIdentity\n readonly then: NodeAst\n readonly else: NodeAst\n}\n\n/**\n * A protected graph and its statically planned typed-failure continuation.\n *\n * The symbolic error `subject` is minted per catch node rather than shared,\n * mirroring {@link Branch}. Failure arms are built before the graph assigns\n * ids, so a nested arm that captured an outer error would resolve it to the\n * inner catch's protected node under one shared token.\n *\n * An absent filter catches the entire typed error channel, while a present\n * schema catches only values it accepts. This mirrors Effect's typed-error\n * boundary and keeps defects outside normal recovery. The serializable AST carries the schema identity; the live schema\n * remains in a side table beside the AST.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport interface Catch extends Scheduled {\n readonly _tag: \"Catch\"\n readonly subject: string\n readonly protected: NodeAst\n readonly failure: NodeAst\n readonly filter?: unknown | undefined\n readonly filterIdentity?: FunctionIdentity | undefined\n}\n\n/**\n * How a flow call joins the caller's plan: `inline` splices the callee's body\n * in, `boundary` makes it one child execution, and `handoff` names the next\n * trampoline round. Why the child call is a mode rather than a second AST tag\n * is in `docs/concepts/authoring-ast.md`.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const CallMode = Schema.Literals([\"inline\", \"boundary\", \"handoff\"])\n\n/**\n * How a flow call joins the caller's plan.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport type CallMode = typeof CallMode.Type\n\n/**\n * A call to another flow. The AST keeps the callee's tag and the payload;\n * the declaration itself is in the side table, because a flow value carries\n * schemas and a body and is not JSON.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport interface FlowCall extends Scheduled {\n readonly _tag: \"FlowCall\"\n readonly flow: string\n readonly mode: CallMode\n readonly payload: unknown\n}\n\n/**\n * A call to an action: the atom that does work. Same split as\n * {@link FlowCall}, and no mode — an action is always one node.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport interface ActionCall extends Scheduled {\n readonly _tag: \"ActionCall\"\n readonly action: string\n readonly payload: unknown\n}\n\n/**\n * The algorithms a {@link FunctionIdentity} may name. The tag is versioned so a\n * change to identity semantics re-keys everything derived from one, rather than\n * colliding with it.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const IdentityAlgorithm = Schema.Literals([\n \"sha256-source-ephemeral/v4\",\n \"sha256-source-captures/v5\",\n \"static-node/v1\"\n])\n\n/**\n * The algorithms a {@link FunctionIdentity} may name.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport type IdentityAlgorithm = typeof IdentityAlgorithm.Type\n\n/**\n * The serializable stand-in for a function. Captured functions digest their\n * exact source and declared inert captures. Unannotated functions additionally\n * carry process-local, per-object entropy so indistinguishable closure sources\n * fail closed instead of sharing a cache key. The tag it names is\n * {@link IdentityAlgorithm}.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport interface FunctionIdentity {\n readonly _tag: \"FunctionIdentity\"\n readonly algorithm: IdentityAlgorithm\n readonly digest: string\n}\n\n/**\n * Brands an operation with every inert value it closes over, so its identity\n * covers what it computes with and not only its source.\n *\n * `@smthrs/crypto` owns the captures, nested metadata, and freezing contract.\n *\n * @since 0.1.0\n * @private\n */\nexport const capture = Identity.capture\n\n/**\n * The serializable form of a planned value embedded in an AST value or\n * payload. The strict proxy itself cannot enter the AST because serializing it\n * is deliberately a build error.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport interface PlannedReference {\n readonly _tag: \"PlannedReference\"\n readonly node: string\n readonly path: ReadonlyArray<string>\n}\n\n// A bundled runtime and a dynamically loaded flow may contain separate\n// compatible package copies. Share only live object metadata: JSON round trips\n// still lose executable bindings, and structural lookalikes remain inert.\nconst stateKey = Symbol.for(\"@smthrs/plan/Node/live/v1\")\ninterface LiveState {\n readonly references: WeakSet<object>\n readonly nodes: WeakSet<object>\n readonly operations: WeakMap<AndThen | Map, Operation>\n readonly predicates: WeakMap<Branch, Predicate>\n readonly filters: WeakMap<Catch, Schema.Top>\n readonly declarations: WeakMap<ActionCall | FlowCall, unknown>\n}\nconst globals = globalThis as typeof globalThis & { [stateKey]?: LiveState }\nconst liveState = globals[stateKey] ??= {\n references: new WeakSet(),\n nodes: new WeakSet(),\n operations: new WeakMap(),\n predicates: new WeakMap(),\n filters: new WeakMap(),\n declarations: new WeakMap()\n}\n\n/** The AST references minted by this module; structural lookalikes are data. */\nconst plannedReferences = liveState.references\n\nconst makePlannedReference = (reference: Planned.Reference): PlannedReference => {\n const value = Object.freeze({\n _tag: \"PlannedReference\" as const,\n node: reference.node,\n path: Object.freeze([...reference.path])\n })\n plannedReferences.add(value)\n return value\n}\n\n/**\n * Reads an AST-owned planned reference without trusting its public data shape.\n *\n * @since 1.0.0\n * @private\n */\nexport const plannedReference = (value: unknown): PlannedReference | undefined =>\n typeof value === \"object\" && value !== null && plannedReferences.has(value)\n ? value as PlannedReference\n : undefined\n\n/**\n * Every AST variant.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport type NodeAst = Succeed | Fail | All | Race | Map | AndThen | Branch | Catch | FlowCall | ActionCall\n\ntype Operation = (value: unknown) => unknown\n\ntype Predicate = (value: unknown) => boolean\n\nconst operations = liveState.operations\nconst predicates = liveState.predicates\nconst filters = liveState.filters\nconst declarations = liveState.declarations\n\n/**\n * The JSON mirror of an AST payload. A planned value becomes an AST-owned\n * {@link PlannedReference}, so the AST stays inert JSON.\n *\n * @private\n */\nconst payloadMirror = (input: unknown): unknown =>\n jsonMirror(input, (_value, reference) => makePlannedReference(reference))\n\n/**\n * Digests a function's exact source as UTF-8 with SHA-256. Exact source matters:\n * whitespace inside a string literal is behavior, and normalizing it before\n * hashing can make different functions share an identity. Unless its inert\n * captures were declared with {@link capture}, the digest also includes\n * process-local, per-function entropy: JavaScript cannot inspect a closure, so\n * source-only identity would permit incorrect cache hits.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const functionIdentity: (operation: unknown) => FunctionIdentity = Identity.functionIdentity\n\n/**\n * The prototype every node shares, so `pipe` is one function rather than one\n * closure per node.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const NodeProto = {\n [TypeId]: {\n _A: identity,\n _E: identity,\n _R: identity\n },\n pipe() {\n // eslint-disable-next-line prefer-rest-params -- `pipeArguments` takes the arguments object itself.\n return pipeArguments(this, arguments)\n }\n}\n\n/**\n * Every node {@link makeNode} built, so a node this module made is recognized\n * in constant time without walking its AST again.\n */\nconst liveNodes = liveState.nodes\n\n/** Recognizes the algorithms {@link IdentityAlgorithm} declares, and nothing else. */\nconst isIdentityAlgorithm = Schema.is(IdentityAlgorithm)\n\n/** Recognizes the modes {@link CallMode} declares, and nothing else. */\nconst isCallMode = Schema.is(CallMode)\n\n/** Recognizes the modes {@link RaceMode} names, and nothing else. @private */\nconst isRaceMode = (value: unknown): value is RaceMode => value === \"race\" || value === \"any\" || value === \"quorum\"\n\n/**\n * Whether `count` is a successes target a {@link Race} of `members` can reach:\n * a quorum needs between one and every member, and the other modes need one.\n *\n * @since 1.0.0\n * @private\n */\nexport const isRaceCount = (mode: RaceMode, count: unknown, members: number): boolean =>\n members > 0 && Number.isSafeInteger(count) &&\n (mode === \"quorum\" ? (count as number) >= 1 && (count as number) <= members : count === 1)\n\n/** @private */\nconst isFunctionIdentity = (value: unknown): value is FunctionIdentity =>\n isRecord(value) && value._tag === \"FunctionIdentity\" && isIdentityAlgorithm(value.algorithm) &&\n typeof value.digest === \"string\"\n\n/** One position of the {@link isNodeAst} walk: an AST to check, or one whose children are all checked. @private */\ntype AstVisit = { readonly enter: unknown } | { readonly exit: object }\n\n/**\n * Checks whether a value has the shape of a {@link NodeAst}: what a JSON round\n * trip of a genuine AST produces, and nothing looser. Every variant's own\n * fields are checked, and every child is walked. The walk is an explicit stack\n * rather than recursion, so an arbitrarily deep AST cannot overflow the native\n * stack, and it tracks the ancestors of the position it has reached, so a\n * cyclic object is refused rather than walked forever while a shared sub-AST\n * still passes.\n *\n * @since 1.0.0\n * @private\n */\nexport const isNodeAst = (value: unknown): value is NodeAst => {\n const ancestors = new Set<object>()\n const pending: Array<AstVisit> = [{ enter: value }]\n while (pending.length > 0) {\n const visit = pending.pop()!\n if (\"exit\" in visit) {\n ancestors.delete(visit.exit)\n continue\n }\n const ast = visit.enter\n if (!isRecord(ast) || ancestors.has(ast)) return false\n if (ast.priority !== undefined && !Number.isSafeInteger(ast.priority)) return false\n const children: Array<unknown> = []\n switch (ast._tag) {\n case \"Succeed\":\n case \"Fail\":\n break\n case \"All\": {\n if (!isRecord(ast.nodes) || Array.isArray(ast.nodes)) return false\n for (const child of Object.values(ast.nodes)) children.push(child)\n break\n }\n case \"Race\": {\n if (!isRecord(ast.nodes) || Array.isArray(ast.nodes)) return false\n const members = Object.values(ast.nodes)\n if (!isRaceMode(ast.mode) || !isRaceCount(ast.mode, ast.count, members.length)) return false\n for (const child of members) children.push(child)\n break\n }\n case \"Map\": {\n if (!isFunctionIdentity(ast.mapper)) return false\n children.push(ast.first)\n break\n }\n case \"AndThen\": {\n if (!isFunctionIdentity(ast.continuation)) return false\n children.push(ast.first)\n if (ast.next !== undefined) children.push(ast.next)\n break\n }\n case \"Branch\": {\n if (typeof ast.subject !== \"string\" || !isFunctionIdentity(ast.predicate)) return false\n children.push(ast.first, ast.then, ast.else)\n break\n }\n case \"Catch\": {\n if (typeof ast.subject !== \"string\") return false\n if (ast.filterIdentity !== undefined && !isFunctionIdentity(ast.filterIdentity)) return false\n children.push(ast.protected, ast.failure)\n break\n }\n case \"FlowCall\": {\n if (typeof ast.flow !== \"string\" || !isCallMode(ast.mode)) return false\n break\n }\n case \"ActionCall\": {\n if (typeof ast.action !== \"string\") return false\n break\n }\n default:\n return false\n }\n if (children.length > 0) {\n ancestors.add(ast)\n pending.push({ exit: ast })\n for (const child of children) pending.push({ enter: child })\n }\n }\n return true\n}\n\n/**\n * Checks whether a value is a node.\n *\n * A node this module built is recognized by registration. A node that crossed\n * a serialization boundary, an object whose prototype is {@link NodeProto} and\n * whose own `ast` data property passes {@link isNodeAst}, is recognized by that\n * shape: `@smthrs/flow` hands a rehydrated AST back as a node, and the side\n * tables are all a round trip loses. The {@link TypeId} marker is an exported\n * string any object can carry, so it counts for nothing on its own, and an\n * `ast` that is missing, malformed, cyclic, or an accessor is refused because\n * every combinator reads it as trusted topology. A proxy is judged by the\n * shape it forwards.\n *\n * @since 1.0.0\n * @private\n */\nexport const isNode = (value: unknown): value is Node<unknown, unknown, any> => {\n if (typeof value !== \"object\" || value === null) return false\n if (liveNodes.has(value)) return true\n if (Object.getPrototypeOf(value) !== NodeProto) return false\n const ast = Object.getOwnPropertyDescriptor(value, \"ast\")\n return ast !== undefined && \"value\" in ast && isNodeAst(ast.value)\n}\n\n/**\n * Wraps an AST as a node.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const makeNode = <A = unknown, E = never, R = never>(ast: NodeAst): Node<A, E, R> => {\n const node: Node<A, E, R> = Object.assign(Object.create(NodeProto), { ast })\n liveNodes.add(node)\n return node\n}\n\n/**\n * Constructs a {@link Succeed}.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const succeed = (input: unknown): Succeed => ({ _tag: \"Succeed\", value: payloadMirror(input) })\n\n/**\n * Constructs a {@link Fail}.\n *\n * @since 1.0.0\n * @private\n * @slop\n */\nexport const fail = (error: unknown): Fail => ({ _tag: \"Fail\", error: payloadMirror(error) })\n\n/**\n * Constructs an {@link All}.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const all = (nodes: Readonly<Record<string, NodeAst>>): All => ({ _tag: \"All\", nodes })\n\n/**\n * Constructs a {@link Race}.\n *\n * @since 1.0.0\n * @private\n */\nexport const race = (mode: RaceMode, count: number, nodes: Readonly<Record<string, NodeAst>>): Race => ({\n _tag: \"Race\",\n mode,\n count,\n nodes\n})\n\n/**\n * Constructs a {@link Map}, filing the mapper under the AST it belongs to.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const map = (first: NodeAst, operation: Operation, source: unknown): Map => {\n const ast: Map = { _tag: \"Map\", first, mapper: functionIdentity(source) }\n operations.set(ast, operation)\n return ast\n}\n\n/**\n * Constructs an {@link AndThen} from a builder. The builder is evaluated once\n * against a planned value when the graph is built, never here.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const andThen = (first: NodeAst, operation: Operation, source: unknown): AndThen => {\n const ast: AndThen = { _tag: \"AndThen\", first, continuation: functionIdentity(source) }\n operations.set(ast, operation)\n return ast\n}\n\n/**\n * Constructs an {@link AndThen} from a node the author supplied directly. There\n * is no function to digest, so the continuation carries a fixed marker.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const andThenNode = (first: NodeAst, next: NodeAst): AndThen => ({\n _tag: \"AndThen\",\n first,\n continuation: { _tag: \"FunctionIdentity\", algorithm: \"static-node/v1\", digest: digestSync(\"static-node\") },\n next\n})\n\n/**\n * Constructs a {@link Branch}. Both arms arrive already evaluated, because a\n * branch that had to be run to reveal its topology would not be a plan.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const branch = (\n subject: string,\n first: NodeAst,\n predicate: Predicate,\n source: unknown,\n then: NodeAst,\n otherwise: NodeAst\n): Branch => {\n const ast: Branch = {\n _tag: \"Branch\",\n subject,\n first,\n predicate: functionIdentity(source),\n then,\n else: otherwise\n }\n predicates.set(ast, predicate)\n return ast\n}\n\n/**\n * JSON Schema does not describe arbitrary checks, declaration parsers, or\n * suspended schema factories. Include every function reachable from the\n * schema AST, preserving declared captures and treating opaque wrappers as\n * ephemeral. The iterative walk also covers nested checks and cyclic ASTs.\n *\n * @private\n */\nconst filterIdentity = (filter: Schema.Top): FunctionIdentity => {\n const identities: Array<readonly [string, FunctionIdentity]> = []\n const pending: Array<readonly [string, unknown]> = [[\"$\", filter.ast]]\n const seen = new WeakSet<object>()\n while (pending.length > 0) {\n const [path, current] = pending.pop()!\n if (typeof current === \"function\") {\n identities.push([path, functionIdentity(current)])\n } else if (typeof current === \"object\" && current !== null && !seen.has(current)) {\n seen.add(current)\n const descriptors = Object.getOwnPropertyDescriptors(current)\n for (const key of Object.keys(descriptors).sort()) {\n const descriptor = descriptors[key]!\n if (\"value\" in descriptor) pending.push([`${path}/${JSON.stringify(key)}`, descriptor.value])\n else {\n if (descriptor.get !== undefined) pending.push([`${path}/${JSON.stringify(key)}/get`, descriptor.get])\n if (descriptor.set !== undefined) pending.push([`${path}/${JSON.stringify(key)}/set`, descriptor.set])\n }\n }\n }\n }\n return {\n _tag: \"FunctionIdentity\",\n algorithm: identities.some(([, identity]) => identity.algorithm === \"sha256-source-ephemeral/v4\")\n ? \"sha256-source-ephemeral/v4\"\n : \"sha256-source-captures/v5\",\n digest: digestSync(JSON.stringify(identities))\n }\n}\n\n/**\n * Constructs a {@link Catch} whose failure topology is already evaluated.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const catch_ = (subject: string, protectedAst: NodeAst, failure: NodeAst, filter?: Schema.Top): Catch => {\n const ast: Catch = {\n _tag: \"Catch\",\n subject,\n protected: protectedAst,\n failure,\n ...(filter === undefined\n ? {}\n : {\n filter: Schema.toJsonSchemaDocument(filter, { onExcessProperty: \"error\" }),\n filterIdentity: filterIdentity(filter)\n })\n }\n if (filter !== undefined) filters.set(ast, filter)\n return ast\n}\n\n/**\n * Constructs a {@link FlowCall}, filing the flow declaration beside it.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const flowCall = (declaration: unknown, flow: string, mode: CallMode, payload: unknown): FlowCall => {\n const ast: FlowCall = { _tag: \"FlowCall\", flow, mode, payload: payloadMirror(payload) }\n declarations.set(ast, declaration)\n return ast\n}\n\n/**\n * Constructs an {@link ActionCall}, filing the action declaration beside\n * it.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const actionCall = (declaration: unknown, action: string, payload: unknown): ActionCall => {\n const ast: ActionCall = { _tag: \"ActionCall\", action, payload: payloadMirror(payload) }\n declarations.set(ast, declaration)\n return ast\n}\n\n/**\n * The deferred function of a map or a continuation.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const operation = (ast: AndThen | Map): Operation | undefined => operations.get(ast)\n\n/**\n * The run-time predicate of a branch.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const predicate = (ast: Branch): Predicate | undefined => predicates.get(ast)\n\n/**\n * The optional error schema filter of a catch node.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const filter = (ast: Catch): Schema.Top | undefined => filters.get(ast)\n\n/**\n * The flow or action declaration a call node names.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const declaration = (ast: ActionCall | FlowCall): unknown => declarations.get(ast)\n\n/**\n * Copies an AST with a scheduling priority attached.\n *\n * The copy is shallow and the side tables are re-filed against it, because a\n * function, a predicate, a filter schema, and a declaration are keyed by the\n * exact AST object they belong to: a copy that left them behind would silently\n * lose the continuation the graph walk has to evaluate.\n *\n * @since 0.1.0\n * @private\n * @slop\n */\nexport const withPriority = (ast: NodeAst, priority: number): NodeAst => {\n const prioritized = { ...ast, priority }\n if (ast._tag === \"AndThen\" || ast._tag === \"Map\") {\n const deferred = operations.get(ast)\n if (deferred !== undefined) operations.set(prioritized as AndThen | Map, deferred)\n }\n if (ast._tag === \"Branch\") {\n const decision = predicates.get(ast)\n if (decision !== undefined) predicates.set(prioritized as Branch, decision)\n }\n if (ast._tag === \"Catch\") {\n const schema = filters.get(ast)\n if (schema !== undefined) filters.set(prioritized as Catch, schema)\n }\n if (ast._tag === \"ActionCall\" || ast._tag === \"FlowCall\") {\n const target = declarations.get(ast)\n if (target !== undefined) declarations.set(prioritized as ActionCall | FlowCall, target)\n }\n return prioritized\n}\n"]}