@smthrs/plan 0.0.0-stage → 1.0.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +183 -0
- package/LICENSE +21 -0
- package/README.md +228 -2
- package/dist/cjs/CachePolicy.d.ts +131 -0
- package/dist/cjs/CachePolicy.d.ts.map +1 -0
- package/dist/cjs/CachePolicy.js +50 -0
- package/dist/cjs/CachePolicy.js.map +7 -0
- package/dist/cjs/Effects.d.ts +240 -0
- package/dist/cjs/Effects.d.ts.map +1 -0
- package/dist/cjs/Effects.js +76 -0
- package/dist/cjs/Effects.js.map +7 -0
- package/dist/cjs/FileSet.d.ts +288 -0
- package/dist/cjs/FileSet.d.ts.map +1 -0
- package/dist/cjs/FileSet.js +191 -0
- package/dist/cjs/FileSet.js.map +7 -0
- package/dist/cjs/GraphBuildError.d.ts +122 -0
- package/dist/cjs/GraphBuildError.d.ts.map +1 -0
- package/dist/cjs/GraphBuildError.js +71 -0
- package/dist/cjs/GraphBuildError.js.map +7 -0
- package/dist/cjs/KeyMaterial.d.ts +102 -0
- package/dist/cjs/KeyMaterial.d.ts.map +1 -0
- package/dist/cjs/KeyMaterial.js +64 -0
- package/dist/cjs/KeyMaterial.js.map +7 -0
- package/dist/cjs/Node.d.ts +606 -0
- package/dist/cjs/Node.d.ts.map +1 -0
- package/dist/cjs/Node.js +207 -0
- package/dist/cjs/Node.js.map +7 -0
- package/dist/cjs/Placement.d.ts +117 -0
- package/dist/cjs/Placement.d.ts.map +1 -0
- package/dist/cjs/Placement.js +46 -0
- package/dist/cjs/Placement.js.map +7 -0
- package/dist/cjs/Plan.d.ts +440 -0
- package/dist/cjs/Plan.d.ts.map +1 -0
- package/dist/cjs/Plan.js +508 -0
- package/dist/cjs/Plan.js.map +7 -0
- package/dist/cjs/PlanDiff.d.ts +58 -0
- package/dist/cjs/PlanDiff.d.ts.map +1 -0
- package/dist/cjs/PlanDiff.js +124 -0
- package/dist/cjs/PlanDiff.js.map +7 -0
- package/dist/cjs/Planned.d.ts +121 -0
- package/dist/cjs/Planned.d.ts.map +1 -0
- package/dist/cjs/Planned.js +72 -0
- package/dist/cjs/Planned.js.map +7 -0
- package/dist/cjs/Repetition.d.ts +31 -0
- package/dist/cjs/Repetition.d.ts.map +1 -0
- package/dist/cjs/Repetition.js +36 -0
- package/dist/cjs/Repetition.js.map +7 -0
- package/dist/cjs/Scheduling.d.ts +89 -0
- package/dist/cjs/Scheduling.d.ts.map +1 -0
- package/dist/cjs/Scheduling.js +76 -0
- package/dist/cjs/Scheduling.js.map +7 -0
- package/dist/cjs/StepKey.d.ts +333 -0
- package/dist/cjs/StepKey.d.ts.map +1 -0
- package/dist/cjs/StepKey.js +311 -0
- package/dist/cjs/StepKey.js.map +7 -0
- package/dist/cjs/index.d.ts +91 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +59 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/ConflictAnnotation.d.ts +59 -0
- package/dist/cjs/internal/ConflictAnnotation.d.ts.map +1 -0
- package/dist/cjs/internal/ConflictAnnotation.js +247 -0
- package/dist/cjs/internal/ConflictAnnotation.js.map +7 -0
- package/dist/cjs/internal/EffectCandidates.d.ts +19 -0
- package/dist/cjs/internal/EffectCandidates.d.ts.map +1 -0
- package/dist/cjs/internal/EffectCandidates.js +92 -0
- package/dist/cjs/internal/EffectCandidates.js.map +7 -0
- package/dist/cjs/internal/JsonMirror.d.ts +34 -0
- package/dist/cjs/internal/JsonMirror.d.ts.map +1 -0
- package/dist/cjs/internal/JsonMirror.js +195 -0
- package/dist/cjs/internal/JsonMirror.js.map +7 -0
- package/dist/cjs/internal/effects.d.ts +245 -0
- package/dist/cjs/internal/effects.d.ts.map +1 -0
- package/dist/cjs/internal/effects.js +290 -0
- package/dist/cjs/internal/effects.js.map +7 -0
- package/dist/cjs/internal/node.d.ts +526 -0
- package/dist/cjs/internal/node.d.ts.map +1 -0
- package/dist/cjs/internal/node.js +312 -0
- package/dist/cjs/internal/node.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/PlanFixtures.d.ts +127 -0
- package/dist/cjs/test/PlanFixtures.d.ts.map +1 -0
- package/dist/cjs/test/PlanFixtures.js +59 -0
- package/dist/cjs/test/PlanFixtures.js.map +7 -0
- package/dist/esm/CachePolicy.d.ts +131 -0
- package/dist/esm/CachePolicy.d.ts.map +1 -0
- package/dist/esm/CachePolicy.js +117 -0
- package/dist/esm/CachePolicy.js.map +1 -0
- package/dist/esm/Effects.d.ts +240 -0
- package/dist/esm/Effects.d.ts.map +1 -0
- package/dist/esm/Effects.js +167 -0
- package/dist/esm/Effects.js.map +1 -0
- package/dist/esm/FileSet.d.ts +288 -0
- package/dist/esm/FileSet.d.ts.map +1 -0
- package/dist/esm/FileSet.js +324 -0
- package/dist/esm/FileSet.js.map +1 -0
- package/dist/esm/GraphBuildError.d.ts +122 -0
- package/dist/esm/GraphBuildError.d.ts.map +1 -0
- package/dist/esm/GraphBuildError.js +146 -0
- package/dist/esm/GraphBuildError.js.map +1 -0
- package/dist/esm/KeyMaterial.d.ts +102 -0
- package/dist/esm/KeyMaterial.d.ts.map +1 -0
- package/dist/esm/KeyMaterial.js +85 -0
- package/dist/esm/KeyMaterial.js.map +1 -0
- package/dist/esm/Node.d.ts +606 -0
- package/dist/esm/Node.d.ts.map +1 -0
- package/dist/esm/Node.js +521 -0
- package/dist/esm/Node.js.map +1 -0
- package/dist/esm/Placement.d.ts +117 -0
- package/dist/esm/Placement.d.ts.map +1 -0
- package/dist/esm/Placement.js +65 -0
- package/dist/esm/Placement.js.map +1 -0
- package/dist/esm/Plan.d.ts +440 -0
- package/dist/esm/Plan.d.ts.map +1 -0
- package/dist/esm/Plan.js +673 -0
- package/dist/esm/Plan.js.map +1 -0
- package/dist/esm/PlanDiff.d.ts +58 -0
- package/dist/esm/PlanDiff.d.ts.map +1 -0
- package/dist/esm/PlanDiff.js +159 -0
- package/dist/esm/PlanDiff.js.map +1 -0
- package/dist/esm/Planned.d.ts +121 -0
- package/dist/esm/Planned.d.ts.map +1 -0
- package/dist/esm/Planned.js +147 -0
- package/dist/esm/Planned.js.map +1 -0
- package/dist/esm/Repetition.d.ts +31 -0
- package/dist/esm/Repetition.d.ts.map +1 -0
- package/dist/esm/Repetition.js +24 -0
- package/dist/esm/Repetition.js.map +1 -0
- package/dist/esm/Scheduling.d.ts +89 -0
- package/dist/esm/Scheduling.d.ts.map +1 -0
- package/dist/esm/Scheduling.js +82 -0
- package/dist/esm/Scheduling.js.map +1 -0
- package/dist/esm/StepKey.d.ts +333 -0
- package/dist/esm/StepKey.d.ts.map +1 -0
- package/dist/esm/StepKey.js +478 -0
- package/dist/esm/StepKey.js.map +1 -0
- package/dist/esm/index.d.ts +91 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +91 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/ConflictAnnotation.d.ts +59 -0
- package/dist/esm/internal/ConflictAnnotation.d.ts.map +1 -0
- package/dist/esm/internal/ConflictAnnotation.js +332 -0
- package/dist/esm/internal/ConflictAnnotation.js.map +1 -0
- package/dist/esm/internal/EffectCandidates.d.ts +19 -0
- package/dist/esm/internal/EffectCandidates.d.ts.map +1 -0
- package/dist/esm/internal/EffectCandidates.js +86 -0
- package/dist/esm/internal/EffectCandidates.js.map +1 -0
- package/dist/esm/internal/JsonMirror.d.ts +34 -0
- package/dist/esm/internal/JsonMirror.d.ts.map +1 -0
- package/dist/esm/internal/JsonMirror.js +204 -0
- package/dist/esm/internal/JsonMirror.js.map +1 -0
- package/dist/esm/internal/effects.d.ts +245 -0
- package/dist/esm/internal/effects.d.ts.map +1 -0
- package/dist/esm/internal/effects.js +459 -0
- package/dist/esm/internal/effects.js.map +1 -0
- package/dist/esm/internal/node.d.ts +526 -0
- package/dist/esm/internal/node.d.ts.map +1 -0
- package/dist/esm/internal/node.js +564 -0
- package/dist/esm/internal/node.js.map +1 -0
- package/dist/esm/test/PlanFixtures.d.ts +127 -0
- package/dist/esm/test/PlanFixtures.d.ts.map +1 -0
- package/dist/esm/test/PlanFixtures.js +60 -0
- package/dist/esm/test/PlanFixtures.js.map +1 -0
- package/docs/README.md +165 -0
- package/docs/api.md +1475 -0
- package/docs/concepts/authoring-ast.md +203 -0
- package/docs/concepts/effects-and-conflicts.md +135 -0
- package/docs/concepts/plan-value.md +168 -0
- package/docs/concepts/step-keys.md +179 -0
- package/docs/guides/author-a-node-graph.md +182 -0
- package/docs/guides/compile-a-plan.md +146 -0
- package/docs/guides/declare-file-effects.md +143 -0
- package/docs/guides/diff-two-plans.md +101 -0
- package/docs/installation.md +71 -0
- package/docs/quickstart.md +128 -0
- package/docs/testing.md +73 -0
- package/docs/troubleshooting.md +314 -0
- package/package.json +254 -3
- package/src/CachePolicy.ts +143 -0
- package/src/Effects.ts +284 -0
- package/src/FileSet.ts +411 -0
- package/src/GraphBuildError.ts +158 -0
- package/src/KeyMaterial.ts +106 -0
- package/src/Node.ts +871 -0
- package/src/Placement.ts +99 -0
- package/src/Plan.ts +868 -0
- package/src/PlanDiff.ts +199 -0
- package/src/Planned.ts +208 -0
- package/src/Repetition.ts +33 -0
- package/src/Scheduling.ts +154 -0
- package/src/StepKey.ts +710 -0
- package/src/index.ts +103 -0
- package/src/internal/ConflictAnnotation.ts +374 -0
- package/src/internal/EffectCandidates.ts +89 -0
- package/src/internal/JsonMirror.ts +255 -0
- package/src/internal/effects.ts +543 -0
- package/src/internal/node.ts +858 -0
- package/src/test/PlanFixtures.ts +76 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/Scheduling.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Shared ready-work admission policy. This module decides priority, aging,\n * deterministic ties, and step/agent capacity. It does not decide which nodes\n * are ready, evaluate branches, launch effects, or own execution lifetimes.\n * Those decisions must use the compiled graph and the caller's activation\n * state before presenting candidates here.\n *\n * @since 1.0.0\n */\n\nimport type * as Plan from \"./Plan.ts\"\n\n/**\n * Admission limits, independent of graph width and provider capacity.\n * Omitted limits are unbounded within JavaScript's safe-integer range.\n *\n * @since 1.0.0\n * @category models\n */\nexport interface Concurrency {\n readonly steps?: number | undefined\n readonly agents?: number | undefined\n}\n\n/**\n * Currently held permits. Every agent also holds a step permit.\n *\n * @since 1.0.0\n * @category models\n */\nexport interface Usage {\n readonly steps: number\n readonly agents: number\n}\n\n/**\n * The part of a plan node admission reads. Declared once here so the three\n * places that name it, {@link Candidate}, {@link Admission} and\n * {@link Policy.admit}, cannot drift apart.\n */\ntype Schedulable = Pick<Plan.PlanNode, \"id\" | \"kind\" | \"priority\">\n\n/**\n * A ready node's scheduling state. `order` is its position in the compiled\n * plan, not arrival order. `waited` counts capacity-constrained admission\n * passes, not wall-clock time.\n *\n * @since 1.0.0\n * @category models\n */\nexport interface Candidate<N extends Schedulable = Plan.PlanNode> {\n readonly node: N\n readonly order: number\n readonly waited: number\n}\n\n/**\n * One admission decision. Deferred candidates carry their incremented age;\n * admitted candidates retain their age for dispatch diagnostics. No input is\n * mutated and neither array owns or freezes the caller's node values.\n *\n * @since 1.0.0\n * @category models\n */\nexport interface Admission<N extends Schedulable = Plan.PlanNode> {\n readonly admitted: ReadonlyArray<Candidate<N>>\n readonly deferred: ReadonlyArray<Candidate<N>>\n readonly agents: number\n}\n\n/**\n * Pure policy shared by execution coordinators.\n *\n * @since 1.0.0\n * @category models\n */\nexport interface Policy {\n readonly limits: Usage\n readonly admit: <N extends Schedulable>(\n ready: ReadonlyArray<Candidate<N>>,\n active: Usage\n ) => Admission<N>\n}\n\nconst integer = (name: string, value: number, minimum: number): number => {\n if (!Number.isSafeInteger(value) || value < minimum) {\n throw new RangeError(`${name} must be a ${minimum === 1 ? \"positive\" : \"non-negative\"} safe integer`)\n }\n return value\n}\n\n/**\n * Snapshots validated limits. Admission orders by declared priority plus age,\n * using exact integer arithmetic even when their sum exceeds MAX_SAFE_INTEGER.\n * Ties preserve compiled plan order. An agent blocked by its subset cap does\n * not prevent ordinary ready work from using spare step capacity.\n *\n * Invalid candidate/permit state is refused before any decision is returned.\n * At MAX_SAFE_INTEGER the age saturates; a bounded plan cannot require that\n * many capacity-constrained passes to drain finite work.\n *\n * @since 1.0.0\n * @category constructors\n */\nexport const make = (concurrency: Concurrency = {}): Policy => {\n const limits = Object.freeze({\n steps: integer(\"concurrency.steps\", concurrency.steps ?? Number.MAX_SAFE_INTEGER, 1),\n agents: integer(\"concurrency.agents\", concurrency.agents ?? Number.MAX_SAFE_INTEGER, 1)\n })\n const admit: Policy[\"admit\"] = (ready, active) => {\n integer(\"active.steps\", active.steps, 0)\n integer(\"active.agents\", active.agents, 0)\n if (active.agents > active.steps) throw new RangeError(\"active.agents cannot exceed active.steps\")\n const ids = new Set<string>()\n const positions = new Set<number>()\n const ranked = ready.map((candidate) => {\n const { node, order, waited } = candidate\n integer(\"candidate.order\", order, 0)\n integer(\"candidate.waited\", waited, 0)\n if (!Number.isSafeInteger(node.priority)) throw new RangeError(\"candidate.priority must be a safe integer\")\n if (node.kind !== \"step\" && node.kind !== \"agent\" && node.kind !== \"merge\") {\n throw new RangeError(\"invalid candidate kind\")\n }\n if (typeof node.id !== \"string\" || node.id === \"\" || ids.has(node.id)) {\n throw new RangeError(\"candidate ids must be non-empty and unique\")\n }\n if (positions.has(order)) throw new RangeError(\"candidate plan positions must be unique\")\n ids.add(node.id)\n positions.add(order)\n return { candidate, priority: BigInt(node.priority) + BigInt(waited) }\n })\n ranked.sort((left, right) =>\n left.priority === right.priority\n ? left.candidate.order - right.candidate.order\n : left.priority > right.priority\n ? -1\n : 1\n )\n const admitted: Array<typeof ready[number]> = []\n const deferred: Array<typeof ready[number]> = []\n let agents = 0\n for (const { candidate } of ranked) {\n const isAgent = candidate.node.kind === \"agent\"\n if (admitted.length >= limits.steps - active.steps || (isAgent && agents >= limits.agents - active.agents)) {\n deferred.push(Object.freeze({ ...candidate, waited: Math.min(Number.MAX_SAFE_INTEGER, candidate.waited + 1) }))\n } else {\n admitted.push(Object.freeze({ ...candidate }))\n if (isAgent) agents++\n }\n }\n return Object.freeze({ admitted: Object.freeze(admitted), deferred: Object.freeze(deferred), agents })\n }\n return Object.freeze({ limits, admit })\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoFA,MAAM,UAAU,CAAC,MAAc,OAAe,YAA4B;AACxE,MAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,SAAS;AACnD,UAAM,IAAI,WAAW,GAAG,IAAI,cAAc,YAAY,IAAI,aAAa,cAAc,eAAe;AAAA,EACtG;AACA,SAAO;AACT;AAeO,MAAM,OAAO,CAAC,cAA2B,CAAC,MAAc;AAC7D,QAAM,SAAS,OAAO,OAAO;AAAA,IAC3B,OAAO,QAAQ,qBAAqB,YAAY,SAAS,OAAO,kBAAkB,CAAC;AAAA,IACnF,QAAQ,QAAQ,sBAAsB,YAAY,UAAU,OAAO,kBAAkB,CAAC;AAAA,EACxF,CAAC;AACD,QAAM,QAAyB,CAAC,OAAO,WAAW;AAChD,YAAQ,gBAAgB,OAAO,OAAO,CAAC;AACvC,YAAQ,iBAAiB,OAAO,QAAQ,CAAC;AACzC,QAAI,OAAO,SAAS,OAAO,MAAO,OAAM,IAAI,WAAW,0CAA0C;AACjG,UAAM,MAAM,oBAAI,IAAY;AAC5B,UAAM,YAAY,oBAAI,IAAY;AAClC,UAAM,SAAS,MAAM,IAAI,CAAC,cAAc;AACtC,YAAM,EAAE,MAAM,OAAO,OAAO,IAAI;AAChC,cAAQ,mBAAmB,OAAO,CAAC;AACnC,cAAQ,oBAAoB,QAAQ,CAAC;AACrC,UAAI,CAAC,OAAO,cAAc,KAAK,QAAQ,EAAG,OAAM,IAAI,WAAW,2CAA2C;AAC1G,UAAI,KAAK,SAAS,UAAU,KAAK,SAAS,WAAW,KAAK,SAAS,SAAS;AAC1E,cAAM,IAAI,WAAW,wBAAwB;AAAA,MAC/C;AACA,UAAI,OAAO,KAAK,OAAO,YAAY,KAAK,OAAO,MAAM,IAAI,IAAI,KAAK,EAAE,GAAG;AACrE,cAAM,IAAI,WAAW,4CAA4C;AAAA,MACnE;AACA,UAAI,UAAU,IAAI,KAAK,EAAG,OAAM,IAAI,WAAW,yCAAyC;AACxF,UAAI,IAAI,KAAK,EAAE;AACf,gBAAU,IAAI,KAAK;AACnB,aAAO,EAAE,WAAW,UAAU,OAAO,KAAK,QAAQ,IAAI,OAAO,MAAM,EAAE;AAAA,IACvE,CAAC;AACD,WAAO;AAAA,MAAK,CAAC,MAAM,UACjB,KAAK,aAAa,MAAM,WACpB,KAAK,UAAU,QAAQ,MAAM,UAAU,QACvC,KAAK,WAAW,MAAM,WACtB,KACA;AAAA,IACN;AACA,UAAM,WAAwC,CAAC;AAC/C,UAAM,WAAwC,CAAC;AAC/C,QAAI,SAAS;AACb,eAAW,EAAE,UAAU,KAAK,QAAQ;AAClC,YAAM,UAAU,UAAU,KAAK,SAAS;AACxC,UAAI,SAAS,UAAU,OAAO,QAAQ,OAAO,SAAU,WAAW,UAAU,OAAO,SAAS,OAAO,QAAS;AAC1G,iBAAS,KAAK,OAAO,OAAO,EAAE,GAAG,WAAW,QAAQ,KAAK,IAAI,OAAO,kBAAkB,UAAU,SAAS,CAAC,EAAE,CAAC,CAAC;AAAA,MAChH,OAAO;AACL,iBAAS,KAAK,OAAO,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC;AAC7C,YAAI,QAAS;AAAA,MACf;AAAA,IACF;AACA,WAAO,OAAO,OAAO,EAAE,UAAU,OAAO,OAAO,QAAQ,GAAG,UAAU,OAAO,OAAO,QAAQ,GAAG,OAAO,CAAC;AAAA,EACvG;AACA,SAAO,OAAO,OAAO,EAAE,QAAQ,MAAM,CAAC;AACxC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The KeyMaterial → step-key compiler.
|
|
3
|
+
*
|
|
4
|
+
* Revived from the deleted `packages/smithers/flows/keys/src/StepKey.ts`. Two
|
|
5
|
+
* deliberate deviations from the original:
|
|
6
|
+
*
|
|
7
|
+
* 1. **It lives here, not in `@smthrs/keys`.** That package owns generic
|
|
8
|
+
* derivation and stored-key validation; a compiler that understands plan
|
|
9
|
+
* material belongs above it.
|
|
10
|
+
* 2. **It produces `@smthrs/keys` `StoredKey` values, not a second `sk1_` digest
|
|
11
|
+
* format.** The original minted its own prefix over a private `Digest`
|
|
12
|
+
* module. The engine dispatches under `StoredKey` (`FlowEngine/ActionKey.ts`),
|
|
13
|
+
* so a plan whose node keys were a *different* string format could never be
|
|
14
|
+
* the thing the cache is consulted against, and that is the whole point of
|
|
15
|
+
* a `Plan`. One key format, one hashing
|
|
16
|
+
* chokepoint (canonical JSON + injected `Crypto`), one namespace field
|
|
17
|
+
* (`kind`) distinguishing content keys from ordinal ones.
|
|
18
|
+
*
|
|
19
|
+
* Everything the original earned is kept: nominally branded digest inputs (a
|
|
20
|
+
* literal that merely *looks* like `{digest}` hashes as a literal), set
|
|
21
|
+
* normalization, the separate environment namespace, per-variant tagging of
|
|
22
|
+
* resolved references, and the hashed `version`.
|
|
23
|
+
*
|
|
24
|
+
* Governing contract: the step-key rules at
|
|
25
|
+
* https://smithers.sh/docs/concepts/content-addressing.
|
|
26
|
+
*
|
|
27
|
+
* @since 0.1.0
|
|
28
|
+
*/
|
|
29
|
+
import { type StoredKey } from "@smthrs/keys";
|
|
30
|
+
import * as Cause from "effect/Cause";
|
|
31
|
+
import type * as Crypto from "effect/Crypto";
|
|
32
|
+
import * as Effect from "effect/Effect";
|
|
33
|
+
import * as Schema from "effect/Schema";
|
|
34
|
+
import type * as FileSet from "./FileSet.ts";
|
|
35
|
+
import type * as KeyMaterial from "./KeyMaterial.ts";
|
|
36
|
+
/**
|
|
37
|
+
* A computed step key. Identical in representation to any other flow key —
|
|
38
|
+
* `key1_` plus a SHA-256 digest of the canonical serialization of the material.
|
|
39
|
+
*
|
|
40
|
+
* @since 0.1.0
|
|
41
|
+
* @category models
|
|
42
|
+
* @slop
|
|
43
|
+
*/
|
|
44
|
+
export type StepKey = StoredKey;
|
|
45
|
+
/**
|
|
46
|
+
* The nominal brand carried by every {@link DigestInput}. Kept private: the
|
|
47
|
+
* only way to produce a value bearing it is {@link digestInput}, so a plain
|
|
48
|
+
* value that merely happens to look like `{ digest: "..." }` — actions pass
|
|
49
|
+
* content hashes around as ordinary data — can never be mistaken for a genuine
|
|
50
|
+
* upstream-result digest reference. This closes a step-key collision where
|
|
51
|
+
* shape-sniffing (`"digest" in value`) hashed the two identically.
|
|
52
|
+
*
|
|
53
|
+
* @since 0.1.0
|
|
54
|
+
* @category symbols
|
|
55
|
+
*/
|
|
56
|
+
declare const DigestInputTypeId: unique symbol;
|
|
57
|
+
/**
|
|
58
|
+
* Which kind of graph reference a {@link DigestInput} stands for. Declared
|
|
59
|
+
* once here so the field on `DigestInput` and the parameter of
|
|
60
|
+
* {@link digestInput} name one set and cannot drift apart.
|
|
61
|
+
*/
|
|
62
|
+
type GraphReference = "ref" | "pending" | "ref-projected";
|
|
63
|
+
/**
|
|
64
|
+
* A precomputed digest supplied as a step input rather than a literal value.
|
|
65
|
+
*
|
|
66
|
+
* @since 0.1.0
|
|
67
|
+
* @category models
|
|
68
|
+
* @slop
|
|
69
|
+
*/
|
|
70
|
+
export interface DigestInput {
|
|
71
|
+
readonly [DigestInputTypeId]: typeof DigestInputTypeId;
|
|
72
|
+
readonly digest: string;
|
|
73
|
+
/**
|
|
74
|
+
* Which kind of graph reference resolved to this digest.
|
|
75
|
+
*
|
|
76
|
+
* Carrying the discriminator on the digest input keeps `normalizeInputs` the
|
|
77
|
+
* single normalizer: it wraps every variant once, and the variants stay
|
|
78
|
+
* mutually distinct because they differ in this field rather than in a
|
|
79
|
+
* hand-built object a second wrap would hide.
|
|
80
|
+
*/
|
|
81
|
+
readonly reference?: GraphReference;
|
|
82
|
+
/** The projection applied to the referenced result, for `ref-projected`. */
|
|
83
|
+
readonly path?: ReadonlyArray<string>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Nominally tags a precomputed digest so it is hashed as a digest reference
|
|
87
|
+
* rather than a literal value.
|
|
88
|
+
*
|
|
89
|
+
* @since 0.1.0
|
|
90
|
+
* @category constructors
|
|
91
|
+
* @slop
|
|
92
|
+
*/
|
|
93
|
+
export declare const digestInput: (digest: string, reference?: {
|
|
94
|
+
readonly reference: GraphReference;
|
|
95
|
+
readonly path?: ReadonlyArray<string>;
|
|
96
|
+
}) => DigestInput;
|
|
97
|
+
/**
|
|
98
|
+
* Type guard for values produced by {@link digestInput}.
|
|
99
|
+
*
|
|
100
|
+
* @since 0.1.0
|
|
101
|
+
* @category guards
|
|
102
|
+
* @slop
|
|
103
|
+
*/
|
|
104
|
+
export declare const isDigestInput: (value: unknown) => value is DigestInput;
|
|
105
|
+
/**
|
|
106
|
+
* The engine-resolved execution environment a content key is computed under,
|
|
107
|
+
* hashed in its OWN namespace rather than merged into the caller's `layers`
|
|
108
|
+
* and `capabilities`.
|
|
109
|
+
*
|
|
110
|
+
* Three properties matter and none survives a merge:
|
|
111
|
+
*
|
|
112
|
+
* 1. **Non-aliasing.** Concatenating environment material onto the caller's
|
|
113
|
+
* made `caller{fs:["a"]} + env{fs:["b"]}` hash identically to
|
|
114
|
+
* `caller{fs:["a","b"]} + env{}` — a cross-run collision in the very
|
|
115
|
+
* material whose purpose is preventing one.
|
|
116
|
+
* 2. **Declared vs undeclared.** `declared: false` is not the same value as
|
|
117
|
+
* `declared: true` with empty sets.
|
|
118
|
+
* 3. **Composition order.** Environment layers retain declaration order,
|
|
119
|
+
* because plugin and service composition order can change behavior. The
|
|
120
|
+
* caller-owned `ContentIdentity.layers` stays set-normalized.
|
|
121
|
+
*
|
|
122
|
+
* `runScope` is set only when `declared` is `false`: it pins the key to one
|
|
123
|
+
* run, so a step whose environment identity is unknown can never serve a
|
|
124
|
+
* cross-run hit. The union enforces this for typed callers; both
|
|
125
|
+
* {@link content} and {@link dispatchIdentity} validate it at run time.
|
|
126
|
+
*
|
|
127
|
+
* @since 0.1.0
|
|
128
|
+
* @category models
|
|
129
|
+
* @slop
|
|
130
|
+
*/
|
|
131
|
+
export type EnvironmentIdentity = typeof EnvironmentIdentity.Type;
|
|
132
|
+
/**
|
|
133
|
+
* Validated, copyable runtime environment identity. Layer order matters;
|
|
134
|
+
* capability patterns are set-like. Unknown fields are not identity material.
|
|
135
|
+
* @since 1.0.0
|
|
136
|
+
* @category schemas
|
|
137
|
+
*/
|
|
138
|
+
export declare const EnvironmentIdentity: Schema.Union<readonly [Schema.Struct<{
|
|
139
|
+
readonly declared: Schema.Literal<true>;
|
|
140
|
+
readonly layers: Schema.$Array<Schema.String>;
|
|
141
|
+
readonly capabilities: Schema.$Record<Schema.String, Schema.$Array<Schema.String>>;
|
|
142
|
+
readonly runScope: Schema.optionalKey<Schema.Undefined>;
|
|
143
|
+
}>, Schema.Struct<{
|
|
144
|
+
readonly declared: Schema.Literal<false>;
|
|
145
|
+
readonly layers: Schema.$Array<Schema.String>;
|
|
146
|
+
readonly capabilities: Schema.$Record<Schema.String, Schema.$Array<Schema.String>>;
|
|
147
|
+
readonly runScope: Schema.NonEmptyString;
|
|
148
|
+
}>]>;
|
|
149
|
+
/**
|
|
150
|
+
* Caller-owned memoization context for projected dependency-value digests.
|
|
151
|
+
*
|
|
152
|
+
* This is the flows counterpart of Bazel's `ActionKeyContext`: one context is
|
|
153
|
+
* passed through related key computations so shared input material is hashed
|
|
154
|
+
* once. Concurrent requests for the same projection share the in-flight
|
|
155
|
+
* digest. Entries are sound only while each settled `from` value is immutable;
|
|
156
|
+
* callers must create a fresh memo when those values can change.
|
|
157
|
+
*
|
|
158
|
+
* @since 0.1.0
|
|
159
|
+
* @category models
|
|
160
|
+
* @slop
|
|
161
|
+
*/
|
|
162
|
+
export interface DigestMemo {
|
|
163
|
+
readonly digest: (from: string, path: ReadonlyArray<string>, compute: Effect.Effect<StepKey, Schema.SchemaError, Crypto.Crypto>) => Effect.Effect<StepKey, Schema.SchemaError, Crypto.Crypto>;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Creates an empty projected-value digest memo. Projection addresses are
|
|
167
|
+
* JSON-encoded `[from, path]` tuples so segment boundaries are unambiguous.
|
|
168
|
+
*
|
|
169
|
+
* @since 0.1.0
|
|
170
|
+
* @category constructors
|
|
171
|
+
* @slop
|
|
172
|
+
*/
|
|
173
|
+
export declare const makeDigestMemo: () => DigestMemo;
|
|
174
|
+
/**
|
|
175
|
+
* Material describing a sealed or hermetic content-addressed step.
|
|
176
|
+
*
|
|
177
|
+
* @since 0.1.0
|
|
178
|
+
* @category models
|
|
179
|
+
* @slop
|
|
180
|
+
*/
|
|
181
|
+
export interface ContentIdentity {
|
|
182
|
+
readonly body: unknown;
|
|
183
|
+
readonly inputs: Readonly<Record<string, unknown | DigestInput>>;
|
|
184
|
+
readonly layers: ReadonlyArray<string>;
|
|
185
|
+
readonly capabilities: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
186
|
+
readonly environment?: EnvironmentIdentity | undefined;
|
|
187
|
+
readonly hermetic?: {
|
|
188
|
+
readonly readSet: ReadonlyArray<{
|
|
189
|
+
readonly path: string;
|
|
190
|
+
readonly digest: string;
|
|
191
|
+
}>;
|
|
192
|
+
readonly writeSet: ReadonlyArray<FileSet.Entry>;
|
|
193
|
+
readonly removes?: ReadonlyArray<string> | undefined;
|
|
194
|
+
readonly boundaryMode: FileSet.BoundaryMode;
|
|
195
|
+
} | undefined;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Material describing the run-local identity of a non-cacheable step.
|
|
199
|
+
*
|
|
200
|
+
* @since 0.1.0
|
|
201
|
+
* @category models
|
|
202
|
+
* @slop
|
|
203
|
+
*/
|
|
204
|
+
export interface OrdinalIdentity {
|
|
205
|
+
readonly runId: string;
|
|
206
|
+
readonly parentScope?: string | undefined;
|
|
207
|
+
readonly ordinal: number;
|
|
208
|
+
readonly tier: "compensable" | "irreversible" | "unsealed";
|
|
209
|
+
}
|
|
210
|
+
declare const KeyMaterialError_base: Schema.Class<KeyMaterialError, Schema.TaggedStruct<"@smthrs/plan/KeyMaterialError", {
|
|
211
|
+
readonly code: Schema.Literals<readonly ["invalid_environment", "missing_dependency", "non_content_material"]>;
|
|
212
|
+
readonly message: Schema.String;
|
|
213
|
+
}>, Cause.YieldableError>;
|
|
214
|
+
/**
|
|
215
|
+
* Stable failures while resolving graph-local dependency references.
|
|
216
|
+
*
|
|
217
|
+
* @since 0.1.0
|
|
218
|
+
* @category errors
|
|
219
|
+
* @slop
|
|
220
|
+
*/
|
|
221
|
+
export declare class KeyMaterialError extends KeyMaterialError_base {
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Fingerprint for binding one execution to its runtime environment, not an
|
|
225
|
+
* action dispatch key. Uses the same normalization as content/dispatch keys.
|
|
226
|
+
* An omitted environment is distinct from declared-empty and run-scoped ones.
|
|
227
|
+
* @since 1.0.0
|
|
228
|
+
* @category constructors
|
|
229
|
+
*/
|
|
230
|
+
export declare const environmentIdentity: (environment?: EnvironmentIdentity) => Effect.Effect<StoredKey, KeyMaterialError | Schema.SchemaError, Crypto.Crypto>;
|
|
231
|
+
/**
|
|
232
|
+
* Produces a cross-run reusable key for a sealed or hermetic step. Set-like
|
|
233
|
+
* declarations are normalized before serialization; write declarations remain
|
|
234
|
+
* part of the identity even when the step writes nothing.
|
|
235
|
+
*
|
|
236
|
+
* @since 0.1.0
|
|
237
|
+
* @category constructors
|
|
238
|
+
* @slop
|
|
239
|
+
*/
|
|
240
|
+
export declare const content: (identity: ContentIdentity) => Effect.Effect<StepKey, KeyMaterialError | Schema.SchemaError, Crypto.Crypto>;
|
|
241
|
+
/**
|
|
242
|
+
* Produces a run-local ordinal key for compensable, irreversible, or unsealed
|
|
243
|
+
* work. These keys intentionally cannot be reused across runs.
|
|
244
|
+
*
|
|
245
|
+
* @since 0.1.0
|
|
246
|
+
* @category constructors
|
|
247
|
+
* @slop
|
|
248
|
+
*/
|
|
249
|
+
export declare const ordinal: (identity: OrdinalIdentity) => Effect.Effect<StepKey, Schema.SchemaError, Crypto.Crypto>;
|
|
250
|
+
/**
|
|
251
|
+
* Resolves graph-local references to dependency digests and constructs a
|
|
252
|
+
* sealed content key. Use {@link planIdentity} to compile any effect tier.
|
|
253
|
+
*
|
|
254
|
+
* This is the handoff the Flow Builder Brief describes: the planner hands over
|
|
255
|
+
* a topologically ordered sequence of `{ nodeId, material }`, and for each one
|
|
256
|
+
* the compiler substitutes every `Ref`/`Pending` with the *already computed
|
|
257
|
+
* key of the referenced node*. Structural node ids are lookup addresses only
|
|
258
|
+
* and never enter the hashed value — rename a node and nothing re-keys; change
|
|
259
|
+
* what a node consumes and everything downstream of it does.
|
|
260
|
+
*
|
|
261
|
+
* @since 0.1.0
|
|
262
|
+
* @category constructors
|
|
263
|
+
* @slop
|
|
264
|
+
*/
|
|
265
|
+
export declare const fromKeyMaterial: (material: KeyMaterial.KeyMaterial, dependencyDigests: Readonly<Record<string, string>>) => Effect.Effect<StepKey, KeyMaterialError | Schema.SchemaError, Crypto.Crypto>;
|
|
266
|
+
/**
|
|
267
|
+
* Identifies a declaration in a plan, independently of whether its execution
|
|
268
|
+
* can be cached. Sealed material retains its existing content-key format;
|
|
269
|
+
* compensable and irreversible material use a distinct, tier-bearing plan
|
|
270
|
+
* namespace. These fingerprints bind approvals, not cross-run result reuse.
|
|
271
|
+
* Dispatch non-cacheable work under a run-local {@link ordinal} instead.
|
|
272
|
+
*
|
|
273
|
+
* @since 1.0.0
|
|
274
|
+
* @category constructors
|
|
275
|
+
*/
|
|
276
|
+
export declare const planIdentity: (material: KeyMaterial.KeyMaterial, dependencyDigests: Readonly<Record<string, string>>) => Effect.Effect<StepKey, KeyMaterialError | Schema.SchemaError, Crypto.Crypto>;
|
|
277
|
+
/**
|
|
278
|
+
* Projects a settled result along a `Ref` path. Only own data properties
|
|
279
|
+
* resolve, so a missing, inherited, or accessor segment yields `undefined`
|
|
280
|
+
* without invoking a getter. This is a stable, distinct value: a projection that
|
|
281
|
+
* walks off the end of a result is a fact about the graph, not a failure.
|
|
282
|
+
* `undefined` drops out of the canonical form, so it hashes distinctly from
|
|
283
|
+
* every JSON value including `null`.
|
|
284
|
+
*
|
|
285
|
+
* Exported because it is the ONE projection semantics for the value channel:
|
|
286
|
+
* {@link dispatchIdentity} digests what this returns, so every consumer that
|
|
287
|
+
* resolves a `Ref` at execution time must resolve it the same way, or two
|
|
288
|
+
* inputs that key identically could be consumed differently — a stale-hit
|
|
289
|
+
* vector.
|
|
290
|
+
*
|
|
291
|
+
* @since 0.1.0
|
|
292
|
+
* @category utilities
|
|
293
|
+
* @slop
|
|
294
|
+
*/
|
|
295
|
+
export declare const project: (value: unknown, path: ReadonlyArray<string>) => unknown;
|
|
296
|
+
/**
|
|
297
|
+
* The key a dispatch is *cached* under, as distinct from the plan key a node
|
|
298
|
+
* is *identified* by.
|
|
299
|
+
*
|
|
300
|
+
* A plan key folds the resolved keys of every upstream node, transitively, so
|
|
301
|
+
* an edit anywhere upstream re-keys everything below it — even when the edited
|
|
302
|
+
* node's output value is byte for byte what it was before. Bazel does not work
|
|
303
|
+
* that way: `ActionCacheChecker` keys an action by the *content* of its
|
|
304
|
+
* inputs, so an unchanged output stops invalidation dead. The file channel in
|
|
305
|
+
* flows already had that property through the measured boundary digests; the
|
|
306
|
+
* JSON value channel did not, and this is what gives it one.
|
|
307
|
+
*
|
|
308
|
+
* The derivation folds the node's OWN material — body, version, effects,
|
|
309
|
+
* placement, layers, capabilities — and never an upstream key. Each material
|
|
310
|
+
* input contributes content instead: a `Literal` its value, a `Ref` the digest
|
|
311
|
+
* of the settled result of `from` projected along `path`, and a `Pending`
|
|
312
|
+
* nothing beyond its tag. The measured hermetic boundary is folded unchanged.
|
|
313
|
+
*
|
|
314
|
+
* `results` must hold every dependency the material names. The scheduler's
|
|
315
|
+
* halt rule guarantees it: a dependent of failed or skipped work never
|
|
316
|
+
* dispatches, so a `Ref` always resolves against a success.
|
|
317
|
+
*
|
|
318
|
+
* @since 0.1.0
|
|
319
|
+
* @category constructors
|
|
320
|
+
* @slop
|
|
321
|
+
*/
|
|
322
|
+
export declare const dispatchIdentity: (options: {
|
|
323
|
+
readonly material: KeyMaterial.KeyMaterial;
|
|
324
|
+
/** The settled output value of each dependency, by node id. */
|
|
325
|
+
readonly results: Readonly<Record<string, unknown>>;
|
|
326
|
+
readonly hermetic: NonNullable<ContentIdentity["hermetic"]>;
|
|
327
|
+
/** The engine-resolved execution environment this dispatch runs under. */
|
|
328
|
+
readonly environment?: EnvironmentIdentity | undefined;
|
|
329
|
+
/** Reuses projected-value digests while the corresponding settled values remain immutable. */
|
|
330
|
+
readonly digestMemo?: DigestMemo | undefined;
|
|
331
|
+
}) => Effect.Effect<StepKey, KeyMaterialError | Schema.SchemaError, Crypto.Crypto>;
|
|
332
|
+
export {};
|
|
333
|
+
//# sourceMappingURL=StepKey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StepKey.d.ts","sourceRoot":"","sources":["../../src/StepKey.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var StepKey_exports = {};
|
|
30
|
+
__export(StepKey_exports, {
|
|
31
|
+
EnvironmentIdentity: () => EnvironmentIdentity,
|
|
32
|
+
KeyMaterialError: () => KeyMaterialError,
|
|
33
|
+
content: () => content,
|
|
34
|
+
digestInput: () => digestInput,
|
|
35
|
+
dispatchIdentity: () => dispatchIdentity,
|
|
36
|
+
environmentIdentity: () => environmentIdentity,
|
|
37
|
+
fromKeyMaterial: () => fromKeyMaterial,
|
|
38
|
+
isDigestInput: () => isDigestInput,
|
|
39
|
+
makeDigestMemo: () => makeDigestMemo,
|
|
40
|
+
ordinal: () => ordinal,
|
|
41
|
+
planIdentity: () => planIdentity,
|
|
42
|
+
project: () => project
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(StepKey_exports);
|
|
45
|
+
var import_keys = require("@smthrs/keys");
|
|
46
|
+
var Cause = __toESM(require("effect/Cause"), 1);
|
|
47
|
+
var Deferred = __toESM(require("effect/Deferred"), 1);
|
|
48
|
+
var Effect = __toESM(require("effect/Effect"), 1);
|
|
49
|
+
var Exit = __toESM(require("effect/Exit"), 1);
|
|
50
|
+
var Schema = __toESM(require("effect/Schema"), 1);
|
|
51
|
+
const DigestInputTypeId = /* @__PURE__ */ Symbol.for("@smthrs/plan/StepKey/DigestInput");
|
|
52
|
+
const digestInput = (digest, reference) => ({
|
|
53
|
+
[DigestInputTypeId]: DigestInputTypeId,
|
|
54
|
+
digest,
|
|
55
|
+
...reference === void 0 ? {} : { reference: reference.reference },
|
|
56
|
+
...reference?.path === void 0 ? {} : { path: reference.path }
|
|
57
|
+
});
|
|
58
|
+
const isDigestInput = (value) => typeof value === "object" && value !== null && DigestInputTypeId in value && value[DigestInputTypeId] === DigestInputTypeId;
|
|
59
|
+
const EnvironmentIdentity = Schema.Union([
|
|
60
|
+
Schema.Struct({
|
|
61
|
+
declared: Schema.Literal(true),
|
|
62
|
+
layers: Schema.Array(Schema.String),
|
|
63
|
+
capabilities: Schema.Record(Schema.String, Schema.Array(Schema.String)),
|
|
64
|
+
runScope: Schema.optionalKey(Schema.Undefined)
|
|
65
|
+
}),
|
|
66
|
+
Schema.Struct({
|
|
67
|
+
declared: Schema.Literal(false),
|
|
68
|
+
layers: Schema.Array(Schema.String),
|
|
69
|
+
capabilities: Schema.Record(Schema.String, Schema.Array(Schema.String)),
|
|
70
|
+
runScope: Schema.NonEmptyString
|
|
71
|
+
})
|
|
72
|
+
]);
|
|
73
|
+
const makeDigestMemo = () => {
|
|
74
|
+
const entries = /* @__PURE__ */ new Map();
|
|
75
|
+
const digest = (from, path, compute) => Effect.suspend(() => {
|
|
76
|
+
const address = JSON.stringify([from, path]);
|
|
77
|
+
const existing = entries.get(address);
|
|
78
|
+
if (existing !== void 0) {
|
|
79
|
+
return Effect.flatMap(Effect.exit(Deferred.await(existing)), (exit) => {
|
|
80
|
+
if (Exit.isSuccess(exit)) return Effect.succeed(exit.value);
|
|
81
|
+
return Cause.hasInterrupts(exit.cause) ? digest(from, path, compute) : Effect.failCause(exit.cause);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const pending = Deferred.makeUnsafe();
|
|
85
|
+
entries.set(address, pending);
|
|
86
|
+
return compute.pipe(
|
|
87
|
+
Effect.onExit(
|
|
88
|
+
(exit) => Effect.gen(function* () {
|
|
89
|
+
if (Exit.isFailure(exit) && entries.get(address) === pending) {
|
|
90
|
+
entries.delete(address);
|
|
91
|
+
}
|
|
92
|
+
yield* Deferred.done(pending, exit);
|
|
93
|
+
})
|
|
94
|
+
)
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
return { digest };
|
|
98
|
+
};
|
|
99
|
+
class KeyMaterialError extends Schema.TaggedError()("@smthrs/plan/KeyMaterialError", {
|
|
100
|
+
code: Schema.Literals(["invalid_environment", "missing_dependency", "non_content_material"]),
|
|
101
|
+
message: Schema.String
|
|
102
|
+
}) {
|
|
103
|
+
}
|
|
104
|
+
const validateEnvironment = (environment) => {
|
|
105
|
+
const candidate = environment;
|
|
106
|
+
if (candidate.declared === false) {
|
|
107
|
+
if (typeof candidate.runScope !== "string" || candidate.runScope.length === 0) {
|
|
108
|
+
return new KeyMaterialError({
|
|
109
|
+
code: "invalid_environment",
|
|
110
|
+
message: "Undeclared environment identity requires a non-empty runScope"
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return void 0;
|
|
114
|
+
}
|
|
115
|
+
if (candidate.declared === true) {
|
|
116
|
+
return candidate.runScope === void 0 ? void 0 : new KeyMaterialError({
|
|
117
|
+
code: "invalid_environment",
|
|
118
|
+
message: "Declared environment identity must not include runScope"
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return new KeyMaterialError({
|
|
122
|
+
code: "invalid_environment",
|
|
123
|
+
message: "Environment identity requires a boolean declared field"
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
const decodeEnvironment = (environment) => Effect.try({
|
|
127
|
+
try: () => {
|
|
128
|
+
const invalid = validateEnvironment(environment);
|
|
129
|
+
if (invalid !== void 0) throw invalid;
|
|
130
|
+
return Schema.decodeUnknownSync(EnvironmentIdentity)(environment, { onExcessProperty: "error" });
|
|
131
|
+
},
|
|
132
|
+
catch: (cause) => cause instanceof KeyMaterialError ? cause : new KeyMaterialError({
|
|
133
|
+
code: "invalid_environment",
|
|
134
|
+
message: "Environment identity must contain only its declared fields, string layers, and string capability patterns"
|
|
135
|
+
})
|
|
136
|
+
});
|
|
137
|
+
const sortStrings = (values) => [...new Set(values.map((value) => value.normalize("NFC")))].sort();
|
|
138
|
+
const normalizeInputs = (inputs) => Object.fromEntries(
|
|
139
|
+
Object.entries(inputs).map(([name, value]) => [
|
|
140
|
+
name,
|
|
141
|
+
isDigestInput(value) ? {
|
|
142
|
+
kind: "digest",
|
|
143
|
+
digest: value.digest,
|
|
144
|
+
...value.reference === void 0 ? {} : { reference: value.reference },
|
|
145
|
+
...value.path === void 0 ? {} : { path: value.path }
|
|
146
|
+
} : { kind: "literal", value }
|
|
147
|
+
])
|
|
148
|
+
);
|
|
149
|
+
const normalizeCapabilities = (capabilities) => Object.fromEntries(Object.entries(capabilities).map(([group, patterns]) => [group, sortStrings(patterns)]));
|
|
150
|
+
const normalizeEnvironment = (environment) => ({
|
|
151
|
+
declared: environment.declared,
|
|
152
|
+
layers: environment.layers.map((layer) => layer.normalize("NFC")),
|
|
153
|
+
capabilities: normalizeCapabilities(environment.capabilities),
|
|
154
|
+
...environment.runScope === void 0 ? {} : { runScope: environment.runScope }
|
|
155
|
+
});
|
|
156
|
+
const compareCodeUnits = (left, right) => left < right ? -1 : 1;
|
|
157
|
+
const writeEntryKey = (entry) => typeof entry === "string" ? JSON.stringify(["Path", entry]) : entry._tag === "TreeArtifact" ? JSON.stringify([entry._tag, entry.path]) : JSON.stringify([entry._tag, entry.include, entry.exclude ?? null]);
|
|
158
|
+
const normalizeHermetic = (hermetic) => {
|
|
159
|
+
const readSet = [...hermetic.readSet].map((entry) => ({ path: entry.path.normalize("NFC"), digest: entry.digest })).sort((left, right) => {
|
|
160
|
+
if (left.path !== right.path) return left.path < right.path ? -1 : 1;
|
|
161
|
+
return left.digest < right.digest ? -1 : left.digest > right.digest ? 1 : 0;
|
|
162
|
+
}).filter(
|
|
163
|
+
(entry, index, entries) => index === 0 || entry.path !== entries[index - 1].path || entry.digest !== entries[index - 1].digest
|
|
164
|
+
);
|
|
165
|
+
const normalizedWrites = [...hermetic.writeSet].map((entry) => {
|
|
166
|
+
if (typeof entry === "string") return entry.normalize("NFC");
|
|
167
|
+
if (entry._tag === "TreeArtifact") return { ...entry, path: entry.path.normalize("NFC") };
|
|
168
|
+
return {
|
|
169
|
+
...entry,
|
|
170
|
+
include: sortStrings(entry.include),
|
|
171
|
+
...entry.exclude === void 0 ? {} : { exclude: sortStrings(entry.exclude) }
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
const writeSet = [...new Map(normalizedWrites.map((entry) => [writeEntryKey(entry), entry])).entries()].sort(([left], [right]) => compareCodeUnits(left, right)).map(([, entry]) => entry);
|
|
175
|
+
return {
|
|
176
|
+
readSet,
|
|
177
|
+
writeSet,
|
|
178
|
+
...hermetic.removes === void 0 ? {} : { removes: sortStrings(hermetic.removes) },
|
|
179
|
+
boundaryMode: hermetic.boundaryMode
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
const decodeKey = Schema.decodeUnknownEffect(import_keys.DerivedKey);
|
|
183
|
+
const environmentIdentity = (environment) => Effect.gen(function* () {
|
|
184
|
+
const captured = environment === void 0 ? void 0 : yield* decodeEnvironment(environment);
|
|
185
|
+
return yield* decodeKey({
|
|
186
|
+
kind: "execution-environment/v1",
|
|
187
|
+
environment: captured === void 0 ? null : normalizeEnvironment(captured)
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
const content = (identity) => Effect.gen(function* () {
|
|
191
|
+
const environment = identity.environment === void 0 ? void 0 : yield* decodeEnvironment(identity.environment);
|
|
192
|
+
return yield* decodeKey({
|
|
193
|
+
kind: "content",
|
|
194
|
+
body: identity.body,
|
|
195
|
+
inputs: normalizeInputs(identity.inputs),
|
|
196
|
+
layers: sortStrings(identity.layers),
|
|
197
|
+
capabilities: normalizeCapabilities(identity.capabilities),
|
|
198
|
+
...environment === void 0 ? {} : { environment: normalizeEnvironment(environment) },
|
|
199
|
+
...identity.hermetic === void 0 ? {} : { hermetic: normalizeHermetic(identity.hermetic) }
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
const ordinal = (identity) => decodeKey({ kind: "ordinal", ...identity });
|
|
203
|
+
const fromKeyMaterial = (material, dependencyDigests) => {
|
|
204
|
+
if (material.kind !== "sealed") {
|
|
205
|
+
return Effect.fail(
|
|
206
|
+
new KeyMaterialError({
|
|
207
|
+
code: "non_content_material",
|
|
208
|
+
message: `Cannot create a content key for ${material.kind} material`
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
return materialIdentity(material, dependencyDigests).pipe(Effect.flatMap(content));
|
|
213
|
+
};
|
|
214
|
+
const planIdentity = (material, dependencyDigests) => material.kind === "sealed" ? fromKeyMaterial(material, dependencyDigests) : materialIdentity(material, dependencyDigests).pipe(Effect.flatMap(
|
|
215
|
+
(identity) => decodeKey({
|
|
216
|
+
kind: "plan-declaration",
|
|
217
|
+
tier: material.kind,
|
|
218
|
+
body: identity.body,
|
|
219
|
+
inputs: normalizeInputs(identity.inputs),
|
|
220
|
+
layers: sortStrings(identity.layers),
|
|
221
|
+
capabilities: normalizeCapabilities(identity.capabilities)
|
|
222
|
+
})
|
|
223
|
+
));
|
|
224
|
+
const ownDataProperty = (record, from, label) => {
|
|
225
|
+
const descriptor = Object.getOwnPropertyDescriptor(record, from);
|
|
226
|
+
if (descriptor === void 0) {
|
|
227
|
+
return Effect.fail(
|
|
228
|
+
new KeyMaterialError({
|
|
229
|
+
code: "missing_dependency",
|
|
230
|
+
message: `Missing ${label.toLowerCase()} for graph dependency ${from}`
|
|
231
|
+
})
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
if (!("value" in descriptor)) {
|
|
235
|
+
return Effect.fail(
|
|
236
|
+
new KeyMaterialError({
|
|
237
|
+
code: "missing_dependency",
|
|
238
|
+
message: `${label} for graph dependency ${from} must be a data property`
|
|
239
|
+
})
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
return Effect.succeed(descriptor.value);
|
|
243
|
+
};
|
|
244
|
+
const resolveInputs = (material, resolve) => Effect.gen(function* () {
|
|
245
|
+
const inputs = {};
|
|
246
|
+
for (let index = 0; index < material.inputs.length; index++) {
|
|
247
|
+
const input = material.inputs[index];
|
|
248
|
+
if (input._tag === "Literal") {
|
|
249
|
+
inputs[String(index)] = input.value;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const digest = yield* resolve(input);
|
|
253
|
+
inputs[String(index)] = input._tag === "Pending" ? digestInput(digest, { reference: "pending" }) : input.path.length > 0 ? digestInput(digest, { reference: "ref-projected", path: input.path }) : digestInput(digest, { reference: "ref" });
|
|
254
|
+
}
|
|
255
|
+
return inputs;
|
|
256
|
+
});
|
|
257
|
+
const materialIdentity = (material, dependencyDigests) => resolveInputs(
|
|
258
|
+
material,
|
|
259
|
+
(reference) => Effect.flatMap(ownDataProperty(dependencyDigests, reference.from, "Digest"), (digest) => typeof digest === "string" ? Effect.succeed(digest) : Effect.fail(
|
|
260
|
+
new KeyMaterialError({
|
|
261
|
+
code: "missing_dependency",
|
|
262
|
+
message: `Digest for graph dependency ${reference.from} must be a string`
|
|
263
|
+
})
|
|
264
|
+
))
|
|
265
|
+
).pipe(Effect.map((inputs) => ({
|
|
266
|
+
body: materialBody(material),
|
|
267
|
+
inputs,
|
|
268
|
+
layers: material.layers,
|
|
269
|
+
capabilities: { declared: material.capabilities }
|
|
270
|
+
})));
|
|
271
|
+
const materialBody = (material) => ({
|
|
272
|
+
version: material.version,
|
|
273
|
+
declaration: material.body,
|
|
274
|
+
...material.nondeterministic === void 0 ? {} : { nondeterministic: material.nondeterministic },
|
|
275
|
+
...material.effects === void 0 ? {} : { effects: material.effects },
|
|
276
|
+
...material.placement === void 0 ? {} : { placement: material.placement }
|
|
277
|
+
});
|
|
278
|
+
const orderingOnly = "ordering-only";
|
|
279
|
+
const project = (value, path) => {
|
|
280
|
+
let current = value;
|
|
281
|
+
for (const segment of path) {
|
|
282
|
+
if (typeof current !== "object" || current === null) return void 0;
|
|
283
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, segment);
|
|
284
|
+
if (descriptor === void 0 || !("value" in descriptor)) return void 0;
|
|
285
|
+
current = descriptor.value;
|
|
286
|
+
}
|
|
287
|
+
return current;
|
|
288
|
+
};
|
|
289
|
+
const dispatchIdentity = (options) => Effect.gen(function* () {
|
|
290
|
+
const material = options.material;
|
|
291
|
+
if (material.kind !== "sealed") {
|
|
292
|
+
return yield* new KeyMaterialError({
|
|
293
|
+
code: "non_content_material",
|
|
294
|
+
message: `Cannot create a dispatch key for ${material.kind} material`
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
const environment = options.environment === void 0 ? void 0 : yield* decodeEnvironment(options.environment);
|
|
298
|
+
const inputs = yield* resolveInputs(material, (reference) => reference._tag === "Pending" ? Effect.succeed(orderingOnly) : Effect.flatMap(ownDataProperty(options.results, reference.from, "Settled result"), (settled) => {
|
|
299
|
+
const compute = decodeKey({ kind: "input-value", value: project(settled, reference.path) });
|
|
300
|
+
return options.digestMemo === void 0 ? compute : options.digestMemo.digest(reference.from, reference.path, compute);
|
|
301
|
+
}));
|
|
302
|
+
return yield* content({
|
|
303
|
+
body: materialBody(material),
|
|
304
|
+
inputs,
|
|
305
|
+
layers: material.layers,
|
|
306
|
+
capabilities: { declared: material.capabilities },
|
|
307
|
+
environment,
|
|
308
|
+
hermetic: options.hermetic
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
//# sourceMappingURL=StepKey.js.map
|