@descryy/core 0.1.0
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/LICENSE +21 -0
- package/dist/capabilities/index.d.ts +3 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +2 -0
- package/dist/capabilities/index.js.map +1 -0
- package/dist/capabilities/preconditions.d.ts +99 -0
- package/dist/capabilities/preconditions.d.ts.map +1 -0
- package/dist/capabilities/preconditions.js +94 -0
- package/dist/capabilities/preconditions.js.map +1 -0
- package/dist/contracts/engine.d.ts +90 -0
- package/dist/contracts/engine.d.ts.map +1 -0
- package/dist/contracts/engine.js +291 -0
- package/dist/contracts/engine.js.map +1 -0
- package/dist/contracts/index.d.ts +9 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +5 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/orphans.d.ts +96 -0
- package/dist/contracts/orphans.d.ts.map +1 -0
- package/dist/contracts/orphans.js +241 -0
- package/dist/contracts/orphans.js.map +1 -0
- package/dist/contracts/paths.d.ts +132 -0
- package/dist/contracts/paths.d.ts.map +1 -0
- package/dist/contracts/paths.js +245 -0
- package/dist/contracts/paths.js.map +1 -0
- package/dist/contracts/shapes.d.ts +99 -0
- package/dist/contracts/shapes.d.ts.map +1 -0
- package/dist/contracts/shapes.js +196 -0
- package/dist/contracts/shapes.js.map +1 -0
- package/dist/governance/budget.d.ts +89 -0
- package/dist/governance/budget.d.ts.map +1 -0
- package/dist/governance/budget.js +101 -0
- package/dist/governance/budget.js.map +1 -0
- package/dist/governance/candidate-boundary.d.ts +49 -0
- package/dist/governance/candidate-boundary.d.ts.map +1 -0
- package/dist/governance/candidate-boundary.js +51 -0
- package/dist/governance/candidate-boundary.js.map +1 -0
- package/dist/governance/escalation.d.ts +63 -0
- package/dist/governance/escalation.d.ts.map +1 -0
- package/dist/governance/escalation.js +72 -0
- package/dist/governance/escalation.js.map +1 -0
- package/dist/governance/fact-boundary.d.ts +77 -0
- package/dist/governance/fact-boundary.d.ts.map +1 -0
- package/dist/governance/fact-boundary.js +91 -0
- package/dist/governance/fact-boundary.js.map +1 -0
- package/dist/governance/finding-funnel.d.ts +109 -0
- package/dist/governance/finding-funnel.d.ts.map +1 -0
- package/dist/governance/finding-funnel.js +137 -0
- package/dist/governance/finding-funnel.js.map +1 -0
- package/dist/governance/index.d.ts +11 -0
- package/dist/governance/index.d.ts.map +1 -0
- package/dist/governance/index.js +6 -0
- package/dist/governance/index.js.map +1 -0
- package/dist/graph/build.d.ts +127 -0
- package/dist/graph/build.d.ts.map +1 -0
- package/dist/graph/build.js +0 -0
- package/dist/graph/build.js.map +1 -0
- package/dist/graph/cross-language.d.ts +70 -0
- package/dist/graph/cross-language.d.ts.map +1 -0
- package/dist/graph/cross-language.js +104 -0
- package/dist/graph/cross-language.js.map +1 -0
- package/dist/graph/index.d.ts +11 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +6 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/merge.d.ts +95 -0
- package/dist/graph/merge.d.ts.map +1 -0
- package/dist/graph/merge.js +131 -0
- package/dist/graph/merge.js.map +1 -0
- package/dist/graph/persist.d.ts +42 -0
- package/dist/graph/persist.d.ts.map +1 -0
- package/dist/graph/persist.js +52 -0
- package/dist/graph/persist.js.map +1 -0
- package/dist/graph/runtime-confirmation.d.ts +169 -0
- package/dist/graph/runtime-confirmation.d.ts.map +1 -0
- package/dist/graph/runtime-confirmation.js +318 -0
- package/dist/graph/runtime-confirmation.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger/index.d.ts +5 -0
- package/dist/ledger/index.d.ts.map +1 -0
- package/dist/ledger/index.js +3 -0
- package/dist/ledger/index.js.map +1 -0
- package/dist/ledger/mint.d.ts +81 -0
- package/dist/ledger/mint.d.ts.map +1 -0
- package/dist/ledger/mint.js +138 -0
- package/dist/ledger/mint.js.map +1 -0
- package/dist/ledger/resolve.d.ts +124 -0
- package/dist/ledger/resolve.d.ts.map +1 -0
- package/dist/ledger/resolve.js +290 -0
- package/dist/ledger/resolve.js.map +1 -0
- package/dist/multipr/fingerprint.d.ts +56 -0
- package/dist/multipr/fingerprint.d.ts.map +1 -0
- package/dist/multipr/fingerprint.js +169 -0
- package/dist/multipr/fingerprint.js.map +1 -0
- package/dist/multipr/hidden-dependency.d.ts +120 -0
- package/dist/multipr/hidden-dependency.d.ts.map +1 -0
- package/dist/multipr/hidden-dependency.js +185 -0
- package/dist/multipr/hidden-dependency.js.map +1 -0
- package/dist/multipr/index.d.ts +14 -0
- package/dist/multipr/index.d.ts.map +1 -0
- package/dist/multipr/index.js +8 -0
- package/dist/multipr/index.js.map +1 -0
- package/dist/multipr/mechanical.d.ts +239 -0
- package/dist/multipr/mechanical.d.ts.map +1 -0
- package/dist/multipr/mechanical.js +333 -0
- package/dist/multipr/mechanical.js.map +1 -0
- package/dist/multipr/migration-heads.d.ts +160 -0
- package/dist/multipr/migration-heads.d.ts.map +1 -0
- package/dist/multipr/migration-heads.js +212 -0
- package/dist/multipr/migration-heads.js.map +1 -0
- package/dist/multipr/overlap.d.ts +186 -0
- package/dist/multipr/overlap.d.ts.map +1 -0
- package/dist/multipr/overlap.js +254 -0
- package/dist/multipr/overlap.js.map +1 -0
- package/dist/multipr/scope-store.d.ts +105 -0
- package/dist/multipr/scope-store.d.ts.map +1 -0
- package/dist/multipr/scope-store.js +182 -0
- package/dist/multipr/scope-store.js.map +1 -0
- package/dist/multipr/superseded.d.ts +133 -0
- package/dist/multipr/superseded.d.ts.map +1 -0
- package/dist/multipr/superseded.js +128 -0
- package/dist/multipr/superseded.js.map +1 -0
- package/dist/query/confirmed-facts.d.ts +56 -0
- package/dist/query/confirmed-facts.d.ts.map +1 -0
- package/dist/query/confirmed-facts.js +64 -0
- package/dist/query/confirmed-facts.js.map +1 -0
- package/dist/query/declared-value-closure.d.ts +126 -0
- package/dist/query/declared-value-closure.d.ts.map +1 -0
- package/dist/query/declared-value-closure.js +156 -0
- package/dist/query/declared-value-closure.js.map +1 -0
- package/dist/query/index.d.ts +28 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +15 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/memory.d.ts +20 -0
- package/dist/query/memory.d.ts.map +1 -0
- package/dist/query/memory.js +55 -0
- package/dist/query/memory.js.map +1 -0
- package/dist/query/provider.d.ts +127 -0
- package/dist/query/provider.d.ts.map +1 -0
- package/dist/query/provider.js +54 -0
- package/dist/query/provider.js.map +1 -0
- package/dist/query/queries.d.ts +119 -0
- package/dist/query/queries.d.ts.map +1 -0
- package/dist/query/queries.js +267 -0
- package/dist/query/queries.js.map +1 -0
- package/dist/query/refusal-fetch.d.ts +114 -0
- package/dist/query/refusal-fetch.d.ts.map +1 -0
- package/dist/query/refusal-fetch.js +200 -0
- package/dist/query/refusal-fetch.js.map +1 -0
- package/dist/query/refusal-questions.d.ts +123 -0
- package/dist/query/refusal-questions.d.ts.map +1 -0
- package/dist/query/refusal-questions.js +122 -0
- package/dist/query/refusal-questions.js.map +1 -0
- package/dist/query/root-cause-score.d.ts +218 -0
- package/dist/query/root-cause-score.d.ts.map +1 -0
- package/dist/query/root-cause-score.js +251 -0
- package/dist/query/root-cause-score.js.map +1 -0
- package/dist/query/row-closure-picture.d.ts +108 -0
- package/dist/query/row-closure-picture.d.ts.map +1 -0
- package/dist/query/row-closure-picture.js +131 -0
- package/dist/query/row-closure-picture.js.map +1 -0
- package/dist/query/similar-incidents.d.ts +76 -0
- package/dist/query/similar-incidents.d.ts.map +1 -0
- package/dist/query/similar-incidents.js +169 -0
- package/dist/query/similar-incidents.js.map +1 -0
- package/dist/query/sqlite.d.ts +13 -0
- package/dist/query/sqlite.d.ts.map +1 -0
- package/dist/query/sqlite.js +54 -0
- package/dist/query/sqlite.js.map +1 -0
- package/dist/query/traverse.d.ts +110 -0
- package/dist/query/traverse.d.ts.map +1 -0
- package/dist/query/traverse.js +0 -0
- package/dist/query/traverse.js.map +1 -0
- package/dist/query/unresolved.d.ts +195 -0
- package/dist/query/unresolved.d.ts.map +1 -0
- package/dist/query/unresolved.js +225 -0
- package/dist/query/unresolved.js.map +1 -0
- package/dist/query/verification-status.d.ts +134 -0
- package/dist/query/verification-status.d.ts.map +1 -0
- package/dist/query/verification-status.js +163 -0
- package/dist/query/verification-status.js.map +1 -0
- package/dist/recording/index.d.ts +26 -0
- package/dist/recording/index.d.ts.map +1 -0
- package/dist/recording/index.js +30 -0
- package/dist/recording/index.js.map +1 -0
- package/dist/recording/migrate.d.ts +22 -0
- package/dist/recording/migrate.d.ts.map +1 -0
- package/dist/recording/migrate.js +51 -0
- package/dist/recording/migrate.js.map +1 -0
- package/dist/recording/reader.d.ts +27 -0
- package/dist/recording/reader.d.ts.map +1 -0
- package/dist/recording/reader.js +55 -0
- package/dist/recording/reader.js.map +1 -0
- package/dist/recording/redact.d.ts +63 -0
- package/dist/recording/redact.d.ts.map +1 -0
- package/dist/recording/redact.js +98 -0
- package/dist/recording/redact.js.map +1 -0
- package/dist/recording/schema.d.ts +41 -0
- package/dist/recording/schema.d.ts.map +1 -0
- package/dist/recording/schema.js +104 -0
- package/dist/recording/schema.js.map +1 -0
- package/dist/recording/writer.d.ts +120 -0
- package/dist/recording/writer.d.ts.map +1 -0
- package/dist/recording/writer.js +81 -0
- package/dist/recording/writer.js.map +1 -0
- package/dist/scoping/fanout.d.ts +86 -0
- package/dist/scoping/fanout.d.ts.map +1 -0
- package/dist/scoping/fanout.js +95 -0
- package/dist/scoping/fanout.js.map +1 -0
- package/dist/scoping/index.d.ts +10 -0
- package/dist/scoping/index.d.ts.map +1 -0
- package/dist/scoping/index.js +6 -0
- package/dist/scoping/index.js.map +1 -0
- package/dist/scoping/score.d.ts +83 -0
- package/dist/scoping/score.d.ts.map +1 -0
- package/dist/scoping/score.js +125 -0
- package/dist/scoping/score.js.map +1 -0
- package/dist/scoping/tiers.d.ts +32 -0
- package/dist/scoping/tiers.d.ts.map +1 -0
- package/dist/scoping/tiers.js +35 -0
- package/dist/scoping/tiers.js.map +1 -0
- package/dist/scoping/traverse.d.ts +60 -0
- package/dist/scoping/traverse.d.ts.map +1 -0
- package/dist/scoping/traverse.js +172 -0
- package/dist/scoping/traverse.js.map +1 -0
- package/dist/scoping/weights.d.ts +97 -0
- package/dist/scoping/weights.d.ts.map +1 -0
- package/dist/scoping/weights.js +167 -0
- package/dist/scoping/weights.js.map +1 -0
- package/dist/sources/git/diff.d.ts +53 -0
- package/dist/sources/git/diff.d.ts.map +1 -0
- package/dist/sources/git/diff.js +140 -0
- package/dist/sources/git/diff.js.map +1 -0
- package/dist/sources/git/env.d.ts +2 -0
- package/dist/sources/git/env.d.ts.map +1 -0
- package/dist/sources/git/env.js +21 -0
- package/dist/sources/git/env.js.map +1 -0
- package/dist/sources/git/history.d.ts +140 -0
- package/dist/sources/git/history.d.ts.map +1 -0
- package/dist/sources/git/history.js +314 -0
- package/dist/sources/git/history.js.map +1 -0
- package/dist/sources/git/index.d.ts +7 -0
- package/dist/sources/git/index.d.ts.map +1 -0
- package/dist/sources/git/index.js +4 -0
- package/dist/sources/git/index.js.map +1 -0
- package/dist/sources/git/source.d.ts +95 -0
- package/dist/sources/git/source.d.ts.map +1 -0
- package/dist/sources/git/source.js +0 -0
- package/dist/sources/git/source.js.map +1 -0
- package/dist/sources/migrations/dialects.d.ts +59 -0
- package/dist/sources/migrations/dialects.d.ts.map +1 -0
- package/dist/sources/migrations/dialects.js +84 -0
- package/dist/sources/migrations/dialects.js.map +1 -0
- package/dist/sources/migrations/index.d.ts +3 -0
- package/dist/sources/migrations/index.d.ts.map +1 -0
- package/dist/sources/migrations/index.js +2 -0
- package/dist/sources/migrations/index.js.map +1 -0
- package/dist/sources/migrations/read.d.ts +113 -0
- package/dist/sources/migrations/read.d.ts.map +1 -0
- package/dist/sources/migrations/read.js +270 -0
- package/dist/sources/migrations/read.js.map +1 -0
- package/dist/sources/workspace/index.d.ts +3 -0
- package/dist/sources/workspace/index.d.ts.map +1 -0
- package/dist/sources/workspace/index.js +2 -0
- package/dist/sources/workspace/index.js.map +1 -0
- package/dist/sources/workspace/workspace.d.ts +64 -0
- package/dist/sources/workspace/workspace.d.ts.map +1 -0
- package/dist/sources/workspace/workspace.js +294 -0
- package/dist/sources/workspace/workspace.js.map +1 -0
- package/dist/store/driver/driver.d.ts +82 -0
- package/dist/store/driver/driver.d.ts.map +1 -0
- package/dist/store/driver/driver.js +31 -0
- package/dist/store/driver/driver.js.map +1 -0
- package/dist/store/driver/node-sqlite.d.ts +28 -0
- package/dist/store/driver/node-sqlite.d.ts.map +1 -0
- package/dist/store/driver/node-sqlite.js +188 -0
- package/dist/store/driver/node-sqlite.js.map +1 -0
- package/dist/store/index.d.ts +33 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +46 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/migrate.d.ts +68 -0
- package/dist/store/migrate.d.ts.map +1 -0
- package/dist/store/migrate.js +140 -0
- package/dist/store/migrate.js.map +1 -0
- package/dist/store/reader.d.ts +127 -0
- package/dist/store/reader.d.ts.map +1 -0
- package/dist/store/reader.js +269 -0
- package/dist/store/reader.js.map +1 -0
- package/dist/store/schema.d.ts +87 -0
- package/dist/store/schema.d.ts.map +1 -0
- package/dist/store/schema.js +322 -0
- package/dist/store/schema.js.map +1 -0
- package/dist/store/writer.d.ts +116 -0
- package/dist/store/writer.d.ts.map +1 -0
- package/dist/store/writer.js +279 -0
- package/dist/store/writer.js.map +1 -0
- package/dist/tiers/certify.d.ts +375 -0
- package/dist/tiers/certify.d.ts.map +1 -0
- package/dist/tiers/certify.js +434 -0
- package/dist/tiers/certify.js.map +1 -0
- package/dist/tiers/index.d.ts +5 -0
- package/dist/tiers/index.d.ts.map +1 -0
- package/dist/tiers/index.js +3 -0
- package/dist/tiers/index.js.map +1 -0
- package/dist/tiers/ladder.d.ts +73 -0
- package/dist/tiers/ladder.d.ts.map +1 -0
- package/dist/tiers/ladder.js +197 -0
- package/dist/tiers/ladder.js.map +1 -0
- package/dist/validation/config-graph.d.ts +104 -0
- package/dist/validation/config-graph.d.ts.map +1 -0
- package/dist/validation/config-graph.js +205 -0
- package/dist/validation/config-graph.js.map +1 -0
- package/dist/validation/env.d.ts +243 -0
- package/dist/validation/env.d.ts.map +1 -0
- package/dist/validation/env.js +447 -0
- package/dist/validation/env.js.map +1 -0
- package/dist/validation/index.d.ts +16 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +9 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/join-substitution.d.ts +186 -0
- package/dist/validation/join-substitution.d.ts.map +1 -0
- package/dist/validation/join-substitution.js +191 -0
- package/dist/validation/join-substitution.js.map +1 -0
- package/dist/validation/migrations.d.ts +133 -0
- package/dist/validation/migrations.d.ts.map +1 -0
- package/dist/validation/migrations.js +315 -0
- package/dist/validation/migrations.js.map +1 -0
- package/dist/validation/nodes.d.ts +32 -0
- package/dist/validation/nodes.d.ts.map +1 -0
- package/dist/validation/nodes.js +45 -0
- package/dist/validation/nodes.js.map +1 -0
- package/dist/validation/rollback.d.ts +124 -0
- package/dist/validation/rollback.d.ts.map +1 -0
- package/dist/validation/rollback.js +302 -0
- package/dist/validation/rollback.js.map +1 -0
- package/dist/validation/route-drift.d.ts +108 -0
- package/dist/validation/route-drift.d.ts.map +1 -0
- package/dist/validation/route-drift.js +103 -0
- package/dist/validation/route-drift.js.map +1 -0
- package/dist/validation/sources.d.ts +153 -0
- package/dist/validation/sources.d.ts.map +1 -0
- package/dist/validation/sources.js +409 -0
- package/dist/validation/sources.js.map +1 -0
- package/dist/worker/index.d.ts +5 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +3 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/pool.d.ts +73 -0
- package/dist/worker/pool.d.ts.map +1 -0
- package/dist/worker/pool.js +203 -0
- package/dist/worker/pool.js.map +1 -0
- package/dist/worker/protocol.d.ts +91 -0
- package/dist/worker/protocol.d.ts.map +1 -0
- package/dist/worker/protocol.js +19 -0
- package/dist/worker/protocol.js.map +1 -0
- package/dist/worker/traversal.worker.d.ts +25 -0
- package/dist/worker/traversal.worker.d.ts.map +1 -0
- package/dist/worker/traversal.worker.js +124 -0
- package/dist/worker/traversal.worker.js.map +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TIER-A-2's value-lookup half — DEC-223, DEC-136.
|
|
3
|
+
*
|
|
4
|
+
* DEC-120/DEC-121 (`validation/{env,sources}.ts`, Lane D) already answer *"is
|
|
5
|
+
* this variable declared anywhere"*. A refusal-ledger row needs a different
|
|
6
|
+
* question answered: *"what value does this blocked blockedBy hold, per
|
|
7
|
+
* environment, from what the repository already has on disk."* Same
|
|
8
|
+
* declaration surfaces, a different consumer — this file only reads
|
|
9
|
+
* `sources.ts`'s exports, it does not change them. See `parallel-agents.md`
|
|
10
|
+
* §3's ledger-lookup row.
|
|
11
|
+
*
|
|
12
|
+
* ## What this deliberately does not take as input, and why
|
|
13
|
+
*
|
|
14
|
+
* The obvious signature would be `resolve(row: UnresolvedRef)`. `UnresolvedRef`
|
|
15
|
+
* does not carry the blocked blockedBy — `rawTarget` is the callee
|
|
16
|
+
* (`"fetch"`, `"api.get"`), and `reason` is human prose with no variable name
|
|
17
|
+
* in it (confirmed by reading real rows: the same sentence fires for every
|
|
18
|
+
* `api.<verb>` call regardless of which variable blocks it). DEC-223 §1 asks
|
|
19
|
+
* for `attrs.blockedBy` to carry this and it does not exist in any shipped
|
|
20
|
+
* adapter yet — that request is open, addressed to whoever ships the next
|
|
21
|
+
* ledger-producing change. So this takes an **blockedBy string** directly.
|
|
22
|
+
* A caller with `attrs.blockedBy` available passes it straight through; a
|
|
23
|
+
* caller without it (today, every real batch) supplies whatever it already
|
|
24
|
+
* determined by reading source — which is what the TIER-A-2 measurement this
|
|
25
|
+
* module was built to close used.
|
|
26
|
+
*
|
|
27
|
+
* ## Formats read
|
|
28
|
+
*
|
|
29
|
+
* `.env*` and `wrangler.toml`'s `[vars]`/`[env.<name>.vars]` tables
|
|
30
|
+
* (`REQUEST-D-wrangler-vars-format.md`, DEC-245). Container manifests and
|
|
31
|
+
* image builds are already in `sources.ts`'s declaration scan but that scan
|
|
32
|
+
* discards the *value* half — it only proves a name is declared, never what
|
|
33
|
+
* it is bound to — and neither measured repository's real questions resolve
|
|
34
|
+
* through either format.
|
|
35
|
+
*
|
|
36
|
+
* The wrangler reading itself lives in `sources.ts` — `readWranglerVarsEntries`
|
|
37
|
+
* — the same file that owns every other format's structural read. This module
|
|
38
|
+
* only turns its output into `DeclaredEnvValue`s, the same way it already does
|
|
39
|
+
* for `readDotenvValues`; one reader per format, never a second one for the
|
|
40
|
+
* same file here.
|
|
41
|
+
*
|
|
42
|
+
* ## Never guessed, always refused
|
|
43
|
+
*
|
|
44
|
+
* `candidateEnvNames` returns `[]` — never a best guess — for any blockedBy
|
|
45
|
+
* shape it does not recognise. An blockedBy this module cannot parse stays
|
|
46
|
+
* exactly as open as it was before this module existed; principle 2 applies
|
|
47
|
+
* to a lookup the same way it applies to an edge.
|
|
48
|
+
*/
|
|
49
|
+
import type { ResolutionLevel } from "@descryy/ir";
|
|
50
|
+
import type { ConfigSurface, SurfaceScan } from "./../validation/sources.ts";
|
|
51
|
+
/** A value this repository states for one variable, in one environment. */
|
|
52
|
+
export interface DeclaredEnvValue {
|
|
53
|
+
readonly name: string;
|
|
54
|
+
/** Derived from the surface's filename — see `environmentOf`'s header. */
|
|
55
|
+
readonly environment: string;
|
|
56
|
+
readonly value: string;
|
|
57
|
+
readonly file: string;
|
|
58
|
+
readonly line: number;
|
|
59
|
+
/** `.env.<environment>.local` — Vite's convention, wins over the non-local peer. */
|
|
60
|
+
readonly local: boolean;
|
|
61
|
+
/** `.env.example` and its siblings — documents that the name exists; the value is a placeholder. */
|
|
62
|
+
readonly example: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** One candidate that satisfied an blockedBy, in one environment. */
|
|
65
|
+
export interface ResolvedValue {
|
|
66
|
+
readonly environment: string;
|
|
67
|
+
/** Which candidate name actually answered — relevant when `blockedBy` is a fallback chain. */
|
|
68
|
+
readonly name: string;
|
|
69
|
+
readonly value: string;
|
|
70
|
+
readonly file: string;
|
|
71
|
+
readonly line: number;
|
|
72
|
+
readonly local: boolean;
|
|
73
|
+
}
|
|
74
|
+
export interface LedgerAccessorResolution {
|
|
75
|
+
readonly blockedBy: string;
|
|
76
|
+
/** `[]` means the blockedBy's shape was not recognised — refused, not guessed. */
|
|
77
|
+
readonly candidates: readonly string[];
|
|
78
|
+
/** One per environment for which some candidate resolved. */
|
|
79
|
+
readonly resolved: readonly ResolvedValue[];
|
|
80
|
+
/**
|
|
81
|
+
* Every environment this repository's own `.env*` naming asserts exists —
|
|
82
|
+
* from filenames actually present, including example/placeholder files.
|
|
83
|
+
* Never invented: a repository with no `.env.production*` file anywhere
|
|
84
|
+
* does not get `"production"` added here on the assumption that one must
|
|
85
|
+
* exist. That is the difference between disclosing a real gap and
|
|
86
|
+
* guessing at one.
|
|
87
|
+
*/
|
|
88
|
+
readonly knownEnvironments: readonly string[];
|
|
89
|
+
/** `knownEnvironments` minus `resolved`'s environments — the honest "still open" list. */
|
|
90
|
+
readonly unresolvedEnvironments: readonly string[];
|
|
91
|
+
/**
|
|
92
|
+
* DEC-244's precedent (corrected from an earlier mis-citation of DEC-111,
|
|
93
|
+
* which is unrelated): a direct read of a declarative document needs no
|
|
94
|
+
* resolution and is treated as R3, the same class of evidence
|
|
95
|
+
* `adapter-openapi`/`adapter-sql` use to reach R3 with no language adapter
|
|
96
|
+
* at all. Present only when at least one candidate was recognised — an
|
|
97
|
+
* unparseable blockedBy has no resolution to state because nothing was
|
|
98
|
+
* read.
|
|
99
|
+
*/
|
|
100
|
+
readonly resolution: ResolutionLevel | null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The candidate variable names an blockedBy could resolve to, in the order
|
|
104
|
+
* they would actually be tried at runtime. `[]` when any side of the
|
|
105
|
+
* blockedBy is neither a recognised accessor nor a literal fallback —
|
|
106
|
+
* refused as a whole rather than partially understood, per rule 2.
|
|
107
|
+
*/
|
|
108
|
+
export declare function candidateEnvNames(blockedBy: string): readonly string[];
|
|
109
|
+
/**
|
|
110
|
+
* Every value this repository's `.env*` and `wrangler.toml` surfaces state,
|
|
111
|
+
* across every environment they name. Read once per repository;
|
|
112
|
+
* `resolveAgainstDeclared` runs many times against the result rather than
|
|
113
|
+
* rescanning per ledger row.
|
|
114
|
+
*/
|
|
115
|
+
export declare function readDeclaredEnvValues(root: string, scan: SurfaceScan, readFile: (file: string) => string): readonly DeclaredEnvValue[];
|
|
116
|
+
/**
|
|
117
|
+
* Resolve one blocked blockedBy against every declared value this
|
|
118
|
+
* repository already has on disk. Never invents an environment, never picks
|
|
119
|
+
* a value from a different repository, never treats an example file's
|
|
120
|
+
* placeholder as an answer.
|
|
121
|
+
*/
|
|
122
|
+
export declare function resolveAgainstDeclared(blockedBy: string, declared: readonly DeclaredEnvValue[]): LedgerAccessorResolution;
|
|
123
|
+
export type { ConfigSurface };
|
|
124
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/ledger/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAG7E,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,oGAAoG;IACpG,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,8FAA8F;IAC9F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,kFAAkF;IAClF,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAC5C;;;;;;;OAOG;IACH,QAAQ,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9C,0FAA0F;IAC1F,QAAQ,CAAC,sBAAsB,EAAE,SAAS,MAAM,EAAE,CAAC;IACnD;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;CAC7C;AA2DD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CActE;AAsCD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,gBAAgB,EAAE,CA8CtI;AAgBD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GACpC,wBAAwB,CAiD1B;AAED,YAAY,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TIER-A-2's value-lookup half — DEC-223, DEC-136.
|
|
3
|
+
*
|
|
4
|
+
* DEC-120/DEC-121 (`validation/{env,sources}.ts`, Lane D) already answer *"is
|
|
5
|
+
* this variable declared anywhere"*. A refusal-ledger row needs a different
|
|
6
|
+
* question answered: *"what value does this blocked blockedBy hold, per
|
|
7
|
+
* environment, from what the repository already has on disk."* Same
|
|
8
|
+
* declaration surfaces, a different consumer — this file only reads
|
|
9
|
+
* `sources.ts`'s exports, it does not change them. See `parallel-agents.md`
|
|
10
|
+
* §3's ledger-lookup row.
|
|
11
|
+
*
|
|
12
|
+
* ## What this deliberately does not take as input, and why
|
|
13
|
+
*
|
|
14
|
+
* The obvious signature would be `resolve(row: UnresolvedRef)`. `UnresolvedRef`
|
|
15
|
+
* does not carry the blocked blockedBy — `rawTarget` is the callee
|
|
16
|
+
* (`"fetch"`, `"api.get"`), and `reason` is human prose with no variable name
|
|
17
|
+
* in it (confirmed by reading real rows: the same sentence fires for every
|
|
18
|
+
* `api.<verb>` call regardless of which variable blocks it). DEC-223 §1 asks
|
|
19
|
+
* for `attrs.blockedBy` to carry this and it does not exist in any shipped
|
|
20
|
+
* adapter yet — that request is open, addressed to whoever ships the next
|
|
21
|
+
* ledger-producing change. So this takes an **blockedBy string** directly.
|
|
22
|
+
* A caller with `attrs.blockedBy` available passes it straight through; a
|
|
23
|
+
* caller without it (today, every real batch) supplies whatever it already
|
|
24
|
+
* determined by reading source — which is what the TIER-A-2 measurement this
|
|
25
|
+
* module was built to close used.
|
|
26
|
+
*
|
|
27
|
+
* ## Formats read
|
|
28
|
+
*
|
|
29
|
+
* `.env*` and `wrangler.toml`'s `[vars]`/`[env.<name>.vars]` tables
|
|
30
|
+
* (`REQUEST-D-wrangler-vars-format.md`, DEC-245). Container manifests and
|
|
31
|
+
* image builds are already in `sources.ts`'s declaration scan but that scan
|
|
32
|
+
* discards the *value* half — it only proves a name is declared, never what
|
|
33
|
+
* it is bound to — and neither measured repository's real questions resolve
|
|
34
|
+
* through either format.
|
|
35
|
+
*
|
|
36
|
+
* The wrangler reading itself lives in `sources.ts` — `readWranglerVarsEntries`
|
|
37
|
+
* — the same file that owns every other format's structural read. This module
|
|
38
|
+
* only turns its output into `DeclaredEnvValue`s, the same way it already does
|
|
39
|
+
* for `readDotenvValues`; one reader per format, never a second one for the
|
|
40
|
+
* same file here.
|
|
41
|
+
*
|
|
42
|
+
* ## Never guessed, always refused
|
|
43
|
+
*
|
|
44
|
+
* `candidateEnvNames` returns `[]` — never a best guess — for any blockedBy
|
|
45
|
+
* shape it does not recognise. An blockedBy this module cannot parse stays
|
|
46
|
+
* exactly as open as it was before this module existed; principle 2 applies
|
|
47
|
+
* to a lookup the same way it applies to an edge.
|
|
48
|
+
*/
|
|
49
|
+
import { readWranglerVarsEntries } from "./../validation/sources.js";
|
|
50
|
+
/** Direct reads of `.env*` need no resolution — DEC-244's precedent, reused rather than restated. */
|
|
51
|
+
const DECLARATIVE_RESOLUTION = 3;
|
|
52
|
+
const ACCESSOR = [
|
|
53
|
+
/^import\.meta\.env\.([A-Za-z_][A-Za-z0-9_]*)$/,
|
|
54
|
+
/^process\.env\.([A-Za-z_][A-Za-z0-9_]*)$/,
|
|
55
|
+
/^process\.env\[["']([A-Za-z_][A-Za-z0-9_]*)["']\]$/,
|
|
56
|
+
/^env\.([A-Za-z_][A-Za-z0-9_]*)$/,
|
|
57
|
+
/^env\[["']([A-Za-z_][A-Za-z0-9_]*)["']\]$/,
|
|
58
|
+
];
|
|
59
|
+
const LITERAL = /^(?:(["'`]).*\1|undefined|null|true|false|\d+(?:\.\d+)?)$/;
|
|
60
|
+
/**
|
|
61
|
+
* Split on top-level `||` / `??`, never inside a quoted literal.
|
|
62
|
+
*
|
|
63
|
+
* The blocked strings this reads are call-site fragments, not general JS — no
|
|
64
|
+
* nested ternaries, no parenthesised groups observed in either measured
|
|
65
|
+
* repository. Good enough for what real code does here; anything stranger
|
|
66
|
+
* fails a `parseSide` downstream and the whole blockedBy is refused rather
|
|
67
|
+
* than partially trusted.
|
|
68
|
+
*/
|
|
69
|
+
function splitTopLevelFallback(blockedBy) {
|
|
70
|
+
const parts = [];
|
|
71
|
+
let depth = 0;
|
|
72
|
+
let quote = null;
|
|
73
|
+
let start = 0;
|
|
74
|
+
for (let i = 0; i < blockedBy.length; i += 1) {
|
|
75
|
+
const c = blockedBy[i];
|
|
76
|
+
if (quote !== null) {
|
|
77
|
+
if (c === quote && blockedBy[i - 1] !== "\\")
|
|
78
|
+
quote = null;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
82
|
+
quote = c;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (c === "(" || c === "[")
|
|
86
|
+
depth += 1;
|
|
87
|
+
else if (c === ")" || c === "]")
|
|
88
|
+
depth -= 1;
|
|
89
|
+
else if (depth === 0 && (blockedBy.startsWith("||", i) || blockedBy.startsWith("??", i))) {
|
|
90
|
+
parts.push(blockedBy.slice(start, i));
|
|
91
|
+
i += 1;
|
|
92
|
+
start = i + 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
parts.push(blockedBy.slice(start));
|
|
96
|
+
return parts.map((p) => p.trim()).filter((p) => p !== "");
|
|
97
|
+
}
|
|
98
|
+
function parseSide(side) {
|
|
99
|
+
for (const pattern of ACCESSOR) {
|
|
100
|
+
const match = pattern.exec(side);
|
|
101
|
+
if (match?.[1] !== undefined)
|
|
102
|
+
return match[1];
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The candidate variable names an blockedBy could resolve to, in the order
|
|
108
|
+
* they would actually be tried at runtime. `[]` when any side of the
|
|
109
|
+
* blockedBy is neither a recognised accessor nor a literal fallback —
|
|
110
|
+
* refused as a whole rather than partially understood, per rule 2.
|
|
111
|
+
*/
|
|
112
|
+
export function candidateEnvNames(blockedBy) {
|
|
113
|
+
const sides = splitTopLevelFallback(blockedBy);
|
|
114
|
+
if (sides.length === 0)
|
|
115
|
+
return [];
|
|
116
|
+
const names = [];
|
|
117
|
+
for (const side of sides) {
|
|
118
|
+
const name = parseSide(side);
|
|
119
|
+
if (name !== null) {
|
|
120
|
+
if (!names.includes(name))
|
|
121
|
+
names.push(name);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (LITERAL.test(side))
|
|
125
|
+
continue; // a string/number/undefined fallback names nothing
|
|
126
|
+
return []; // an unrecognised shape anywhere refuses the whole blockedBy
|
|
127
|
+
}
|
|
128
|
+
return names;
|
|
129
|
+
}
|
|
130
|
+
const EXAMPLE_SUFFIX = /\.(example|sample|template|dist|defaults?)$/i;
|
|
131
|
+
const ENV_BASENAME = /^\.env(?:\.([A-Za-z0-9_-]+))?$/;
|
|
132
|
+
/** `.env`, `.env.local`, `.env.<name>`, `.env.<name>.local`, and each with an example suffix. */
|
|
133
|
+
function environmentOf(basename) {
|
|
134
|
+
const example = EXAMPLE_SUFFIX.test(basename) ? basename.replace(EXAMPLE_SUFFIX, "") : basename;
|
|
135
|
+
if (example === ".env" || example === ".env.local") {
|
|
136
|
+
return { environment: "default", local: example.endsWith(".local") };
|
|
137
|
+
}
|
|
138
|
+
const local = example.endsWith(".local");
|
|
139
|
+
const stem = local ? example.slice(0, -6) : example;
|
|
140
|
+
const match = ENV_BASENAME.exec(stem);
|
|
141
|
+
const name = match?.[1];
|
|
142
|
+
if (name === undefined)
|
|
143
|
+
return null;
|
|
144
|
+
return { environment: name, local };
|
|
145
|
+
}
|
|
146
|
+
/** `KEY=value` and `# KEY=value` (the commented-declaration case is skipped — no active value to read). */
|
|
147
|
+
function readDotenvValues(text) {
|
|
148
|
+
const out = [];
|
|
149
|
+
const lines = text.split(/\r?\n/);
|
|
150
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
151
|
+
const raw = lines[i] ?? "";
|
|
152
|
+
if (/^\s*#/.test(raw))
|
|
153
|
+
continue;
|
|
154
|
+
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(raw);
|
|
155
|
+
if (match === null)
|
|
156
|
+
continue;
|
|
157
|
+
const name = match[1];
|
|
158
|
+
let value = (match[2] ?? "").trim();
|
|
159
|
+
if (name === undefined)
|
|
160
|
+
continue;
|
|
161
|
+
const quoted = /^(["'])(.*)\1$/.exec(value);
|
|
162
|
+
if (quoted?.[2] !== undefined)
|
|
163
|
+
value = quoted[2];
|
|
164
|
+
out.push({ name, value, line: i + 1 });
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Every value this repository's `.env*` and `wrangler.toml` surfaces state,
|
|
170
|
+
* across every environment they name. Read once per repository;
|
|
171
|
+
* `resolveAgainstDeclared` runs many times against the result rather than
|
|
172
|
+
* rescanning per ledger row.
|
|
173
|
+
*/
|
|
174
|
+
export function readDeclaredEnvValues(root, scan, readFile) {
|
|
175
|
+
const out = [];
|
|
176
|
+
for (const surface of scan.surfaces) {
|
|
177
|
+
if (surface.format !== "dotenv" && surface.format !== "wranglerVars")
|
|
178
|
+
continue;
|
|
179
|
+
let text;
|
|
180
|
+
try {
|
|
181
|
+
text = readFile(`${root}/${surface.file}`);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
continue; // unreadable — already surfaced by scanConfigSurfaces's own `unreadable` list
|
|
185
|
+
}
|
|
186
|
+
if (surface.format === "wranglerVars") {
|
|
187
|
+
// A commented-out `# KEY = "value"` line documents, it does not supply —
|
|
188
|
+
// the same rule `readDotenvValues` applies by simply never reading a
|
|
189
|
+
// commented line at all.
|
|
190
|
+
for (const entry of readWranglerVarsEntries(text)) {
|
|
191
|
+
if (entry.commented || entry.value === null)
|
|
192
|
+
continue;
|
|
193
|
+
out.push({
|
|
194
|
+
name: entry.name,
|
|
195
|
+
environment: entry.environment,
|
|
196
|
+
value: entry.value,
|
|
197
|
+
file: surface.file,
|
|
198
|
+
line: entry.line,
|
|
199
|
+
local: false,
|
|
200
|
+
example: false,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const basename = surface.file.split("/").pop() ?? surface.file;
|
|
206
|
+
const env = environmentOf(basename);
|
|
207
|
+
if (env === null)
|
|
208
|
+
continue;
|
|
209
|
+
for (const { name, value, line } of readDotenvValues(text)) {
|
|
210
|
+
out.push({
|
|
211
|
+
name,
|
|
212
|
+
environment: env.environment,
|
|
213
|
+
value,
|
|
214
|
+
file: surface.file,
|
|
215
|
+
line,
|
|
216
|
+
local: env.local,
|
|
217
|
+
example: surface.example,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
/** `knownEnvironments` — every environment tag any `.env*` surface asserts, example files included. */
|
|
224
|
+
function knownEnvironmentsOf(declared) {
|
|
225
|
+
const set = new Set();
|
|
226
|
+
for (const d of declared)
|
|
227
|
+
set.add(d.environment);
|
|
228
|
+
return [...set].sort();
|
|
229
|
+
}
|
|
230
|
+
/** A `.local` value for the same (name, environment) outranks the non-local one, same as Vite resolves them. */
|
|
231
|
+
function pickBest(candidates) {
|
|
232
|
+
const local = candidates.find((c) => c.local && !c.example);
|
|
233
|
+
if (local !== undefined)
|
|
234
|
+
return local;
|
|
235
|
+
return candidates.find((c) => !c.example);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Resolve one blocked blockedBy against every declared value this
|
|
239
|
+
* repository already has on disk. Never invents an environment, never picks
|
|
240
|
+
* a value from a different repository, never treats an example file's
|
|
241
|
+
* placeholder as an answer.
|
|
242
|
+
*/
|
|
243
|
+
export function resolveAgainstDeclared(blockedBy, declared) {
|
|
244
|
+
const candidates = candidateEnvNames(blockedBy);
|
|
245
|
+
const knownEnvironments = knownEnvironmentsOf(declared);
|
|
246
|
+
if (candidates.length === 0) {
|
|
247
|
+
return {
|
|
248
|
+
blockedBy,
|
|
249
|
+
candidates: [],
|
|
250
|
+
resolved: [],
|
|
251
|
+
knownEnvironments,
|
|
252
|
+
unresolvedEnvironments: knownEnvironments,
|
|
253
|
+
resolution: null,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const byEnvironment = new Map();
|
|
257
|
+
for (const d of declared) {
|
|
258
|
+
if (!candidates.includes(d.name))
|
|
259
|
+
continue;
|
|
260
|
+
const list = byEnvironment.get(d.environment) ?? [];
|
|
261
|
+
list.push(d);
|
|
262
|
+
byEnvironment.set(d.environment, list);
|
|
263
|
+
}
|
|
264
|
+
const resolved = [];
|
|
265
|
+
for (const environment of knownEnvironments) {
|
|
266
|
+
const options = byEnvironment.get(environment);
|
|
267
|
+
if (options === undefined)
|
|
268
|
+
continue;
|
|
269
|
+
// Earlier candidates win, same priority order the fallback chain itself uses.
|
|
270
|
+
for (const name of candidates) {
|
|
271
|
+
const forName = options.filter((o) => o.name === name);
|
|
272
|
+
const best = pickBest(forName);
|
|
273
|
+
if (best !== undefined) {
|
|
274
|
+
resolved.push({ environment, name, value: best.value, file: best.file, line: best.line, local: best.local });
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const resolvedEnvironments = new Set(resolved.map((r) => r.environment));
|
|
280
|
+
const unresolvedEnvironments = knownEnvironments.filter((e) => !resolvedEnvironments.has(e));
|
|
281
|
+
return {
|
|
282
|
+
blockedBy,
|
|
283
|
+
candidates,
|
|
284
|
+
resolved,
|
|
285
|
+
knownEnvironments,
|
|
286
|
+
unresolvedEnvironments,
|
|
287
|
+
resolution: DECLARATIVE_RESOLUTION,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/ledger/resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §14.5's missing half: producing the fingerprints `findSuperseded` compares.
|
|
3
|
+
*
|
|
4
|
+
* ## The two git semantics, and why they differ on purpose
|
|
5
|
+
*
|
|
6
|
+
* The question is *"is this change's work already on base?"*, and answering it
|
|
7
|
+
* needs **two different diffs**:
|
|
8
|
+
*
|
|
9
|
+
* - **What the change touched** comes from `base...head` — three dots, against
|
|
10
|
+
* the merge base. That is what a reviewer sees, and it does not include
|
|
11
|
+
* somebody else's commits that landed on base afterwards.
|
|
12
|
+
* - **What base holds now** comes from the **tip of base**, not the merge base.
|
|
13
|
+
*
|
|
14
|
+
* That asymmetry *is* the mechanism. If someone else landed the same work while
|
|
15
|
+
* this change sat open, the three-dot diff still shows the change touching the
|
|
16
|
+
* file — its merge base is old — while base's tip already matches. Comparing
|
|
17
|
+
* against the merge base instead would report every open change as unsuperseded
|
|
18
|
+
* forever, because a merge base by construction predates the change.
|
|
19
|
+
*
|
|
20
|
+
* ## Granularity is the honest limit, and it is not a resolution problem
|
|
21
|
+
*
|
|
22
|
+
* These are **whole-file** hashes — git's own blob ids, which is the strongest
|
|
23
|
+
* available statement that two versions are identical, and the weakest useful
|
|
24
|
+
* unit. A change that edited one file for two reasons, one of which landed, is
|
|
25
|
+
* reported as differing. That is the conservative direction and the correct one:
|
|
26
|
+
* the finding this feeds tells a human to close a pull request, so a missed
|
|
27
|
+
* supersession costs a review and a false one costs the work.
|
|
28
|
+
*
|
|
29
|
+
* Node-level fingerprints would need the same file re-parsed at two commits,
|
|
30
|
+
* which is adapter work rather than core's. When that exists it changes the
|
|
31
|
+
* basis string and nothing else — `findSuperseded` compares opaque strings by
|
|
32
|
+
* construction, so this is a producer swap, not a redesign.
|
|
33
|
+
*
|
|
34
|
+
* ## What is not language knowledge
|
|
35
|
+
*
|
|
36
|
+
* Nothing here reads code. It asks git for content hashes and compares them,
|
|
37
|
+
* which is why it can ship in core at all — rule 1 is satisfied by never
|
|
38
|
+
* looking inside the bytes.
|
|
39
|
+
*/
|
|
40
|
+
import type { SupersededCandidate } from "./superseded.ts";
|
|
41
|
+
/** What `git ls-tree`/`diff` produced, and what a caller must be told about it. */
|
|
42
|
+
export interface CandidateReport {
|
|
43
|
+
readonly candidates: readonly SupersededCandidate[];
|
|
44
|
+
readonly notes: readonly string[];
|
|
45
|
+
/** The string `findSuperseded` should carry into its disclosure. */
|
|
46
|
+
readonly fingerprintBasis: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Fingerprint every file a change touched, as the change leaves it and as base
|
|
50
|
+
* holds it now.
|
|
51
|
+
*
|
|
52
|
+
* @param base the branch this change would merge into — compared at its **tip**
|
|
53
|
+
* @param head the tip of the change
|
|
54
|
+
*/
|
|
55
|
+
export declare function supersededCandidates(cwd: string, base: string, head?: string): Promise<CandidateReport>;
|
|
56
|
+
//# sourceMappingURL=fingerprint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/multipr/fingerprint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAKH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,UAAU,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACpD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,oEAAoE;IACpE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAkED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,IAAI,SAAS,GACZ,OAAO,CAAC,eAAe,CAAC,CAoE1B"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §14.5's missing half: producing the fingerprints `findSuperseded` compares.
|
|
3
|
+
*
|
|
4
|
+
* ## The two git semantics, and why they differ on purpose
|
|
5
|
+
*
|
|
6
|
+
* The question is *"is this change's work already on base?"*, and answering it
|
|
7
|
+
* needs **two different diffs**:
|
|
8
|
+
*
|
|
9
|
+
* - **What the change touched** comes from `base...head` — three dots, against
|
|
10
|
+
* the merge base. That is what a reviewer sees, and it does not include
|
|
11
|
+
* somebody else's commits that landed on base afterwards.
|
|
12
|
+
* - **What base holds now** comes from the **tip of base**, not the merge base.
|
|
13
|
+
*
|
|
14
|
+
* That asymmetry *is* the mechanism. If someone else landed the same work while
|
|
15
|
+
* this change sat open, the three-dot diff still shows the change touching the
|
|
16
|
+
* file — its merge base is old — while base's tip already matches. Comparing
|
|
17
|
+
* against the merge base instead would report every open change as unsuperseded
|
|
18
|
+
* forever, because a merge base by construction predates the change.
|
|
19
|
+
*
|
|
20
|
+
* ## Granularity is the honest limit, and it is not a resolution problem
|
|
21
|
+
*
|
|
22
|
+
* These are **whole-file** hashes — git's own blob ids, which is the strongest
|
|
23
|
+
* available statement that two versions are identical, and the weakest useful
|
|
24
|
+
* unit. A change that edited one file for two reasons, one of which landed, is
|
|
25
|
+
* reported as differing. That is the conservative direction and the correct one:
|
|
26
|
+
* the finding this feeds tells a human to close a pull request, so a missed
|
|
27
|
+
* supersession costs a review and a false one costs the work.
|
|
28
|
+
*
|
|
29
|
+
* Node-level fingerprints would need the same file re-parsed at two commits,
|
|
30
|
+
* which is adapter work rather than core's. When that exists it changes the
|
|
31
|
+
* basis string and nothing else — `findSuperseded` compares opaque strings by
|
|
32
|
+
* construction, so this is a producer swap, not a redesign.
|
|
33
|
+
*
|
|
34
|
+
* ## What is not language knowledge
|
|
35
|
+
*
|
|
36
|
+
* Nothing here reads code. It asks git for content hashes and compares them,
|
|
37
|
+
* which is why it can ship in core at all — rule 1 is satisfied by never
|
|
38
|
+
* looking inside the bytes.
|
|
39
|
+
*/
|
|
40
|
+
import { spawn } from "node:child_process";
|
|
41
|
+
import { cleanGitEnv } from "../sources/git/env.js";
|
|
42
|
+
const BASIS = "git blob identity at whole-file granularity — two versions are called identical when git " +
|
|
43
|
+
"gives them the same object id, and a difference means only that the file bytes differ";
|
|
44
|
+
/**
|
|
45
|
+
* Pathspecs are chunked rather than passed whole. A large change can exceed the
|
|
46
|
+
* argument limit, and the failure mode is an OS-level error whose message says
|
|
47
|
+
* nothing about paths — the kind of thing that gets diagnosed as a git problem.
|
|
48
|
+
*/
|
|
49
|
+
const PATH_CHUNK = 200;
|
|
50
|
+
async function git(cwd, args) {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const child = spawn("git", args, { cwd, env: cleanGitEnv() });
|
|
53
|
+
let out = "";
|
|
54
|
+
let err = "";
|
|
55
|
+
child.stdout.on("data", (d) => (out += d.toString("utf8")));
|
|
56
|
+
child.stderr.on("data", (d) => (err += d.toString("utf8")));
|
|
57
|
+
child.on("error", reject);
|
|
58
|
+
child.on("close", (code) => {
|
|
59
|
+
if (code === 0)
|
|
60
|
+
resolve(out);
|
|
61
|
+
else
|
|
62
|
+
reject(new Error(`git ${args.slice(0, 2).join(" ")} failed: ${err.trim() || code}`));
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Blob ids for `paths` at `ref`. A path absent from the tree is **absent from
|
|
68
|
+
* the map**, which the caller turns into `"absent"` — distinct from a git
|
|
69
|
+
* failure, which throws and becomes `"unknown"`.
|
|
70
|
+
*
|
|
71
|
+
* `-z` because a path may legally contain a newline. (It may not contain NUL,
|
|
72
|
+
* which is why NUL is the correct delimiter *on a pipe* even though it is the
|
|
73
|
+
* wrong thing to write into a file a human is meant to review.)
|
|
74
|
+
*/
|
|
75
|
+
async function blobIds(cwd, ref, paths) {
|
|
76
|
+
const found = new Map();
|
|
77
|
+
for (let i = 0; i < paths.length; i += PATH_CHUNK) {
|
|
78
|
+
const chunk = paths.slice(i, i + PATH_CHUNK);
|
|
79
|
+
const out = await git(cwd, ["ls-tree", "-r", "-z", ref, "--", ...chunk]);
|
|
80
|
+
for (const record of out.split("\0")) {
|
|
81
|
+
if (record === "")
|
|
82
|
+
continue;
|
|
83
|
+
// "<mode> <type> <sha>\t<path>"
|
|
84
|
+
const tab = record.indexOf("\t");
|
|
85
|
+
if (tab === -1)
|
|
86
|
+
continue;
|
|
87
|
+
const meta = record.slice(0, tab).split(/\s+/);
|
|
88
|
+
const sha = meta[2];
|
|
89
|
+
const path = record.slice(tab + 1);
|
|
90
|
+
if (sha !== undefined && meta[1] === "blob")
|
|
91
|
+
found.set(path, sha);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return found;
|
|
95
|
+
}
|
|
96
|
+
function diffTypeOf(status) {
|
|
97
|
+
if (status.startsWith("A"))
|
|
98
|
+
return "added";
|
|
99
|
+
if (status.startsWith("D"))
|
|
100
|
+
return "deleted";
|
|
101
|
+
return "modified";
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Fingerprint every file a change touched, as the change leaves it and as base
|
|
105
|
+
* holds it now.
|
|
106
|
+
*
|
|
107
|
+
* @param base the branch this change would merge into — compared at its **tip**
|
|
108
|
+
* @param head the tip of the change
|
|
109
|
+
*/
|
|
110
|
+
export async function supersededCandidates(cwd, base, head = "HEAD") {
|
|
111
|
+
const notes = [];
|
|
112
|
+
// Three dots: what this change touched, against the merge base. Renames are
|
|
113
|
+
// followed so a moved file is one candidate rather than a delete and an add.
|
|
114
|
+
const statusOut = await git(cwd, ["diff", "--name-status", "-M", "-z", `${base}...${head}`]);
|
|
115
|
+
const touched = [];
|
|
116
|
+
const fields = statusOut.split("\0");
|
|
117
|
+
for (let i = 0; i < fields.length; i += 1) {
|
|
118
|
+
const status = fields[i];
|
|
119
|
+
if (status === undefined || status === "")
|
|
120
|
+
continue;
|
|
121
|
+
// A rename record is `R<score>\0<old>\0<new>` — three fields, and the new
|
|
122
|
+
// path is the one that exists to be compared.
|
|
123
|
+
if (status.startsWith("R") || status.startsWith("C")) {
|
|
124
|
+
const to = fields[i + 2];
|
|
125
|
+
if (to !== undefined && to !== "")
|
|
126
|
+
touched.push({ path: to, diffType: "modified" });
|
|
127
|
+
i += 2;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const path = fields[i + 1];
|
|
131
|
+
if (path !== undefined && path !== "")
|
|
132
|
+
touched.push({ path, diffType: diffTypeOf(status) });
|
|
133
|
+
i += 1;
|
|
134
|
+
}
|
|
135
|
+
if (touched.length === 0) {
|
|
136
|
+
notes.push(`No file differs between ${base} and ${head}, so there was nothing to fingerprint. An ` +
|
|
137
|
+
"empty diff is a real answer and is not the same as a change whose work is already on base.");
|
|
138
|
+
return { candidates: [], notes, fingerprintBasis: BASIS };
|
|
139
|
+
}
|
|
140
|
+
const paths = touched.map((t) => t.path);
|
|
141
|
+
// The tip of base, deliberately — see the header. Resolved once so a branch
|
|
142
|
+
// that moves mid-run cannot make two halves of one answer disagree.
|
|
143
|
+
const baseSha = (await git(cwd, ["rev-parse", base])).trim();
|
|
144
|
+
const [afterBlobs, baseBlobs] = await Promise.all([
|
|
145
|
+
blobIds(cwd, head, paths),
|
|
146
|
+
blobIds(cwd, baseSha, paths),
|
|
147
|
+
]);
|
|
148
|
+
const candidates = touched.map((t) => ({
|
|
149
|
+
nodeId: t.path,
|
|
150
|
+
file: t.path,
|
|
151
|
+
diffType: t.diffType,
|
|
152
|
+
after: afterBlobs.get(t.path) ?? "absent",
|
|
153
|
+
base: baseBlobs.get(t.path) ?? "absent",
|
|
154
|
+
// R0: nothing was parsed. The comparison itself is exact — git object ids
|
|
155
|
+
// are equal only for identical content — but the *claim* rests on
|
|
156
|
+
// whole-file granularity being the right unit for "this work is already
|
|
157
|
+
// there", which is a judgement no resolution level supports. Rule 2's
|
|
158
|
+
// direction: understate.
|
|
159
|
+
resolution: 0,
|
|
160
|
+
}));
|
|
161
|
+
notes.push(`${candidates.length} file(s) fingerprinted at ${head} and at ${base} (${baseSha.slice(0, 12)}). ` +
|
|
162
|
+
"Base is compared at its tip rather than at the merge base: if this work landed by another " +
|
|
163
|
+
"route while the change was open, the merge base still predates it and only the tip shows it.");
|
|
164
|
+
notes.push("Fingerprints are whole-file. A file edited for two reasons, one of which landed, reports as " +
|
|
165
|
+
"differing — the conservative direction, and the right one for a check whose finding tells " +
|
|
166
|
+
"someone to discard work.");
|
|
167
|
+
return { candidates, notes, fingerprintBasis: BASIS };
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=fingerprint.js.map
|