@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,127 @@
1
+ /**
2
+ * Graph fixtures shared by every suite that needs a compiled plan.
3
+ *
4
+ * These live outside a `*.test.ts` file on purpose. `PlanStore.test.ts` used to
5
+ * import `compile` and `draft` from `Plan.test.ts`, and importing a test module
6
+ * REGISTERS ITS SUITES in the importer: every case in `Plan.test.ts` ran a
7
+ * second time under `PlanStore.test.ts`, including the 10,000-node chain, which
8
+ * is the most expensive case in the package. CI reported it as
9
+ * `PlanStore.test.ts > Plan.compile > compiles a 10,000-node Ref chain` and it
10
+ * timed out there, on a slower runner, for work `PlanStore` never asked for.
11
+ *
12
+ * They ship from `src/test` rather than `test/` because `@smthrs/plan-store`
13
+ * drives the same fixtures against the persisted form, the way
14
+ * `@smthrs/database` ships `test/TestDatabase` and `@smthrs/engine-store`
15
+ * ships `test/TestStores`. One definition, two packages.
16
+ *
17
+ * @since 0.1.0
18
+ */
19
+ import * as KeyMaterial from "../KeyMaterial.ts";
20
+ import * as Plan from "../Plan.ts";
21
+ /**
22
+ * A hard-boundary effect declaration over plain path lists.
23
+ *
24
+ * @category models
25
+ * @since 0.1.0
26
+ */
27
+ export declare const effects: (reads: ReadonlyArray<string>, writes: ReadonlyArray<string>) => Plan.NodeEffects;
28
+ /**
29
+ * A sealed node draft whose body defaults to `{ action: id }`.
30
+ *
31
+ * @category constructors
32
+ * @since 0.1.0
33
+ */
34
+ export declare const draft: (id: string, options?: {
35
+ readonly body?: unknown;
36
+ readonly inputs?: ReadonlyArray<KeyMaterial.InputRef>;
37
+ readonly reads?: ReadonlyArray<string>;
38
+ readonly writes?: ReadonlyArray<string>;
39
+ readonly removes?: ReadonlyArray<string>;
40
+ } & Omit<Plan.NodeDraft, "id" | "material" | "effects">) => Plan.NodeDraft;
41
+ /**
42
+ * Compiles drafts into a plan under the `example/Build` flow.
43
+ *
44
+ * @category constructors
45
+ * @since 0.1.0
46
+ */
47
+ export declare const compile: (nodes: ReadonlyArray<Plan.NodeDraft>, planId?: string) => import("effect/Effect").Effect<{
48
+ readonly planId: string;
49
+ readonly flow: string;
50
+ readonly generation: number;
51
+ readonly baseDigest: string & import("effect/Brand").Brand<"@smthrs/keys/Key">;
52
+ readonly digest: string & import("effect/Brand").Brand<"@smthrs/keys/Key">;
53
+ readonly nodes: readonly {
54
+ readonly id: string;
55
+ readonly kind: "step" | "agent" | "merge";
56
+ readonly key: string & import("effect/Brand").Brand<"@smthrs/keys/Key">;
57
+ readonly material: {
58
+ readonly version: "flows/key-material/v2";
59
+ readonly inputs: readonly ({
60
+ readonly _tag: "Literal";
61
+ readonly value: unknown;
62
+ } | {
63
+ readonly _tag: "Ref";
64
+ readonly from: string;
65
+ readonly path: readonly string[];
66
+ } | {
67
+ readonly _tag: "Pending";
68
+ readonly from: string;
69
+ })[];
70
+ readonly layers: readonly string[];
71
+ readonly capabilities: readonly string[];
72
+ readonly kind: "sealed" | "compensable" | "irreversible";
73
+ readonly body: unknown;
74
+ readonly nondeterministic?: true | undefined;
75
+ readonly effects?: unknown;
76
+ readonly placement?: unknown;
77
+ };
78
+ readonly effects: {
79
+ readonly reads: readonly (string | {
80
+ readonly include: readonly [string, ...string[]];
81
+ readonly _tag: "Glob";
82
+ readonly exclude?: readonly string[] | undefined;
83
+ } | {
84
+ readonly _tag: "Filegroup";
85
+ readonly name: string;
86
+ readonly entries: readonly (string | {
87
+ readonly include: readonly [string, ...string[]];
88
+ readonly _tag: "Glob";
89
+ readonly exclude?: readonly string[] | undefined;
90
+ })[];
91
+ })[];
92
+ readonly writes: readonly (string | {
93
+ readonly include: readonly [string, ...string[]];
94
+ readonly _tag: "Glob";
95
+ readonly exclude?: readonly string[] | undefined;
96
+ } | {
97
+ readonly _tag: "TreeArtifact";
98
+ readonly path: string;
99
+ } | {
100
+ readonly _tag: "Filegroup";
101
+ readonly name: string;
102
+ readonly entries: readonly (string | {
103
+ readonly include: readonly [string, ...string[]];
104
+ readonly _tag: "Glob";
105
+ readonly exclude?: readonly string[] | undefined;
106
+ } | {
107
+ readonly _tag: "TreeArtifact";
108
+ readonly path: string;
109
+ })[];
110
+ })[];
111
+ readonly boundaryMode: "expected" | "hard";
112
+ readonly removes?: readonly string[] | undefined;
113
+ };
114
+ readonly dependsOn: readonly string[];
115
+ readonly conflicts: readonly {
116
+ readonly with: string;
117
+ readonly paths: readonly string[];
118
+ readonly strategy: "serialize" | "lane" | "fail";
119
+ readonly runtime: "delay-rebase" | "stop-merge";
120
+ }[];
121
+ readonly strategy: "serialize" | "lane" | "fail";
122
+ readonly runtime: "delay-rebase" | "stop-merge";
123
+ readonly priority: number;
124
+ readonly generation: number;
125
+ }[];
126
+ }, import("effect/Schema").SchemaError | import("../StepKey.ts").KeyMaterialError | Plan.PlanError, import("effect/Crypto").Crypto>;
127
+ //# sourceMappingURL=PlanFixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlanFixtures.d.ts","sourceRoot":"","sources":["../../../src/test/PlanFixtures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,WAAW,MAAM,mBAAmB,CAAA;AAChD,OAAO,KAAK,IAAI,MAAM,YAAY,CAAA;AAElC;;;;;GAKG;AACH,eAAO,MAAM,OAAO,GAClB,OAAO,aAAa,CAAC,MAAM,CAAC,EAC5B,QAAQ,aAAa,CAAC,MAAM,CAAC,KAC5B,IAAI,CAAC,WAAwD,CAAA;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,KAAK,GAChB,IAAI,MAAM,EACV,UAAS;IACP,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAA;IACrD,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACzC,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,UAAU,GAAG,SAAS,CAAM,KAC3D,IAAI,CAAC,SAkBN,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,OAAO,GAAI,OAAO,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,eAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mIACvB,CAAA"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Graph fixtures shared by every suite that needs a compiled plan.
3
+ *
4
+ * These live outside a `*.test.ts` file on purpose. `PlanStore.test.ts` used to
5
+ * import `compile` and `draft` from `Plan.test.ts`, and importing a test module
6
+ * REGISTERS ITS SUITES in the importer: every case in `Plan.test.ts` ran a
7
+ * second time under `PlanStore.test.ts`, including the 10,000-node chain, which
8
+ * is the most expensive case in the package. CI reported it as
9
+ * `PlanStore.test.ts > Plan.compile > compiles a 10,000-node Ref chain` and it
10
+ * timed out there, on a slower runner, for work `PlanStore` never asked for.
11
+ *
12
+ * They ship from `src/test` rather than `test/` because `@smthrs/plan-store`
13
+ * drives the same fixtures against the persisted form, the way
14
+ * `@smthrs/database` ships `test/TestDatabase` and `@smthrs/engine-store`
15
+ * ships `test/TestStores`. One definition, two packages.
16
+ *
17
+ * @since 0.1.0
18
+ */
19
+ import * as KeyMaterial from "../KeyMaterial.js";
20
+ import * as Plan from "../Plan.js";
21
+ /**
22
+ * A hard-boundary effect declaration over plain path lists.
23
+ *
24
+ * @category models
25
+ * @since 0.1.0
26
+ */
27
+ export const effects = (reads, writes) => ({ reads, writes, boundaryMode: "hard" });
28
+ /**
29
+ * A sealed node draft whose body defaults to `{ action: id }`.
30
+ *
31
+ * @category constructors
32
+ * @since 0.1.0
33
+ */
34
+ export const draft = (id, options = {}) => ({
35
+ id,
36
+ material: {
37
+ version: KeyMaterial.version,
38
+ kind: "sealed",
39
+ body: options.body ?? { action: id },
40
+ inputs: options.inputs ?? [],
41
+ layers: [],
42
+ capabilities: []
43
+ },
44
+ effects: {
45
+ ...effects(options.reads ?? [], options.writes ?? []),
46
+ ...(options.removes === undefined ? {} : { removes: options.removes })
47
+ },
48
+ ...(options.kind === undefined ? {} : { kind: options.kind }),
49
+ ...(options.priority === undefined ? {} : { priority: options.priority }),
50
+ ...(options.conflictStrategy === undefined ? {} : { conflictStrategy: options.conflictStrategy }),
51
+ ...(options.runtimeStrategy === undefined ? {} : { runtimeStrategy: options.runtimeStrategy })
52
+ });
53
+ /**
54
+ * Compiles drafts into a plan under the `example/Build` flow.
55
+ *
56
+ * @category constructors
57
+ * @since 0.1.0
58
+ */
59
+ export const compile = (nodes, planId = "plan-1") => Plan.compile({ planId, flow: "example/Build", nodes });
60
+ //# sourceMappingURL=PlanFixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlanFixtures.js","sourceRoot":"","sources":["../../../src/test/PlanFixtures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,WAAW,MAAM,mBAAmB,CAAA;AAChD,OAAO,KAAK,IAAI,MAAM,YAAY,CAAA;AAElC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,KAA4B,EAC5B,MAA6B,EACX,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAA;AAEhE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,EAAU,EACV,UAM0D,EAAE,EAC5C,EAAE,CAAC,CAAC;IACpB,EAAE;IACF,QAAQ,EAAE;QACR,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;QACpC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;QAC5B,MAAM,EAAE,EAAE;QACV,YAAY,EAAE,EAAE;KACjB;IACD,OAAO,EAAE;QACP,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;QACrD,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;KACvE;IACD,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;IAC7D,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;IACzE,GAAG,CAAC,OAAO,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC;IACjG,GAAG,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE,CAAC;CAC/F,CAAC,CAAA;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,KAAoC,EAAE,MAAM,GAAG,QAAQ,EAAE,EAAE,CACjF,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAA","sourcesContent":["/**\n * Graph fixtures shared by every suite that needs a compiled plan.\n *\n * These live outside a `*.test.ts` file on purpose. `PlanStore.test.ts` used to\n * import `compile` and `draft` from `Plan.test.ts`, and importing a test module\n * REGISTERS ITS SUITES in the importer: every case in `Plan.test.ts` ran a\n * second time under `PlanStore.test.ts`, including the 10,000-node chain, which\n * is the most expensive case in the package. CI reported it as\n * `PlanStore.test.ts > Plan.compile > compiles a 10,000-node Ref chain` and it\n * timed out there, on a slower runner, for work `PlanStore` never asked for.\n *\n * They ship from `src/test` rather than `test/` because `@smthrs/plan-store`\n * drives the same fixtures against the persisted form, the way\n * `@smthrs/database` ships `test/TestDatabase` and `@smthrs/engine-store`\n * ships `test/TestStores`. One definition, two packages.\n *\n * @since 0.1.0\n */\n\nimport * as KeyMaterial from \"../KeyMaterial.ts\"\nimport * as Plan from \"../Plan.ts\"\n\n/**\n * A hard-boundary effect declaration over plain path lists.\n *\n * @category models\n * @since 0.1.0\n */\nexport const effects = (\n reads: ReadonlyArray<string>,\n writes: ReadonlyArray<string>\n): Plan.NodeEffects => ({ reads, writes, boundaryMode: \"hard\" })\n\n/**\n * A sealed node draft whose body defaults to `{ action: id }`.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const draft = (\n id: string,\n options: {\n readonly body?: unknown\n readonly inputs?: ReadonlyArray<KeyMaterial.InputRef>\n readonly reads?: ReadonlyArray<string>\n readonly writes?: ReadonlyArray<string>\n readonly removes?: ReadonlyArray<string>\n } & Omit<Plan.NodeDraft, \"id\" | \"material\" | \"effects\"> = {}\n): Plan.NodeDraft => ({\n id,\n material: {\n version: KeyMaterial.version,\n kind: \"sealed\",\n body: options.body ?? { action: id },\n inputs: options.inputs ?? [],\n layers: [],\n capabilities: []\n },\n effects: {\n ...effects(options.reads ?? [], options.writes ?? []),\n ...(options.removes === undefined ? {} : { removes: options.removes })\n },\n ...(options.kind === undefined ? {} : { kind: options.kind }),\n ...(options.priority === undefined ? {} : { priority: options.priority }),\n ...(options.conflictStrategy === undefined ? {} : { conflictStrategy: options.conflictStrategy }),\n ...(options.runtimeStrategy === undefined ? {} : { runtimeStrategy: options.runtimeStrategy })\n})\n\n/**\n * Compiles drafts into a plan under the `example/Build` flow.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const compile = (nodes: ReadonlyArray<Plan.NodeDraft>, planId = \"plan-1\") =>\n Plan.compile({ planId, flow: \"example/Build\", nodes })\n"]}
package/docs/README.md ADDED
@@ -0,0 +1,165 @@
1
+ ---
2
+ title: "@smthrs/plan"
3
+ description: "Compile what a run intends to do into one keyed, content-addressed graph, grow it later without rewriting a node, and diff two graphs to say exactly what changed."
4
+ ---
5
+
6
+ `@smthrs/plan` turns a description of work into one durable value. It compiles a
7
+ list of declared steps into a graph in which every node carries a content key
8
+ derived from what that node declares, grows that graph a generation at a time
9
+ without rewriting a node, and compares two graphs to say exactly what changed.
10
+ It runs nothing itself and performs no I/O at all.
11
+
12
+ ## What it solves
13
+
14
+ A long run that calls models, builds code, and touches files has to answer two
15
+ questions that are awkward to answer after the fact.
16
+
17
+ The first is which work is the same work as last time. If identity is a name or
18
+ a position in a list, a harmless edit invalidates everything downstream and a
19
+ meaningful edit sometimes invalidates nothing. This package makes identity
20
+ computable: a node's key is a hash of its payload, its declared dependencies,
21
+ its layers, and its capabilities, so an edit re-keys that node and the nodes
22
+ that depend on it, and leaves every other key untouched. Renaming a node changes
23
+ no key at all, because ids are lookup addresses rather than identity.
24
+
25
+ The second is whether what executed is what somebody approved. A plan carries
26
+ two digests: `digest`, which moves as the plan elaborates, and `baseDigest`,
27
+ which still names the shape a reviewer signed off on. `Plan.append` is the only
28
+ way a plan changes, and it leaves every earlier node byte for byte.
29
+ [`@smthrs/plan-store`](/api/plan-store) keeps that honest in SQL rather than by
30
+ convention, with triggers that refuse a rewritten node.
31
+
32
+ Reach for this package when you are building a scheduler, a cache, or an
33
+ approval flow over declared work and you need step identity, declared file
34
+ effects, and an auditable history as values you can hold, hash, and store.
35
+
36
+ ## Install
37
+
38
+ Not on npm yet; see [Installation](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/flow/docs/installation.md#use-the-libraries).
39
+
40
+ Compiling asks Effect for its `Crypto` service, which a platform package
41
+ supplies:
42
+
43
+ ```bash
44
+ pnpm add @effect/platform-node@4.0.0-rc.115 @effect/platform-node-shared@4.0.0-rc.115
45
+ ```
46
+
47
+ [Installation](./installation.md) covers the import forms, the package
48
+ persistence lives in, and browser support.
49
+
50
+ ## Compile a plan
51
+
52
+ Two steps, where the second consumes the first's result and reads the file the
53
+ first writes:
54
+
55
+ ```ts
56
+ import * as NodeCrypto from "@effect/platform-node/NodeCrypto"
57
+ import * as KeyMaterial from "@smthrs/plan/KeyMaterial"
58
+ import * as Plan from "@smthrs/plan/Plan"
59
+ import * as Effect from "effect/Effect"
60
+
61
+ /** Everything hashed about one node. `sealed` is work whose result may be reused across runs. */
62
+ const sealed = (body: unknown, inputs: ReadonlyArray<KeyMaterial.InputRef> = []): KeyMaterial.KeyMaterial => ({
63
+ version: KeyMaterial.version,
64
+ kind: "sealed",
65
+ body,
66
+ inputs,
67
+ layers: [],
68
+ capabilities: []
69
+ })
70
+
71
+ const drafts: ReadonlyArray<Plan.NodeDraft> = [
72
+ {
73
+ id: "run-tests",
74
+ material: sealed({ action: "run-tests" }, [{ _tag: "Ref", from: "read-pr", path: [] }]),
75
+ effects: { reads: ["pr.json"], writes: ["report.json"], boundaryMode: "hard" }
76
+ },
77
+ {
78
+ id: "read-pr",
79
+ material: sealed({ action: "read-pr", pr: 4821 }),
80
+ effects: { reads: [], writes: ["pr.json"], boundaryMode: "hard" }
81
+ }
82
+ ]
83
+
84
+ const plan = await Effect.runPromise(
85
+ Plan.compile({ planId: "review-4821", flow: "example/Review", nodes: drafts }).pipe(
86
+ Effect.provide(NodeCrypto.layer),
87
+ Effect.orDie
88
+ )
89
+ )
90
+
91
+ console.log(plan.digest)
92
+ for (const node of plan.nodes) console.log(node.id, node.key, node.dependsOn)
93
+ ```
94
+
95
+ `layers` and `capabilities` are empty here. They name the composition a node
96
+ runs under and the authority it claims, and both are hashed into the node's
97
+ key. `boundaryMode` is `hard` or `expected`: the plan carries it through to
98
+ whatever measures the boundary at run time and never interprets it itself.
99
+ [Step keys](./concepts/step-keys.md) tables every field.
100
+
101
+ The digests on your machine match these, because a plan is a pure function of
102
+ its declarations. Compiling reads no file, no clock, and no network:
103
+
104
+ ```text
105
+ key1_61edccc875ff69b798e0c3874d167f65ff0a250b9fe67d27558b854fa2832302
106
+ read-pr key1_6353326ab2a03804d6acfe916debc4f04f09f9deb8f0e93c1ebea3cdd983db18 []
107
+ run-tests key1_e58229e3be2f6abf6f818baf8577fe7045edba6f5e88097b633898478da93985 [ 'read-pr' ]
108
+ ```
109
+
110
+ The drafts went in out of order and came back topologically sorted.
111
+ `run-tests` names `read-pr` once, in its key material, and that single
112
+ declaration became both the hashed dependency and the graph edge, so an edge and
113
+ a key can never disagree about what a node consumes. Change `pr: 4821` to
114
+ another number and both keys move; rename `read-pr` to `fetch-pr` and neither
115
+ does.
116
+
117
+ ## What is in the package
118
+
119
+ | Module | Role |
120
+ | ------------- | ------------------------------------------------------------------------------- |
121
+ | `Node` | The pure authoring AST: `succeed`, `all`, `map`, `andThen`, `branch`, `catch`. |
122
+ | `Planned` | The placeholder a body sees where a step result will be. |
123
+ | `KeyMaterial` | What a planner declares about a node: body, inputs, layers, capabilities. |
124
+ | `StepKey` | The compiler that turns material and resolved dependencies into a key. |
125
+ | `FileSet` | Patterns, globs, tree artifacts, and filegroups: the vocabulary `effects` uses. |
126
+ | `Plan` | `compile`, `append`, the node schemas, and the digest an approval binds to. |
127
+ | `PlanDiff` | A comparison of two plans as a value, with each re-key attributed to a field. |
128
+ | `Placement` | Where a node runs: four directives, their host detail, and the annotation key. |
129
+
130
+ The [API reference](./api.md) documents every export.
131
+
132
+ ## Where this sits
133
+
134
+ `@smthrs/plan` is one package of the Smithers durable flow engine, and it owns
135
+ the plan phase: step identity, graph compilation, and declared file effects.
136
+ Storing a plan is [`@smthrs/plan-store`](/api/plan-store), which is a separate
137
+ package so that compiling does not drag a database in.
138
+ [`@smthrs/flows`](/api/flows) is the whole engine as a single dependency, and it
139
+ re-exports this package as its `Plan` namespace, so `Plan.compile` there is
140
+ `Plan.Plan.compile`. If you are writing an application rather than a scheduler,
141
+ install `@smthrs/flows` and reach for this package's names through it; install
142
+ `@smthrs/plan` on its own when you want the plan value without the engine that
143
+ executes it.
144
+
145
+ Nothing here executes a node. A compiled plan is inert until a scheduler drives
146
+ it, which is [`@smthrs/engine-store`](/api/engine-store)'s `PlanScheduler`.
147
+ Above this layer, [`@smthrs/flow`](/api/flow) is the authoring API most flow
148
+ authors write against, and it produces the drafts that arrive here. Above all of
149
+ it, [`@smthrs/cli`](/api/cli) is the `smthrs` command line that plans, approves,
150
+ runs, and inspects flows for someone who never imports a package at all.
151
+
152
+ ## Next
153
+
154
+ - [Quickstart](./quickstart.md): compile a plan, append a generation, and diff
155
+ the two graphs.
156
+ - [The plan value](./concepts/plan-value.md): generations, the two digests, and
157
+ why a plan grows instead of being rewritten.
158
+ - [Step keys](./concepts/step-keys.md): what goes into a key, what deliberately
159
+ stays out, and why invalidation is re-keying.
160
+ - [Declared effects and conflicts](./concepts/effects-and-conflicts.md): how
161
+ reads and writes become ordering edges.
162
+ - [Persist a plan](https://plan-store.smithers.sh/guides/persist-a-plan/): the
163
+ store composition and every outcome `record` can answer with.
164
+ - [Troubleshooting](./troubleshooting.md): every refusal this package raises and
165
+ what to change.