@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.
- package/CHANGELOG.md +183 -0
- package/LICENSE +21 -0
- package/README.md +228 -2
- package/dist/cjs/CachePolicy.d.ts +131 -0
- package/dist/cjs/CachePolicy.d.ts.map +1 -0
- package/dist/cjs/CachePolicy.js +50 -0
- package/dist/cjs/CachePolicy.js.map +7 -0
- package/dist/cjs/Effects.d.ts +240 -0
- package/dist/cjs/Effects.d.ts.map +1 -0
- package/dist/cjs/Effects.js +76 -0
- package/dist/cjs/Effects.js.map +7 -0
- package/dist/cjs/FileSet.d.ts +288 -0
- package/dist/cjs/FileSet.d.ts.map +1 -0
- package/dist/cjs/FileSet.js +191 -0
- package/dist/cjs/FileSet.js.map +7 -0
- package/dist/cjs/GraphBuildError.d.ts +122 -0
- package/dist/cjs/GraphBuildError.d.ts.map +1 -0
- package/dist/cjs/GraphBuildError.js +71 -0
- package/dist/cjs/GraphBuildError.js.map +7 -0
- package/dist/cjs/KeyMaterial.d.ts +102 -0
- package/dist/cjs/KeyMaterial.d.ts.map +1 -0
- package/dist/cjs/KeyMaterial.js +64 -0
- package/dist/cjs/KeyMaterial.js.map +7 -0
- package/dist/cjs/Node.d.ts +606 -0
- package/dist/cjs/Node.d.ts.map +1 -0
- package/dist/cjs/Node.js +207 -0
- package/dist/cjs/Node.js.map +7 -0
- package/dist/cjs/Placement.d.ts +117 -0
- package/dist/cjs/Placement.d.ts.map +1 -0
- package/dist/cjs/Placement.js +46 -0
- package/dist/cjs/Placement.js.map +7 -0
- package/dist/cjs/Plan.d.ts +440 -0
- package/dist/cjs/Plan.d.ts.map +1 -0
- package/dist/cjs/Plan.js +508 -0
- package/dist/cjs/Plan.js.map +7 -0
- package/dist/cjs/PlanDiff.d.ts +58 -0
- package/dist/cjs/PlanDiff.d.ts.map +1 -0
- package/dist/cjs/PlanDiff.js +124 -0
- package/dist/cjs/PlanDiff.js.map +7 -0
- package/dist/cjs/Planned.d.ts +121 -0
- package/dist/cjs/Planned.d.ts.map +1 -0
- package/dist/cjs/Planned.js +72 -0
- package/dist/cjs/Planned.js.map +7 -0
- package/dist/cjs/Repetition.d.ts +31 -0
- package/dist/cjs/Repetition.d.ts.map +1 -0
- package/dist/cjs/Repetition.js +36 -0
- package/dist/cjs/Repetition.js.map +7 -0
- package/dist/cjs/Scheduling.d.ts +89 -0
- package/dist/cjs/Scheduling.d.ts.map +1 -0
- package/dist/cjs/Scheduling.js +76 -0
- package/dist/cjs/Scheduling.js.map +7 -0
- package/dist/cjs/StepKey.d.ts +333 -0
- package/dist/cjs/StepKey.d.ts.map +1 -0
- package/dist/cjs/StepKey.js +311 -0
- package/dist/cjs/StepKey.js.map +7 -0
- package/dist/cjs/index.d.ts +91 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +59 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/ConflictAnnotation.d.ts +59 -0
- package/dist/cjs/internal/ConflictAnnotation.d.ts.map +1 -0
- package/dist/cjs/internal/ConflictAnnotation.js +247 -0
- package/dist/cjs/internal/ConflictAnnotation.js.map +7 -0
- package/dist/cjs/internal/EffectCandidates.d.ts +19 -0
- package/dist/cjs/internal/EffectCandidates.d.ts.map +1 -0
- package/dist/cjs/internal/EffectCandidates.js +92 -0
- package/dist/cjs/internal/EffectCandidates.js.map +7 -0
- package/dist/cjs/internal/JsonMirror.d.ts +34 -0
- package/dist/cjs/internal/JsonMirror.d.ts.map +1 -0
- package/dist/cjs/internal/JsonMirror.js +195 -0
- package/dist/cjs/internal/JsonMirror.js.map +7 -0
- package/dist/cjs/internal/effects.d.ts +245 -0
- package/dist/cjs/internal/effects.d.ts.map +1 -0
- package/dist/cjs/internal/effects.js +290 -0
- package/dist/cjs/internal/effects.js.map +7 -0
- package/dist/cjs/internal/node.d.ts +526 -0
- package/dist/cjs/internal/node.d.ts.map +1 -0
- package/dist/cjs/internal/node.js +312 -0
- package/dist/cjs/internal/node.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/PlanFixtures.d.ts +127 -0
- package/dist/cjs/test/PlanFixtures.d.ts.map +1 -0
- package/dist/cjs/test/PlanFixtures.js +59 -0
- package/dist/cjs/test/PlanFixtures.js.map +7 -0
- package/dist/esm/CachePolicy.d.ts +131 -0
- package/dist/esm/CachePolicy.d.ts.map +1 -0
- package/dist/esm/CachePolicy.js +117 -0
- package/dist/esm/CachePolicy.js.map +1 -0
- package/dist/esm/Effects.d.ts +240 -0
- package/dist/esm/Effects.d.ts.map +1 -0
- package/dist/esm/Effects.js +167 -0
- package/dist/esm/Effects.js.map +1 -0
- package/dist/esm/FileSet.d.ts +288 -0
- package/dist/esm/FileSet.d.ts.map +1 -0
- package/dist/esm/FileSet.js +324 -0
- package/dist/esm/FileSet.js.map +1 -0
- package/dist/esm/GraphBuildError.d.ts +122 -0
- package/dist/esm/GraphBuildError.d.ts.map +1 -0
- package/dist/esm/GraphBuildError.js +146 -0
- package/dist/esm/GraphBuildError.js.map +1 -0
- package/dist/esm/KeyMaterial.d.ts +102 -0
- package/dist/esm/KeyMaterial.d.ts.map +1 -0
- package/dist/esm/KeyMaterial.js +85 -0
- package/dist/esm/KeyMaterial.js.map +1 -0
- package/dist/esm/Node.d.ts +606 -0
- package/dist/esm/Node.d.ts.map +1 -0
- package/dist/esm/Node.js +521 -0
- package/dist/esm/Node.js.map +1 -0
- package/dist/esm/Placement.d.ts +117 -0
- package/dist/esm/Placement.d.ts.map +1 -0
- package/dist/esm/Placement.js +65 -0
- package/dist/esm/Placement.js.map +1 -0
- package/dist/esm/Plan.d.ts +440 -0
- package/dist/esm/Plan.d.ts.map +1 -0
- package/dist/esm/Plan.js +673 -0
- package/dist/esm/Plan.js.map +1 -0
- package/dist/esm/PlanDiff.d.ts +58 -0
- package/dist/esm/PlanDiff.d.ts.map +1 -0
- package/dist/esm/PlanDiff.js +159 -0
- package/dist/esm/PlanDiff.js.map +1 -0
- package/dist/esm/Planned.d.ts +121 -0
- package/dist/esm/Planned.d.ts.map +1 -0
- package/dist/esm/Planned.js +147 -0
- package/dist/esm/Planned.js.map +1 -0
- package/dist/esm/Repetition.d.ts +31 -0
- package/dist/esm/Repetition.d.ts.map +1 -0
- package/dist/esm/Repetition.js +24 -0
- package/dist/esm/Repetition.js.map +1 -0
- package/dist/esm/Scheduling.d.ts +89 -0
- package/dist/esm/Scheduling.d.ts.map +1 -0
- package/dist/esm/Scheduling.js +82 -0
- package/dist/esm/Scheduling.js.map +1 -0
- package/dist/esm/StepKey.d.ts +333 -0
- package/dist/esm/StepKey.d.ts.map +1 -0
- package/dist/esm/StepKey.js +478 -0
- package/dist/esm/StepKey.js.map +1 -0
- package/dist/esm/index.d.ts +91 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +91 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/ConflictAnnotation.d.ts +59 -0
- package/dist/esm/internal/ConflictAnnotation.d.ts.map +1 -0
- package/dist/esm/internal/ConflictAnnotation.js +332 -0
- package/dist/esm/internal/ConflictAnnotation.js.map +1 -0
- package/dist/esm/internal/EffectCandidates.d.ts +19 -0
- package/dist/esm/internal/EffectCandidates.d.ts.map +1 -0
- package/dist/esm/internal/EffectCandidates.js +86 -0
- package/dist/esm/internal/EffectCandidates.js.map +1 -0
- package/dist/esm/internal/JsonMirror.d.ts +34 -0
- package/dist/esm/internal/JsonMirror.d.ts.map +1 -0
- package/dist/esm/internal/JsonMirror.js +204 -0
- package/dist/esm/internal/JsonMirror.js.map +1 -0
- package/dist/esm/internal/effects.d.ts +245 -0
- package/dist/esm/internal/effects.d.ts.map +1 -0
- package/dist/esm/internal/effects.js +459 -0
- package/dist/esm/internal/effects.js.map +1 -0
- package/dist/esm/internal/node.d.ts +526 -0
- package/dist/esm/internal/node.d.ts.map +1 -0
- package/dist/esm/internal/node.js +564 -0
- package/dist/esm/internal/node.js.map +1 -0
- package/dist/esm/test/PlanFixtures.d.ts +127 -0
- package/dist/esm/test/PlanFixtures.d.ts.map +1 -0
- package/dist/esm/test/PlanFixtures.js +60 -0
- package/dist/esm/test/PlanFixtures.js.map +1 -0
- package/docs/README.md +165 -0
- package/docs/api.md +1475 -0
- package/docs/concepts/authoring-ast.md +203 -0
- package/docs/concepts/effects-and-conflicts.md +135 -0
- package/docs/concepts/plan-value.md +168 -0
- package/docs/concepts/step-keys.md +179 -0
- package/docs/guides/author-a-node-graph.md +182 -0
- package/docs/guides/compile-a-plan.md +146 -0
- package/docs/guides/declare-file-effects.md +143 -0
- package/docs/guides/diff-two-plans.md +101 -0
- package/docs/installation.md +71 -0
- package/docs/quickstart.md +128 -0
- package/docs/testing.md +73 -0
- package/docs/troubleshooting.md +314 -0
- package/package.json +254 -3
- package/src/CachePolicy.ts +143 -0
- package/src/Effects.ts +284 -0
- package/src/FileSet.ts +411 -0
- package/src/GraphBuildError.ts +158 -0
- package/src/KeyMaterial.ts +106 -0
- package/src/Node.ts +871 -0
- package/src/Placement.ts +99 -0
- package/src/Plan.ts +868 -0
- package/src/PlanDiff.ts +199 -0
- package/src/Planned.ts +208 -0
- package/src/Repetition.ts +33 -0
- package/src/Scheduling.ts +154 -0
- package/src/StepKey.ts +710 -0
- package/src/index.ts +103 -0
- package/src/internal/ConflictAnnotation.ts +374 -0
- package/src/internal/EffectCandidates.ts +89 -0
- package/src/internal/JsonMirror.ts +255 -0
- package/src/internal/effects.ts +543 -0
- package/src/internal/node.ts +858 -0
- 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"}
|