@clossys/launcher 0.3.1 → 0.5.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/README.md +1373 -60
- package/contracts/conversation-contract.md +2 -2
- package/contracts/product-ci-workflow.yml +74 -0
- package/contracts/repository-inventory.json +53 -0
- package/dist/admission-fixture.d.ts +168 -0
- package/dist/admission-fixture.d.ts.map +1 -0
- package/dist/admission-fixture.js +467 -0
- package/dist/admission-fixture.js.map +1 -0
- package/dist/admission.d.ts +124 -0
- package/dist/admission.d.ts.map +1 -0
- package/dist/admission.js +804 -0
- package/dist/admission.js.map +1 -0
- package/dist/agents-guide.d.ts +9 -0
- package/dist/agents-guide.d.ts.map +1 -0
- package/dist/agents-guide.js +26 -0
- package/dist/agents-guide.js.map +1 -0
- package/dist/apply-command-options.check.d.ts +12 -0
- package/dist/apply-command-options.check.d.ts.map +1 -0
- package/dist/apply-command-options.check.js +20 -0
- package/dist/apply-command-options.check.js.map +1 -0
- package/dist/apply-plan-cli.d.ts +39 -1
- package/dist/apply-plan-cli.d.ts.map +1 -1
- package/dist/apply-plan-cli.js +432 -15
- package/dist/apply-plan-cli.js.map +1 -1
- package/dist/apply-plan.d.ts +46 -59
- package/dist/apply-plan.d.ts.map +1 -1
- package/dist/apply-plan.js +112 -97
- package/dist/apply-plan.js.map +1 -1
- package/dist/apply-step-fixture.d.ts +87 -0
- package/dist/apply-step-fixture.d.ts.map +1 -0
- package/dist/apply-step-fixture.js +199 -0
- package/dist/apply-step-fixture.js.map +1 -0
- package/dist/apply-store.d.ts +93 -0
- package/dist/apply-store.d.ts.map +1 -0
- package/dist/apply-store.js +625 -0
- package/dist/apply-store.js.map +1 -0
- package/dist/approval-sheet.d.ts +21 -0
- package/dist/approval-sheet.d.ts.map +1 -0
- package/dist/approval-sheet.js +163 -0
- package/dist/approval-sheet.js.map +1 -0
- package/dist/body-command.d.ts +42 -0
- package/dist/body-command.d.ts.map +1 -0
- package/dist/body-command.js +143 -0
- package/dist/body-command.js.map +1 -0
- package/dist/change-set-contract.d.ts +403 -0
- package/dist/change-set-contract.d.ts.map +1 -0
- package/dist/change-set-contract.js +781 -0
- package/dist/change-set-contract.js.map +1 -0
- package/dist/change-set-digest.d.ts +28 -0
- package/dist/change-set-digest.d.ts.map +1 -0
- package/dist/change-set-digest.js +65 -0
- package/dist/change-set-digest.js.map +1 -0
- package/dist/check-cli.d.ts.map +1 -1
- package/dist/check-cli.js +14 -3
- package/dist/check-cli.js.map +1 -1
- package/dist/cli.d.ts +17 -6
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +84 -23
- package/dist/cli.js.map +1 -1
- package/dist/core.d.ts +79 -22
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +843 -268
- package/dist/core.js.map +1 -1
- package/dist/dry-materialize.d.ts +63 -0
- package/dist/dry-materialize.d.ts.map +1 -0
- package/dist/dry-materialize.js +330 -0
- package/dist/dry-materialize.js.map +1 -0
- package/dist/existing-declaration-adoption.check.d.ts +2 -0
- package/dist/existing-declaration-adoption.check.d.ts.map +1 -0
- package/dist/existing-declaration-adoption.check.js +10 -0
- package/dist/existing-declaration-adoption.check.js.map +1 -0
- package/dist/generated/contract-schema.generated.d.ts +97 -0
- package/dist/generated/contract-schema.generated.d.ts.map +1 -0
- package/dist/generated/contract-schema.generated.js +496 -0
- package/dist/generated/contract-schema.generated.js.map +1 -0
- package/dist/generated/package-scope.generated.d.ts +6 -0
- package/dist/generated/package-scope.generated.d.ts.map +1 -0
- package/dist/generated/package-scope.generated.js +10 -0
- package/dist/generated/package-scope.generated.js.map +1 -0
- package/dist/generated/plan-contracts.generated.d.ts +3 -0
- package/dist/generated/plan-contracts.generated.d.ts.map +1 -0
- package/dist/generated/plan-contracts.generated.js +3101 -0
- package/dist/generated/plan-contracts.generated.js.map +1 -0
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +11 -0
- package/dist/host.js.map +1 -1
- package/dist/identity.d.ts +15 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +48 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +35 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/dist/inventory-adoption.d.ts +24 -5
- package/dist/inventory-adoption.d.ts.map +1 -1
- package/dist/inventory-adoption.js +70 -25
- package/dist/inventory-adoption.js.map +1 -1
- package/dist/inventory-choice.d.ts +40 -0
- package/dist/inventory-choice.d.ts.map +1 -0
- package/dist/inventory-choice.js +156 -0
- package/dist/inventory-choice.js.map +1 -0
- package/dist/inventory-contract.d.ts +89 -0
- package/dist/inventory-contract.d.ts.map +1 -0
- package/dist/inventory-contract.js +121 -0
- package/dist/inventory-contract.js.map +1 -0
- package/dist/key-editor.d.ts +30 -0
- package/dist/key-editor.d.ts.map +1 -0
- package/dist/key-editor.js +445 -0
- package/dist/key-editor.js.map +1 -0
- package/dist/ledger-contract.d.ts +190 -0
- package/dist/ledger-contract.d.ts.map +1 -0
- package/dist/ledger-contract.js +555 -0
- package/dist/ledger-contract.js.map +1 -0
- package/dist/ledger-trust.d.ts +90 -0
- package/dist/ledger-trust.d.ts.map +1 -0
- package/dist/ledger-trust.js +203 -0
- package/dist/ledger-trust.js.map +1 -0
- package/dist/lockfile-invariants.d.ts +48 -0
- package/dist/lockfile-invariants.d.ts.map +1 -0
- package/dist/lockfile-invariants.js +375 -0
- package/dist/lockfile-invariants.js.map +1 -0
- package/dist/lockfile-readers.d.ts +72 -0
- package/dist/lockfile-readers.d.ts.map +1 -0
- package/dist/lockfile-readers.js +713 -0
- package/dist/lockfile-readers.js.map +1 -0
- package/dist/lockfile-regen.d.ts +106 -0
- package/dist/lockfile-regen.d.ts.map +1 -0
- package/dist/lockfile-regen.js +760 -0
- package/dist/lockfile-regen.js.map +1 -0
- package/dist/lockfile-tool-env.d.ts +29 -0
- package/dist/lockfile-tool-env.d.ts.map +1 -0
- package/dist/lockfile-tool-env.js +111 -0
- package/dist/lockfile-tool-env.js.map +1 -0
- package/dist/materialize.d.ts +113 -0
- package/dist/materialize.d.ts.map +1 -0
- package/dist/materialize.js +881 -0
- package/dist/materialize.js.map +1 -0
- package/dist/observe-repository.d.ts +90 -0
- package/dist/observe-repository.d.ts.map +1 -0
- package/dist/observe-repository.js +1367 -0
- package/dist/observe-repository.js.map +1 -0
- package/dist/plan-bundle-setup-fixture.d.ts +68 -0
- package/dist/plan-bundle-setup-fixture.d.ts.map +1 -0
- package/dist/plan-bundle-setup-fixture.js +167 -0
- package/dist/plan-bundle-setup-fixture.js.map +1 -0
- package/dist/plan-bundle.d.ts +256 -0
- package/dist/plan-bundle.d.ts.map +1 -0
- package/dist/plan-bundle.js +882 -0
- package/dist/plan-bundle.js.map +1 -0
- package/dist/plan-command.d.ts +29 -0
- package/dist/plan-command.d.ts.map +1 -0
- package/dist/plan-command.js +523 -0
- package/dist/plan-command.js.map +1 -0
- package/dist/plan-contract.d.ts +153 -0
- package/dist/plan-contract.d.ts.map +1 -0
- package/dist/plan-contract.js +61 -0
- package/dist/plan-contract.js.map +1 -0
- package/dist/plan-digest.d.ts +25 -0
- package/dist/plan-digest.d.ts.map +1 -0
- package/dist/plan-digest.js +106 -0
- package/dist/plan-digest.js.map +1 -0
- package/dist/plan-rules.d.ts +23 -0
- package/dist/plan-rules.d.ts.map +1 -0
- package/dist/plan-rules.js +177 -0
- package/dist/plan-rules.js.map +1 -0
- package/dist/planned-bundle.d.ts +20 -0
- package/dist/planned-bundle.d.ts.map +1 -0
- package/dist/planned-bundle.js +191 -0
- package/dist/planned-bundle.js.map +1 -0
- package/dist/product-repository.d.ts +4 -0
- package/dist/product-repository.d.ts.map +1 -1
- package/dist/product-repository.js +9 -1
- package/dist/product-repository.js.map +1 -1
- package/dist/provenance-gate.d.ts +48 -0
- package/dist/provenance-gate.d.ts.map +1 -0
- package/dist/provenance-gate.js +324 -0
- package/dist/provenance-gate.js.map +1 -0
- package/dist/pull-request-body.d.ts +45 -0
- package/dist/pull-request-body.d.ts.map +1 -0
- package/dist/pull-request-body.js +232 -0
- package/dist/pull-request-body.js.map +1 -0
- package/dist/registry-snapshot.d.ts +141 -0
- package/dist/registry-snapshot.d.ts.map +1 -0
- package/dist/registry-snapshot.js +483 -0
- package/dist/registry-snapshot.js.map +1 -0
- package/dist/release-age-edit.d.ts +52 -0
- package/dist/release-age-edit.d.ts.map +1 -0
- package/dist/release-age-edit.js +413 -0
- package/dist/release-age-edit.js.map +1 -0
- package/dist/root-entries.d.ts +36 -0
- package/dist/root-entries.d.ts.map +1 -0
- package/dist/root-entries.js +80 -0
- package/dist/root-entries.js.map +1 -0
- package/dist/setup-template-scripts.d.ts +36 -0
- package/dist/setup-template-scripts.d.ts.map +1 -0
- package/dist/setup-template-scripts.js +568 -0
- package/dist/setup-template-scripts.js.map +1 -0
- package/dist/setup-templates.d.ts +55 -0
- package/dist/setup-templates.d.ts.map +1 -0
- package/dist/setup-templates.js +438 -0
- package/dist/setup-templates.js.map +1 -0
- package/dist/skills.d.ts +34 -1
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +129 -17
- package/dist/skills.js.map +1 -1
- package/dist/status.d.ts +63 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +539 -0
- package/dist/status.js.map +1 -0
- package/dist/types.d.ts +151 -13
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/skeleton/README.md +14 -9
- package/skeleton/package.json +2 -1
- package/skill/SKILL.md +23 -7
- package/skill-catalogue/advisor/SKILL.md +59 -6
- package/skill-catalogue/architect/SKILL.md +2 -2
- package/skill-catalogue/bouncer/SKILL.md +2 -2
- package/skill-catalogue/builder/SKILL.md +2 -2
- package/skill-catalogue/butler/SKILL.md +2 -2
- package/skill-catalogue/controller/SKILL.md +2 -2
- package/skill-catalogue/customer/SKILL.md +2 -2
- package/skill-catalogue/designer/SKILL.md +4 -2
- package/skill-catalogue/giver/SKILL.md +2 -2
- package/skill-catalogue/influencer/SKILL.md +2 -2
- package/skill-catalogue/inspector/SKILL.md +2 -2
- package/skill-catalogue/integrator/SKILL.md +2 -2
- package/skill-catalogue/keeper/SKILL.md +2 -2
- package/skill-catalogue/launcher/SKILL.md +23 -7
- package/skill-catalogue/locksmith/SKILL.md +2 -2
- package/skill-catalogue/messenger/SKILL.md +2 -2
- package/skill-catalogue/observer/SKILL.md +2 -2
- package/skill-catalogue/publisher/SKILL.md +2 -2
- package/skill-catalogue/starter/SKILL.md +3 -2
- package/skill-catalogue/strategist/SKILL.md +12 -4
- package/skill-catalogue/writer/SKILL.md +2 -2
- package/src/admission-fixture.ts +585 -0
- package/src/admission.ts +819 -0
- package/src/agents-guide.ts +29 -0
- package/src/apply-command-options.check.ts +27 -0
- package/src/apply-plan-cli.ts +454 -14
- package/src/apply-plan.ts +112 -124
- package/src/apply-step-fixture.ts +236 -0
- package/src/apply-store.ts +584 -0
- package/src/approval-sheet.ts +170 -0
- package/src/body-command.ts +162 -0
- package/src/change-set-contract.ts +987 -0
- package/src/change-set-digest.ts +70 -0
- package/src/check-cli.ts +14 -3
- package/src/cli.ts +90 -22
- package/src/core.ts +973 -275
- package/src/dry-materialize.ts +353 -0
- package/src/existing-declaration-adoption.check.ts +12 -0
- package/src/generated/contract-schema.generated.ts +520 -0
- package/src/generated/package-scope.generated.ts +10 -0
- package/src/generated/plan-contracts.generated.ts +3101 -0
- package/src/host.ts +10 -0
- package/src/identity.ts +51 -0
- package/src/index.ts +74 -3
- package/src/inventory-adoption.ts +107 -29
- package/src/inventory-choice.ts +172 -0
- package/src/inventory-contract.ts +166 -0
- package/src/key-editor.ts +446 -0
- package/src/ledger-contract.ts +660 -0
- package/src/ledger-trust.ts +272 -0
- package/src/lockfile-invariants.ts +421 -0
- package/src/lockfile-readers.ts +749 -0
- package/src/lockfile-regen.ts +851 -0
- package/src/lockfile-tool-env.ts +131 -0
- package/src/materialize.ts +915 -0
- package/src/observe-repository.ts +1365 -0
- package/src/plan-bundle-setup-fixture.ts +200 -0
- package/src/plan-bundle.ts +1014 -0
- package/src/plan-command.ts +532 -0
- package/src/plan-contract.ts +179 -0
- package/src/plan-digest.ts +102 -0
- package/src/plan-rules.ts +188 -0
- package/src/planned-bundle.ts +211 -0
- package/src/product-repository.ts +10 -1
- package/src/provenance-gate.ts +352 -0
- package/src/pull-request-body.ts +261 -0
- package/src/registry-snapshot.ts +534 -0
- package/src/release-age-edit.ts +430 -0
- package/src/root-entries.ts +81 -0
- package/src/setup-template-scripts.ts +580 -0
- package/src/setup-templates.ts +479 -0
- package/src/skills.ts +161 -18
- package/src/status.ts +557 -0
- package/src/types.ts +148 -13
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Writing the hub inventory from the repositories a founder chose on
|
|
2
|
+
// Advisor's repository-choice card (issue #1179), so a founder never
|
|
3
|
+
// hand-writes JSON. `launcher --repositories` is the command; this module
|
|
4
|
+
// decides what that command may write, and never writes anything itself.
|
|
5
|
+
//
|
|
6
|
+
// The chosen ids and the document built from them are validated against
|
|
7
|
+
// docs/contracts/repository-inventory.json (in the public repository;
|
|
8
|
+
// that exact path does not ship in this package, but this package's
|
|
9
|
+
// build packs and ships its own copy of the contract) through the shared
|
|
10
|
+
// contract checker (./inventory-contract.ts) -- the same check every later read of
|
|
11
|
+
// the stored inventory applies, so what this writes, Launcher reads back.
|
|
12
|
+
//
|
|
13
|
+
// An inventory already on disk that names a different set of repositories
|
|
14
|
+
// is never merged into or overwritten silently: the choice is refused, with
|
|
15
|
+
// the difference in counts and positions, unless the caller passes an
|
|
16
|
+
// explicit replace approval (`--replace-inventory`).
|
|
17
|
+
//
|
|
18
|
+
// Every message this module builds -- the refusal and the success line
|
|
19
|
+
// alike -- names a repository only by its position, never by its id: a
|
|
20
|
+
// stored inventory file, and the `--repositories` argument, are both
|
|
21
|
+
// document-shaped input an agent may relay verbatim, and a repository id is
|
|
22
|
+
// exactly the kind of short string a hostile entry could shape as
|
|
23
|
+
// prompt-injection text (see ./inventory-contract.ts's own header). The
|
|
24
|
+
// caller looks the reported position up in its own copy of the inventory
|
|
25
|
+
// file or its own `--repositories` argument to learn which repository it
|
|
26
|
+
// names.
|
|
27
|
+
import { inventoryKey } from "./identity.js";
|
|
28
|
+
import { readInventoryDocument, renderInventoryDocument, validateInventoryDocument, validateInventoryValue } from "./inventory-contract.js";
|
|
29
|
+
/** The command a refusal names for an explicit replace approval. */
|
|
30
|
+
export const REPLACE_INVENTORY_FLAG = "--replace-inventory";
|
|
31
|
+
function repositories(count) {
|
|
32
|
+
return `${count} ${count === 1 ? "repository" : "repositories"}`;
|
|
33
|
+
}
|
|
34
|
+
/** Added ids are named by their position in the `--repositories` argument, never by the id itself. */
|
|
35
|
+
function listAddedPositions(positions) {
|
|
36
|
+
return positions.length === 0 ? "" : ` (${positions.map((index) => `--repositories[${index}]`).join(", ")})`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Removed ids are named by their position in an inventory's `repositories`
|
|
40
|
+
* array, never by the id itself. `label` distinguishes which file that
|
|
41
|
+
* position indexes into: `"stored inventory"` for the refusal, where the
|
|
42
|
+
* file on disk is still the one the positions were computed against, and
|
|
43
|
+
* `"replaced inventory"` for the success line after `--replace-inventory`
|
|
44
|
+
* writes -- by the time that line prints, the file on disk is already the
|
|
45
|
+
* new one, so reusing "stored inventory" there would point a reader at the
|
|
46
|
+
* wrong document (#1179).
|
|
47
|
+
*/
|
|
48
|
+
function listRemovedPositions(positions, label) {
|
|
49
|
+
return positions.length === 0 ? "" : ` (${positions.map((index) => `repositories[${index}] in the ${label}`).join(", ")})`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Decides what `launcher --repositories` writes into a hub whose stored
|
|
53
|
+
* inventory is `onDisk` -- the file's exact bytes, or `null` when there is
|
|
54
|
+
* none.
|
|
55
|
+
*
|
|
56
|
+
* Every comparison uses `inventoryKey(id, hubOwner)`: a bare id names a
|
|
57
|
+
* repository of the hub's own owner, and letter case is ignored.
|
|
58
|
+
*
|
|
59
|
+
* - The chosen ids must form a valid inventory on their own: at least one
|
|
60
|
+
* id, each one satisfying the contract's repository id rule, and no two
|
|
61
|
+
* naming the same repository. A refusal names positions only.
|
|
62
|
+
* - The stored inventory is read by the same strict reader, with the same
|
|
63
|
+
* owner: one that fails its contract, including one listing a repository
|
|
64
|
+
* twice (for example `app` and `<hubOwner>/app`), is replaced only with
|
|
65
|
+
* `replace`, and then with the chosen ids alone. It is never merged.
|
|
66
|
+
* - No inventory, or an empty one: the chosen repositories are written.
|
|
67
|
+
* - An inventory naming exactly the same repositories: nothing is written.
|
|
68
|
+
* - An inventory naming a different set: refused, reporting how many
|
|
69
|
+
* repositories each side has and the positions that would be added and
|
|
70
|
+
* removed (never the ids themselves), unless `replace` is true. A
|
|
71
|
+
* replacement keeps each kept repository's existing entry as it was (its
|
|
72
|
+
* `packages` included).
|
|
73
|
+
*
|
|
74
|
+
* The document to write is checked again by the same reader every later
|
|
75
|
+
* read uses, before it is returned.
|
|
76
|
+
*/
|
|
77
|
+
export function resolveChosenInventory(onDisk, chosenIds, hubOwner, replace) {
|
|
78
|
+
if (chosenIds.length === 0) {
|
|
79
|
+
return { kind: "refuse", message: "--repositories must name at least one repository" };
|
|
80
|
+
}
|
|
81
|
+
const chosenCheck = validateInventoryValue({ schemaVersion: 1, repositories: chosenIds.map((id) => ({ id })) }, { hubOwner });
|
|
82
|
+
if (!chosenCheck.valid)
|
|
83
|
+
return { kind: "refuse", message: `--repositories is not a valid inventory: ${chosenCheck.reason}` };
|
|
84
|
+
let entries;
|
|
85
|
+
let added = chosenIds;
|
|
86
|
+
let removed = [];
|
|
87
|
+
let addedPositions = chosenIds.map((_, index) => index);
|
|
88
|
+
let removedPositions = [];
|
|
89
|
+
let replaced = "nothing";
|
|
90
|
+
let previousCount = 0;
|
|
91
|
+
if (onDisk === null) {
|
|
92
|
+
entries = chosenIds.map((id) => ({ id }));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const stored = readInventoryDocument(onDisk, { hubOwner });
|
|
96
|
+
if (!stored.valid) {
|
|
97
|
+
if (!replace) {
|
|
98
|
+
return {
|
|
99
|
+
kind: "refuse",
|
|
100
|
+
message: `the hub inventory ${stored.reason}; to replace it with the ${repositories(chosenIds.length)} chosen, run again with ${REPLACE_INVENTORY_FLAG}`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
entries = chosenIds.map((id) => ({ id }));
|
|
104
|
+
replaced = "invalid";
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
previousCount = stored.ids.length;
|
|
108
|
+
// Keys are unique on both sides: the reader and the chosen-id check above both refuse two ids with one key.
|
|
109
|
+
const storedByKey = new Map(stored.entries.map((entry) => [inventoryKey(entry.id, hubOwner), entry]));
|
|
110
|
+
const chosenKeys = new Set(chosenIds.map((id) => inventoryKey(id, hubOwner)));
|
|
111
|
+
added = chosenIds.filter((id) => !storedByKey.has(inventoryKey(id, hubOwner)));
|
|
112
|
+
removed = stored.ids.filter((id) => !chosenKeys.has(inventoryKey(id, hubOwner)));
|
|
113
|
+
// Positions, not ids: `added` indexes into the --repositories argument the caller passed; `removed` indexes into the stored inventory's own `repositories` array.
|
|
114
|
+
addedPositions = added.map((id) => chosenIds.indexOf(id));
|
|
115
|
+
removedPositions = removed.map((id) => stored.ids.indexOf(id));
|
|
116
|
+
if (stored.ids.length > 0 && added.length === 0 && removed.length === 0) {
|
|
117
|
+
return { kind: "resolved", chosen: { kind: "unchanged", count: stored.ids.length } };
|
|
118
|
+
}
|
|
119
|
+
if (stored.ids.length > 0) {
|
|
120
|
+
if (!replace) {
|
|
121
|
+
return {
|
|
122
|
+
kind: "refuse",
|
|
123
|
+
message: `the hub inventory already lists ${repositories(stored.ids.length)} and the choice has ${chosenIds.length}: ` +
|
|
124
|
+
`${added.length} to add${listAddedPositions(addedPositions)}, ${removed.length} to remove${listRemovedPositions(removedPositions, "stored inventory")}. ` +
|
|
125
|
+
`Launcher never merges or overwrites an inventory silently; to replace it with the choice, run again with ${REPLACE_INVENTORY_FLAG}`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
replaced = "differing";
|
|
129
|
+
}
|
|
130
|
+
entries = chosenIds.map((id) => storedByKey.get(inventoryKey(id, hubOwner)) ?? { id });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const document = renderInventoryDocument(entries);
|
|
134
|
+
const written = validateInventoryDocument(document, { hubOwner });
|
|
135
|
+
if (!written.valid)
|
|
136
|
+
return { kind: "refuse", message: `the inventory to write ${written.reason}` };
|
|
137
|
+
return {
|
|
138
|
+
kind: "resolved",
|
|
139
|
+
chosen: { kind: "write", document, count: written.ids.length, previousCount, added, removed, addedPositions, removedPositions, replaced },
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** One line for the apply message saying what happened to the inventory. */
|
|
143
|
+
export function describeChosenInventory(chosen) {
|
|
144
|
+
if (chosen.kind === "unchanged") {
|
|
145
|
+
return `inventory: unchanged -- it already lists the ${repositories(chosen.count)} you chose`;
|
|
146
|
+
}
|
|
147
|
+
const count = repositories(chosen.count);
|
|
148
|
+
if (chosen.replaced === "invalid")
|
|
149
|
+
return `inventory: replaced one that failed its contract with the ${count} you chose`;
|
|
150
|
+
if (chosen.replaced === "differing") {
|
|
151
|
+
return (`inventory: replaced ${chosen.previousCount} with the ${count} you chose -- ` +
|
|
152
|
+
`${chosen.added.length} added${listAddedPositions(chosen.addedPositions)}, ${chosen.removed.length} removed${listRemovedPositions(chosen.removedPositions, "replaced inventory")}`);
|
|
153
|
+
}
|
|
154
|
+
return `inventory: wrote the ${count} you chose`;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=inventory-choice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inventory-choice.js","sourceRoot":"","sources":["../src/inventory-choice.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,qEAAqE;AACrE,0EAA0E;AAC1E,yEAAyE;AACzE,EAAE;AACF,wEAAwE;AACxE,sEAAsE;AACtE,oEAAoE;AACpE,yEAAyE;AACzE,mFAAmF;AACnF,0EAA0E;AAC1E,EAAE;AACF,0EAA0E;AAC1E,4EAA4E;AAC5E,sEAAsE;AACtE,qDAAqD;AACrD,EAAE;AACF,uEAAuE;AACvE,uEAAuE;AACvE,qEAAqE;AACrE,4EAA4E;AAC5E,kEAAkE;AAClE,wEAAwE;AACxE,yEAAyE;AACzE,yEAAyE;AACzE,SAAS;AAET,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,sBAAsB,EAAuB,MAAM,yBAAyB,CAAC;AAOjK,oEAAoE;AACpE,MAAM,CAAC,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AAE5D,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC;AACnE,CAAC;AAED,sGAAsG;AACtG,SAAS,kBAAkB,CAAC,SAA4B;IACtD,OAAO,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,kBAAkB,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAC/G,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,oBAAoB,CAAC,SAA4B,EAAE,KAAgD;IAC1G,OAAO,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,KAAK,YAAY,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAC7H,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAkC,EAClC,SAA4B,EAC5B,QAAgB,EAChB,OAAgB;IAEhB,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,kDAAkD,EAAE,CAAC;IACzF,CAAC;IACD,MAAM,WAAW,GAAG,sBAAsB,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC9H,IAAI,CAAC,WAAW,CAAC,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,4CAA4C,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;IAE7H,IAAI,OAAyB,CAAC;IAC9B,IAAI,KAAK,GAAsB,SAAS,CAAC;IACzC,IAAI,OAAO,GAAsB,EAAE,CAAC;IACpC,IAAI,cAAc,GAAsB,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAC3E,IAAI,gBAAgB,GAAsB,EAAE,CAAC;IAC7C,IAAI,QAAQ,GAA4D,SAAS,CAAC;IAClF,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,GAAG,qBAAqB,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO;oBACL,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,qBAAqB,MAAM,CAAC,MAAM,4BAA4B,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,2BAA2B,sBAAsB,EAAE;iBACzJ,CAAC;YACJ,CAAC;YACD,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAC1C,QAAQ,GAAG,SAAS,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,aAAa,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;YAClC,4GAA4G;YAC5G,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAU,CAAC,CAAC,CAAC;YAC/G,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC9E,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC/E,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;YACjF,kKAAkK;YAClK,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1D,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/D,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;YACvF,CAAC;YACD,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1B,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO;wBACL,IAAI,EAAE,QAAQ;wBACd,OAAO,EACL,mCAAmC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,uBAAuB,SAAS,CAAC,MAAM,IAAI;4BAC7G,GAAG,KAAK,CAAC,MAAM,UAAU,kBAAkB,CAAC,cAAc,CAAC,KAAK,OAAO,CAAC,MAAM,aAAa,oBAAoB,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,IAAI;4BACzJ,4GAA4G,sBAAsB,EAAE;qBACvI,CAAC;gBACJ,CAAC;gBACD,QAAQ,GAAG,WAAW,CAAC;YACzB,CAAC;YACD,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACzF,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,yBAAyB,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAClE,IAAI,CAAC,OAAO,CAAC,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,0BAA0B,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IACnG,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,QAAQ,EAAE;KAC1I,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,uBAAuB,CAAC,MAAuB;IAC7D,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,OAAO,gDAAgD,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC;IAChG,CAAC;IACD,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzC,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,6DAA6D,KAAK,YAAY,CAAC;IACzH,IAAI,MAAM,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,OAAO,CACL,uBAAuB,MAAM,CAAC,aAAa,aAAa,KAAK,gBAAgB;YAC7E,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,SAAS,kBAAkB,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,MAAM,CAAC,OAAO,CAAC,MAAM,WAAW,oBAAoB,CAAC,MAAM,CAAC,gBAAgB,EAAE,oBAAoB,CAAC,EAAE,CACnL,CAAC;IACJ,CAAC;IACD,OAAO,wBAAwB,KAAK,YAAY,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** The contract's file name, as packed. */
|
|
2
|
+
export declare const INVENTORY_CONTRACT = "repository-inventory.json";
|
|
3
|
+
/** Points a refusal at the one place the shape is written down. */
|
|
4
|
+
export declare const INVENTORY_CONTRACT_POINTER = "see docs/contracts/repository-inventory.json";
|
|
5
|
+
/**
|
|
6
|
+
* The result of checking one inventory document: its repository ids, in
|
|
7
|
+
* document order, or the reason it is refused. `reason` names every field
|
|
8
|
+
* at fault, separated by `; `, and ends with a pointer to the contract.
|
|
9
|
+
*/
|
|
10
|
+
export type InventoryValidation = {
|
|
11
|
+
readonly valid: true;
|
|
12
|
+
readonly ids: readonly string[];
|
|
13
|
+
} | {
|
|
14
|
+
readonly valid: false;
|
|
15
|
+
readonly reason: string;
|
|
16
|
+
};
|
|
17
|
+
/** One inventory entry, exactly as the contract allows it. */
|
|
18
|
+
export interface InventoryEntry {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly packages?: readonly {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly version?: string;
|
|
23
|
+
readonly wiring?: string;
|
|
24
|
+
}[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The first id, if any, that names the same repository as an earlier one
|
|
28
|
+
* under `inventoryKey()`: its position and the earlier position.
|
|
29
|
+
*/
|
|
30
|
+
export declare function firstRepeatedRepository(ids: readonly string[], hubOwner?: string): {
|
|
31
|
+
readonly index: number;
|
|
32
|
+
readonly earlier: number;
|
|
33
|
+
} | undefined;
|
|
34
|
+
/** Options for reading an inventory document. */
|
|
35
|
+
export interface InventoryReadOptions {
|
|
36
|
+
/**
|
|
37
|
+
* The hub's owner, whenever the caller knows it. A bare id and
|
|
38
|
+
* `<hubOwner>/<id>` then name the same repository, so a document listing
|
|
39
|
+
* both is refused instead of carrying two entries for one repository.
|
|
40
|
+
*/
|
|
41
|
+
readonly hubOwner?: string;
|
|
42
|
+
}
|
|
43
|
+
/** A valid document's entries, as internal readers need them; `InventoryValidation` exposes only the ids. */
|
|
44
|
+
export type InventoryRead = {
|
|
45
|
+
readonly valid: true;
|
|
46
|
+
readonly ids: readonly string[];
|
|
47
|
+
readonly entries: readonly InventoryEntry[];
|
|
48
|
+
} | {
|
|
49
|
+
readonly valid: false;
|
|
50
|
+
readonly reason: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Validates an already-parsed value against the inventory contract through
|
|
54
|
+
* the shared checker, then applies the duplicate rule. Never throws for a
|
|
55
|
+
* bad value.
|
|
56
|
+
*/
|
|
57
|
+
export declare function validateInventoryValue(value: unknown, options?: InventoryReadOptions): InventoryValidation;
|
|
58
|
+
/**
|
|
59
|
+
* Reads and checks one inventory document. Bytes -- as every file read
|
|
60
|
+
* passes them, from `WorkspaceHost.readBytes()` -- go straight to the
|
|
61
|
+
* shared strict reader, so bytes that are not valid UTF-8 are refused
|
|
62
|
+
* rather than silently replaced. Text is accepted for a document built in
|
|
63
|
+
* memory, and one holding a lone surrogate is refused before encoding.
|
|
64
|
+
*/
|
|
65
|
+
export declare function readInventoryDocument(raw: string | Uint8Array, options?: InventoryReadOptions): InventoryRead;
|
|
66
|
+
/**
|
|
67
|
+
* Strictly validates an inventory document against
|
|
68
|
+
* docs/contracts/repository-inventory.json. It is read as strict JSON by the
|
|
69
|
+
* shared reader -- bytes that are not valid UTF-8, a syntax error (reported
|
|
70
|
+
* by position only), a key repeated in any object, or a leading byte order
|
|
71
|
+
* mark is refused -- and the value is then checked by the shared contract
|
|
72
|
+
* checker and the duplicate rule. Every read of an inventory file passes
|
|
73
|
+
* its exact bytes: `--inventory`, the stored `clossys/.state/inventory.json`
|
|
74
|
+
* on every run, `readInventoryRepositories()`, and the document
|
|
75
|
+
* `launcher --repositories` writes, before it writes it. The contract's
|
|
76
|
+
* source path does not ship in this package -- this package's build packs
|
|
77
|
+
* and ships its own copy of it -- and the stored inventory path is
|
|
78
|
+
* generated on the hub at runtime and is not part of this package at all.
|
|
79
|
+
*/
|
|
80
|
+
export declare function validateInventoryDocument(raw: string | Uint8Array, options?: InventoryReadOptions): InventoryValidation;
|
|
81
|
+
/**
|
|
82
|
+
* Whether one id satisfies the contract's `definitions/repositoryId` -- a
|
|
83
|
+
* bare repository name or `owner/name`, by the same GitHub owner and
|
|
84
|
+
* repository rules Launcher applies when it resolves a sibling clone.
|
|
85
|
+
*/
|
|
86
|
+
export declare function isValidInventoryId(id: string): boolean;
|
|
87
|
+
/** The inventory document text Launcher writes for these entries: two-space JSON and a final newline. */
|
|
88
|
+
export declare function renderInventoryDocument(entries: readonly InventoryEntry[]): string;
|
|
89
|
+
//# sourceMappingURL=inventory-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inventory-contract.d.ts","sourceRoot":"","sources":["../src/inventory-contract.ts"],"names":[],"mappings":"AAoBA,2CAA2C;AAC3C,eAAO,MAAM,kBAAkB,8BAA8B,CAAC;AAE9D,mEAAmE;AACnE,eAAO,MAAM,0BAA0B,iDAAiD,CAAC;AAEzF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC/G;AAMD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,SAAS,MAAM,EAAE,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CASlE;AAED,iDAAiD;AACjD,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,6GAA6G;AAC7G,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAA;CAAE,GACtG;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AA2BvD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,oBAAyB,GAAG,mBAAmB,CAE9G;AAKD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,GAAE,oBAAyB,GAAG,aAAa,CAWjH;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,GAAE,oBAAyB,GAAG,mBAAmB,CAE3H;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,yGAAyG;AACzG,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,CAElF"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// The hub repository inventory (issues #996, #1334 and #1179), validated
|
|
2
|
+
// against the shared contract docs/contracts/repository-inventory.json (in
|
|
3
|
+
// the public repository; that exact path does not ship in this package,
|
|
4
|
+
// but this package's build packs and ships its own copy of the contract).
|
|
5
|
+
// This package's build packs that file into src/generated/ beside the plan
|
|
6
|
+
// and brief contracts, and checks it with the generated copy of the one
|
|
7
|
+
// contract checker @clossys/advisor also uses -- so an inventory Launcher
|
|
8
|
+
// writes, an inventory Launcher reads back, and a repository id Advisor
|
|
9
|
+
// offers on its repository-choice card are all judged by one definition.
|
|
10
|
+
//
|
|
11
|
+
// Refusals follow the checker's conventions: they name a field by its
|
|
12
|
+
// position (for example `repositories[2].id`), never quote a repository id
|
|
13
|
+
// or any other value from the document, and report a JSON syntax error by
|
|
14
|
+
// position only.
|
|
15
|
+
import { readContractDocument, validateAgainstContract } from "./generated/contract-schema.generated.js";
|
|
16
|
+
import { inventoryKey } from "./identity.js";
|
|
17
|
+
import { loadContract } from "./plan-contract.js";
|
|
18
|
+
/** The contract's file name, as packed. */
|
|
19
|
+
export const INVENTORY_CONTRACT = "repository-inventory.json";
|
|
20
|
+
/** Points a refusal at the one place the shape is written down. */
|
|
21
|
+
export const INVENTORY_CONTRACT_POINTER = "see docs/contracts/repository-inventory.json";
|
|
22
|
+
function describeViolation(violation) {
|
|
23
|
+
return violation.path === "" ? violation.message : `${violation.path} ${violation.message}`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The first id, if any, that names the same repository as an earlier one
|
|
27
|
+
* under `inventoryKey()`: its position and the earlier position.
|
|
28
|
+
*/
|
|
29
|
+
export function firstRepeatedRepository(ids, hubOwner) {
|
|
30
|
+
const seen = new Map();
|
|
31
|
+
for (const [index, id] of ids.entries()) {
|
|
32
|
+
const key = inventoryKey(id, hubOwner);
|
|
33
|
+
const earlier = seen.get(key);
|
|
34
|
+
if (earlier !== undefined)
|
|
35
|
+
return { index, earlier };
|
|
36
|
+
seen.set(key, index);
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
function checkInventoryValue(value, options) {
|
|
41
|
+
const violations = validateAgainstContract(loadContract(INVENTORY_CONTRACT), value, loadContract);
|
|
42
|
+
if (violations.length > 0) {
|
|
43
|
+
return { valid: false, reason: `${violations.map(describeViolation).join("; ")} (${INVENTORY_CONTRACT_POINTER})` };
|
|
44
|
+
}
|
|
45
|
+
const entries = value.repositories;
|
|
46
|
+
const ids = entries.map((entry) => entry.id);
|
|
47
|
+
const repeated = firstRepeatedRepository(ids, options.hubOwner);
|
|
48
|
+
if (repeated !== undefined) {
|
|
49
|
+
const rule = options.hubOwner === undefined
|
|
50
|
+
? "repository ids are compared case-insensitively"
|
|
51
|
+
: "repository ids are compared case-insensitively, and a bare id names a repository of the hub's own owner";
|
|
52
|
+
return {
|
|
53
|
+
valid: false,
|
|
54
|
+
reason: `repositories[${repeated.index}].id names the same repository as repositories[${repeated.earlier}].id (${rule}; ${INVENTORY_CONTRACT_POINTER})`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return { valid: true, ids, entries };
|
|
58
|
+
}
|
|
59
|
+
function withoutEntries(read) {
|
|
60
|
+
return read.valid ? { valid: true, ids: read.ids } : read;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validates an already-parsed value against the inventory contract through
|
|
64
|
+
* the shared checker, then applies the duplicate rule. Never throws for a
|
|
65
|
+
* bad value.
|
|
66
|
+
*/
|
|
67
|
+
export function validateInventoryValue(value, options = {}) {
|
|
68
|
+
return withoutEntries(checkInventoryValue(value, options));
|
|
69
|
+
}
|
|
70
|
+
/** A lone surrogate in a JS string, which has no UTF-8 encoding; TextEncoder would silently replace it. */
|
|
71
|
+
const LONE_SURROGATE = /[\uD800-\uDFFF]/u;
|
|
72
|
+
/**
|
|
73
|
+
* Reads and checks one inventory document. Bytes -- as every file read
|
|
74
|
+
* passes them, from `WorkspaceHost.readBytes()` -- go straight to the
|
|
75
|
+
* shared strict reader, so bytes that are not valid UTF-8 are refused
|
|
76
|
+
* rather than silently replaced. Text is accepted for a document built in
|
|
77
|
+
* memory, and one holding a lone surrogate is refused before encoding.
|
|
78
|
+
*/
|
|
79
|
+
export function readInventoryDocument(raw, options = {}) {
|
|
80
|
+
if (typeof raw === "string" && LONE_SURROGATE.test(raw)) {
|
|
81
|
+
return { valid: false, reason: `is not well-formed Unicode: it contains a lone surrogate, which has no UTF-8 encoding (${INVENTORY_CONTRACT_POINTER})` };
|
|
82
|
+
}
|
|
83
|
+
let value;
|
|
84
|
+
try {
|
|
85
|
+
value = readContractDocument(typeof raw === "string" ? new TextEncoder().encode(raw) : raw);
|
|
86
|
+
}
|
|
87
|
+
catch (cause) {
|
|
88
|
+
return { valid: false, reason: `${cause instanceof Error ? cause.message : String(cause)} (${INVENTORY_CONTRACT_POINTER})` };
|
|
89
|
+
}
|
|
90
|
+
return checkInventoryValue(value, options);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Strictly validates an inventory document against
|
|
94
|
+
* docs/contracts/repository-inventory.json. It is read as strict JSON by the
|
|
95
|
+
* shared reader -- bytes that are not valid UTF-8, a syntax error (reported
|
|
96
|
+
* by position only), a key repeated in any object, or a leading byte order
|
|
97
|
+
* mark is refused -- and the value is then checked by the shared contract
|
|
98
|
+
* checker and the duplicate rule. Every read of an inventory file passes
|
|
99
|
+
* its exact bytes: `--inventory`, the stored `clossys/.state/inventory.json`
|
|
100
|
+
* on every run, `readInventoryRepositories()`, and the document
|
|
101
|
+
* `launcher --repositories` writes, before it writes it. The contract's
|
|
102
|
+
* source path does not ship in this package -- this package's build packs
|
|
103
|
+
* and ships its own copy of it -- and the stored inventory path is
|
|
104
|
+
* generated on the hub at runtime and is not part of this package at all.
|
|
105
|
+
*/
|
|
106
|
+
export function validateInventoryDocument(raw, options = {}) {
|
|
107
|
+
return withoutEntries(readInventoryDocument(raw, options));
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Whether one id satisfies the contract's `definitions/repositoryId` -- a
|
|
111
|
+
* bare repository name or `owner/name`, by the same GitHub owner and
|
|
112
|
+
* repository rules Launcher applies when it resolves a sibling clone.
|
|
113
|
+
*/
|
|
114
|
+
export function isValidInventoryId(id) {
|
|
115
|
+
return validateAgainstContract({ $ref: `${INVENTORY_CONTRACT}#/definitions/repositoryId` }, id, loadContract).length === 0;
|
|
116
|
+
}
|
|
117
|
+
/** The inventory document text Launcher writes for these entries: two-space JSON and a final newline. */
|
|
118
|
+
export function renderInventoryDocument(entries) {
|
|
119
|
+
return `${JSON.stringify({ schemaVersion: 1, repositories: entries }, null, 2)}\n`;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=inventory-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inventory-contract.js","sourceRoot":"","sources":["../src/inventory-contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** One JSON-pointer edit. `remove` deletes the value the pointer addresses; otherwise `value` is written there. */
|
|
2
|
+
export interface JsonPointerEdit {
|
|
3
|
+
readonly pointer: string;
|
|
4
|
+
readonly value?: unknown;
|
|
5
|
+
readonly remove?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** What `valueAtJsonPointer` found. `found` is false when any token along the pointer is absent. */
|
|
8
|
+
export interface JsonPointerValue {
|
|
9
|
+
readonly found: boolean;
|
|
10
|
+
readonly value: unknown;
|
|
11
|
+
}
|
|
12
|
+
/** Thrown when an edit would rewrite bytes outside the edited tokens. */
|
|
13
|
+
export declare class JsonEditUnstableError extends TypeError {
|
|
14
|
+
constructor();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The value a JSON pointer addresses in `text`, or `{ found: false }` when
|
|
18
|
+
* a token along the pointer is absent. The text must be JSON; a parse
|
|
19
|
+
* failure throws a TypeError that does not quote the text.
|
|
20
|
+
*/
|
|
21
|
+
export declare function valueAtJsonPointer(text: string, pointer: string): JsonPointerValue;
|
|
22
|
+
/**
|
|
23
|
+
* Applies `edits` to `text` and returns the edited document. An empty edit
|
|
24
|
+
* list returns `text` unchanged. Indent, the order of existing keys, and
|
|
25
|
+
* whether the text ended in a newline are taken from `text` and used for
|
|
26
|
+
* the render. The render is parsed and rendered a second time, and the two
|
|
27
|
+
* results must be the same bytes.
|
|
28
|
+
*/
|
|
29
|
+
export declare function editJsonPointer(text: string, edits: readonly JsonPointerEdit[]): string;
|
|
30
|
+
//# sourceMappingURL=key-editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-editor.d.ts","sourceRoot":"","sources":["../src/key-editor.ts"],"names":[],"mappings":"AASA,mHAAmH;AACnH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAUD,oGAAoG;AACpG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,yEAAyE;AACzE,qBAAa,qBAAsB,SAAQ,SAAS;;CAInD;AAgWD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAmBlF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,eAAe,EAAE,GAAG,MAAM,CAyBvF"}
|