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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/CHANGELOG.md +183 -0
  2. package/LICENSE +21 -0
  3. package/README.md +228 -2
  4. package/dist/cjs/CachePolicy.d.ts +131 -0
  5. package/dist/cjs/CachePolicy.d.ts.map +1 -0
  6. package/dist/cjs/CachePolicy.js +50 -0
  7. package/dist/cjs/CachePolicy.js.map +7 -0
  8. package/dist/cjs/Effects.d.ts +240 -0
  9. package/dist/cjs/Effects.d.ts.map +1 -0
  10. package/dist/cjs/Effects.js +76 -0
  11. package/dist/cjs/Effects.js.map +7 -0
  12. package/dist/cjs/FileSet.d.ts +288 -0
  13. package/dist/cjs/FileSet.d.ts.map +1 -0
  14. package/dist/cjs/FileSet.js +191 -0
  15. package/dist/cjs/FileSet.js.map +7 -0
  16. package/dist/cjs/GraphBuildError.d.ts +122 -0
  17. package/dist/cjs/GraphBuildError.d.ts.map +1 -0
  18. package/dist/cjs/GraphBuildError.js +71 -0
  19. package/dist/cjs/GraphBuildError.js.map +7 -0
  20. package/dist/cjs/KeyMaterial.d.ts +102 -0
  21. package/dist/cjs/KeyMaterial.d.ts.map +1 -0
  22. package/dist/cjs/KeyMaterial.js +64 -0
  23. package/dist/cjs/KeyMaterial.js.map +7 -0
  24. package/dist/cjs/Node.d.ts +606 -0
  25. package/dist/cjs/Node.d.ts.map +1 -0
  26. package/dist/cjs/Node.js +207 -0
  27. package/dist/cjs/Node.js.map +7 -0
  28. package/dist/cjs/Placement.d.ts +117 -0
  29. package/dist/cjs/Placement.d.ts.map +1 -0
  30. package/dist/cjs/Placement.js +46 -0
  31. package/dist/cjs/Placement.js.map +7 -0
  32. package/dist/cjs/Plan.d.ts +440 -0
  33. package/dist/cjs/Plan.d.ts.map +1 -0
  34. package/dist/cjs/Plan.js +508 -0
  35. package/dist/cjs/Plan.js.map +7 -0
  36. package/dist/cjs/PlanDiff.d.ts +58 -0
  37. package/dist/cjs/PlanDiff.d.ts.map +1 -0
  38. package/dist/cjs/PlanDiff.js +124 -0
  39. package/dist/cjs/PlanDiff.js.map +7 -0
  40. package/dist/cjs/Planned.d.ts +121 -0
  41. package/dist/cjs/Planned.d.ts.map +1 -0
  42. package/dist/cjs/Planned.js +72 -0
  43. package/dist/cjs/Planned.js.map +7 -0
  44. package/dist/cjs/Repetition.d.ts +31 -0
  45. package/dist/cjs/Repetition.d.ts.map +1 -0
  46. package/dist/cjs/Repetition.js +36 -0
  47. package/dist/cjs/Repetition.js.map +7 -0
  48. package/dist/cjs/Scheduling.d.ts +89 -0
  49. package/dist/cjs/Scheduling.d.ts.map +1 -0
  50. package/dist/cjs/Scheduling.js +76 -0
  51. package/dist/cjs/Scheduling.js.map +7 -0
  52. package/dist/cjs/StepKey.d.ts +333 -0
  53. package/dist/cjs/StepKey.d.ts.map +1 -0
  54. package/dist/cjs/StepKey.js +311 -0
  55. package/dist/cjs/StepKey.js.map +7 -0
  56. package/dist/cjs/index.d.ts +91 -0
  57. package/dist/cjs/index.d.ts.map +1 -0
  58. package/dist/cjs/index.js +59 -0
  59. package/dist/cjs/index.js.map +7 -0
  60. package/dist/cjs/internal/ConflictAnnotation.d.ts +59 -0
  61. package/dist/cjs/internal/ConflictAnnotation.d.ts.map +1 -0
  62. package/dist/cjs/internal/ConflictAnnotation.js +247 -0
  63. package/dist/cjs/internal/ConflictAnnotation.js.map +7 -0
  64. package/dist/cjs/internal/EffectCandidates.d.ts +19 -0
  65. package/dist/cjs/internal/EffectCandidates.d.ts.map +1 -0
  66. package/dist/cjs/internal/EffectCandidates.js +92 -0
  67. package/dist/cjs/internal/EffectCandidates.js.map +7 -0
  68. package/dist/cjs/internal/JsonMirror.d.ts +34 -0
  69. package/dist/cjs/internal/JsonMirror.d.ts.map +1 -0
  70. package/dist/cjs/internal/JsonMirror.js +195 -0
  71. package/dist/cjs/internal/JsonMirror.js.map +7 -0
  72. package/dist/cjs/internal/effects.d.ts +245 -0
  73. package/dist/cjs/internal/effects.d.ts.map +1 -0
  74. package/dist/cjs/internal/effects.js +290 -0
  75. package/dist/cjs/internal/effects.js.map +7 -0
  76. package/dist/cjs/internal/node.d.ts +526 -0
  77. package/dist/cjs/internal/node.d.ts.map +1 -0
  78. package/dist/cjs/internal/node.js +312 -0
  79. package/dist/cjs/internal/node.js.map +7 -0
  80. package/dist/cjs/package.json +1 -0
  81. package/dist/cjs/test/PlanFixtures.d.ts +127 -0
  82. package/dist/cjs/test/PlanFixtures.d.ts.map +1 -0
  83. package/dist/cjs/test/PlanFixtures.js +59 -0
  84. package/dist/cjs/test/PlanFixtures.js.map +7 -0
  85. package/dist/esm/CachePolicy.d.ts +131 -0
  86. package/dist/esm/CachePolicy.d.ts.map +1 -0
  87. package/dist/esm/CachePolicy.js +117 -0
  88. package/dist/esm/CachePolicy.js.map +1 -0
  89. package/dist/esm/Effects.d.ts +240 -0
  90. package/dist/esm/Effects.d.ts.map +1 -0
  91. package/dist/esm/Effects.js +167 -0
  92. package/dist/esm/Effects.js.map +1 -0
  93. package/dist/esm/FileSet.d.ts +288 -0
  94. package/dist/esm/FileSet.d.ts.map +1 -0
  95. package/dist/esm/FileSet.js +324 -0
  96. package/dist/esm/FileSet.js.map +1 -0
  97. package/dist/esm/GraphBuildError.d.ts +122 -0
  98. package/dist/esm/GraphBuildError.d.ts.map +1 -0
  99. package/dist/esm/GraphBuildError.js +146 -0
  100. package/dist/esm/GraphBuildError.js.map +1 -0
  101. package/dist/esm/KeyMaterial.d.ts +102 -0
  102. package/dist/esm/KeyMaterial.d.ts.map +1 -0
  103. package/dist/esm/KeyMaterial.js +85 -0
  104. package/dist/esm/KeyMaterial.js.map +1 -0
  105. package/dist/esm/Node.d.ts +606 -0
  106. package/dist/esm/Node.d.ts.map +1 -0
  107. package/dist/esm/Node.js +521 -0
  108. package/dist/esm/Node.js.map +1 -0
  109. package/dist/esm/Placement.d.ts +117 -0
  110. package/dist/esm/Placement.d.ts.map +1 -0
  111. package/dist/esm/Placement.js +65 -0
  112. package/dist/esm/Placement.js.map +1 -0
  113. package/dist/esm/Plan.d.ts +440 -0
  114. package/dist/esm/Plan.d.ts.map +1 -0
  115. package/dist/esm/Plan.js +673 -0
  116. package/dist/esm/Plan.js.map +1 -0
  117. package/dist/esm/PlanDiff.d.ts +58 -0
  118. package/dist/esm/PlanDiff.d.ts.map +1 -0
  119. package/dist/esm/PlanDiff.js +159 -0
  120. package/dist/esm/PlanDiff.js.map +1 -0
  121. package/dist/esm/Planned.d.ts +121 -0
  122. package/dist/esm/Planned.d.ts.map +1 -0
  123. package/dist/esm/Planned.js +147 -0
  124. package/dist/esm/Planned.js.map +1 -0
  125. package/dist/esm/Repetition.d.ts +31 -0
  126. package/dist/esm/Repetition.d.ts.map +1 -0
  127. package/dist/esm/Repetition.js +24 -0
  128. package/dist/esm/Repetition.js.map +1 -0
  129. package/dist/esm/Scheduling.d.ts +89 -0
  130. package/dist/esm/Scheduling.d.ts.map +1 -0
  131. package/dist/esm/Scheduling.js +82 -0
  132. package/dist/esm/Scheduling.js.map +1 -0
  133. package/dist/esm/StepKey.d.ts +333 -0
  134. package/dist/esm/StepKey.d.ts.map +1 -0
  135. package/dist/esm/StepKey.js +478 -0
  136. package/dist/esm/StepKey.js.map +1 -0
  137. package/dist/esm/index.d.ts +91 -0
  138. package/dist/esm/index.d.ts.map +1 -0
  139. package/dist/esm/index.js +91 -0
  140. package/dist/esm/index.js.map +1 -0
  141. package/dist/esm/internal/ConflictAnnotation.d.ts +59 -0
  142. package/dist/esm/internal/ConflictAnnotation.d.ts.map +1 -0
  143. package/dist/esm/internal/ConflictAnnotation.js +332 -0
  144. package/dist/esm/internal/ConflictAnnotation.js.map +1 -0
  145. package/dist/esm/internal/EffectCandidates.d.ts +19 -0
  146. package/dist/esm/internal/EffectCandidates.d.ts.map +1 -0
  147. package/dist/esm/internal/EffectCandidates.js +86 -0
  148. package/dist/esm/internal/EffectCandidates.js.map +1 -0
  149. package/dist/esm/internal/JsonMirror.d.ts +34 -0
  150. package/dist/esm/internal/JsonMirror.d.ts.map +1 -0
  151. package/dist/esm/internal/JsonMirror.js +204 -0
  152. package/dist/esm/internal/JsonMirror.js.map +1 -0
  153. package/dist/esm/internal/effects.d.ts +245 -0
  154. package/dist/esm/internal/effects.d.ts.map +1 -0
  155. package/dist/esm/internal/effects.js +459 -0
  156. package/dist/esm/internal/effects.js.map +1 -0
  157. package/dist/esm/internal/node.d.ts +526 -0
  158. package/dist/esm/internal/node.d.ts.map +1 -0
  159. package/dist/esm/internal/node.js +564 -0
  160. package/dist/esm/internal/node.js.map +1 -0
  161. package/dist/esm/test/PlanFixtures.d.ts +127 -0
  162. package/dist/esm/test/PlanFixtures.d.ts.map +1 -0
  163. package/dist/esm/test/PlanFixtures.js +60 -0
  164. package/dist/esm/test/PlanFixtures.js.map +1 -0
  165. package/docs/README.md +165 -0
  166. package/docs/api.md +1475 -0
  167. package/docs/concepts/authoring-ast.md +203 -0
  168. package/docs/concepts/effects-and-conflicts.md +135 -0
  169. package/docs/concepts/plan-value.md +168 -0
  170. package/docs/concepts/step-keys.md +179 -0
  171. package/docs/guides/author-a-node-graph.md +182 -0
  172. package/docs/guides/compile-a-plan.md +146 -0
  173. package/docs/guides/declare-file-effects.md +143 -0
  174. package/docs/guides/diff-two-plans.md +101 -0
  175. package/docs/installation.md +71 -0
  176. package/docs/quickstart.md +128 -0
  177. package/docs/testing.md +73 -0
  178. package/docs/troubleshooting.md +314 -0
  179. package/package.json +254 -3
  180. package/src/CachePolicy.ts +143 -0
  181. package/src/Effects.ts +284 -0
  182. package/src/FileSet.ts +411 -0
  183. package/src/GraphBuildError.ts +158 -0
  184. package/src/KeyMaterial.ts +106 -0
  185. package/src/Node.ts +871 -0
  186. package/src/Placement.ts +99 -0
  187. package/src/Plan.ts +868 -0
  188. package/src/PlanDiff.ts +199 -0
  189. package/src/Planned.ts +208 -0
  190. package/src/Repetition.ts +33 -0
  191. package/src/Scheduling.ts +154 -0
  192. package/src/StepKey.ts +710 -0
  193. package/src/index.ts +103 -0
  194. package/src/internal/ConflictAnnotation.ts +374 -0
  195. package/src/internal/EffectCandidates.ts +89 -0
  196. package/src/internal/JsonMirror.ts +255 -0
  197. package/src/internal/effects.ts +543 -0
  198. package/src/internal/node.ts +858 -0
  199. package/src/test/PlanFixtures.ts +76 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/StepKey.ts"],
4
+ "sourcesContent": ["/**\n * The KeyMaterial \u2192 step-key compiler.\n *\n * Revived from the deleted `packages/smithers/flows/keys/src/StepKey.ts`. Two\n * deliberate deviations from the original:\n *\n * 1. **It lives here, not in `@smthrs/keys`.** That package owns generic\n * derivation and stored-key validation; a compiler that understands plan\n * material belongs above it.\n * 2. **It produces `@smthrs/keys` `StoredKey` values, not a second `sk1_` digest\n * format.** The original minted its own prefix over a private `Digest`\n * module. The engine dispatches under `StoredKey` (`FlowEngine/ActionKey.ts`),\n * so a plan whose node keys were a *different* string format could never be\n * the thing the cache is consulted against, and that is the whole point of\n * a `Plan`. One key format, one hashing\n * chokepoint (canonical JSON + injected `Crypto`), one namespace field\n * (`kind`) distinguishing content keys from ordinal ones.\n *\n * Everything the original earned is kept: nominally branded digest inputs (a\n * literal that merely *looks* like `{digest}` hashes as a literal), set\n * normalization, the separate environment namespace, per-variant tagging of\n * resolved references, and the hashed `version`.\n *\n * Governing contract: the step-key rules at\n * https://smithers.sh/docs/concepts/content-addressing.\n *\n * @since 0.1.0\n */\n\nimport { DerivedKey, type StoredKey } from \"@smthrs/keys\"\nimport * as Cause from \"effect/Cause\"\nimport type * as Crypto from \"effect/Crypto\"\nimport * as Deferred from \"effect/Deferred\"\nimport * as Effect from \"effect/Effect\"\nimport * as Exit from \"effect/Exit\"\nimport * as Schema from \"effect/Schema\"\nimport type * as FileSet from \"./FileSet.ts\"\nimport type * as KeyMaterial from \"./KeyMaterial.ts\"\n\n/**\n * A computed step key. Identical in representation to any other flow key \u2014\n * `key1_` plus a SHA-256 digest of the canonical serialization of the material.\n *\n * @since 0.1.0\n * @category models\n * @slop\n */\nexport type StepKey = StoredKey\n\n/**\n * The nominal brand carried by every {@link DigestInput}. Kept private: the\n * only way to produce a value bearing it is {@link digestInput}, so a plain\n * value that merely happens to look like `{ digest: \"...\" }` \u2014 actions pass\n * content hashes around as ordinary data \u2014 can never be mistaken for a genuine\n * upstream-result digest reference. This closes a step-key collision where\n * shape-sniffing (`\"digest\" in value`) hashed the two identically.\n *\n * @since 0.1.0\n * @category symbols\n */\nconst DigestInputTypeId: unique symbol = Symbol.for(\"@smthrs/plan/StepKey/DigestInput\")\n\n/**\n * Which kind of graph reference a {@link DigestInput} stands for. Declared\n * once here so the field on `DigestInput` and the parameter of\n * {@link digestInput} name one set and cannot drift apart.\n */\ntype GraphReference = \"ref\" | \"pending\" | \"ref-projected\"\n\n/**\n * A precomputed digest supplied as a step input rather than a literal value.\n *\n * @since 0.1.0\n * @category models\n * @slop\n */\nexport interface DigestInput {\n readonly [DigestInputTypeId]: typeof DigestInputTypeId\n readonly digest: string\n /**\n * Which kind of graph reference resolved to this digest.\n *\n * Carrying the discriminator on the digest input keeps `normalizeInputs` the\n * single normalizer: it wraps every variant once, and the variants stay\n * mutually distinct because they differ in this field rather than in a\n * hand-built object a second wrap would hide.\n */\n readonly reference?: GraphReference\n /** The projection applied to the referenced result, for `ref-projected`. */\n readonly path?: ReadonlyArray<string>\n}\n\n/**\n * Nominally tags a precomputed digest so it is hashed as a digest reference\n * rather than a literal value.\n *\n * @since 0.1.0\n * @category constructors\n * @slop\n */\nexport const digestInput = (\n digest: string,\n reference?: {\n readonly reference: GraphReference\n readonly path?: ReadonlyArray<string>\n }\n): DigestInput => ({\n [DigestInputTypeId]: DigestInputTypeId,\n digest,\n ...(reference === undefined ? {} : { reference: reference.reference }),\n ...(reference?.path === undefined ? {} : { path: reference.path })\n})\n\n/**\n * Type guard for values produced by {@link digestInput}.\n *\n * @since 0.1.0\n * @category guards\n * @slop\n */\nexport const isDigestInput = (value: unknown): value is DigestInput =>\n typeof value === \"object\" && value !== null && DigestInputTypeId in value &&\n (value as Record<PropertyKey, unknown>)[DigestInputTypeId] === DigestInputTypeId\n\n/**\n * The engine-resolved execution environment a content key is computed under,\n * hashed in its OWN namespace rather than merged into the caller's `layers`\n * and `capabilities`.\n *\n * Three properties matter and none survives a merge:\n *\n * 1. **Non-aliasing.** Concatenating environment material onto the caller's\n * made `caller{fs:[\"a\"]} + env{fs:[\"b\"]}` hash identically to\n * `caller{fs:[\"a\",\"b\"]} + env{}` \u2014 a cross-run collision in the very\n * material whose purpose is preventing one.\n * 2. **Declared vs undeclared.** `declared: false` is not the same value as\n * `declared: true` with empty sets.\n * 3. **Composition order.** Environment layers retain declaration order,\n * because plugin and service composition order can change behavior. The\n * caller-owned `ContentIdentity.layers` stays set-normalized.\n *\n * `runScope` is set only when `declared` is `false`: it pins the key to one\n * run, so a step whose environment identity is unknown can never serve a\n * cross-run hit. The union enforces this for typed callers; both\n * {@link content} and {@link dispatchIdentity} validate it at run time.\n *\n * @since 0.1.0\n * @category models\n * @slop\n */\nexport type EnvironmentIdentity = typeof EnvironmentIdentity.Type\n\n/**\n * Validated, copyable runtime environment identity. Layer order matters;\n * capability patterns are set-like. Unknown fields are not identity material.\n * @since 1.0.0\n * @category schemas\n */\nexport const EnvironmentIdentity = Schema.Union([\n Schema.Struct({\n declared: Schema.Literal(true),\n layers: Schema.Array(Schema.String),\n capabilities: Schema.Record(Schema.String, Schema.Array(Schema.String)),\n runScope: Schema.optionalKey(Schema.Undefined)\n }),\n Schema.Struct({\n declared: Schema.Literal(false),\n layers: Schema.Array(Schema.String),\n capabilities: Schema.Record(Schema.String, Schema.Array(Schema.String)),\n runScope: Schema.NonEmptyString\n })\n])\n\n/**\n * Caller-owned memoization context for projected dependency-value digests.\n *\n * This is the flows counterpart of Bazel's `ActionKeyContext`: one context is\n * passed through related key computations so shared input material is hashed\n * once. Concurrent requests for the same projection share the in-flight\n * digest. Entries are sound only while each settled `from` value is immutable;\n * callers must create a fresh memo when those values can change.\n *\n * @since 0.1.0\n * @category models\n * @slop\n */\nexport interface DigestMemo {\n readonly digest: (\n from: string,\n path: ReadonlyArray<string>,\n compute: Effect.Effect<StepKey, Schema.SchemaError, Crypto.Crypto>\n ) => Effect.Effect<StepKey, Schema.SchemaError, Crypto.Crypto>\n}\n\n/**\n * Creates an empty projected-value digest memo. Projection addresses are\n * JSON-encoded `[from, path]` tuples so segment boundaries are unambiguous.\n *\n * @since 0.1.0\n * @category constructors\n * @slop\n */\nexport const makeDigestMemo = (): DigestMemo => {\n const entries = new Map<string, Deferred.Deferred<StepKey, Schema.SchemaError>>()\n const digest: DigestMemo[\"digest\"] = (from, path, compute) =>\n Effect.suspend(() => {\n const address = JSON.stringify([from, path])\n const existing = entries.get(address)\n if (existing !== undefined) {\n return Effect.flatMap(Effect.exit(Deferred.await(existing)), (exit) => {\n if (Exit.isSuccess(exit)) return Effect.succeed(exit.value)\n // A waiter did not request the leader's cancellation. It competes to\n // become the replacement leader instead of inheriting that interrupt.\n return Cause.hasInterrupts(exit.cause) ? digest(from, path, compute) : Effect.failCause(exit.cause)\n })\n }\n const pending = Deferred.makeUnsafe<StepKey, Schema.SchemaError>()\n entries.set(address, pending)\n return compute.pipe(\n Effect.onExit((exit) =>\n Effect.gen(function*() {\n if (Exit.isFailure(exit) && entries.get(address) === pending) {\n entries.delete(address)\n }\n yield* Deferred.done(pending, exit)\n })\n )\n )\n })\n return { digest }\n}\n\n/**\n * Material describing a sealed or hermetic content-addressed step.\n *\n * @since 0.1.0\n * @category models\n * @slop\n */\nexport interface ContentIdentity {\n readonly body: unknown\n readonly inputs: Readonly<Record<string, unknown | DigestInput>>\n readonly layers: ReadonlyArray<string>\n readonly capabilities: Readonly<Record<string, ReadonlyArray<string>>>\n readonly environment?: EnvironmentIdentity | undefined\n readonly hermetic?: {\n readonly readSet: ReadonlyArray<{ readonly path: string; readonly digest: string }>\n readonly writeSet: ReadonlyArray<FileSet.Entry>\n readonly removes?: ReadonlyArray<string> | undefined\n readonly boundaryMode: FileSet.BoundaryMode\n } | undefined\n}\n\n/**\n * Material describing the run-local identity of a non-cacheable step.\n *\n * @since 0.1.0\n * @category models\n * @slop\n */\nexport interface OrdinalIdentity {\n readonly runId: string\n readonly parentScope?: string | undefined\n readonly ordinal: number\n readonly tier: \"compensable\" | \"irreversible\" | \"unsealed\"\n}\n\n/**\n * Stable failures while resolving graph-local dependency references.\n *\n * @since 0.1.0\n * @category errors\n * @slop\n */\nexport class KeyMaterialError extends Schema.TaggedError<KeyMaterialError>()(\"@smthrs/plan/KeyMaterialError\", {\n code: Schema.Literals([\"invalid_environment\", \"missing_dependency\", \"non_content_material\"]),\n message: Schema.String\n}) {}\n\nconst validateEnvironment = (environment: EnvironmentIdentity): KeyMaterialError | undefined => {\n const candidate = environment as { readonly declared?: unknown; readonly runScope?: unknown }\n if (candidate.declared === false) {\n if (typeof candidate.runScope !== \"string\" || candidate.runScope.length === 0) {\n return new KeyMaterialError({\n code: \"invalid_environment\",\n message: \"Undeclared environment identity requires a non-empty runScope\"\n })\n }\n return undefined\n }\n if (candidate.declared === true) {\n return candidate.runScope === undefined\n ? undefined\n : new KeyMaterialError({\n code: \"invalid_environment\",\n message: \"Declared environment identity must not include runScope\"\n })\n }\n return new KeyMaterialError({\n code: \"invalid_environment\",\n message: \"Environment identity requires a boolean declared field\"\n })\n}\n\nconst decodeEnvironment = (environment: EnvironmentIdentity) =>\n Effect.try({\n try: () => {\n const invalid = validateEnvironment(environment)\n if (invalid !== undefined) throw invalid\n return Schema.decodeUnknownSync(EnvironmentIdentity)(environment, { onExcessProperty: \"error\" })\n },\n catch: (cause) =>\n cause instanceof KeyMaterialError ? cause : new KeyMaterialError({\n code: \"invalid_environment\",\n message:\n \"Environment identity must contain only its declared fields, string layers, and string capability patterns\"\n })\n })\n\nconst sortStrings = (values: ReadonlyArray<string>): Array<string> =>\n [...new Set(values.map((value) => value.normalize(\"NFC\")))].sort()\n\nconst normalizeInputs = (inputs: ContentIdentity[\"inputs\"]): Record<string, unknown> =>\n Object.fromEntries(\n Object.entries(inputs).map(([name, value]) => [\n name,\n isDigestInput(value)\n ? {\n kind: \"digest\",\n digest: value.digest,\n ...(value.reference === undefined ? {} : { reference: value.reference }),\n ...(value.path === undefined ? {} : { path: value.path })\n }\n : { kind: \"literal\", value }\n ])\n )\n\nconst normalizeCapabilities = (capabilities: ContentIdentity[\"capabilities\"]): Record<string, Array<string>> =>\n Object.fromEntries(Object.entries(capabilities).map(([group, patterns]) => [group, sortStrings(patterns)]))\n\nconst normalizeEnvironment = (environment: EnvironmentIdentity) => ({\n declared: environment.declared,\n layers: environment.layers.map((layer) => layer.normalize(\"NFC\")),\n capabilities: normalizeCapabilities(environment.capabilities),\n ...(environment.runScope === undefined ? {} : { runScope: environment.runScope })\n})\n\nconst compareCodeUnits = (left: string, right: string): number => left < right ? -1 : 1\n\ntype NormalizedWriteEntry =\n | string\n | { readonly _tag: \"TreeArtifact\"; readonly path: string }\n | {\n readonly _tag: \"Glob\"\n readonly include: ReadonlyArray<string>\n readonly exclude?: ReadonlyArray<string> | undefined\n }\n\nconst writeEntryKey = (entry: NormalizedWriteEntry): string =>\n typeof entry === \"string\"\n ? JSON.stringify([\"Path\", entry])\n : entry._tag === \"TreeArtifact\"\n ? JSON.stringify([entry._tag, entry.path])\n : JSON.stringify([entry._tag, entry.include, entry.exclude ?? null])\n\nconst normalizeHermetic = (hermetic: NonNullable<ContentIdentity[\"hermetic\"]>) => {\n const readSet = [...hermetic.readSet]\n .map((entry) => ({ path: entry.path.normalize(\"NFC\"), digest: entry.digest }))\n .sort((left, right) => {\n if (left.path !== right.path) return left.path < right.path ? -1 : 1\n return left.digest < right.digest ? -1 : left.digest > right.digest ? 1 : 0\n })\n .filter((entry, index, entries) =>\n index === 0 || entry.path !== entries[index - 1]!.path || entry.digest !== entries[index - 1]!.digest\n )\n const normalizedWrites = [...hermetic.writeSet].map((entry) => {\n if (typeof entry === \"string\") return entry.normalize(\"NFC\")\n if (entry._tag === \"TreeArtifact\") return { ...entry, path: entry.path.normalize(\"NFC\") }\n return {\n ...entry,\n include: sortStrings(entry.include),\n ...(entry.exclude === undefined ? {} : { exclude: sortStrings(entry.exclude) })\n }\n })\n const writeSet = [...new Map(normalizedWrites.map((entry) => [writeEntryKey(entry), entry])).entries()]\n .sort(([left], [right]) => compareCodeUnits(left, right))\n .map(([, entry]) => entry)\n return {\n readSet,\n writeSet,\n ...(hermetic.removes === undefined ? {} : { removes: sortStrings(hermetic.removes) }),\n boundaryMode: hermetic.boundaryMode\n }\n}\n\nconst decodeKey = Schema.decodeUnknownEffect(DerivedKey)\n\n/**\n * Fingerprint for binding one execution to its runtime environment, not an\n * action dispatch key. Uses the same normalization as content/dispatch keys.\n * An omitted environment is distinct from declared-empty and run-scoped ones.\n * @since 1.0.0\n * @category constructors\n */\nexport const environmentIdentity = (\n environment?: EnvironmentIdentity\n): Effect.Effect<StoredKey, KeyMaterialError | Schema.SchemaError, Crypto.Crypto> =>\n Effect.gen(function*() {\n const captured = environment === undefined ? undefined : yield* decodeEnvironment(environment)\n return yield* decodeKey({\n kind: \"execution-environment/v1\",\n environment: captured === undefined ? null : normalizeEnvironment(captured)\n })\n })\n\n/**\n * Produces a cross-run reusable key for a sealed or hermetic step. Set-like\n * declarations are normalized before serialization; write declarations remain\n * part of the identity even when the step writes nothing.\n *\n * @since 0.1.0\n * @category constructors\n * @slop\n */\nexport const content = (\n identity: ContentIdentity\n): Effect.Effect<StepKey, KeyMaterialError | Schema.SchemaError, Crypto.Crypto> =>\n Effect.gen(function*() {\n const environment = identity.environment === undefined ? undefined : yield* decodeEnvironment(identity.environment)\n return yield* decodeKey({\n kind: \"content\",\n body: identity.body,\n inputs: normalizeInputs(identity.inputs),\n layers: sortStrings(identity.layers),\n capabilities: normalizeCapabilities(identity.capabilities),\n ...(environment === undefined ? {} : { environment: normalizeEnvironment(environment) }),\n ...(identity.hermetic === undefined ? {} : { hermetic: normalizeHermetic(identity.hermetic) })\n })\n })\n\n/**\n * Produces a run-local ordinal key for compensable, irreversible, or unsealed\n * work. These keys intentionally cannot be reused across runs.\n *\n * @since 0.1.0\n * @category constructors\n * @slop\n */\nexport const ordinal = (\n identity: OrdinalIdentity\n): Effect.Effect<StepKey, Schema.SchemaError, Crypto.Crypto> => decodeKey({ kind: \"ordinal\", ...identity })\n\n/**\n * Resolves graph-local references to dependency digests and constructs a\n * sealed content key. Use {@link planIdentity} to compile any effect tier.\n *\n * This is the handoff the Flow Builder Brief describes: the planner hands over\n * a topologically ordered sequence of `{ nodeId, material }`, and for each one\n * the compiler substitutes every `Ref`/`Pending` with the *already computed\n * key of the referenced node*. Structural node ids are lookup addresses only\n * and never enter the hashed value \u2014 rename a node and nothing re-keys; change\n * what a node consumes and everything downstream of it does.\n *\n * @since 0.1.0\n * @category constructors\n * @slop\n */\nexport const fromKeyMaterial = (\n material: KeyMaterial.KeyMaterial,\n dependencyDigests: Readonly<Record<string, string>>\n): Effect.Effect<StepKey, KeyMaterialError | Schema.SchemaError, Crypto.Crypto> => {\n if (material.kind !== \"sealed\") {\n return Effect.fail(\n new KeyMaterialError({\n code: \"non_content_material\",\n message: `Cannot create a content key for ${material.kind} material`\n })\n )\n }\n return materialIdentity(material, dependencyDigests).pipe(Effect.flatMap(content))\n}\n\n/**\n * Identifies a declaration in a plan, independently of whether its execution\n * can be cached. Sealed material retains its existing content-key format;\n * compensable and irreversible material use a distinct, tier-bearing plan\n * namespace. These fingerprints bind approvals, not cross-run result reuse.\n * Dispatch non-cacheable work under a run-local {@link ordinal} instead.\n *\n * @since 1.0.0\n * @category constructors\n */\nexport const planIdentity = (\n material: KeyMaterial.KeyMaterial,\n dependencyDigests: Readonly<Record<string, string>>\n): Effect.Effect<StepKey, KeyMaterialError | Schema.SchemaError, Crypto.Crypto> =>\n material.kind === \"sealed\" ?\n fromKeyMaterial(material, dependencyDigests) :\n materialIdentity(material, dependencyDigests).pipe(Effect.flatMap((identity) =>\n decodeKey({\n kind: \"plan-declaration\",\n tier: material.kind,\n body: identity.body,\n inputs: normalizeInputs(identity.inputs),\n layers: sortStrings(identity.layers),\n capabilities: normalizeCapabilities(identity.capabilities)\n })\n ))\n\n/**\n * Reads the entry a graph reference names. Only an own data property counts:\n * an absent name or an accessor is a missing dependency, and no getter runs.\n * `label` names what the record holds, as each refusal says it.\n *\n * @private\n */\nconst ownDataProperty = (\n record: Readonly<Record<string, unknown>>,\n from: string,\n label: \"Digest\" | \"Settled result\"\n): Effect.Effect<unknown, KeyMaterialError> => {\n const descriptor = Object.getOwnPropertyDescriptor(record, from)\n if (descriptor === undefined) {\n return Effect.fail(\n new KeyMaterialError({\n code: \"missing_dependency\",\n message: `Missing ${label.toLowerCase()} for graph dependency ${from}`\n })\n )\n }\n if (!(\"value\" in descriptor)) {\n return Effect.fail(\n new KeyMaterialError({\n code: \"missing_dependency\",\n message: `${label} for graph dependency ${from} must be a data property`\n })\n )\n }\n return Effect.succeed(descriptor.value)\n}\n\n/**\n * The positional inputs both identities hash. A `Literal` contributes its raw\n * value, so `normalizeInputs` applies the one literal wrap; `resolve` supplies\n * the digest for each `Ref` and `Pending`, tagged with the reference variant.\n *\n * @private\n */\nconst resolveInputs = <E, R>(\n material: KeyMaterial.KeyMaterial,\n resolve: (reference: Exclude<KeyMaterial.InputRef, { readonly _tag: \"Literal\" }>) => Effect.Effect<string, E, R>\n): Effect.Effect<Record<string, unknown>, E, R> =>\n Effect.gen(function*() {\n const inputs: Record<string, unknown> = {}\n for (let index = 0; index < material.inputs.length; index++) {\n const input = material.inputs[index]!\n if (input._tag === \"Literal\") {\n inputs[String(index)] = input.value\n continue\n }\n const digest = yield* resolve(input)\n inputs[String(index)] = input._tag === \"Pending\"\n ? digestInput(digest, { reference: \"pending\" })\n : input.path.length > 0\n ? digestInput(digest, { reference: \"ref-projected\", path: input.path })\n : digestInput(digest, { reference: \"ref\" })\n }\n return inputs\n })\n\n/** Resolves plan references once for both content and non-cacheable declarations. */\nconst materialIdentity = (\n material: KeyMaterial.KeyMaterial,\n dependencyDigests: Readonly<Record<string, string>>\n): Effect.Effect<ContentIdentity, KeyMaterialError> =>\n resolveInputs(\n material,\n (reference) =>\n Effect.flatMap(ownDataProperty(dependencyDigests, reference.from, \"Digest\"), (digest) =>\n typeof digest === \"string\" ? Effect.succeed(digest) : Effect.fail(\n new KeyMaterialError({\n code: \"missing_dependency\",\n message: `Digest for graph dependency ${reference.from} must be a string`\n })\n ))\n ).pipe(Effect.map((inputs) => ({\n body: materialBody(material),\n inputs,\n layers: material.layers,\n capabilities: { declared: material.capabilities }\n })))\n\n/**\n * The node's own declaration, hashed. Shared by {@link planIdentity},\n * {@link fromKeyMaterial}, and {@link dispatchIdentity} so derivations cannot drift about what\n * \"the node itself\" means.\n *\n * @private\n */\nconst materialBody = (material: KeyMaterial.KeyMaterial) => ({\n version: material.version,\n declaration: material.body,\n ...(material.nondeterministic === undefined ? {} : { nondeterministic: material.nondeterministic }),\n ...(material.effects === undefined ? {} : { effects: material.effects }),\n ...(material.placement === undefined ? {} : { placement: material.placement })\n})\n\n/**\n * The digest a `Pending` input contributes to a dispatch key. It is a\n * constant, and that is the point: `Pending` is an ordering reference, and\n * ordering does not change what a node consumes, so it must not change what\n * the node caches under. The string is self-describing, so a reader of a\n * hashed payload can see that no upstream value was folded.\n *\n * @private\n */\nconst orderingOnly = \"ordering-only\"\n\n/**\n * Projects a settled result along a `Ref` path. Only own data properties\n * resolve, so a missing, inherited, or accessor segment yields `undefined`\n * without invoking a getter. This is a stable, distinct value: a projection that\n * walks off the end of a result is a fact about the graph, not a failure.\n * `undefined` drops out of the canonical form, so it hashes distinctly from\n * every JSON value including `null`.\n *\n * Exported because it is the ONE projection semantics for the value channel:\n * {@link dispatchIdentity} digests what this returns, so every consumer that\n * resolves a `Ref` at execution time must resolve it the same way, or two\n * inputs that key identically could be consumed differently \u2014 a stale-hit\n * vector.\n *\n * @since 0.1.0\n * @category utilities\n * @slop\n */\nexport const project = (value: unknown, path: ReadonlyArray<string>): unknown => {\n let current = value\n for (const segment of path) {\n if (typeof current !== \"object\" || current === null) return undefined\n const descriptor = Object.getOwnPropertyDescriptor(current, segment)\n if (descriptor === undefined || !(\"value\" in descriptor)) return undefined\n current = descriptor.value\n }\n return current\n}\n\n/**\n * The key a dispatch is *cached* under, as distinct from the plan key a node\n * is *identified* by.\n *\n * A plan key folds the resolved keys of every upstream node, transitively, so\n * an edit anywhere upstream re-keys everything below it \u2014 even when the edited\n * node's output value is byte for byte what it was before. Bazel does not work\n * that way: `ActionCacheChecker` keys an action by the *content* of its\n * inputs, so an unchanged output stops invalidation dead. The file channel in\n * flows already had that property through the measured boundary digests; the\n * JSON value channel did not, and this is what gives it one.\n *\n * The derivation folds the node's OWN material \u2014 body, version, effects,\n * placement, layers, capabilities \u2014 and never an upstream key. Each material\n * input contributes content instead: a `Literal` its value, a `Ref` the digest\n * of the settled result of `from` projected along `path`, and a `Pending`\n * nothing beyond its tag. The measured hermetic boundary is folded unchanged.\n *\n * `results` must hold every dependency the material names. The scheduler's\n * halt rule guarantees it: a dependent of failed or skipped work never\n * dispatches, so a `Ref` always resolves against a success.\n *\n * @since 0.1.0\n * @category constructors\n * @slop\n */\nexport const dispatchIdentity = (options: {\n readonly material: KeyMaterial.KeyMaterial\n /** The settled output value of each dependency, by node id. */\n readonly results: Readonly<Record<string, unknown>>\n readonly hermetic: NonNullable<ContentIdentity[\"hermetic\"]>\n /** The engine-resolved execution environment this dispatch runs under. */\n readonly environment?: EnvironmentIdentity | undefined\n /** Reuses projected-value digests while the corresponding settled values remain immutable. */\n readonly digestMemo?: DigestMemo | undefined\n}): Effect.Effect<StepKey, KeyMaterialError | Schema.SchemaError, Crypto.Crypto> =>\n Effect.gen(function*() {\n const material = options.material\n if (material.kind !== \"sealed\") {\n return yield* new KeyMaterialError({\n code: \"non_content_material\",\n message: `Cannot create a dispatch key for ${material.kind} material`\n })\n }\n const environment = options.environment === undefined ? undefined : yield* decodeEnvironment(options.environment)\n const inputs = yield* resolveInputs(material, (reference) =>\n reference._tag === \"Pending\"\n ? Effect.succeed(orderingOnly)\n : Effect.flatMap(ownDataProperty(options.results, reference.from, \"Settled result\"), (settled) => {\n const compute = decodeKey({ kind: \"input-value\", value: project(settled, reference.path) })\n return options.digestMemo === undefined\n ? compute\n : options.digestMemo.digest(reference.from, reference.path, compute)\n }))\n return yield* content({\n body: materialBody(material),\n inputs,\n layers: material.layers,\n capabilities: { declared: material.capabilities },\n environment,\n hermetic: options.hermetic\n })\n })\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -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.ts";
23
+ /**
24
+ * @since 1.0.0-rc.0
25
+ * @category models
26
+ */
27
+ export * as CachePolicy from "./CachePolicy.ts";
28
+ /**
29
+ * @since 1.0.0-rc.0
30
+ * @category models
31
+ */
32
+ export * as Repetition from "./Repetition.ts";
33
+ /**
34
+ * @since 0.1.0
35
+ * @category models
36
+ * @slop
37
+ */
38
+ export * as KeyMaterial from "./KeyMaterial.ts";
39
+ /**
40
+ * @since 0.1.0
41
+ * @category models
42
+ * @slop
43
+ */
44
+ export * as FileSet from "./FileSet.ts";
45
+ /**
46
+ * @since 0.1.0
47
+ * @category models
48
+ * @slop
49
+ */
50
+ export * as Effects from "./Effects.ts";
51
+ /**
52
+ * @since 0.1.0
53
+ * @category models
54
+ * @slop
55
+ */
56
+ export * as Node from "./Node.ts";
57
+ /**
58
+ * @since 0.1.0
59
+ * @category models
60
+ * @slop
61
+ */
62
+ export * as Plan from "./Plan.ts";
63
+ /**
64
+ * @since 0.1.0
65
+ * @category constructors
66
+ * @slop
67
+ */
68
+ export * as PlanDiff from "./PlanDiff.ts";
69
+ /**
70
+ * @since 1.0.0
71
+ * @category models
72
+ */
73
+ export * as Placement from "./Placement.ts";
74
+ /**
75
+ * @since 0.1.0
76
+ * @category models
77
+ * @slop
78
+ */
79
+ export * as Planned from "./Planned.ts";
80
+ /**
81
+ * @since 0.1.0
82
+ * @category constructors
83
+ * @slop
84
+ */
85
+ export * as StepKey from "./StepKey.ts";
86
+ /**
87
+ * @since 1.0.0
88
+ * @category constructors
89
+ */
90
+ export * as Scheduling from "./Scheduling.ts";
91
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","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"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var index_exports = {};
30
+ __export(index_exports, {
31
+ CachePolicy: () => CachePolicy,
32
+ Effects: () => Effects,
33
+ FileSet: () => FileSet,
34
+ GraphBuildError: () => GraphBuildError,
35
+ KeyMaterial: () => KeyMaterial,
36
+ Node: () => Node,
37
+ Placement: () => Placement,
38
+ Plan: () => Plan,
39
+ PlanDiff: () => PlanDiff,
40
+ Planned: () => Planned,
41
+ Repetition: () => Repetition,
42
+ Scheduling: () => Scheduling,
43
+ StepKey: () => StepKey
44
+ });
45
+ module.exports = __toCommonJS(index_exports);
46
+ var GraphBuildError = __toESM(require("./GraphBuildError.js"), 1);
47
+ var CachePolicy = __toESM(require("./CachePolicy.js"), 1);
48
+ var Repetition = __toESM(require("./Repetition.js"), 1);
49
+ var KeyMaterial = __toESM(require("./KeyMaterial.js"), 1);
50
+ var FileSet = __toESM(require("./FileSet.js"), 1);
51
+ var Effects = __toESM(require("./Effects.js"), 1);
52
+ var Node = __toESM(require("./Node.js"), 1);
53
+ var Plan = __toESM(require("./Plan.js"), 1);
54
+ var PlanDiff = __toESM(require("./PlanDiff.js"), 1);
55
+ var Placement = __toESM(require("./Placement.js"), 1);
56
+ var Planned = __toESM(require("./Planned.js"), 1);
57
+ var StepKey = __toESM(require("./StepKey.js"), 1);
58
+ var Scheduling = __toESM(require("./Scheduling.js"), 1);
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/index.ts"],
4
+ "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"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBA,sBAAiC;AAMjC,kBAA6B;AAM7B,iBAA4B;AAO5B,kBAA6B;AAO7B,cAAyB;AAOzB,cAAyB;AAOzB,WAAsB;AAOtB,WAAsB;AAOtB,eAA0B;AAM1B,gBAA2B;AAO3B,cAAyB;AAOzB,cAAyB;AAMzB,iBAA4B;",
6
+ "names": []
7
+ }
@@ -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,247 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var ConflictAnnotation_exports = {};
30
+ __export(ConflictAnnotation_exports, {
31
+ annotate: () => annotate
32
+ });
33
+ module.exports = __toCommonJS(ConflictAnnotation_exports);
34
+ var Effect = __toESM(require("effect/Effect"), 1);
35
+ var FileSet = __toESM(require("../FileSet.js"), 1);
36
+ var KeyMaterial = __toESM(require("../KeyMaterial.js"), 1);
37
+ var EffectCandidates = __toESM(require("./EffectCandidates.js"), 1);
38
+ const refuse = (code, message) => Effect.fail({ code, message });
39
+ const pairStrategy = (left, right) => left === "fail" || right === "fail" ? "fail" : left === "lane" || right === "lane" ? "lane" : "serialize";
40
+ const pairRuntime = (left, right) => left === "stop-merge" || right === "stop-merge" ? "stop-merge" : "delay-rebase";
41
+ const producedPaths = (effects) => [
42
+ ...FileSet.expand(effects.writes),
43
+ ...effects.removes ?? []
44
+ ];
45
+ const describe = (entry) => typeof entry === "string" ? entry : entry._tag === "TreeArtifact" ? entry.path : entry.include.join(",");
46
+ const overlap = (left, right) => left.flatMap(
47
+ (leftEntry) => right.some((rightEntry) => FileSet.overlaps(leftEntry, rightEntry)) ? [describe(leftEntry)] : []
48
+ );
49
+ const readOverlap = (reads, produced) => reads.flatMap((entry) => produced.some((output) => FileSet.overlaps(entry, output)) ? [describe(entry)] : []);
50
+ const annotate = (nodes, frozen, replayGenerations) => Effect.gen(function* () {
51
+ const expanded = new Map(nodes.map((node) => [node.id, {
52
+ produced: producedPaths(node.effects),
53
+ reads: FileSet.expandReads(node.effects.reads)
54
+ }]));
55
+ const candidates = EffectCandidates.make(nodes.map((node) => expanded.get(node.id).produced));
56
+ const conflicts = /* @__PURE__ */ new Map();
57
+ const ordering = /* @__PURE__ */ new Map();
58
+ const edges = new Map(nodes.map((node) => [node.id, new Set(node.dependsOn)]));
59
+ const declaredEdges = new Map(nodes.map((node) => [node.id, KeyMaterial.dependencies(node.material)]));
60
+ const ordinals = new Map(nodes.map((node, index) => [node.id, index]));
61
+ const dependents = new Map(nodes.map((node) => [node.id, /* @__PURE__ */ new Set()]));
62
+ for (const node of nodes) {
63
+ for (const dependency of node.dependsOn) dependents.get(dependency).add(node.id);
64
+ }
65
+ const words = Math.ceil(nodes.length / 32);
66
+ const ancestors = /* @__PURE__ */ new Map();
67
+ let work = 0;
68
+ let pairs = 0;
69
+ let nextYield = 2048;
70
+ const checkpoint = Effect.gen(function* () {
71
+ if (work > 1e7 || pairs > 25e4) {
72
+ return yield* refuse(
73
+ "graph_too_large",
74
+ "Plan effect analysis exceeds its work budget (10000000 work units or 250000 candidate pairs)"
75
+ );
76
+ }
77
+ nextYield = Math.min(work + 2048, 10000001);
78
+ yield* Effect.yieldNow;
79
+ });
80
+ const closure = (id) => Effect.gen(function* () {
81
+ const cached = ancestors.get(id);
82
+ if (cached !== void 0) return cached;
83
+ const frame = (id2) => {
84
+ const bits = new Uint32Array(words);
85
+ const ordinal = ordinals.get(id2);
86
+ bits[ordinal >>> 5] |= 1 << (ordinal & 31);
87
+ return { id: id2, bits, dependencies: Array.from(edges.get(id2)), next: 0 };
88
+ };
89
+ const stack = [frame(id)];
90
+ while (stack.length > 0) {
91
+ const current = stack[stack.length - 1];
92
+ const dependency = current.dependencies[current.next];
93
+ if (dependency === void 0) {
94
+ ancestors.set(current.id, current.bits);
95
+ stack.pop();
96
+ continue;
97
+ }
98
+ const bits = ancestors.get(dependency);
99
+ if (bits === void 0) {
100
+ stack.push(frame(dependency));
101
+ continue;
102
+ }
103
+ for (let word = 0; word < words; word++) {
104
+ current.bits[word] |= bits[word];
105
+ if (++work >= nextYield) yield* checkpoint;
106
+ }
107
+ current.next++;
108
+ }
109
+ return ancestors.get(id);
110
+ });
111
+ const reaches = (from, to) => Effect.gen(function* () {
112
+ const bits = yield* closure(from);
113
+ const ordinal = ordinals.get(to);
114
+ return (bits[ordinal >>> 5] & 1 << (ordinal & 31)) !== 0;
115
+ });
116
+ const addEdge = (from, to) => Effect.gen(function* () {
117
+ const source = yield* closure(from);
118
+ const target = yield* closure(to);
119
+ for (let word = 0; word < words; word++) {
120
+ source[word] |= target[word];
121
+ if (++work >= nextYield) yield* checkpoint;
122
+ }
123
+ const stack = [from];
124
+ while (stack.length > 0) {
125
+ for (const dependent of dependents.get(stack.pop())) {
126
+ if (++work >= nextYield) yield* checkpoint;
127
+ if (!ancestors.delete(dependent)) continue;
128
+ stack.push(dependent);
129
+ }
130
+ }
131
+ edges.get(from).add(to);
132
+ dependents.get(to).add(from);
133
+ });
134
+ const route = (from, to, graph = edges) => Effect.gen(function* () {
135
+ const arrivedFrom = /* @__PURE__ */ new Map();
136
+ const seen = /* @__PURE__ */ new Set([from]);
137
+ const stack = [from];
138
+ while (stack.length > 0) {
139
+ const current = stack.pop();
140
+ if (current === to) {
141
+ const chain = [current];
142
+ for (let step = arrivedFrom.get(current); step !== void 0; step = arrivedFrom.get(step)) {
143
+ chain.push(step);
144
+ }
145
+ return chain.reverse();
146
+ }
147
+ for (const next of graph.get(current)) {
148
+ if (++work >= nextYield) yield* checkpoint;
149
+ if (seen.has(next)) continue;
150
+ seen.add(next);
151
+ arrivedFrom.set(next, current);
152
+ stack.push(next);
153
+ }
154
+ }
155
+ return void 0;
156
+ });
157
+ const ranges = [];
158
+ let start = frozen;
159
+ for (let end = start + 1; end <= nodes.length; end++) {
160
+ if (end === nodes.length || replayGenerations && nodes[end].generation !== nodes[start].generation) {
161
+ ranges.push({ start, end });
162
+ start = end;
163
+ }
164
+ }
165
+ for (const { start: start2, end } of ranges) {
166
+ for (let index = start2; index < end; index++) {
167
+ const later = nodes[index];
168
+ const laterProduced = expanded.get(later.id).produced;
169
+ if (laterProduced.length === 0) continue;
170
+ const matches = candidates(laterProduced);
171
+ work += matches.length + laterProduced.length;
172
+ if (work >= nextYield) yield* checkpoint;
173
+ for (const before of matches) {
174
+ if (before >= index) break;
175
+ pairs++;
176
+ work += 32;
177
+ if (work >= nextYield || pairs > 25e4) yield* checkpoint;
178
+ const earlier = nodes[before];
179
+ const earlierProduced = expanded.get(earlier.id).produced;
180
+ work += earlierProduced.length * laterProduced.length;
181
+ if (work >= nextYield) yield* checkpoint;
182
+ const paths = overlap(earlierProduced, laterProduced);
183
+ if (paths.length === 0) continue;
184
+ if (yield* reaches(later.id, earlier.id)) continue;
185
+ const strategy = pairStrategy(earlier.strategy, later.strategy);
186
+ const runtime = pairRuntime(earlier.runtime, later.runtime);
187
+ if (strategy === "fail") {
188
+ return yield* refuse(
189
+ "overlap_forbidden",
190
+ `Nodes ${earlier.id} and ${later.id} both write ${paths.join(", ")}`
191
+ );
192
+ }
193
+ const annotation = (other) => ({ with: other, paths, strategy, runtime });
194
+ if (before >= start2) {
195
+ const previous2 = conflicts.get(earlier.id) ?? [];
196
+ previous2.push(annotation(later.id));
197
+ conflicts.set(earlier.id, previous2);
198
+ }
199
+ const previous = conflicts.get(later.id) ?? [];
200
+ previous.push(annotation(earlier.id));
201
+ conflicts.set(later.id, previous);
202
+ if (strategy === "serialize") {
203
+ const added = ordering.get(later.id) ?? [];
204
+ added.push(earlier.id);
205
+ ordering.set(later.id, added);
206
+ yield* addEdge(later.id, earlier.id);
207
+ }
208
+ }
209
+ }
210
+ for (let index = start2; index < end; index++) {
211
+ const reader = nodes[index];
212
+ const reads = expanded.get(reader.id).reads;
213
+ if (reads.length === 0) continue;
214
+ const matches = candidates(reads);
215
+ work += matches.length + reads.length;
216
+ if (work >= nextYield) yield* checkpoint;
217
+ for (const writerIndex of matches) {
218
+ if (writerIndex >= end) break;
219
+ pairs++;
220
+ work += 32;
221
+ if (work >= nextYield || pairs > 25e4) yield* checkpoint;
222
+ const writer = nodes[writerIndex];
223
+ if (writer.id === reader.id) continue;
224
+ const produced = expanded.get(writer.id).produced;
225
+ work += reads.length * produced.length;
226
+ if (work >= nextYield) yield* checkpoint;
227
+ const paths = readOverlap(reads, produced);
228
+ if (paths.length === 0) continue;
229
+ if (yield* reaches(reader.id, writer.id)) continue;
230
+ if (yield* reaches(writer.id, reader.id)) {
231
+ if ((yield* route(writer.id, reader.id, declaredEdges)) !== void 0) continue;
232
+ const contradiction = yield* route(writer.id, reader.id);
233
+ return yield* refuse(
234
+ "cycle",
235
+ `Plan cycle: node ${reader.id} reads ${paths.join(", ")}, which node ${writer.id} produces, so ${reader.id} must follow ${writer.id}, but ${writer.id} already depends on ${reader.id} through ${contradiction.join(" -> ")}`
236
+ );
237
+ }
238
+ const added = ordering.get(reader.id) ?? [];
239
+ added.push(writer.id);
240
+ ordering.set(reader.id, added);
241
+ yield* addEdge(reader.id, writer.id);
242
+ }
243
+ }
244
+ }
245
+ return { conflicts, ordering };
246
+ });
247
+ //# sourceMappingURL=ConflictAnnotation.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/internal/ConflictAnnotation.ts"],
4
+ "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 \u2014 a flow that promised\n * disjointness must not be quietly serialized \u2014 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 \u2014 a reader that runs before it sees different bytes than one that\n * runs after \u2014 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 \u2014 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 \u2014 because the\n // dispatch key honestly folds the digest it measured \u2014 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 \u2014 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"],
5
+ "mappings": "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",
6
+ "names": ["id", "start", "previous"]
7
+ }