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

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,91 @@
1
+ /**
2
+ * The plan value: a keyed action graph, its diff, and the step-key compiler
3
+ * that gives every node its identity.
4
+ *
5
+ * A plan is a `Node` graph with every key computed, produced by the plan phase
6
+ * and inert until run. This package is that value and nothing more: it
7
+ * performs no I/O at all and never executes anything. Persisting a plan is
8
+ * `@smthrs/plan-store`; driving one is `@smthrs/engine-store`'s
9
+ * `PlanScheduler`.
10
+ *
11
+ * Above the persisted form sits the authoring AST: `Node` describes a plan as
12
+ * pure data, and `Planned` is the placeholder a body sees where a step result
13
+ * will be. Both build plans; neither runs one.
14
+ *
15
+ * @since 0.1.0
16
+ */
17
+ /**
18
+ * @since 0.1.0
19
+ * @category errors
20
+ * @slop
21
+ */
22
+ export * as GraphBuildError from "./GraphBuildError.js";
23
+ /**
24
+ * @since 1.0.0-rc.0
25
+ * @category models
26
+ */
27
+ export * as CachePolicy from "./CachePolicy.js";
28
+ /**
29
+ * @since 1.0.0-rc.0
30
+ * @category models
31
+ */
32
+ export * as Repetition from "./Repetition.js";
33
+ /**
34
+ * @since 0.1.0
35
+ * @category models
36
+ * @slop
37
+ */
38
+ export * as KeyMaterial from "./KeyMaterial.js";
39
+ /**
40
+ * @since 0.1.0
41
+ * @category models
42
+ * @slop
43
+ */
44
+ export * as FileSet from "./FileSet.js";
45
+ /**
46
+ * @since 0.1.0
47
+ * @category models
48
+ * @slop
49
+ */
50
+ export * as Effects from "./Effects.js";
51
+ /**
52
+ * @since 0.1.0
53
+ * @category models
54
+ * @slop
55
+ */
56
+ export * as Node from "./Node.js";
57
+ /**
58
+ * @since 0.1.0
59
+ * @category models
60
+ * @slop
61
+ */
62
+ export * as Plan from "./Plan.js";
63
+ /**
64
+ * @since 0.1.0
65
+ * @category constructors
66
+ * @slop
67
+ */
68
+ export * as PlanDiff from "./PlanDiff.js";
69
+ /**
70
+ * @since 1.0.0
71
+ * @category models
72
+ */
73
+ export * as Placement from "./Placement.js";
74
+ /**
75
+ * @since 0.1.0
76
+ * @category models
77
+ * @slop
78
+ */
79
+ export * as Planned from "./Planned.js";
80
+ /**
81
+ * @since 0.1.0
82
+ * @category constructors
83
+ * @slop
84
+ */
85
+ export * as StepKey from "./StepKey.js";
86
+ /**
87
+ * @since 1.0.0
88
+ * @category constructors
89
+ */
90
+ export * as Scheduling from "./Scheduling.js";
91
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;GAIG;AACH,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA;AAEvD;;;GAGG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAE/C;;;GAGG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAE7C;;;;GAIG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAE/C;;;;GAIG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC;;;;GAIG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC;;;;GAIG;AACH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AAEjC;;;;GAIG;AACH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AAEjC;;;;GAIG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC;;;GAGG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAE3C;;;;GAIG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC;;;;GAIG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAEvC;;;GAGG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA","sourcesContent":["/**\n * The plan value: a keyed action graph, its diff, and the step-key compiler\n * that gives every node its identity.\n *\n * A plan is a `Node` graph with every key computed, produced by the plan phase\n * and inert until run. This package is that value and nothing more: it\n * performs no I/O at all and never executes anything. Persisting a plan is\n * `@smthrs/plan-store`; driving one is `@smthrs/engine-store`'s\n * `PlanScheduler`.\n *\n * Above the persisted form sits the authoring AST: `Node` describes a plan as\n * pure data, and `Planned` is the placeholder a body sees where a step result\n * will be. Both build plans; neither runs one.\n *\n * @since 0.1.0\n */\n\n/**\n * @since 0.1.0\n * @category errors\n * @slop\n */\nexport * as GraphBuildError from \"./GraphBuildError.ts\"\n\n/**\n * @since 1.0.0-rc.0\n * @category models\n */\nexport * as CachePolicy from \"./CachePolicy.ts\"\n\n/**\n * @since 1.0.0-rc.0\n * @category models\n */\nexport * as Repetition from \"./Repetition.ts\"\n\n/**\n * @since 0.1.0\n * @category models\n * @slop\n */\nexport * as KeyMaterial from \"./KeyMaterial.ts\"\n\n/**\n * @since 0.1.0\n * @category models\n * @slop\n */\nexport * as FileSet from \"./FileSet.ts\"\n\n/**\n * @since 0.1.0\n * @category models\n * @slop\n */\nexport * as Effects from \"./Effects.ts\"\n\n/**\n * @since 0.1.0\n * @category models\n * @slop\n */\nexport * as Node from \"./Node.ts\"\n\n/**\n * @since 0.1.0\n * @category models\n * @slop\n */\nexport * as Plan from \"./Plan.ts\"\n\n/**\n * @since 0.1.0\n * @category constructors\n * @slop\n */\nexport * as PlanDiff from \"./PlanDiff.ts\"\n\n/**\n * @since 1.0.0\n * @category models\n */\nexport * as Placement from \"./Placement.ts\"\n\n/**\n * @since 0.1.0\n * @category models\n * @slop\n */\nexport * as Planned from \"./Planned.ts\"\n\n/**\n * @since 0.1.0\n * @category constructors\n * @slop\n */\nexport * as StepKey from \"./StepKey.ts\"\n\n/**\n * @since 1.0.0\n * @category constructors\n */\nexport * as Scheduling from \"./Scheduling.ts\"\n"]}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The compiler's effect analysis: which writers conflict, and which readers
3
+ * must wait for a producer. Its vocabulary is pairs of declared effects; it
4
+ * never re-keys a node, because ordering is not key material.
5
+ *
6
+ * @since 0.1.0
7
+ * @private
8
+ */
9
+ import * as Effect from "effect/Effect";
10
+ import type * as Plan from "../Plan.ts";
11
+ /**
12
+ * What {@link annotate} found, by node id and in discovery order: the conflict
13
+ * annotations a node carries and the ordering edges it gains. A frozen node
14
+ * never appears, because its row is never rewritten.
15
+ *
16
+ * @private
17
+ */
18
+ interface Annotations {
19
+ readonly conflicts: ReadonlyMap<string, ReadonlyArray<Plan.ConflictAnnotation>>;
20
+ readonly ordering: ReadonlyMap<string, ReadonlyArray<string>>;
21
+ }
22
+ /**
23
+ * A graph the analysis refuses, in the fields a `PlanError` carries.
24
+ *
25
+ * @private
26
+ */
27
+ interface Refusal {
28
+ readonly code: "cycle" | "graph_too_large" | "overlap_forbidden";
29
+ readonly message: string;
30
+ }
31
+ /**
32
+ * Two passes over the graph.
33
+ *
34
+ * The first detects write overlaps and annotates the conflicting pair, adding
35
+ * the ordering edge a `serialize` verdict implies. Nodes already ordered by a
36
+ * dependency path are not conflicts.
37
+ *
38
+ * The second adds reader-after-writer edges: a node that reads a path another
39
+ * node writes is ordered behind its producer. That pair is not a conflict,
40
+ * because nothing needs annotating and no strategy applies. It is a missing
41
+ * edge, so only `dependsOn` grows. When the graph already orders the producer
42
+ * behind its reader by explicit material dependencies, the reader consumes
43
+ * the earlier version. An opposing inferred ordering still fails with `cycle`.
44
+ *
45
+ * Nodes are visited in plan order, so a `serialize` edge always points from
46
+ * the earlier declaration to the later one and can never close a cycle. The
47
+ * first `frozen` nodes were recorded by an earlier generation: append-only
48
+ * means their rows are never rewritten, so a pair discovered during
49
+ * elaboration is annotated on the NEW node only — which is also the node the
50
+ * ordering edge lands on, and the annotation names the other side either way.
51
+ * `replayGenerations` re-derives a recorded plan one generation at a time
52
+ * under that same rule.
53
+ *
54
+ * @since 0.1.0
55
+ * @private
56
+ */
57
+ export declare const annotate: (nodes: ReadonlyArray<Plan.PlanNode>, frozen: number, replayGenerations: boolean) => Effect.Effect<Annotations, Refusal>;
58
+ export {};
59
+ //# sourceMappingURL=ConflictAnnotation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConflictAnnotation.d.ts","sourceRoot":"","sources":["../../../src/internal/ConflictAnnotation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,KAAK,KAAK,IAAI,MAAM,YAAY,CAAA;AAGvC;;;;;;GAMG;AACH,UAAU,WAAW;IACnB,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAA;IAC/E,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;CAC9D;AAED;;;;GAIG;AACH,UAAU,OAAO;IACf,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,iBAAiB,GAAG,mBAAmB,CAAA;IAChE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAoED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,QAAQ,GACnB,OAAO,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,EACnC,QAAQ,MAAM,EACd,mBAAmB,OAAO,KACzB,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAgPjC,CAAA"}
@@ -0,0 +1,332 @@
1
+ /**
2
+ * The compiler's effect analysis: which writers conflict, and which readers
3
+ * must wait for a producer. Its vocabulary is pairs of declared effects; it
4
+ * never re-keys a node, because ordering is not key material.
5
+ *
6
+ * @since 0.1.0
7
+ * @private
8
+ */
9
+ import * as Effect from "effect/Effect";
10
+ import * as FileSet from "../FileSet.js";
11
+ import * as KeyMaterial from "../KeyMaterial.js";
12
+ import * as EffectCandidates from "./EffectCandidates.js";
13
+ /** @private */
14
+ const refuse = (code, message) => Effect.fail({ code, message });
15
+ /**
16
+ * Resolves the pair's verdict. `fail` dominates — a flow that promised
17
+ * disjointness must not be quietly serialized — then `lane`, because "when
18
+ * either writer requests `lane`, both receive lane annotations"; `serialize`
19
+ * is the default whenever an overlap is detected at all.
20
+ *
21
+ * @private
22
+ */
23
+ const pairStrategy = (left, right) => left === "fail" || right === "fail" ? "fail" : left === "lane" || right === "lane" ? "lane" : "serialize";
24
+ /**
25
+ * `stop-merge` dominates for the same reason `lane` does: it is the strategy a
26
+ * declaration opts into, and a pair cannot half-merge.
27
+ *
28
+ * @private
29
+ */
30
+ const pairRuntime = (left, right) => left === "stop-merge" || right === "stop-merge" ? "stop-merge" : "delay-rebase";
31
+ /**
32
+ * Every path a node mutates. A removal moves a path's content exactly as a
33
+ * write does — a reader that runs before it sees different bytes than one that
34
+ * runs after — so both passes below treat the two as one set.
35
+ *
36
+ * @private
37
+ */
38
+ const producedPaths = (effects) => [
39
+ ...FileSet.expand(effects.writes),
40
+ ...effects.removes ?? []
41
+ ];
42
+ /**
43
+ * How an annotation and a refusal name one declared entry.
44
+ *
45
+ * @private
46
+ */
47
+ const describe = (entry) => typeof entry === "string" ? entry : entry._tag === "TreeArtifact" ? entry.path : entry.include.join(",");
48
+ /** @private */
49
+ const overlap = (left, right) => left.flatMap((leftEntry) => right.some((rightEntry) => FileSet.overlaps(leftEntry, rightEntry)) ? [describe(leftEntry)] : []);
50
+ /**
51
+ * The read declarations of one node that a writer's produced set covers. The
52
+ * conflict pass compares write sets against write sets, so this relation is
53
+ * the one it cannot see. Rendered the way {@link overlap} renders a write,
54
+ * because a refusal names them.
55
+ *
56
+ * @private
57
+ */
58
+ const readOverlap = (reads, produced) => reads.flatMap((entry) => produced.some((output) => FileSet.overlaps(entry, output)) ? [describe(entry)] : []);
59
+ /**
60
+ * Two passes over the graph.
61
+ *
62
+ * The first detects write overlaps and annotates the conflicting pair, adding
63
+ * the ordering edge a `serialize` verdict implies. Nodes already ordered by a
64
+ * dependency path are not conflicts.
65
+ *
66
+ * The second adds reader-after-writer edges: a node that reads a path another
67
+ * node writes is ordered behind its producer. That pair is not a conflict,
68
+ * because nothing needs annotating and no strategy applies. It is a missing
69
+ * edge, so only `dependsOn` grows. When the graph already orders the producer
70
+ * behind its reader by explicit material dependencies, the reader consumes
71
+ * the earlier version. An opposing inferred ordering still fails with `cycle`.
72
+ *
73
+ * Nodes are visited in plan order, so a `serialize` edge always points from
74
+ * the earlier declaration to the later one and can never close a cycle. The
75
+ * first `frozen` nodes were recorded by an earlier generation: append-only
76
+ * means their rows are never rewritten, so a pair discovered during
77
+ * elaboration is annotated on the NEW node only — which is also the node the
78
+ * ordering edge lands on, and the annotation names the other side either way.
79
+ * `replayGenerations` re-derives a recorded plan one generation at a time
80
+ * under that same rule.
81
+ *
82
+ * @since 0.1.0
83
+ * @private
84
+ */
85
+ export const annotate = (nodes, frozen, replayGenerations) => Effect.gen(function* () {
86
+ const expanded = new Map(nodes.map((node) => [node.id, {
87
+ produced: producedPaths(node.effects),
88
+ reads: FileSet.expandReads(node.effects.reads)
89
+ }]));
90
+ const candidates = EffectCandidates.make(nodes.map((node) => expanded.get(node.id).produced));
91
+ const conflicts = new Map();
92
+ const ordering = new Map();
93
+ const edges = new Map(nodes.map((node) => [node.id, new Set(node.dependsOn)]));
94
+ // Keep declaration sequencing separate from inferred ordering, including
95
+ // for frozen generations whose `dependsOn` already contains inferred edges.
96
+ const declaredEdges = new Map(nodes.map((node) => [node.id, KeyMaterial.dependencies(node.material)]));
97
+ // One index and mutable graph serve every replayed generation. Future
98
+ // producers are filtered at the generation boundary, never re-indexed.
99
+ const ordinals = new Map(nodes.map((node, index) => [node.id, index]));
100
+ const dependents = new Map(nodes.map((node) => [node.id, new Set()]));
101
+ for (const node of nodes) {
102
+ for (const dependency of node.dependsOn)
103
+ dependents.get(dependency).add(node.id);
104
+ }
105
+ const words = Math.ceil(nodes.length / 32);
106
+ const ancestors = new Map();
107
+ // Charge a pair for bookkeeping as well as its path comparisons. The
108
+ // separate pair ceiling also bounds persisted annotations and edges.
109
+ let work = 0;
110
+ let pairs = 0;
111
+ let nextYield = 2_048;
112
+ const checkpoint = Effect.gen(function* () {
113
+ if (work > 10_000_000 || pairs > 250_000) {
114
+ return yield* refuse("graph_too_large", "Plan effect analysis exceeds its work budget (10000000 work units or 250000 candidate pairs)");
115
+ }
116
+ nextYield = Math.min(work + 2_048, 10_000_001);
117
+ yield* Effect.yieldNow;
118
+ });
119
+ // Cache transitive ancestors as bitsets. Adding an edge updates its source
120
+ // immediately and invalidates only cached dependents. In particular, dense
121
+ // writers never walk the growing dependency DAG for each conflicting pair.
122
+ const closure = (id) => Effect.gen(function* () {
123
+ const cached = ancestors.get(id);
124
+ if (cached !== undefined)
125
+ return cached;
126
+ const frame = (id) => {
127
+ const bits = new Uint32Array(words);
128
+ const ordinal = ordinals.get(id);
129
+ bits[ordinal >>> 5] |= 1 << (ordinal & 31);
130
+ return { id, bits, dependencies: Array.from(edges.get(id)), next: 0 };
131
+ };
132
+ const stack = [frame(id)];
133
+ while (stack.length > 0) {
134
+ const current = stack[stack.length - 1];
135
+ const dependency = current.dependencies[current.next];
136
+ if (dependency === undefined) {
137
+ ancestors.set(current.id, current.bits);
138
+ stack.pop();
139
+ continue;
140
+ }
141
+ const bits = ancestors.get(dependency);
142
+ if (bits === undefined) {
143
+ stack.push(frame(dependency));
144
+ continue;
145
+ }
146
+ for (let word = 0; word < words; word++) {
147
+ current.bits[word] |= bits[word];
148
+ if (++work >= nextYield)
149
+ yield* checkpoint;
150
+ }
151
+ current.next++;
152
+ }
153
+ return ancestors.get(id);
154
+ });
155
+ const reaches = (from, to) => Effect.gen(function* () {
156
+ const bits = yield* closure(from);
157
+ const ordinal = ordinals.get(to);
158
+ return (bits[ordinal >>> 5] & (1 << (ordinal & 31))) !== 0;
159
+ });
160
+ const addEdge = (from, to) => Effect.gen(function* () {
161
+ const source = yield* closure(from);
162
+ const target = yield* closure(to);
163
+ for (let word = 0; word < words; word++) {
164
+ source[word] |= target[word];
165
+ if (++work >= nextYield)
166
+ yield* checkpoint;
167
+ }
168
+ const stack = [from];
169
+ while (stack.length > 0) {
170
+ for (const dependent of dependents.get(stack.pop())) {
171
+ if (++work >= nextYield)
172
+ yield* checkpoint;
173
+ if (!ancestors.delete(dependent))
174
+ continue;
175
+ stack.push(dependent);
176
+ }
177
+ }
178
+ edges.get(from).add(to);
179
+ dependents.get(to).add(from);
180
+ });
181
+ // Routes are only needed for explicit version sequencing and a cycle's
182
+ // diagnostic. Bound and yield these walks too.
183
+ const route = (from, to, graph = edges) => Effect.gen(function* () {
184
+ const arrivedFrom = new Map();
185
+ const seen = new Set([from]);
186
+ const stack = [from];
187
+ while (stack.length > 0) {
188
+ const current = stack.pop();
189
+ if (current === to) {
190
+ const chain = [current];
191
+ for (let step = arrivedFrom.get(current); step !== undefined; step = arrivedFrom.get(step)) {
192
+ chain.push(step);
193
+ }
194
+ return chain.reverse();
195
+ }
196
+ for (const next of graph.get(current)) {
197
+ if (++work >= nextYield)
198
+ yield* checkpoint;
199
+ if (seen.has(next))
200
+ continue;
201
+ seen.add(next);
202
+ arrivedFrom.set(next, current);
203
+ stack.push(next);
204
+ }
205
+ }
206
+ return undefined;
207
+ });
208
+ const ranges = [];
209
+ let start = frozen;
210
+ for (let end = start + 1; end <= nodes.length; end++) {
211
+ if (end === nodes.length || (replayGenerations && nodes[end].generation !== nodes[start].generation)) {
212
+ ranges.push({ start, end });
213
+ start = end;
214
+ }
215
+ }
216
+ for (const { start, end } of ranges) {
217
+ for (let index = start; index < end; index++) {
218
+ const later = nodes[index];
219
+ const laterProduced = expanded.get(later.id).produced;
220
+ if (laterProduced.length === 0)
221
+ continue;
222
+ const matches = candidates(laterProduced);
223
+ work += matches.length + laterProduced.length;
224
+ if (work >= nextYield)
225
+ yield* checkpoint;
226
+ for (const before of matches) {
227
+ if (before >= index)
228
+ break;
229
+ pairs++;
230
+ work += 32;
231
+ if (work >= nextYield || pairs > 250_000)
232
+ yield* checkpoint;
233
+ const earlier = nodes[before];
234
+ const earlierProduced = expanded.get(earlier.id).produced;
235
+ work += earlierProduced.length * laterProduced.length;
236
+ if (work >= nextYield)
237
+ yield* checkpoint;
238
+ const paths = overlap(earlierProduced, laterProduced);
239
+ if (paths.length === 0)
240
+ continue;
241
+ if (yield* reaches(later.id, earlier.id))
242
+ continue;
243
+ const strategy = pairStrategy(earlier.strategy, later.strategy);
244
+ const runtime = pairRuntime(earlier.runtime, later.runtime);
245
+ if (strategy === "fail") {
246
+ return yield* refuse("overlap_forbidden", `Nodes ${earlier.id} and ${later.id} both write ${paths.join(", ")}`);
247
+ }
248
+ const annotation = (other) => ({ with: other, paths, strategy, runtime });
249
+ if (before >= start) {
250
+ const previous = conflicts.get(earlier.id) ?? [];
251
+ previous.push(annotation(later.id));
252
+ conflicts.set(earlier.id, previous);
253
+ }
254
+ const previous = conflicts.get(later.id) ?? [];
255
+ previous.push(annotation(earlier.id));
256
+ conflicts.set(later.id, previous);
257
+ if (strategy === "serialize") {
258
+ const added = ordering.get(later.id) ?? [];
259
+ added.push(earlier.id);
260
+ ordering.set(later.id, added);
261
+ yield* addEdge(later.id, earlier.id);
262
+ }
263
+ }
264
+ }
265
+ // Reader-after-writer. A node that READS a path another node WRITES was
266
+ // ordered by nothing: the pass above compares write sets against write
267
+ // sets, so reader and writer could be admitted in the same wavefront
268
+ // round. The reader then measures pre-producer bytes and — because the
269
+ // dispatch key honestly folds the digest it measured — caches that wrong
270
+ // execution as a legitimate one. `PlanScheduler.measure` already assumes
271
+ // "their preceding producer has settled"; this pass is what makes that
272
+ // assumption true. Explicit read-before-write sequencing instead consumes
273
+ // an earlier version: a source input, or another preceding writer's output.
274
+ //
275
+ // Ordering only, exactly like a `serialize` edge: it enters `dependsOn`
276
+ // and never key material, because the reader computes the same result
277
+ // either way and its content dependence is already keyed by the hermetic
278
+ // boundary digests measured at dispatch.
279
+ for (let index = start; index < end; index++) {
280
+ const reader = nodes[index];
281
+ // Append-only: a frozen node's row is never rewritten, so the edge lands
282
+ // on the new node only — the same rule the conflict pass follows.
283
+ const reads = expanded.get(reader.id).reads;
284
+ if (reads.length === 0)
285
+ continue;
286
+ const matches = candidates(reads);
287
+ work += matches.length + reads.length;
288
+ if (work >= nextYield)
289
+ yield* checkpoint;
290
+ for (const writerIndex of matches) {
291
+ if (writerIndex >= end)
292
+ break;
293
+ pairs++;
294
+ work += 32;
295
+ if (work >= nextYield || pairs > 250_000)
296
+ yield* checkpoint;
297
+ const writer = nodes[writerIndex];
298
+ if (writer.id === reader.id)
299
+ continue;
300
+ const produced = expanded.get(writer.id).produced;
301
+ work += reads.length * produced.length;
302
+ if (work >= nextYield)
303
+ yield* checkpoint;
304
+ const paths = readOverlap(reads, produced);
305
+ if (paths.length === 0)
306
+ continue;
307
+ // Already ordered, by a material edge, a serialize edge, or a path
308
+ // through either.
309
+ if (yield* reaches(reader.id, writer.id))
310
+ continue;
311
+ // Explicit sequencing selects the version being read. Do not reverse
312
+ // it just because a later node writes the same path. Only declared
313
+ // Ref/Pending paths establish this intent; a serialize edge or an
314
+ // earlier inferred producer edge cannot silently select old bytes.
315
+ if (yield* reaches(writer.id, reader.id)) {
316
+ if ((yield* route(writer.id, reader.id, declaredEdges)) !== undefined)
317
+ continue;
318
+ const contradiction = (yield* route(writer.id, reader.id));
319
+ return yield* refuse("cycle", `Plan cycle: node ${reader.id} reads ${paths.join(", ")}, which node ${writer.id} produces, ` +
320
+ `so ${reader.id} must follow ${writer.id}, but ${writer.id} already depends on ${reader.id} ` +
321
+ `through ${contradiction.join(" -> ")}`);
322
+ }
323
+ const added = ordering.get(reader.id) ?? [];
324
+ added.push(writer.id);
325
+ ordering.set(reader.id, added);
326
+ yield* addEdge(reader.id, writer.id);
327
+ }
328
+ }
329
+ }
330
+ return { conflicts, ordering };
331
+ });
332
+ //# sourceMappingURL=ConflictAnnotation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConflictAnnotation.js","sourceRoot":"","sources":["../../../src/internal/ConflictAnnotation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The compiler's effect analysis: which writers conflict, and which readers\n * must wait for a producer. Its vocabulary is pairs of declared effects; it\n * never re-keys a node, because ordering is not key material.\n *\n * @since 0.1.0\n * @private\n */\n\nimport * as Effect from \"effect/Effect\"\nimport * as FileSet from \"../FileSet.ts\"\nimport * as KeyMaterial from \"../KeyMaterial.ts\"\nimport type * as Plan from \"../Plan.ts\"\nimport * as EffectCandidates from \"./EffectCandidates.ts\"\n\n/**\n * What {@link annotate} found, by node id and in discovery order: the conflict\n * annotations a node carries and the ordering edges it gains. A frozen node\n * never appears, because its row is never rewritten.\n *\n * @private\n */\ninterface Annotations {\n readonly conflicts: ReadonlyMap<string, ReadonlyArray<Plan.ConflictAnnotation>>\n readonly ordering: ReadonlyMap<string, ReadonlyArray<string>>\n}\n\n/**\n * A graph the analysis refuses, in the fields a `PlanError` carries.\n *\n * @private\n */\ninterface Refusal {\n readonly code: \"cycle\" | \"graph_too_large\" | \"overlap_forbidden\"\n readonly message: string\n}\n\n/** @private */\nconst refuse = (code: Refusal[\"code\"], message: string) => Effect.fail<Refusal>({ code, message })\n\n/**\n * Resolves the pair's verdict. `fail` dominates — a flow that promised\n * disjointness must not be quietly serialized — then `lane`, because \"when\n * either writer requests `lane`, both receive lane annotations\"; `serialize`\n * is the default whenever an overlap is detected at all.\n *\n * @private\n */\nconst pairStrategy = (left: Plan.PairStrategy, right: Plan.PairStrategy): Plan.PairStrategy =>\n left === \"fail\" || right === \"fail\" ? \"fail\" : left === \"lane\" || right === \"lane\" ? \"lane\" : \"serialize\"\n\n/**\n * `stop-merge` dominates for the same reason `lane` does: it is the strategy a\n * declaration opts into, and a pair cannot half-merge.\n *\n * @private\n */\nconst pairRuntime = (left: Plan.RuntimeStrategy, right: Plan.RuntimeStrategy): Plan.RuntimeStrategy =>\n left === \"stop-merge\" || right === \"stop-merge\" ? \"stop-merge\" : \"delay-rebase\"\n\n/**\n * Every path a node mutates. A removal moves a path's content exactly as a\n * write does — a reader that runs before it sees different bytes than one that\n * runs after — so both passes below treat the two as one set.\n *\n * @private\n */\nconst producedPaths = (effects: Plan.NodeEffects): ReadonlyArray<FileSet.Entry> => [\n ...FileSet.expand(effects.writes),\n ...effects.removes ?? []\n]\n\n/**\n * How an annotation and a refusal name one declared entry.\n *\n * @private\n */\nconst describe = (entry: FileSet.Entry): string =>\n typeof entry === \"string\" ? entry : entry._tag === \"TreeArtifact\" ? entry.path : entry.include.join(\",\")\n\n/** @private */\nconst overlap = (\n left: ReadonlyArray<FileSet.Entry>,\n right: ReadonlyArray<FileSet.Entry>\n): ReadonlyArray<string> =>\n left.flatMap((leftEntry) =>\n right.some((rightEntry) => FileSet.overlaps(leftEntry, rightEntry)) ? [describe(leftEntry)] : []\n )\n\n/**\n * The read declarations of one node that a writer's produced set covers. The\n * conflict pass compares write sets against write sets, so this relation is\n * the one it cannot see. Rendered the way {@link overlap} renders a write,\n * because a refusal names them.\n *\n * @private\n */\nconst readOverlap = (\n reads: ReadonlyArray<FileSet.ReadEntry>,\n produced: ReadonlyArray<FileSet.Entry>\n): ReadonlyArray<string> =>\n reads.flatMap((entry) => produced.some((output) => FileSet.overlaps(entry, output)) ? [describe(entry)] : [])\n\n/**\n * Two passes over the graph.\n *\n * The first detects write overlaps and annotates the conflicting pair, adding\n * the ordering edge a `serialize` verdict implies. Nodes already ordered by a\n * dependency path are not conflicts.\n *\n * The second adds reader-after-writer edges: a node that reads a path another\n * node writes is ordered behind its producer. That pair is not a conflict,\n * because nothing needs annotating and no strategy applies. It is a missing\n * edge, so only `dependsOn` grows. When the graph already orders the producer\n * behind its reader by explicit material dependencies, the reader consumes\n * the earlier version. An opposing inferred ordering still fails with `cycle`.\n *\n * Nodes are visited in plan order, so a `serialize` edge always points from\n * the earlier declaration to the later one and can never close a cycle. The\n * first `frozen` nodes were recorded by an earlier generation: append-only\n * means their rows are never rewritten, so a pair discovered during\n * elaboration is annotated on the NEW node only — which is also the node the\n * ordering edge lands on, and the annotation names the other side either way.\n * `replayGenerations` re-derives a recorded plan one generation at a time\n * under that same rule.\n *\n * @since 0.1.0\n * @private\n */\nexport const annotate = (\n nodes: ReadonlyArray<Plan.PlanNode>,\n frozen: number,\n replayGenerations: boolean\n): Effect.Effect<Annotations, Refusal> =>\n Effect.gen(function*() {\n const expanded = new Map(nodes.map((node) => [node.id, {\n produced: producedPaths(node.effects),\n reads: FileSet.expandReads(node.effects.reads)\n }]))\n const candidates = EffectCandidates.make(nodes.map((node) => expanded.get(node.id)!.produced))\n const conflicts = new Map<string, Array<Plan.ConflictAnnotation>>()\n const ordering = new Map<string, Array<string>>()\n const edges = new Map(nodes.map((node) => [node.id, new Set(node.dependsOn)]))\n // Keep declaration sequencing separate from inferred ordering, including\n // for frozen generations whose `dependsOn` already contains inferred edges.\n const declaredEdges = new Map(nodes.map((node) => [node.id, KeyMaterial.dependencies(node.material)]))\n // One index and mutable graph serve every replayed generation. Future\n // producers are filtered at the generation boundary, never re-indexed.\n const ordinals = new Map(nodes.map((node, index) => [node.id, index]))\n const dependents = new Map(nodes.map((node) => [node.id, new Set<string>()]))\n for (const node of nodes) {\n for (const dependency of node.dependsOn) dependents.get(dependency)!.add(node.id)\n }\n const words = Math.ceil(nodes.length / 32)\n const ancestors = new Map<string, Uint32Array>()\n // Charge a pair for bookkeeping as well as its path comparisons. The\n // separate pair ceiling also bounds persisted annotations and edges.\n let work = 0\n let pairs = 0\n let nextYield = 2_048\n const checkpoint = Effect.gen(function*() {\n if (work > 10_000_000 || pairs > 250_000) {\n return yield* refuse(\n \"graph_too_large\",\n \"Plan effect analysis exceeds its work budget (10000000 work units or 250000 candidate pairs)\"\n )\n }\n nextYield = Math.min(work + 2_048, 10_000_001)\n yield* Effect.yieldNow\n })\n // Cache transitive ancestors as bitsets. Adding an edge updates its source\n // immediately and invalidates only cached dependents. In particular, dense\n // writers never walk the growing dependency DAG for each conflicting pair.\n const closure = (id: string): Effect.Effect<Uint32Array, Refusal> =>\n Effect.gen(function*() {\n const cached = ancestors.get(id)\n if (cached !== undefined) return cached\n const frame = (id: string) => {\n const bits = new Uint32Array(words)\n const ordinal = ordinals.get(id)!\n bits[ordinal >>> 5]! |= 1 << (ordinal & 31)\n return { id, bits, dependencies: Array.from(edges.get(id)!), next: 0 }\n }\n const stack = [frame(id)]\n while (stack.length > 0) {\n const current = stack[stack.length - 1]!\n const dependency = current.dependencies[current.next]\n if (dependency === undefined) {\n ancestors.set(current.id, current.bits)\n stack.pop()\n continue\n }\n const bits = ancestors.get(dependency)\n if (bits === undefined) {\n stack.push(frame(dependency))\n continue\n }\n for (let word = 0; word < words; word++) {\n current.bits[word]! |= bits[word]!\n if (++work >= nextYield) yield* checkpoint\n }\n current.next++\n }\n return ancestors.get(id)!\n })\n const reaches = (from: string, to: string) =>\n Effect.gen(function*() {\n const bits = yield* closure(from)\n const ordinal = ordinals.get(to)!\n return (bits[ordinal >>> 5]! & (1 << (ordinal & 31))) !== 0\n })\n const addEdge = (from: string, to: string) =>\n Effect.gen(function*() {\n const source = yield* closure(from)\n const target = yield* closure(to)\n for (let word = 0; word < words; word++) {\n source[word]! |= target[word]!\n if (++work >= nextYield) yield* checkpoint\n }\n const stack = [from]\n while (stack.length > 0) {\n for (const dependent of dependents.get(stack.pop()!)!) {\n if (++work >= nextYield) yield* checkpoint\n if (!ancestors.delete(dependent)) continue\n stack.push(dependent)\n }\n }\n edges.get(from)!.add(to)\n dependents.get(to)!.add(from)\n })\n // Routes are only needed for explicit version sequencing and a cycle's\n // diagnostic. Bound and yield these walks too.\n const route = (\n from: string,\n to: string,\n graph: ReadonlyMap<string, Iterable<string>> = edges\n ): Effect.Effect<ReadonlyArray<string> | undefined, Refusal> =>\n Effect.gen(function*() {\n const arrivedFrom = new Map<string, string>()\n const seen = new Set<string>([from])\n const stack = [from]\n while (stack.length > 0) {\n const current = stack.pop()!\n if (current === to) {\n const chain = [current]\n for (let step = arrivedFrom.get(current); step !== undefined; step = arrivedFrom.get(step)) {\n chain.push(step)\n }\n return chain.reverse()\n }\n for (const next of graph.get(current)!) {\n if (++work >= nextYield) yield* checkpoint\n if (seen.has(next)) continue\n seen.add(next)\n arrivedFrom.set(next, current)\n stack.push(next)\n }\n }\n return undefined\n })\n const ranges: Array<{ start: number; end: number }> = []\n let start = frozen\n for (let end = start + 1; end <= nodes.length; end++) {\n if (end === nodes.length || (replayGenerations && nodes[end]!.generation !== nodes[start]!.generation)) {\n ranges.push({ start, end })\n start = end\n }\n }\n for (const { start, end } of ranges) {\n for (let index = start; index < end; index++) {\n const later = nodes[index]!\n const laterProduced = expanded.get(later.id)!.produced\n if (laterProduced.length === 0) continue\n const matches = candidates(laterProduced)\n work += matches.length + laterProduced.length\n if (work >= nextYield) yield* checkpoint\n for (const before of matches) {\n if (before >= index) break\n pairs++\n work += 32\n if (work >= nextYield || pairs > 250_000) yield* checkpoint\n const earlier = nodes[before]!\n const earlierProduced = expanded.get(earlier.id)!.produced\n work += earlierProduced.length * laterProduced.length\n if (work >= nextYield) yield* checkpoint\n const paths = overlap(earlierProduced, laterProduced)\n if (paths.length === 0) continue\n if (yield* reaches(later.id, earlier.id)) continue\n const strategy = pairStrategy(earlier.strategy, later.strategy)\n const runtime = pairRuntime(earlier.runtime, later.runtime)\n if (strategy === \"fail\") {\n return yield* refuse(\n \"overlap_forbidden\",\n `Nodes ${earlier.id} and ${later.id} both write ${paths.join(\", \")}`\n )\n }\n const annotation = (other: string): Plan.ConflictAnnotation => ({ with: other, paths, strategy, runtime })\n if (before >= start) {\n const previous = conflicts.get(earlier.id) ?? []\n previous.push(annotation(later.id))\n conflicts.set(earlier.id, previous)\n }\n const previous = conflicts.get(later.id) ?? []\n previous.push(annotation(earlier.id))\n conflicts.set(later.id, previous)\n if (strategy === \"serialize\") {\n const added = ordering.get(later.id) ?? []\n added.push(earlier.id)\n ordering.set(later.id, added)\n yield* addEdge(later.id, earlier.id)\n }\n }\n }\n // Reader-after-writer. A node that READS a path another node WRITES was\n // ordered by nothing: the pass above compares write sets against write\n // sets, so reader and writer could be admitted in the same wavefront\n // round. The reader then measures pre-producer bytes and — because the\n // dispatch key honestly folds the digest it measured — caches that wrong\n // execution as a legitimate one. `PlanScheduler.measure` already assumes\n // \"their preceding producer has settled\"; this pass is what makes that\n // assumption true. Explicit read-before-write sequencing instead consumes\n // an earlier version: a source input, or another preceding writer's output.\n //\n // Ordering only, exactly like a `serialize` edge: it enters `dependsOn`\n // and never key material, because the reader computes the same result\n // either way and its content dependence is already keyed by the hermetic\n // boundary digests measured at dispatch.\n for (let index = start; index < end; index++) {\n const reader = nodes[index]!\n // Append-only: a frozen node's row is never rewritten, so the edge lands\n // on the new node only — the same rule the conflict pass follows.\n const reads = expanded.get(reader.id)!.reads\n if (reads.length === 0) continue\n const matches = candidates(reads)\n work += matches.length + reads.length\n if (work >= nextYield) yield* checkpoint\n for (const writerIndex of matches) {\n if (writerIndex >= end) break\n pairs++\n work += 32\n if (work >= nextYield || pairs > 250_000) yield* checkpoint\n const writer = nodes[writerIndex]!\n if (writer.id === reader.id) continue\n const produced = expanded.get(writer.id)!.produced\n work += reads.length * produced.length\n if (work >= nextYield) yield* checkpoint\n const paths = readOverlap(reads, produced)\n if (paths.length === 0) continue\n // Already ordered, by a material edge, a serialize edge, or a path\n // through either.\n if (yield* reaches(reader.id, writer.id)) continue\n // Explicit sequencing selects the version being read. Do not reverse\n // it just because a later node writes the same path. Only declared\n // Ref/Pending paths establish this intent; a serialize edge or an\n // earlier inferred producer edge cannot silently select old bytes.\n if (yield* reaches(writer.id, reader.id)) {\n if ((yield* route(writer.id, reader.id, declaredEdges)) !== undefined) continue\n const contradiction = (yield* route(writer.id, reader.id))!\n return yield* refuse(\n \"cycle\",\n `Plan cycle: node ${reader.id} reads ${paths.join(\", \")}, which node ${writer.id} produces, ` +\n `so ${reader.id} must follow ${writer.id}, but ${writer.id} already depends on ${reader.id} ` +\n `through ${contradiction.join(\" -> \")}`\n )\n }\n const added = ordering.get(reader.id) ?? []\n added.push(writer.id)\n ordering.set(reader.id, added)\n yield* addEdge(reader.id, writer.id)\n }\n }\n }\n return { conflicts, ordering }\n })\n"]}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Rebuildable candidate index for the compiler's existing overlap predicate.
3
+ * Exact paths and tree prefixes share a trie keyed by case-folded segments, the
4
+ * form FileSet.overlaps compares. A plain string is a literal path there, even
5
+ * one holding `*`. Globs stay conservative: their exclusions and pair semantics
6
+ * are decided by FileSet.overlaps, never here.
7
+ * Nothing in this index becomes persisted plan or key material.
8
+ * @since 0.1.0
9
+ * @private
10
+ */
11
+ import * as FileSet from "../FileSet.ts";
12
+ /**
13
+ * Selects possible producers in declaration order; callers retain the final
14
+ * FileSet.overlaps check for every selected pair.
15
+ * @since 0.1.0
16
+ * @private
17
+ */
18
+ export declare const make: (produced: ReadonlyArray<ReadonlyArray<FileSet.Entry>>) => (entries: ReadonlyArray<FileSet.Entry>) => ReadonlyArray<number>;
19
+ //# sourceMappingURL=EffectCandidates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EffectCandidates.d.ts","sourceRoot":"","sources":["../../../src/internal/EffectCandidates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,OAAO,MAAM,eAAe,CAAA;AAgBxC;;;;;GAKG;AACH,eAAO,MAAM,IAAI,GAAI,UAAU,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MA0BhE,SAAS,aAAa,CAAC,OAAO,CAAC,KAAK,CAAC,KAAG,aAAa,CAAC,MAAM,CA6BrE,CAAA"}