@smthrs/plan 1.0.0-rc.8 → 1.0.0-rc.9

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 (59) hide show
  1. package/dist/cjs/Effects.d.ts +29 -11
  2. package/dist/cjs/Effects.d.ts.map +1 -1
  3. package/dist/cjs/Effects.js.map +2 -2
  4. package/dist/cjs/FileSet.d.ts +121 -51
  5. package/dist/cjs/FileSet.d.ts.map +1 -1
  6. package/dist/cjs/FileSet.js.map +2 -2
  7. package/dist/cjs/GraphBuildError.d.ts +2 -4
  8. package/dist/cjs/GraphBuildError.d.ts.map +1 -1
  9. package/dist/cjs/GraphBuildError.js.map +2 -2
  10. package/dist/cjs/KeyMaterial.d.ts +13 -8
  11. package/dist/cjs/KeyMaterial.d.ts.map +1 -1
  12. package/dist/cjs/KeyMaterial.js.map +2 -2
  13. package/dist/cjs/Plan.d.ts +72 -23
  14. package/dist/cjs/Plan.d.ts.map +1 -1
  15. package/dist/cjs/Plan.js +3 -0
  16. package/dist/cjs/Plan.js.map +2 -2
  17. package/dist/cjs/PlanDiff.d.ts +7 -3
  18. package/dist/cjs/PlanDiff.d.ts.map +1 -1
  19. package/dist/cjs/PlanDiff.js.map +2 -2
  20. package/dist/cjs/StepKey.d.ts +38 -17
  21. package/dist/cjs/StepKey.d.ts.map +1 -1
  22. package/dist/cjs/StepKey.js.map +2 -2
  23. package/dist/esm/Effects.d.ts +29 -11
  24. package/dist/esm/Effects.d.ts.map +1 -1
  25. package/dist/esm/Effects.js +18 -7
  26. package/dist/esm/Effects.js.map +1 -1
  27. package/dist/esm/FileSet.d.ts +121 -51
  28. package/dist/esm/FileSet.d.ts.map +1 -1
  29. package/dist/esm/FileSet.js +97 -35
  30. package/dist/esm/FileSet.js.map +1 -1
  31. package/dist/esm/GraphBuildError.d.ts +2 -4
  32. package/dist/esm/GraphBuildError.d.ts.map +1 -1
  33. package/dist/esm/GraphBuildError.js +2 -3
  34. package/dist/esm/GraphBuildError.js.map +1 -1
  35. package/dist/esm/KeyMaterial.d.ts +13 -8
  36. package/dist/esm/KeyMaterial.d.ts.map +1 -1
  37. package/dist/esm/KeyMaterial.js +11 -4
  38. package/dist/esm/KeyMaterial.js.map +1 -1
  39. package/dist/esm/Plan.d.ts +72 -23
  40. package/dist/esm/Plan.d.ts.map +1 -1
  41. package/dist/esm/Plan.js +61 -16
  42. package/dist/esm/Plan.js.map +1 -1
  43. package/dist/esm/PlanDiff.d.ts +7 -3
  44. package/dist/esm/PlanDiff.d.ts.map +1 -1
  45. package/dist/esm/PlanDiff.js +0 -1
  46. package/dist/esm/PlanDiff.js.map +1 -1
  47. package/dist/esm/StepKey.d.ts +38 -17
  48. package/dist/esm/StepKey.d.ts.map +1 -1
  49. package/dist/esm/StepKey.js +33 -11
  50. package/dist/esm/StepKey.js.map +1 -1
  51. package/docs/guides/diff-two-plans.md +78 -1
  52. package/package.json +4 -4
  53. package/src/Effects.ts +29 -11
  54. package/src/FileSet.ts +121 -51
  55. package/src/GraphBuildError.ts +2 -4
  56. package/src/KeyMaterial.ts +13 -8
  57. package/src/Plan.ts +66 -23
  58. package/src/PlanDiff.ts +7 -3
  59. package/src/StepKey.ts +38 -17
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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://github.com/smithersai/smithers/blob/main/packages/smithers/flows/plan/docs/concepts/step-keys.md.\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 // Registering the entry and installing its exit handler run uninterruptibly,\n // so an interrupt can never land between them and strand a Deferred that no\n // fiber will complete. The wait and the computation stay interruptible.\n const digest: DigestMemo[\"digest\"] = (from, path, compute) =>\n Effect.uninterruptibleMask((restore) =>\n Effect.suspend(() => {\n const address = JSON.stringify([from, path])\n const existing = entries.get(address)\n if (existing !== undefined) {\n return restore(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 restore(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 )\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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",
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://github.com/smithersai/smithers/blob/main/packages/smithers/flows/plan/docs/concepts/step-keys.md.\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 */\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 */\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 * @param digest - The precomputed digest.\n * @param reference - Which graph reference resolved to the digest, and the\n * projection path for `ref-projected`. Omit for a plain digest.\n * @returns The tagged digest input.\n * @since 0.1.0\n * @category constructors\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}. A plain object\n * shaped like `{ digest: \"...\" }` is not one.\n *\n * @param value - Any value.\n * @returns `true` only for a value carrying the private digest-input brand.\n * @since 0.1.0\n * @category guards\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 */\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 * `digest(from, path, compute)` runs `compute` once per address and shares its\n * result with concurrent callers. A failed or interrupted computation is not\n * kept: the next caller recomputes, and a waiter whose leader was interrupted\n * becomes the new leader instead of inheriting the interrupt.\n *\n * @since 0.1.0\n * @category models\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 * @returns A memo with no entries.\n * @since 0.1.0\n * @category constructors\n */\nexport const makeDigestMemo = (): DigestMemo => {\n const entries = new Map<string, Deferred.Deferred<StepKey, Schema.SchemaError>>()\n // Registering the entry and installing its exit handler run uninterruptibly,\n // so an interrupt can never land between them and strand a Deferred that no\n // fiber will complete. The wait and the computation stay interruptible.\n const digest: DigestMemo[\"digest\"] = (from, path, compute) =>\n Effect.uninterruptibleMask((restore) =>\n Effect.suspend(() => {\n const address = JSON.stringify([from, path])\n const existing = entries.get(address)\n if (existing !== undefined) {\n return restore(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 restore(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 )\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 */\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 */\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 deriving a key. `code` names the rule:\n *\n * - `invalid_environment`: an {@link EnvironmentIdentity} that is neither\n * declared nor run-scoped.\n * - `missing_dependency`: a `Ref` or `Pending` input names a node with no\n * recorded digest, or, at dispatch, a `Ref` names one with no settled\n * result.\n * - `non_content_material`: a content-key constructor received material whose\n * tier is not `sealed`.\n *\n * @since 0.1.0\n * @category errors\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 * @param identity - The step's content material.\n * @returns The content key. Fails with `invalid_environment` for a malformed\n * environment, or `SchemaError` when the material cannot be serialized.\n * @since 0.1.0\n * @category constructors\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 * @param identity - The run id, optional parent scope, ordinal, and tier.\n * @returns The ordinal key.\n * @since 0.1.0\n * @category constructors\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 * @param material - The node's key material; its tier must be `sealed`.\n * @param dependencyDigests - The key of every node the material references,\n * by node id.\n * @returns The content key. Fails with `non_content_material` for a\n * non-sealed tier and `missing_dependency` for an unresolved reference.\n * @since 0.1.0\n * @category constructors\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 */\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 * @param options - The material, the dependencies' settled results, the\n * measured hermetic boundary, and an optional environment and memo.\n * @returns The dispatch key. Fails with `non_content_material` for a\n * non-sealed tier, `missing_dependency` for a `Ref` absent from `results`,\n * or `invalid_environment` for a malformed environment.\n * @since 0.1.0\n * @category constructors\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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6BA,kBAA2C;AAC3C,YAAuB;AAEvB,eAA0B;AAC1B,aAAwB;AACxB,WAAsB;AACtB,aAAwB;AAwBxB,MAAM,oBAAmC,uBAAO,IAAI,kCAAkC;AA0C/E,MAAM,cAAc,CACzB,QACA,eAIiB;AAAA,EACjB,CAAC,iBAAiB,GAAG;AAAA,EACrB;AAAA,EACA,GAAI,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,UAAU,UAAU;AAAA,EACpE,GAAI,WAAW,SAAS,SAAY,CAAC,IAAI,EAAE,MAAM,UAAU,KAAK;AAClE;AAWO,MAAM,gBAAgB,CAAC,UAC5B,OAAO,UAAU,YAAY,UAAU,QAAQ,qBAAqB,SACnE,MAAuC,iBAAiB,MAAM;AAmC1D,MAAM,sBAAsB,OAAO,MAAM;AAAA,EAC9C,OAAO,OAAO;AAAA,IACZ,UAAU,OAAO,QAAQ,IAAI;AAAA,IAC7B,QAAQ,OAAO,MAAM,OAAO,MAAM;AAAA,IAClC,cAAc,OAAO,OAAO,OAAO,QAAQ,OAAO,MAAM,OAAO,MAAM,CAAC;AAAA,IACtE,UAAU,OAAO,YAAY,OAAO,SAAS;AAAA,EAC/C,CAAC;AAAA,EACD,OAAO,OAAO;AAAA,IACZ,UAAU,OAAO,QAAQ,KAAK;AAAA,IAC9B,QAAQ,OAAO,MAAM,OAAO,MAAM;AAAA,IAClC,cAAc,OAAO,OAAO,OAAO,QAAQ,OAAO,MAAM,OAAO,MAAM,CAAC;AAAA,IACtE,UAAU,OAAO;AAAA,EACnB,CAAC;AACH,CAAC;AAmCM,MAAM,iBAAiB,MAAkB;AAC9C,QAAM,UAAU,oBAAI,IAA4D;AAIhF,QAAM,SAA+B,CAAC,MAAM,MAAM,YAChD,OAAO;AAAA,IAAoB,CAAC,YAC1B,OAAO,QAAQ,MAAM;AACnB,YAAM,UAAU,KAAK,UAAU,CAAC,MAAM,IAAI,CAAC;AAC3C,YAAM,WAAW,QAAQ,IAAI,OAAO;AACpC,UAAI,aAAa,QAAW;AAC1B,eAAO,QAAQ,OAAO,QAAQ,OAAO,KAAK,SAAS,MAAM,QAAQ,CAAC,GAAG,CAAC,SAAS;AAC7E,cAAI,KAAK,UAAU,IAAI,EAAG,QAAO,OAAO,QAAQ,KAAK,KAAK;AAG1D,iBAAO,MAAM,cAAc,KAAK,KAAK,IAAI,OAAO,MAAM,MAAM,OAAO,IAAI,OAAO,UAAU,KAAK,KAAK;AAAA,QACpG,CAAC,CAAC;AAAA,MACJ;AACA,YAAM,UAAU,SAAS,WAAwC;AACjE,cAAQ,IAAI,SAAS,OAAO;AAC5B,aAAO,QAAQ,OAAO,EAAE;AAAA,QACtB,OAAO;AAAA,UAAO,CAAC,SACb,OAAO,IAAI,aAAY;AACrB,gBAAI,KAAK,UAAU,IAAI,KAAK,QAAQ,IAAI,OAAO,MAAM,SAAS;AAC5D,sBAAQ,OAAO,OAAO;AAAA,YACxB;AACA,mBAAO,SAAS,KAAK,SAAS,IAAI;AAAA,UACpC,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF,SAAO,EAAE,OAAO;AAClB;AAiDO,MAAM,yBAAyB,OAAO,YAA8B,EAAE,iCAAiC;AAAA,EAC5G,MAAM,OAAO,SAAS,CAAC,uBAAuB,sBAAsB,sBAAsB,CAAC;AAAA,EAC3F,SAAS,OAAO;AAClB,CAAC,EAAE;AAAC;AAEJ,MAAM,sBAAsB,CAAC,gBAAmE;AAC9F,QAAM,YAAY;AAClB,MAAI,UAAU,aAAa,OAAO;AAChC,QAAI,OAAO,UAAU,aAAa,YAAY,UAAU,SAAS,WAAW,GAAG;AAC7E,aAAO,IAAI,iBAAiB;AAAA,QAC1B,MAAM;AAAA,QACN,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACA,MAAI,UAAU,aAAa,MAAM;AAC/B,WAAO,UAAU,aAAa,SAC1B,SACA,IAAI,iBAAiB;AAAA,MACrB,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACL;AACA,SAAO,IAAI,iBAAiB;AAAA,IAC1B,MAAM;AAAA,IACN,SAAS;AAAA,EACX,CAAC;AACH;AAEA,MAAM,oBAAoB,CAAC,gBACzB,OAAO,IAAI;AAAA,EACT,KAAK,MAAM;AACT,UAAM,UAAU,oBAAoB,WAAW;AAC/C,QAAI,YAAY,OAAW,OAAM;AACjC,WAAO,OAAO,kBAAkB,mBAAmB,EAAE,aAAa,EAAE,kBAAkB,QAAQ,CAAC;AAAA,EACjG;AAAA,EACA,OAAO,CAAC,UACN,iBAAiB,mBAAmB,QAAQ,IAAI,iBAAiB;AAAA,IAC/D,MAAM;AAAA,IACN,SACE;AAAA,EACJ,CAAC;AACL,CAAC;AAEH,MAAM,cAAc,CAAC,WACnB,CAAC,GAAG,IAAI,IAAI,OAAO,IAAI,CAAC,UAAU,MAAM,UAAU,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK;AAEnE,MAAM,kBAAkB,CAAC,WACvB,OAAO;AAAA,EACL,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;AAAA,IAC5C;AAAA,IACA,cAAc,KAAK,IACf;AAAA,MACA,MAAM;AAAA,MACN,QAAQ,MAAM;AAAA,MACd,GAAI,MAAM,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,MAAM,UAAU;AAAA,MACtE,GAAI,MAAM,SAAS,SAAY,CAAC,IAAI,EAAE,MAAM,MAAM,KAAK;AAAA,IACzD,IACE,EAAE,MAAM,WAAW,MAAM;AAAA,EAC/B,CAAC;AACH;AAEF,MAAM,wBAAwB,CAAC,iBAC7B,OAAO,YAAY,OAAO,QAAQ,YAAY,EAAE,IAAI,CAAC,CAAC,OAAO,QAAQ,MAAM,CAAC,OAAO,YAAY,QAAQ,CAAC,CAAC,CAAC;AAE5G,MAAM,uBAAuB,CAAC,iBAAsC;AAAA,EAClE,UAAU,YAAY;AAAA,EACtB,QAAQ,YAAY,OAAO,IAAI,CAAC,UAAU,MAAM,UAAU,KAAK,CAAC;AAAA,EAChE,cAAc,sBAAsB,YAAY,YAAY;AAAA,EAC5D,GAAI,YAAY,aAAa,SAAY,CAAC,IAAI,EAAE,UAAU,YAAY,SAAS;AACjF;AAEA,MAAM,mBAAmB,CAAC,MAAc,UAA0B,OAAO,QAAQ,KAAK;AAWtF,MAAM,gBAAgB,CAAC,UACrB,OAAO,UAAU,WACb,KAAK,UAAU,CAAC,QAAQ,KAAK,CAAC,IAC9B,MAAM,SAAS,iBACf,KAAK,UAAU,CAAC,MAAM,MAAM,MAAM,IAAI,CAAC,IACvC,KAAK,UAAU,CAAC,MAAM,MAAM,MAAM,SAAS,MAAM,WAAW,IAAI,CAAC;AAEvE,MAAM,oBAAoB,CAAC,aAAuD;AAChF,QAAM,UAAU,CAAC,GAAG,SAAS,OAAO,EACjC,IAAI,CAAC,WAAW,EAAE,MAAM,MAAM,KAAK,UAAU,KAAK,GAAG,QAAQ,MAAM,OAAO,EAAE,EAC5E,KAAK,CAAC,MAAM,UAAU;AACrB,QAAI,KAAK,SAAS,MAAM,KAAM,QAAO,KAAK,OAAO,MAAM,OAAO,KAAK;AACnE,WAAO,KAAK,SAAS,MAAM,SAAS,KAAK,KAAK,SAAS,MAAM,SAAS,IAAI;AAAA,EAC5E,CAAC,EACA;AAAA,IAAO,CAAC,OAAO,OAAO,YACrB,UAAU,KAAK,MAAM,SAAS,QAAQ,QAAQ,CAAC,EAAG,QAAQ,MAAM,WAAW,QAAQ,QAAQ,CAAC,EAAG;AAAA,EACjG;AACF,QAAM,mBAAmB,CAAC,GAAG,SAAS,QAAQ,EAAE,IAAI,CAAC,UAAU;AAC7D,QAAI,OAAO,UAAU,SAAU,QAAO,MAAM,UAAU,KAAK;AAC3D,QAAI,MAAM,SAAS,eAAgB,QAAO,EAAE,GAAG,OAAO,MAAM,MAAM,KAAK,UAAU,KAAK,EAAE;AACxF,WAAO;AAAA,MACL,GAAG;AAAA,MACH,SAAS,YAAY,MAAM,OAAO;AAAA,MAClC,GAAI,MAAM,YAAY,SAAY,CAAC,IAAI,EAAE,SAAS,YAAY,MAAM,OAAO,EAAE;AAAA,IAC/E;AAAA,EACF,CAAC;AACD,QAAM,WAAW,CAAC,GAAG,IAAI,IAAI,iBAAiB,IAAI,CAAC,UAAU,CAAC,cAAc,KAAK,GAAG,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,EACnG,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,MAAM,iBAAiB,MAAM,KAAK,CAAC,EACvD,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,KAAK;AAC3B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,GAAI,SAAS,YAAY,SAAY,CAAC,IAAI,EAAE,SAAS,YAAY,SAAS,OAAO,EAAE;AAAA,IACnF,cAAc,SAAS;AAAA,EACzB;AACF;AAEA,MAAM,YAAY,OAAO,oBAAoB,sBAAU;AAShD,MAAM,sBAAsB,CACjC,gBAEA,OAAO,IAAI,aAAY;AACrB,QAAM,WAAW,gBAAgB,SAAY,SAAY,OAAO,kBAAkB,WAAW;AAC7F,SAAO,OAAO,UAAU;AAAA,IACtB,MAAM;AAAA,IACN,aAAa,aAAa,SAAY,OAAO,qBAAqB,QAAQ;AAAA,EAC5E,CAAC;AACH,CAAC;AAaI,MAAM,UAAU,CACrB,aAEA,OAAO,IAAI,aAAY;AACrB,QAAM,cAAc,SAAS,gBAAgB,SAAY,SAAY,OAAO,kBAAkB,SAAS,WAAW;AAClH,SAAO,OAAO,UAAU;AAAA,IACtB,MAAM;AAAA,IACN,MAAM,SAAS;AAAA,IACf,QAAQ,gBAAgB,SAAS,MAAM;AAAA,IACvC,QAAQ,YAAY,SAAS,MAAM;AAAA,IACnC,cAAc,sBAAsB,SAAS,YAAY;AAAA,IACzD,GAAI,gBAAgB,SAAY,CAAC,IAAI,EAAE,aAAa,qBAAqB,WAAW,EAAE;AAAA,IACtF,GAAI,SAAS,aAAa,SAAY,CAAC,IAAI,EAAE,UAAU,kBAAkB,SAAS,QAAQ,EAAE;AAAA,EAC9F,CAAC;AACH,CAAC;AAWI,MAAM,UAAU,CACrB,aAC8D,UAAU,EAAE,MAAM,WAAW,GAAG,SAAS,CAAC;AAqBnG,MAAM,kBAAkB,CAC7B,UACA,sBACiF;AACjF,MAAI,SAAS,SAAS,UAAU;AAC9B,WAAO,OAAO;AAAA,MACZ,IAAI,iBAAiB;AAAA,QACnB,MAAM;AAAA,QACN,SAAS,mCAAmC,SAAS,IAAI;AAAA,MAC3D,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,iBAAiB,UAAU,iBAAiB,EAAE,KAAK,OAAO,QAAQ,OAAO,CAAC;AACnF;AAYO,MAAM,eAAe,CAC1B,UACA,sBAEA,SAAS,SAAS,WAChB,gBAAgB,UAAU,iBAAiB,IAC3C,iBAAiB,UAAU,iBAAiB,EAAE,KAAK,OAAO;AAAA,EAAQ,CAAC,aACjE,UAAU;AAAA,IACR,MAAM;AAAA,IACN,MAAM,SAAS;AAAA,IACf,MAAM,SAAS;AAAA,IACf,QAAQ,gBAAgB,SAAS,MAAM;AAAA,IACvC,QAAQ,YAAY,SAAS,MAAM;AAAA,IACnC,cAAc,sBAAsB,SAAS,YAAY;AAAA,EAC3D,CAAC;AACH,CAAC;AASL,MAAM,kBAAkB,CACtB,QACA,MACA,UAC6C;AAC7C,QAAM,aAAa,OAAO,yBAAyB,QAAQ,IAAI;AAC/D,MAAI,eAAe,QAAW;AAC5B,WAAO,OAAO;AAAA,MACZ,IAAI,iBAAiB;AAAA,QACnB,MAAM;AAAA,QACN,SAAS,WAAW,MAAM,YAAY,CAAC,yBAAyB,IAAI;AAAA,MACtE,CAAC;AAAA,IACH;AAAA,EACF;AACA,MAAI,EAAE,WAAW,aAAa;AAC5B,WAAO,OAAO;AAAA,MACZ,IAAI,iBAAiB;AAAA,QACnB,MAAM;AAAA,QACN,SAAS,GAAG,KAAK,yBAAyB,IAAI;AAAA,MAChD,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,OAAO,QAAQ,WAAW,KAAK;AACxC;AASA,MAAM,gBAAgB,CACpB,UACA,YAEA,OAAO,IAAI,aAAY;AACrB,QAAM,SAAkC,CAAC;AACzC,WAAS,QAAQ,GAAG,QAAQ,SAAS,OAAO,QAAQ,SAAS;AAC3D,UAAM,QAAQ,SAAS,OAAO,KAAK;AACnC,QAAI,MAAM,SAAS,WAAW;AAC5B,aAAO,OAAO,KAAK,CAAC,IAAI,MAAM;AAC9B;AAAA,IACF;AACA,UAAM,SAAS,OAAO,QAAQ,KAAK;AACnC,WAAO,OAAO,KAAK,CAAC,IAAI,MAAM,SAAS,YACnC,YAAY,QAAQ,EAAE,WAAW,UAAU,CAAC,IAC5C,MAAM,KAAK,SAAS,IACpB,YAAY,QAAQ,EAAE,WAAW,iBAAiB,MAAM,MAAM,KAAK,CAAC,IACpE,YAAY,QAAQ,EAAE,WAAW,MAAM,CAAC;AAAA,EAC9C;AACA,SAAO;AACT,CAAC;AAGH,MAAM,mBAAmB,CACvB,UACA,sBAEA;AAAA,EACE;AAAA,EACA,CAAC,cACC,OAAO,QAAQ,gBAAgB,mBAAmB,UAAU,MAAM,QAAQ,GAAG,CAAC,WAC5E,OAAO,WAAW,WAAW,OAAO,QAAQ,MAAM,IAAI,OAAO;AAAA,IAC3D,IAAI,iBAAiB;AAAA,MACnB,MAAM;AAAA,MACN,SAAS,+BAA+B,UAAU,IAAI;AAAA,IACxD,CAAC;AAAA,EACH,CAAC;AACP,EAAE,KAAK,OAAO,IAAI,CAAC,YAAY;AAAA,EAC7B,MAAM,aAAa,QAAQ;AAAA,EAC3B;AAAA,EACA,QAAQ,SAAS;AAAA,EACjB,cAAc,EAAE,UAAU,SAAS,aAAa;AAClD,EAAE,CAAC;AASL,MAAM,eAAe,CAAC,cAAuC;AAAA,EAC3D,SAAS,SAAS;AAAA,EAClB,aAAa,SAAS;AAAA,EACtB,GAAI,SAAS,qBAAqB,SAAY,CAAC,IAAI,EAAE,kBAAkB,SAAS,iBAAiB;AAAA,EACjG,GAAI,SAAS,YAAY,SAAY,CAAC,IAAI,EAAE,SAAS,SAAS,QAAQ;AAAA,EACtE,GAAI,SAAS,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,SAAS,UAAU;AAC9E;AAWA,MAAM,eAAe;AAmBd,MAAM,UAAU,CAAC,OAAgB,SAAyC;AAC/E,MAAI,UAAU;AACd,aAAW,WAAW,MAAM;AAC1B,QAAI,OAAO,YAAY,YAAY,YAAY,KAAM,QAAO;AAC5D,UAAM,aAAa,OAAO,yBAAyB,SAAS,OAAO;AACnE,QAAI,eAAe,UAAa,EAAE,WAAW,YAAa,QAAO;AACjE,cAAU,WAAW;AAAA,EACvB;AACA,SAAO;AACT;AAgCO,MAAM,mBAAmB,CAAC,YAU/B,OAAO,IAAI,aAAY;AACrB,QAAM,WAAW,QAAQ;AACzB,MAAI,SAAS,SAAS,UAAU;AAC9B,WAAO,OAAO,IAAI,iBAAiB;AAAA,MACjC,MAAM;AAAA,MACN,SAAS,oCAAoC,SAAS,IAAI;AAAA,IAC5D,CAAC;AAAA,EACH;AACA,QAAM,cAAc,QAAQ,gBAAgB,SAAY,SAAY,OAAO,kBAAkB,QAAQ,WAAW;AAChH,QAAM,SAAS,OAAO,cAAc,UAAU,CAAC,cAC7C,UAAU,SAAS,YACf,OAAO,QAAQ,YAAY,IAC3B,OAAO,QAAQ,gBAAgB,QAAQ,SAAS,UAAU,MAAM,gBAAgB,GAAG,CAAC,YAAY;AAChG,UAAM,UAAU,UAAU,EAAE,MAAM,eAAe,OAAO,QAAQ,SAAS,UAAU,IAAI,EAAE,CAAC;AAC1F,WAAO,QAAQ,eAAe,SAC1B,UACA,QAAQ,WAAW,OAAO,UAAU,MAAM,UAAU,MAAM,OAAO;AAAA,EACvE,CAAC,CAAC;AACN,SAAO,OAAO,QAAQ;AAAA,IACpB,MAAM,aAAa,QAAQ;AAAA,IAC3B;AAAA,IACA,QAAQ,SAAS;AAAA,IACjB,cAAc,EAAE,UAAU,SAAS,aAAa;AAAA,IAChD;AAAA,IACA,UAAU,QAAQ;AAAA,EACpB,CAAC;AACH,CAAC;",
6
6
  "names": []
7
7
  }
@@ -118,17 +118,21 @@ export type { PathIndex, PreparedEnvelope, Ranked } from "./internal/effects.ts"
118
118
  export { boundedEffects, indexPaths, maximumGlobs, maximumPathLength, narrowPrepared, overlapRanks, prepareEnvelope, rankPaths } from "./internal/effects.ts";
119
119
  /**
120
120
  * A normalized description of the resources a flow or step may read and
121
- * write.
121
+ * write. Build one with {@link make}.
122
122
  *
123
123
  * @category models
124
124
  * @since 0.0.0
125
- * @slop
126
125
  */
127
126
  export interface Declaration {
127
+ /** Readable paths or envelope patterns, sorted and duplicate-free. */
128
128
  readonly reads: ReadonlyArray<string>;
129
+ /** Writable paths or envelope patterns, sorted and duplicate-free. */
129
130
  readonly writes: ReadonlyArray<string>;
131
+ /** `hermetic` or `expected` enforcement. */
130
132
  readonly mode: Mode;
133
+ /** What a plan does when another node writes an overlapping path. */
131
134
  readonly onConflict: ConflictStrategy;
135
+ /** The effect tier. Absent means `sealed`. */
132
136
  readonly tier?: Tier | undefined;
133
137
  }
134
138
  /**
@@ -141,7 +145,6 @@ export interface Declaration {
141
145
  *
142
146
  * @category models
143
147
  * @since 0.0.0
144
- * @slop
145
148
  */
146
149
  export interface MakeOptions {
147
150
  readonly reads: Iterable<string>;
@@ -153,9 +156,13 @@ export interface MakeOptions {
153
156
  /**
154
157
  * A result of checking that a step declaration narrows a flow envelope.
155
158
  *
159
+ * On failure, `code` names the first rule broken, checked in this order:
160
+ * `effect_outside_envelope` (with the uncovered read and write paths, sorted),
161
+ * `effect_mode_widening`, then `effect_tier_widening` (both with empty
162
+ * `paths`).
163
+ *
156
164
  * @category models
157
165
  * @since 0.0.0
158
- * @slop
159
166
  */
160
167
  export type NarrowResult = {
161
168
  readonly ok: true;
@@ -165,11 +172,14 @@ export type NarrowResult = {
165
172
  readonly paths: ReadonlyArray<string>;
166
173
  };
167
174
  /**
168
- * Constructs a deterministic effect declaration.
175
+ * Constructs a deterministic effect declaration: `reads` and `writes` are
176
+ * collected, deduplicated, and sorted; an absent `tier` is omitted. Paths are
177
+ * not validated or rewritten.
169
178
  *
179
+ * @param input - The paths, mode, conflict strategy, and optional tier.
180
+ * @returns The normalized declaration.
170
181
  * @category constructors
171
182
  * @since 0.0.0
172
- * @slop
173
183
  */
174
184
  export declare const make: (input: MakeOptions) => Declaration;
175
185
  /**
@@ -183,9 +193,11 @@ export declare const make: (input: MakeOptions) => Declaration;
183
193
  * covered, so {@link narrow} reports it as `effect_outside_envelope` rather
184
194
  * than silently accepting an escape.
185
195
  *
196
+ * @param envelope - One envelope entry.
197
+ * @param path - One declared path.
198
+ * @returns `true` when the entry covers the path.
186
199
  * @category predicates
187
200
  * @since 0.0.0
188
- * @slop
189
201
  */
190
202
  export declare const covers: (envelope: string, path: string) => boolean;
191
203
  /**
@@ -202,9 +214,11 @@ export declare const covers: (envelope: string, path: string) => boolean;
202
214
  * patterns nest. A graph build prepares each envelope once for every node it
203
215
  * encloses.
204
216
  *
217
+ * @param envelope - The flow's declaration.
218
+ * @param step - The step's declaration.
219
+ * @returns `{ ok: true }`, or the first rule the step breaks.
205
220
  * @category validation
206
221
  * @since 0.0.0
207
- * @slop
208
222
  */
209
223
  export declare const narrow: (envelope: Declaration, step: Declaration) => NarrowResult;
210
224
  /**
@@ -224,17 +238,21 @@ export declare const narrow: (envelope: Declaration, step: Declaration) => Narro
224
238
  * ranks inside its interval, so the cost is linear in the two declarations
225
239
  * plus the matches, whatever the paths' lengths or how many patterns nest.
226
240
  *
241
+ * @param a - One declaration.
242
+ * @param b - The other declaration.
243
+ * @returns The shared write paths; empty when the writes are disjoint.
227
244
  * @category analysis
228
245
  * @since 0.0.0
229
- * @slop
230
246
  */
231
247
  export declare const overlaps: (a: Declaration, b: Declaration) => ReadonlyArray<string>;
232
248
  /**
233
- * Returns a sealed, hermetic copy of an effect declaration.
249
+ * Returns a sealed, hermetic copy of an effect declaration, normalized by
250
+ * {@link make}. Paths and `onConflict` are kept.
234
251
  *
252
+ * @param declaration - The declaration to copy.
253
+ * @returns The copy with `mode: "hermetic"` and `tier: "sealed"`.
235
254
  * @category constructors
236
255
  * @since 0.0.0
237
- * @slop
238
256
  */
239
257
  export declare const sealed: (declaration: Declaration) => Declaration;
240
258
  //# sourceMappingURL=Effects.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Effects.d.ts","sourceRoot":"","sources":["../../src/Effects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,IAAI,qEAA6D,CAAA;AAE9E;;;;;GAKG;AACH,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,CAAA;AAEnC;;;;;;;;GAQG;AACH,eAAO,MAAM,IAAI,oDAA4C,CAAA;AAE7D;;;;;GAKG;AACH,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,CAAA;AAEnC;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,yDAAiD,CAAA;AAE9E;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,gBAAgB,CAAC,IAAI,CAAA;AAE3D;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,QAAQ,2CAAgE,CAAA;AAErF;;;;;;;;;;;;;;;;;;GAkBG;AACH,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAEhF;;;;;;GAMG;AACH,OAAO,EACL,cAAc,EACd,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,SAAS,EACV,MAAM,uBAAuB,CAAA;AAE9B;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACrC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACtC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAA;IACrC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;CACjC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAA;IACjC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAA;IACrC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;CACjC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;CAAE,GACrB;IACA,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAA;IAClB,QAAQ,CAAC,IAAI,EAAE,yBAAyB,GAAG,sBAAsB,GAAG,sBAAsB,CAAA;IAC1F,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACtC,CAAA;AAIH;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,GAAI,OAAO,WAAW,KAAG,WAMxC,CAAA;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,MAAM,GAAI,UAAU,MAAM,EAAE,MAAM,MAAM,KAAG,OAGvD,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,MAAM,GAAI,UAAU,WAAW,EAAE,MAAM,WAAW,KAAG,YACL,CAAA;AAE7D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,QAAQ,GAAI,GAAG,WAAW,EAAE,GAAG,WAAW,KAAG,aAAa,CAAC,MAAM,CAI7E,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,GAAI,aAAa,WAAW,KAAG,WACU,CAAA"}
1
+ {"version":3,"file":"Effects.d.ts","sourceRoot":"","sources":["../../src/Effects.ts"],"names":[],"mappings":"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"}
@@ -78,11 +78,14 @@ export const Envelope = Context.Service("@smthrs/plan/Effects/Envelope");
78
78
  export { boundedEffects, indexPaths, maximumGlobs, maximumPathLength, narrowPrepared, overlapRanks, prepareEnvelope, rankPaths } from "./internal/effects.js";
79
79
  const normalize = (paths) => [...new Set(paths)].sort();
80
80
  /**
81
- * Constructs a deterministic effect declaration.
81
+ * Constructs a deterministic effect declaration: `reads` and `writes` are
82
+ * collected, deduplicated, and sorted; an absent `tier` is omitted. Paths are
83
+ * not validated or rewritten.
82
84
  *
85
+ * @param input - The paths, mode, conflict strategy, and optional tier.
86
+ * @returns The normalized declaration.
83
87
  * @category constructors
84
88
  * @since 0.0.0
85
- * @slop
86
89
  */
87
90
  export const make = (input) => ({
88
91
  reads: normalize(input.reads),
@@ -102,9 +105,11 @@ export const make = (input) => ({
102
105
  * covered, so {@link narrow} reports it as `effect_outside_envelope` rather
103
106
  * than silently accepting an escape.
104
107
  *
108
+ * @param envelope - One envelope entry.
109
+ * @param path - One declared path.
110
+ * @returns `true` when the entry covers the path.
105
111
  * @category predicates
106
112
  * @since 0.0.0
107
- * @slop
108
113
  */
109
114
  export const covers = (envelope, path) => {
110
115
  if (Index.hasDotSegment(path))
@@ -125,9 +130,11 @@ export const covers = (envelope, path) => {
125
130
  * patterns nest. A graph build prepares each envelope once for every node it
126
131
  * encloses.
127
132
  *
133
+ * @param envelope - The flow's declaration.
134
+ * @param step - The step's declaration.
135
+ * @returns `{ ok: true }`, or the first rule the step breaks.
128
136
  * @category validation
129
137
  * @since 0.0.0
130
- * @slop
131
138
  */
132
139
  export const narrow = (envelope, step) => Index.narrowPrepared(Index.prepareEnvelope(envelope), step);
133
140
  /**
@@ -147,9 +154,11 @@ export const narrow = (envelope, step) => Index.narrowPrepared(Index.prepareEnve
147
154
  * ranks inside its interval, so the cost is linear in the two declarations
148
155
  * plus the matches, whatever the paths' lengths or how many patterns nest.
149
156
  *
157
+ * @param a - One declaration.
158
+ * @param b - The other declaration.
159
+ * @returns The shared write paths; empty when the writes are disjoint.
150
160
  * @category analysis
151
161
  * @since 0.0.0
152
- * @slop
153
162
  */
154
163
  export const overlaps = (a, b) => {
155
164
  const indexed = Index.indexPaths([a.writes, b.writes]);
@@ -157,11 +166,13 @@ export const overlaps = (a, b) => {
157
166
  .map((rank) => indexed.paths[rank]);
158
167
  };
159
168
  /**
160
- * Returns a sealed, hermetic copy of an effect declaration.
169
+ * Returns a sealed, hermetic copy of an effect declaration, normalized by
170
+ * {@link make}. Paths and `onConflict` are kept.
161
171
  *
172
+ * @param declaration - The declaration to copy.
173
+ * @returns The copy with `mode: "hermetic"` and `tier: "sealed"`.
162
174
  * @category constructors
163
175
  * @since 0.0.0
164
- * @slop
165
176
  */
166
177
  export const sealed = (declaration) => make({ ...declaration, mode: "hermetic", tier: "sealed" });
167
178
  //# sourceMappingURL=Effects.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Effects.js","sourceRoot":"","sources":["../../src/Effects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,uBAAuB,CAAA;AAE9C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC,CAAA;AAU9E;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAA;AAU7D;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AAU9E;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAc,+BAA+B,CAAC,CAAA;AAuBrF;;;;;;GAMG;AACH,OAAO,EACL,cAAc,EACd,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,SAAS,EACV,MAAM,uBAAuB,CAAA;AAqD9B,MAAM,SAAS,GAAG,CAAC,KAAuB,EAAyB,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AAEhG;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,KAAkB,EAAe,EAAE,CAAC,CAAC;IACxD,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;IAC7B,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC;IAC/B,IAAI,EAAE,KAAK,CAAC,IAAI;IAChB,UAAU,EAAE,KAAK,CAAC,UAAU;IAC5B,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;CAC1D,CAAC,CAAA;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,QAAgB,EAAE,IAAY,EAAW,EAAE;IAChE,IAAI,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAC3C,OAAO,QAAQ,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;AACrG,CAAC,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,QAAqB,EAAE,IAAiB,EAAgB,EAAE,CAC/E,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAA;AAE7D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAc,EAAE,CAAc,EAAyB,EAAE;IAChF,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;IACtD,OAAO,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;SACvG,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC,CAAA;AACxC,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,WAAwB,EAAe,EAAE,CAC9D,IAAI,CAAC,EAAE,GAAG,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA","sourcesContent":["/**\n * Pure effect declarations used to describe flow read and write envelopes.\n *\n * This is the ONE effect-envelope model. `@smthrs/flow` enforces it while it\n * builds a graph, `@smthrs/core` re-exports it, and `@smthrs/patterns` reads it\n * to intersect a decorator's declaration with the flow it wraps. It lives here\n * because `@smthrs/plan` is the lowest package all three depend on, beside the\n * `tier` and `onConflict` vocabularies a plan already speaks.\n *\n * Two layers: the declaration API, which a flow author and a catalog use, and\n * the prepared matching API below it, which a graph builder uses to check one\n * envelope against many steps without re-reading the envelope each time.\n *\n * Governing contract: `packages/smithers/flows/plan/docs/api.md`, published as\n * https://smithers.sh/docs/reference/api/plan.\n *\n * @since 0.0.0\n */\n\nimport * as Context from \"effect/Context\"\nimport * as Schema from \"effect/Schema\"\nimport * as Index from \"./internal/effects.ts\"\n\n/**\n * How reversible the work beneath a declaration is.\n *\n * This is the ONE tier vocabulary. `KeyMaterial.kind` is this schema, and so\n * is `@smthrs/flow`'s `Action.Tier`: an action's durability and a declared\n * envelope's ceiling are the same three words, and a fourth spelling of them\n * in one of the three places would fork identity silently.\n *\n * @category schemas\n * @since 1.0.0-rc.0\n */\nexport const Tier = Schema.Literals([\"sealed\", \"compensable\", \"irreversible\"])\n\n/**\n * The value form of {@link Tier}.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type Tier = typeof Tier.Type\n\n/**\n * How hermetic the work beneath a declaration is.\n *\n * This is the ONE mode vocabulary: the two words were written out in\n * `Declaration`, in `MakeOptions`, and twice more inside the matcher.\n *\n * @category schemas\n * @since 1.0.0-rc.0\n */\nexport const Mode = Schema.Literals([\"hermetic\", \"expected\"])\n\n/**\n * The value form of {@link Mode}.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type Mode = typeof Mode.Type\n\n/**\n * What a plan does when two nodes write the same path.\n *\n * This is the ONE conflict vocabulary: `Declaration.onConflict` declares it and\n * `Plan.PairStrategy` is this same schema, resolved per pair by\n * `Plan.compile`.\n *\n * @category schemas\n * @since 1.0.0-rc.0\n */\nexport const ConflictStrategy = Schema.Literals([\"serialize\", \"lane\", \"fail\"])\n\n/**\n * The value form of {@link ConflictStrategy}.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type ConflictStrategy = typeof ConflictStrategy.Type\n\n/**\n * The annotation key a flow's declared effect ENVELOPE is carried under.\n *\n * One key, here, because two graph builders read it: `@smthrs/flow`'s\n * `Graph.build` enforces the envelope it names, and `@smthrs/core` re-exports\n * this key as `Annotations.Effects`. While they were separate keys a flow\n * annotated for one builder was invisible to the other.\n *\n * It is a `Context` key, not hashed material: annotating a flow with an\n * envelope changes what a build ADMITS, and the declaration reaches identity\n * only through `KeyMaterial.kind`.\n *\n * @category annotations\n * @since 1.0.0-rc.0\n */\nexport const Envelope = Context.Service<Declaration>(\"@smthrs/plan/Effects/Envelope\")\n\n/**\n * The prepared matching API: what a graph builder needs to check one envelope\n * against every step it encloses, and to find the write overlaps between two\n * declarations, at the cost the public functions above pay once rather than\n * once per step.\n *\n * {@link prepareEnvelope} reads an envelope once into the form\n * {@link narrowPrepared} answers from, which is what makes a wide envelope cost\n * its size per build rather than per node. {@link indexPaths},\n * {@link rankPaths} and {@link overlapRanks} are the same decomposition for\n * write overlap, which {@link overlaps} wraps for the pairwise case.\n * {@link boundedEffects} snapshots a declaration into builder-owned data,\n * refusing through the caller's own error before the copy grows past its limit,\n * and {@link maximumPathLength} and {@link maximumGlobs} are the per-path bounds\n * that refusal enforces.\n *\n * @category matching\n * @since 1.0.0-rc.0\n */\nexport type { PathIndex, PreparedEnvelope, Ranked } from \"./internal/effects.ts\"\n\n/**\n * The prepared matching API. See the type re-exports above for what each member\n * is for.\n *\n * @category matching\n * @since 1.0.0-rc.0\n */\nexport {\n boundedEffects,\n indexPaths,\n maximumGlobs,\n maximumPathLength,\n narrowPrepared,\n overlapRanks,\n prepareEnvelope,\n rankPaths\n} from \"./internal/effects.ts\"\n\n/**\n * A normalized description of the resources a flow or step may read and\n * write.\n *\n * @category models\n * @since 0.0.0\n * @slop\n */\nexport interface Declaration {\n readonly reads: ReadonlyArray<string>\n readonly writes: ReadonlyArray<string>\n readonly mode: Mode\n readonly onConflict: ConflictStrategy\n readonly tier?: Tier | undefined\n}\n\n/**\n * Input accepted by {@link make}. Iterables are normalized into sorted,\n * duplicate-free arrays so declarations are deterministic key material.\n * Envelope entries and declared paths must already be path-normalized: no\n * separator or dot-segment rewriting is performed. A declared path containing\n * a whole `.` or `..` segment is never covered and therefore surfaces from\n * {@link narrow} as an `effect_outside_envelope` diagnostic.\n *\n * @category models\n * @since 0.0.0\n * @slop\n */\nexport interface MakeOptions {\n readonly reads: Iterable<string>\n readonly writes: Iterable<string>\n readonly mode: Mode\n readonly onConflict: ConflictStrategy\n readonly tier?: Tier | undefined\n}\n\n/**\n * A result of checking that a step declaration narrows a flow envelope.\n *\n * @category models\n * @since 0.0.0\n * @slop\n */\nexport type NarrowResult =\n | { readonly ok: true }\n | {\n readonly ok: false\n readonly code: \"effect_outside_envelope\" | \"effect_mode_widening\" | \"effect_tier_widening\"\n readonly paths: ReadonlyArray<string>\n }\n\nconst normalize = (paths: Iterable<string>): ReadonlyArray<string> => [...new Set(paths)].sort()\n\n/**\n * Constructs a deterministic effect declaration.\n *\n * @category constructors\n * @since 0.0.0\n * @slop\n */\nexport const make = (input: MakeOptions): Declaration => ({\n reads: normalize(input.reads),\n writes: normalize(input.writes),\n mode: input.mode,\n onConflict: input.onConflict,\n ...(input.tier === undefined ? {} : { tier: input.tier })\n})\n\n/**\n * Checks whether `path` is covered by an envelope entry.\n *\n * The grammar is exhaustive: an exact path matches itself; `*` and `**` match\n * everything; `prefix*` matches by string prefix; and `prefix/**` matches\n * `prefix/` and everything below it, but not bare `prefix`. This is\n * intentionally not full minimatch syntax. Envelope entries and declared paths\n * must be normalized. A path containing a whole `.` or `..` segment is never\n * covered, so {@link narrow} reports it as `effect_outside_envelope` rather\n * than silently accepting an escape.\n *\n * @category predicates\n * @since 0.0.0\n * @slop\n */\nexport const covers = (envelope: string, path: string): boolean => {\n if (Index.hasDotSegment(path)) return false\n return envelope === path || (Index.isGlob(envelope) && path.startsWith(Index.globPrefix(envelope)))\n}\n\n/**\n * Verifies that a step declaration stays within an enclosing flow envelope.\n *\n * Read and write paths must be covered independently. A step may tighten\n * `expected` to `hermetic`, but cannot widen `hermetic` to `expected`.\n * Effect tiers narrow from irreversible to compensable to sealed.\n *\n * The envelope's lists are prepared once: exact entries go into a set and\n * covering patterns collapse to their outermost prefixes, sorted, so each step\n * path costs one dot-segment scan, one lookup, and one binary search ending in\n * one prefix comparison, whatever the envelope's width or how many of its\n * patterns nest. A graph build prepares each envelope once for every node it\n * encloses.\n *\n * @category validation\n * @since 0.0.0\n * @slop\n */\nexport const narrow = (envelope: Declaration, step: Declaration): NarrowResult =>\n Index.narrowPrepared(Index.prepareEnvelope(envelope), step)\n\n/**\n * Returns the concrete or narrower write declarations shared by two effect\n * declarations. The result is sorted and duplicate-free.\n *\n * Two declarations of the same literal path always overlap, including a path\n * {@link covers} refuses to match because it carries a `.` or `..` segment.\n * Glob coverage stays strict for those paths, so an unnormalized declaration\n * still escapes no envelope, but two writers naming the same unnormalized path\n * are still detected as writing the same resource.\n *\n * The union of both declarations is indexed once: the distinct paths are\n * sorted, each is scanned once for a dot segment, and each pattern's prefix\n * is located by binary search. Exact paths then match through a merge of two\n * rank lists and each covering pattern enumerates the other declaration's\n * ranks inside its interval, so the cost is linear in the two declarations\n * plus the matches, whatever the paths' lengths or how many patterns nest.\n *\n * @category analysis\n * @since 0.0.0\n * @slop\n */\nexport const overlaps = (a: Declaration, b: Declaration): ReadonlyArray<string> => {\n const indexed = Index.indexPaths([a.writes, b.writes])\n return Index.overlapRanks(indexed, Index.rankPaths(indexed, a.writes), Index.rankPaths(indexed, b.writes))\n .map((rank) => indexed.paths[rank]!)\n}\n\n/**\n * Returns a sealed, hermetic copy of an effect declaration.\n *\n * @category constructors\n * @since 0.0.0\n * @slop\n */\nexport const sealed = (declaration: Declaration): Declaration =>\n make({ ...declaration, mode: \"hermetic\", tier: \"sealed\" })\n"]}
1
+ {"version":3,"file":"Effects.js","sourceRoot":"","sources":["../../src/Effects.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure effect declarations used to describe flow read and write envelopes.\n *\n * This is the ONE effect-envelope model. `@smthrs/flow` enforces it while it\n * builds a graph, `@smthrs/core` re-exports it, and `@smthrs/patterns` reads it\n * to intersect a decorator's declaration with the flow it wraps. It lives here\n * because `@smthrs/plan` is the lowest package all three depend on, beside the\n * `tier` and `onConflict` vocabularies a plan already speaks.\n *\n * Two layers: the declaration API, which a flow author and a catalog use, and\n * the prepared matching API below it, which a graph builder uses to check one\n * envelope against many steps without re-reading the envelope each time.\n *\n * Governing contract: `packages/smithers/flows/plan/docs/api.md`, published as\n * https://smithers.sh/docs/reference/api/plan.\n *\n * @since 0.0.0\n */\n\nimport * as Context from \"effect/Context\"\nimport * as Schema from \"effect/Schema\"\nimport * as Index from \"./internal/effects.ts\"\n\n/**\n * How reversible the work beneath a declaration is.\n *\n * This is the ONE tier vocabulary. `KeyMaterial.kind` is this schema, and so\n * is `@smthrs/flow`'s `Action.Tier`: an action's durability and a declared\n * envelope's ceiling are the same three words, and a fourth spelling of them\n * in one of the three places would fork identity silently.\n *\n * @category schemas\n * @since 1.0.0-rc.0\n */\nexport const Tier = Schema.Literals([\"sealed\", \"compensable\", \"irreversible\"])\n\n/**\n * The value form of {@link Tier}.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type Tier = typeof Tier.Type\n\n/**\n * How hermetic the work beneath a declaration is.\n *\n * This is the ONE mode vocabulary: the two words were written out in\n * `Declaration`, in `MakeOptions`, and twice more inside the matcher.\n *\n * @category schemas\n * @since 1.0.0-rc.0\n */\nexport const Mode = Schema.Literals([\"hermetic\", \"expected\"])\n\n/**\n * The value form of {@link Mode}.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type Mode = typeof Mode.Type\n\n/**\n * What a plan does when two nodes write the same path.\n *\n * This is the ONE conflict vocabulary: `Declaration.onConflict` declares it and\n * `Plan.PairStrategy` is this same schema, resolved per pair by\n * `Plan.compile`.\n *\n * @category schemas\n * @since 1.0.0-rc.0\n */\nexport const ConflictStrategy = Schema.Literals([\"serialize\", \"lane\", \"fail\"])\n\n/**\n * The value form of {@link ConflictStrategy}.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type ConflictStrategy = typeof ConflictStrategy.Type\n\n/**\n * The annotation key a flow's declared effect ENVELOPE is carried under.\n *\n * One key, here, because two graph builders read it: `@smthrs/flow`'s\n * `Graph.build` enforces the envelope it names, and `@smthrs/core` re-exports\n * this key as `Annotations.Effects`. While they were separate keys a flow\n * annotated for one builder was invisible to the other.\n *\n * It is a `Context` key, not hashed material: annotating a flow with an\n * envelope changes what a build ADMITS, and the declaration reaches identity\n * only through `KeyMaterial.kind`.\n *\n * @category annotations\n * @since 1.0.0-rc.0\n */\nexport const Envelope = Context.Service<Declaration>(\"@smthrs/plan/Effects/Envelope\")\n\n/**\n * The prepared matching API: what a graph builder needs to check one envelope\n * against every step it encloses, and to find the write overlaps between two\n * declarations, at the cost the public functions above pay once rather than\n * once per step.\n *\n * {@link prepareEnvelope} reads an envelope once into the form\n * {@link narrowPrepared} answers from, which is what makes a wide envelope cost\n * its size per build rather than per node. {@link indexPaths},\n * {@link rankPaths} and {@link overlapRanks} are the same decomposition for\n * write overlap, which {@link overlaps} wraps for the pairwise case.\n * {@link boundedEffects} snapshots a declaration into builder-owned data,\n * refusing through the caller's own error before the copy grows past its limit,\n * and {@link maximumPathLength} and {@link maximumGlobs} are the per-path bounds\n * that refusal enforces.\n *\n * @category matching\n * @since 1.0.0-rc.0\n */\nexport type { PathIndex, PreparedEnvelope, Ranked } from \"./internal/effects.ts\"\n\n/**\n * The prepared matching API. See the type re-exports above for what each member\n * is for.\n *\n * @category matching\n * @since 1.0.0-rc.0\n */\nexport {\n boundedEffects,\n indexPaths,\n maximumGlobs,\n maximumPathLength,\n narrowPrepared,\n overlapRanks,\n prepareEnvelope,\n rankPaths\n} from \"./internal/effects.ts\"\n\n/**\n * A normalized description of the resources a flow or step may read and\n * write. Build one with {@link make}.\n *\n * @category models\n * @since 0.0.0\n */\nexport interface Declaration {\n /** Readable paths or envelope patterns, sorted and duplicate-free. */\n readonly reads: ReadonlyArray<string>\n /** Writable paths or envelope patterns, sorted and duplicate-free. */\n readonly writes: ReadonlyArray<string>\n /** `hermetic` or `expected` enforcement. */\n readonly mode: Mode\n /** What a plan does when another node writes an overlapping path. */\n readonly onConflict: ConflictStrategy\n /** The effect tier. Absent means `sealed`. */\n readonly tier?: Tier | undefined\n}\n\n/**\n * Input accepted by {@link make}. Iterables are normalized into sorted,\n * duplicate-free arrays so declarations are deterministic key material.\n * Envelope entries and declared paths must already be path-normalized: no\n * separator or dot-segment rewriting is performed. A declared path containing\n * a whole `.` or `..` segment is never covered and therefore surfaces from\n * {@link narrow} as an `effect_outside_envelope` diagnostic.\n *\n * @category models\n * @since 0.0.0\n */\nexport interface MakeOptions {\n readonly reads: Iterable<string>\n readonly writes: Iterable<string>\n readonly mode: Mode\n readonly onConflict: ConflictStrategy\n readonly tier?: Tier | undefined\n}\n\n/**\n * A result of checking that a step declaration narrows a flow envelope.\n *\n * On failure, `code` names the first rule broken, checked in this order:\n * `effect_outside_envelope` (with the uncovered read and write paths, sorted),\n * `effect_mode_widening`, then `effect_tier_widening` (both with empty\n * `paths`).\n *\n * @category models\n * @since 0.0.0\n */\nexport type NarrowResult =\n | { readonly ok: true }\n | {\n readonly ok: false\n readonly code: \"effect_outside_envelope\" | \"effect_mode_widening\" | \"effect_tier_widening\"\n readonly paths: ReadonlyArray<string>\n }\n\nconst normalize = (paths: Iterable<string>): ReadonlyArray<string> => [...new Set(paths)].sort()\n\n/**\n * Constructs a deterministic effect declaration: `reads` and `writes` are\n * collected, deduplicated, and sorted; an absent `tier` is omitted. Paths are\n * not validated or rewritten.\n *\n * @param input - The paths, mode, conflict strategy, and optional tier.\n * @returns The normalized declaration.\n * @category constructors\n * @since 0.0.0\n */\nexport const make = (input: MakeOptions): Declaration => ({\n reads: normalize(input.reads),\n writes: normalize(input.writes),\n mode: input.mode,\n onConflict: input.onConflict,\n ...(input.tier === undefined ? {} : { tier: input.tier })\n})\n\n/**\n * Checks whether `path` is covered by an envelope entry.\n *\n * The grammar is exhaustive: an exact path matches itself; `*` and `**` match\n * everything; `prefix*` matches by string prefix; and `prefix/**` matches\n * `prefix/` and everything below it, but not bare `prefix`. This is\n * intentionally not full minimatch syntax. Envelope entries and declared paths\n * must be normalized. A path containing a whole `.` or `..` segment is never\n * covered, so {@link narrow} reports it as `effect_outside_envelope` rather\n * than silently accepting an escape.\n *\n * @param envelope - One envelope entry.\n * @param path - One declared path.\n * @returns `true` when the entry covers the path.\n * @category predicates\n * @since 0.0.0\n */\nexport const covers = (envelope: string, path: string): boolean => {\n if (Index.hasDotSegment(path)) return false\n return envelope === path || (Index.isGlob(envelope) && path.startsWith(Index.globPrefix(envelope)))\n}\n\n/**\n * Verifies that a step declaration stays within an enclosing flow envelope.\n *\n * Read and write paths must be covered independently. A step may tighten\n * `expected` to `hermetic`, but cannot widen `hermetic` to `expected`.\n * Effect tiers narrow from irreversible to compensable to sealed.\n *\n * The envelope's lists are prepared once: exact entries go into a set and\n * covering patterns collapse to their outermost prefixes, sorted, so each step\n * path costs one dot-segment scan, one lookup, and one binary search ending in\n * one prefix comparison, whatever the envelope's width or how many of its\n * patterns nest. A graph build prepares each envelope once for every node it\n * encloses.\n *\n * @param envelope - The flow's declaration.\n * @param step - The step's declaration.\n * @returns `{ ok: true }`, or the first rule the step breaks.\n * @category validation\n * @since 0.0.0\n */\nexport const narrow = (envelope: Declaration, step: Declaration): NarrowResult =>\n Index.narrowPrepared(Index.prepareEnvelope(envelope), step)\n\n/**\n * Returns the concrete or narrower write declarations shared by two effect\n * declarations. The result is sorted and duplicate-free.\n *\n * Two declarations of the same literal path always overlap, including a path\n * {@link covers} refuses to match because it carries a `.` or `..` segment.\n * Glob coverage stays strict for those paths, so an unnormalized declaration\n * still escapes no envelope, but two writers naming the same unnormalized path\n * are still detected as writing the same resource.\n *\n * The union of both declarations is indexed once: the distinct paths are\n * sorted, each is scanned once for a dot segment, and each pattern's prefix\n * is located by binary search. Exact paths then match through a merge of two\n * rank lists and each covering pattern enumerates the other declaration's\n * ranks inside its interval, so the cost is linear in the two declarations\n * plus the matches, whatever the paths' lengths or how many patterns nest.\n *\n * @param a - One declaration.\n * @param b - The other declaration.\n * @returns The shared write paths; empty when the writes are disjoint.\n * @category analysis\n * @since 0.0.0\n */\nexport const overlaps = (a: Declaration, b: Declaration): ReadonlyArray<string> => {\n const indexed = Index.indexPaths([a.writes, b.writes])\n return Index.overlapRanks(indexed, Index.rankPaths(indexed, a.writes), Index.rankPaths(indexed, b.writes))\n .map((rank) => indexed.paths[rank]!)\n}\n\n/**\n * Returns a sealed, hermetic copy of an effect declaration, normalized by\n * {@link make}. Paths and `onConflict` are kept.\n *\n * @param declaration - The declaration to copy.\n * @returns The copy with `mode: \"hermetic\"` and `tier: \"sealed\"`.\n * @category constructors\n * @since 0.0.0\n */\nexport const sealed = (declaration: Declaration): Declaration =>\n make({ ...declaration, mode: \"hermetic\", tier: \"sealed\" })\n"]}