@intentius/chant 0.100.0 → 0.101.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/dist/build.d.ts +6 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/cli/build-options.d.ts +2 -0
- package/dist/cli/build-options.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts +8 -0
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +22 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/lexicon.d.ts +24 -1
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/plan-digest.d.ts +26 -5
- package/dist/lifecycle/plan-digest.d.ts.map +1 -1
- package/dist/lint/config.d.ts +4 -4
- package/dist/op/activities/activity-contracts.d.ts +1 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/propose-upgrade.d.ts +2 -0
- package/dist/op/activities/propose-upgrade.d.ts.map +1 -1
- package/dist/op/index.d.ts +1 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/serializer.d.ts +8 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/telemetry-attribution.d.ts +77 -0
- package/dist/telemetry-attribution.d.ts.map +1 -0
- package/dist/workspace/agent-cli.d.ts +83 -0
- package/dist/workspace/agent-cli.d.ts.map +1 -0
- package/dist/workspace/changes-cli.d.ts.map +1 -1
- package/dist/workspace/changes.d.ts +8 -1
- package/dist/workspace/changes.d.ts.map +1 -1
- package/dist/workspace/checks/links.d.ts +1 -0
- package/dist/workspace/checks/links.d.ts.map +1 -1
- package/dist/workspace/checks/live.d.ts +40 -0
- package/dist/workspace/checks/live.d.ts.map +1 -0
- package/dist/workspace/checks.d.ts +21 -2
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/compose-graph.d.ts +63 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/decide.d.ts +1 -1
- package/dist/workspace/decide.d.ts.map +1 -1
- package/dist/workspace/declaration.d.ts +32 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +138 -3
- package/dist/workspace/export-cli.d.ts +12 -0
- package/dist/workspace/export-cli.d.ts.map +1 -0
- package/dist/workspace/export.d.ts +145 -0
- package/dist/workspace/export.d.ts.map +1 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/import.d.ts +73 -0
- package/dist/workspace/import.d.ts.map +1 -0
- package/dist/workspace/kinds.d.ts +6 -2
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/lineage-adopt-cli.d.ts +15 -0
- package/dist/workspace/lineage-adopt-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-adopt.d.ts +106 -0
- package/dist/workspace/lineage-adopt.d.ts.map +1 -0
- package/dist/workspace/lineage-check.d.ts +9 -2
- package/dist/workspace/lineage-check.d.ts.map +1 -1
- package/dist/workspace/lineage-cli.d.ts +6 -1
- package/dist/workspace/lineage-cli.d.ts.map +1 -1
- package/dist/workspace/lineage-hash-index.d.ts +110 -0
- package/dist/workspace/lineage-hash-index.d.ts.map +1 -0
- package/dist/workspace/lineage-init.d.ts +10 -0
- package/dist/workspace/lineage-init.d.ts.map +1 -1
- package/dist/workspace/lineage-lock.d.ts +147 -0
- package/dist/workspace/lineage-lock.d.ts.map +1 -1
- package/dist/workspace/lineage-migrations.d.ts +15 -3
- package/dist/workspace/lineage-migrations.d.ts.map +1 -1
- package/dist/workspace/lineage-provenance.d.ts +18 -0
- package/dist/workspace/lineage-provenance.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts +2 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.d.ts +30 -1
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/lineage-versions.d.ts +116 -0
- package/dist/workspace/lineage-versions.d.ts.map +1 -0
- package/dist/workspace/links.d.ts +31 -5
- package/dist/workspace/links.d.ts.map +1 -1
- package/dist/workspace/ls-generated.d.ts +37 -0
- package/dist/workspace/ls-generated.d.ts.map +1 -0
- package/dist/workspace/ls.d.ts +4 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/member-commands.d.ts.map +1 -1
- package/dist/workspace/nested-graph.d.ts +56 -0
- package/dist/workspace/nested-graph.d.ts.map +1 -0
- package/dist/workspace/nesting.d.ts +21 -0
- package/dist/workspace/nesting.d.ts.map +1 -0
- package/dist/workspace/pin-cli.d.ts +10 -0
- package/dist/workspace/pin-cli.d.ts.map +1 -0
- package/dist/workspace/pin-integrity.d.ts +51 -0
- package/dist/workspace/pin-integrity.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +26 -2
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/record-sessions.d.ts +7 -11
- package/dist/workspace/record-sessions.d.ts.map +1 -1
- package/dist/workspace/records-cli.d.ts +30 -1
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-close.d.ts +5 -2
- package/dist/workspace/records-close.d.ts.map +1 -1
- package/dist/workspace/records-write.d.ts +22 -4
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +43 -5
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/returns.d.ts +129 -0
- package/dist/workspace/returns.d.ts.map +1 -0
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/template-manifest.d.ts +11 -3
- package/dist/workspace/template-manifest.d.ts.map +1 -1
- package/dist/workspace/trust/attestor.d.ts +8 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -1
- package/dist/workspace/trust/dsse.d.ts +58 -0
- package/dist/workspace/trust/dsse.d.ts.map +1 -0
- package/dist/workspace/trust/evidence-cli.d.ts +66 -0
- package/dist/workspace/trust/evidence-cli.d.ts.map +1 -0
- package/dist/workspace/trust/evidence.d.ts +93 -0
- package/dist/workspace/trust/evidence.d.ts.map +1 -0
- package/dist/workspace/trust/policy.d.ts +54 -1
- package/dist/workspace/trust/policy.d.ts.map +1 -1
- package/dist/workspace/trust/provenance.d.ts +21 -1
- package/dist/workspace/trust/provenance.d.ts.map +1 -1
- package/dist/workspace/trust/rotation.d.ts +132 -0
- package/dist/workspace/trust/rotation.d.ts.map +1 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/signers-cli.d.ts +49 -0
- package/dist/workspace/trust/signers-cli.d.ts.map +1 -0
- package/dist/workspace/trust/ssh-commit.d.ts +12 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
- package/dist/workspace/trust/test-repo.d.ts +13 -0
- package/dist/workspace/trust/test-repo.d.ts.map +1 -1
- package/dist/workspace/trust/verify.d.ts +15 -0
- package/dist/workspace/trust/verify.d.ts.map +1 -1
- package/dist/workspace/work-evidence.d.ts +1 -1
- package/dist/workspace/work-evidence.d.ts.map +1 -1
- package/dist/workspace/write-scope.d.ts +199 -0
- package/dist/workspace/write-scope.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/build.ts +8 -0
- package/src/cli/build-options.ts +4 -1
- package/src/cli/commands/build.ts +2 -0
- package/src/cli/commands/import-live.test.ts +69 -1
- package/src/cli/commands/import.ts +48 -22
- package/src/cli/handlers/fan-out.test.ts +6 -6
- package/src/cli/handlers/fan-out.ts +2 -1
- package/src/cli/handlers/graph.test.ts +42 -0
- package/src/cli/handlers/graph.ts +22 -0
- package/src/cli/handlers/operator.ts +1 -1
- package/src/cli/main.test.ts +32 -0
- package/src/cli/main.ts +103 -6
- package/src/cli/mcp/workspace-tools.test.ts +1 -1
- package/src/cli/mcp/workspace-tools.ts +33 -2
- package/src/cli/registry.ts +22 -0
- package/src/cli/serve-mcp-workspace.test.ts +1 -1
- package/src/codegen/release-wiring.test.ts +5 -1
- package/src/components/fan-out-output.test.ts +1 -1
- package/src/components/fan-out.test.ts +1 -1
- package/src/components/promote.test.ts +1 -1
- package/src/config.ts +12 -0
- package/src/content-digest.test.ts +2 -2
- package/src/lexicon.ts +25 -1
- package/src/lifecycle/gate-ledger.test.ts +14 -0
- package/src/lifecycle/gate-ledger.ts +2 -1
- package/src/lifecycle/plan-digest.test.ts +54 -3
- package/src/lifecycle/plan-digest.ts +38 -8
- package/src/op/activities/activity-contracts.ts +1 -0
- package/src/op/activities/propose-upgrade.ts +8 -5
- package/src/op/gate-approval.test.ts +17 -0
- package/src/op/gate.ts +3 -3
- package/src/op/index.ts +1 -1
- package/src/serializer.ts +9 -0
- package/src/telemetry-attribution.test.ts +91 -0
- package/src/telemetry-attribution.ts +145 -0
- package/src/workspace/agent-cli.ts +134 -0
- package/src/workspace/agent.schema.json +356 -0
- package/src/workspace/behold-kinds.test.ts +1 -1
- package/src/workspace/changes-cli.ts +5 -0
- package/src/workspace/changes.schema.json +179 -1
- package/src/workspace/changes.ts +58 -4
- package/src/workspace/check-live.test.ts +192 -0
- package/src/workspace/check.schema.json +64 -0
- package/src/workspace/checks/links.ts +23 -2
- package/src/workspace/checks/live.ts +113 -0
- package/src/workspace/checks.test.ts +2 -2
- package/src/workspace/checks.ts +20 -3
- package/src/workspace/compose-graph.test.ts +47 -0
- package/src/workspace/compose-graph.ts +120 -3
- package/src/workspace/conformance/__fixture__/decisions/decision.kind.mjs +7 -1
- package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +5 -0
- package/src/workspace/declaration.schema.json +138 -3
- package/src/workspace/declaration.ts +106 -1
- package/src/workspace/declared-kinds.test.ts +33 -0
- package/src/workspace/evidence.schema.json +279 -0
- package/src/workspace/export-cli.ts +180 -0
- package/src/workspace/export-import.test.ts +282 -0
- package/src/workspace/export.ts +486 -0
- package/src/workspace/graph-cli.ts +47 -1
- package/src/workspace/graph-contract.test.ts +89 -4
- package/src/workspace/graph.schema.json +206 -1
- package/src/workspace/import.ts +325 -0
- package/src/workspace/kinds.test.ts +5 -5
- package/src/workspace/kinds.ts +25 -3
- package/src/workspace/lineage-adopt-cli.ts +103 -0
- package/src/workspace/lineage-adopt.test.ts +552 -0
- package/src/workspace/lineage-adopt.ts +452 -0
- package/src/workspace/lineage-check.ts +26 -5
- package/src/workspace/lineage-cli.ts +12 -2
- package/src/workspace/lineage-hash-index.ts +305 -0
- package/src/workspace/lineage-init.test.ts +9 -0
- package/src/workspace/lineage-init.ts +29 -9
- package/src/workspace/lineage-lock.ts +54 -0
- package/src/workspace/lineage-migrations.test.ts +27 -0
- package/src/workspace/lineage-migrations.ts +44 -14
- package/src/workspace/lineage-provenance.ts +40 -0
- package/src/workspace/lineage-upgrade-cli.ts +9 -5
- package/src/workspace/lineage-upgrade.test.ts +92 -1
- package/src/workspace/lineage-upgrade.ts +111 -16
- package/src/workspace/lineage-versions.ts +348 -0
- package/src/workspace/links.test.ts +121 -2
- package/src/workspace/links.ts +97 -6
- package/src/workspace/ls-contract.test.ts +84 -1
- package/src/workspace/ls-generated.ts +111 -0
- package/src/workspace/ls.schema.json +19 -0
- package/src/workspace/ls.ts +8 -1
- package/src/workspace/member-commands.ts +3 -1
- package/src/workspace/nested-graph.test.ts +176 -0
- package/src/workspace/nested-graph.ts +169 -0
- package/src/workspace/nesting.ts +37 -0
- package/src/workspace/pin-cli.test.ts +71 -0
- package/src/workspace/pin-cli.ts +57 -0
- package/src/workspace/pin-integrity.test.ts +121 -0
- package/src/workspace/pin-integrity.ts +104 -0
- package/src/workspace/points-write.schema.json +1 -0
- package/src/workspace/read-contract.test.ts +24 -0
- package/src/workspace/reason-codes.test.ts +10 -0
- package/src/workspace/reason-codes.ts +29 -2
- package/src/workspace/record-sessions.ts +12 -14
- package/src/workspace/records-amend.schema.json +4 -0
- package/src/workspace/records-cli.ts +93 -12
- package/src/workspace/records-close.schema.json +6 -2
- package/src/workspace/records-close.ts +10 -2
- package/src/workspace/records-formats.test.ts +13 -5
- package/src/workspace/records-new.schema.json +4 -0
- package/src/workspace/records-review.schema.json +4 -0
- package/src/workspace/records-sessions.test.ts +23 -6
- package/src/workspace/records-write.test.ts +65 -8
- package/src/workspace/records-write.ts +70 -6
- package/src/workspace/records.schema.json +55 -3
- package/src/workspace/records.ts +96 -5
- package/src/workspace/returns.ts +328 -0
- package/src/workspace/signers.schema.json +206 -0
- package/src/workspace/status-gates.ts +2 -1
- package/src/workspace/template-manifest.ts +22 -4
- package/src/workspace/trust/attestor.ts +15 -0
- package/src/workspace/trust/dsse.ts +134 -0
- package/src/workspace/trust/evidence-cli.ts +195 -0
- package/src/workspace/trust/evidence.test.ts +241 -0
- package/src/workspace/trust/evidence.ts +207 -0
- package/src/workspace/trust/policy.ts +110 -3
- package/src/workspace/trust/provenance.ts +41 -4
- package/src/workspace/trust/record-seal.test.ts +1 -1
- package/src/workspace/trust/rotation.test.ts +258 -0
- package/src/workspace/trust/rotation.ts +336 -0
- package/src/workspace/trust/seal.ts +11 -0
- package/src/workspace/trust/signers-cli.ts +178 -0
- package/src/workspace/trust/ssh-commit.ts +53 -4
- package/src/workspace/trust/test-repo.ts +18 -0
- package/src/workspace/trust/trust.test.ts +8 -2
- package/src/workspace/trust/verify-cli.ts +1 -0
- package/src/workspace/trust/verify.ts +22 -0
- package/src/workspace/work-evidence.schema.json +4 -0
- package/src/workspace/write-scope.test.ts +340 -0
- package/src/workspace/write-scope.ts +448 -0
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"exports",
|
|
30
30
|
"imports",
|
|
31
31
|
"links",
|
|
32
|
+
"collectors",
|
|
32
33
|
"records"
|
|
33
34
|
],
|
|
34
35
|
"properties": {
|
|
@@ -223,6 +224,13 @@
|
|
|
223
224
|
"$ref": "#/$defs/link"
|
|
224
225
|
}
|
|
225
226
|
},
|
|
227
|
+
"collectors": {
|
|
228
|
+
"type": "array",
|
|
229
|
+
"description": "The collector pipelines and exporters of each member that declares a collector, in declaration order (#2559). A member reports them from its own IR (`meta.collector`), which the lexicon that types the collector config writes, so chant itself reads no collector config. Empty when no member declares one. A reader joins a telemetry endpoint to the pipeline or exporter it names through `exporters[].endpoints` and `pipelines[].id`.",
|
|
230
|
+
"items": {
|
|
231
|
+
"$ref": "#/$defs/collector"
|
|
232
|
+
}
|
|
233
|
+
},
|
|
226
234
|
"records": {
|
|
227
235
|
"type": "array",
|
|
228
236
|
"description": "The records read with --kind (#2524 D4, #2549), sorted by path; empty without it. A record link in `links` names its record by id.",
|
|
@@ -330,6 +338,40 @@
|
|
|
330
338
|
},
|
|
331
339
|
"pipeline": {
|
|
332
340
|
"description": "The member IR's pipeline projection, when it has one."
|
|
341
|
+
},
|
|
342
|
+
"nested": {
|
|
343
|
+
"type": "object",
|
|
344
|
+
"description": "For a member of kind workspace (#2551): the nested workspace, read read-only through its own chant workspace graph. Its nodes, edges, exports and imports are in the outer sections with `<member>/<nested member>/<id>` ids, and each node names its nested member in `nested`. The outer workspace never writes inside it.",
|
|
345
|
+
"required": [
|
|
346
|
+
"name",
|
|
347
|
+
"contract",
|
|
348
|
+
"members",
|
|
349
|
+
"links"
|
|
350
|
+
],
|
|
351
|
+
"properties": {
|
|
352
|
+
"name": {
|
|
353
|
+
"type": "string",
|
|
354
|
+
"description": "The nested declaration's name."
|
|
355
|
+
},
|
|
356
|
+
"contract": {
|
|
357
|
+
"type": "integer",
|
|
358
|
+
"description": "The contract version of the nested read's document."
|
|
359
|
+
},
|
|
360
|
+
"members": {
|
|
361
|
+
"type": "array",
|
|
362
|
+
"items": {
|
|
363
|
+
"$ref": "#/$defs/member"
|
|
364
|
+
},
|
|
365
|
+
"description": "The nested workspace's members as its own graph lists them, with their own names and directories (relative to the nested root)."
|
|
366
|
+
},
|
|
367
|
+
"links": {
|
|
368
|
+
"type": "array",
|
|
369
|
+
"items": {
|
|
370
|
+
"$ref": "#/$defs/link"
|
|
371
|
+
},
|
|
372
|
+
"description": "The nested workspace's links, naming its members as the nested side does."
|
|
373
|
+
}
|
|
374
|
+
}
|
|
333
375
|
}
|
|
334
376
|
},
|
|
335
377
|
"if": {
|
|
@@ -396,6 +438,10 @@
|
|
|
396
438
|
"runtimeOwner": {
|
|
397
439
|
"type": "string"
|
|
398
440
|
},
|
|
441
|
+
"nested": {
|
|
442
|
+
"type": "string",
|
|
443
|
+
"description": "For a node read through a nested workspace (#2551): the nested workspace's member it belongs to. `member` is then the outer member, and the id is `<member>/<nested>/<id>`."
|
|
444
|
+
},
|
|
399
445
|
"sourceLoc": {
|
|
400
446
|
"type": "object",
|
|
401
447
|
"required": [
|
|
@@ -544,7 +590,7 @@
|
|
|
544
590
|
},
|
|
545
591
|
"kind": {
|
|
546
592
|
"type": "string",
|
|
547
|
-
"description": "What the link joins,
|
|
593
|
+
"description": "What the link joins: `output`, or `telemetry` (#2558), where the consumer sends its telemetry to a pipeline or exporter of the producer's collector and `output` names it."
|
|
548
594
|
},
|
|
549
595
|
"origin": {
|
|
550
596
|
"enum": [
|
|
@@ -599,6 +645,21 @@
|
|
|
599
645
|
"pointer": {
|
|
600
646
|
"type": "string",
|
|
601
647
|
"description": "The declared link's JSON Pointer in the declaration; left out of the graph for a declared link."
|
|
648
|
+
},
|
|
649
|
+
"protocol": {
|
|
650
|
+
"enum": [
|
|
651
|
+
"grpc",
|
|
652
|
+
"http/protobuf",
|
|
653
|
+
"http/json"
|
|
654
|
+
],
|
|
655
|
+
"description": "A telemetry link: the OTLP protocol it states."
|
|
656
|
+
},
|
|
657
|
+
"target": {
|
|
658
|
+
"enum": [
|
|
659
|
+
"pipeline",
|
|
660
|
+
"exporter"
|
|
661
|
+
],
|
|
662
|
+
"description": "A telemetry link that found its target: whether `output` named a pipeline or an exporter in the producer's `collectors` entry."
|
|
602
663
|
}
|
|
603
664
|
}
|
|
604
665
|
},
|
|
@@ -764,6 +825,150 @@
|
|
|
764
825
|
}
|
|
765
826
|
]
|
|
766
827
|
},
|
|
828
|
+
"collector": {
|
|
829
|
+
"type": "object",
|
|
830
|
+
"required": [
|
|
831
|
+
"member",
|
|
832
|
+
"pipelines",
|
|
833
|
+
"components",
|
|
834
|
+
"exporters"
|
|
835
|
+
],
|
|
836
|
+
"properties": {
|
|
837
|
+
"member": {
|
|
838
|
+
"type": "string",
|
|
839
|
+
"description": "The member whose source declares the collector."
|
|
840
|
+
},
|
|
841
|
+
"pipelines": {
|
|
842
|
+
"type": "array",
|
|
843
|
+
"description": "Each pipeline under `service.pipelines`.",
|
|
844
|
+
"items": {
|
|
845
|
+
"type": "object",
|
|
846
|
+
"required": [
|
|
847
|
+
"id",
|
|
848
|
+
"signal",
|
|
849
|
+
"receivers",
|
|
850
|
+
"processors",
|
|
851
|
+
"exporters"
|
|
852
|
+
],
|
|
853
|
+
"properties": {
|
|
854
|
+
"id": {
|
|
855
|
+
"type": "string",
|
|
856
|
+
"description": "The pipeline id, such as `traces` or `traces/backend`."
|
|
857
|
+
},
|
|
858
|
+
"signal": {
|
|
859
|
+
"type": "string",
|
|
860
|
+
"description": "`traces`, `metrics` or `logs`."
|
|
861
|
+
},
|
|
862
|
+
"receivers": {
|
|
863
|
+
"type": "array",
|
|
864
|
+
"items": {
|
|
865
|
+
"type": "string"
|
|
866
|
+
}
|
|
867
|
+
},
|
|
868
|
+
"processors": {
|
|
869
|
+
"type": "array",
|
|
870
|
+
"items": {
|
|
871
|
+
"type": "string"
|
|
872
|
+
}
|
|
873
|
+
},
|
|
874
|
+
"exporters": {
|
|
875
|
+
"type": "array",
|
|
876
|
+
"items": {
|
|
877
|
+
"type": "string"
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
},
|
|
883
|
+
"components": {
|
|
884
|
+
"type": "array",
|
|
885
|
+
"description": "Every receiver, processor, exporter, connector and extension, with the addresses its config states.",
|
|
886
|
+
"items": {
|
|
887
|
+
"type": "object",
|
|
888
|
+
"required": [
|
|
889
|
+
"id",
|
|
890
|
+
"kind",
|
|
891
|
+
"type",
|
|
892
|
+
"endpoints",
|
|
893
|
+
"protocols",
|
|
894
|
+
"pipelines"
|
|
895
|
+
],
|
|
896
|
+
"properties": {
|
|
897
|
+
"id": {
|
|
898
|
+
"type": "string",
|
|
899
|
+
"description": "The collector id, `type` or `type/name`."
|
|
900
|
+
},
|
|
901
|
+
"kind": {
|
|
902
|
+
"type": "string",
|
|
903
|
+
"description": "`receiver`, `processor`, `exporter`, `connector` or `extension`."
|
|
904
|
+
},
|
|
905
|
+
"type": {
|
|
906
|
+
"type": "string"
|
|
907
|
+
},
|
|
908
|
+
"endpoints": {
|
|
909
|
+
"type": "array",
|
|
910
|
+
"items": {
|
|
911
|
+
"type": "string"
|
|
912
|
+
}
|
|
913
|
+
},
|
|
914
|
+
"protocols": {
|
|
915
|
+
"type": "array",
|
|
916
|
+
"items": {
|
|
917
|
+
"type": "string"
|
|
918
|
+
},
|
|
919
|
+
"description": "The wire protocols the component speaks, as OTLP SDKs name them: `grpc`, `http/protobuf` or `http/json`. Empty when its definition states none, which a telemetry link's protocol check reads as unknown."
|
|
920
|
+
},
|
|
921
|
+
"pipelines": {
|
|
922
|
+
"type": "array",
|
|
923
|
+
"items": {
|
|
924
|
+
"type": "string"
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
},
|
|
930
|
+
"exporters": {
|
|
931
|
+
"type": "array",
|
|
932
|
+
"description": "The exporters again, with the signals that reach each: where the member's telemetry is sent.",
|
|
933
|
+
"items": {
|
|
934
|
+
"type": "object",
|
|
935
|
+
"required": [
|
|
936
|
+
"id",
|
|
937
|
+
"type",
|
|
938
|
+
"endpoints",
|
|
939
|
+
"pipelines",
|
|
940
|
+
"signals"
|
|
941
|
+
],
|
|
942
|
+
"properties": {
|
|
943
|
+
"id": {
|
|
944
|
+
"type": "string"
|
|
945
|
+
},
|
|
946
|
+
"type": {
|
|
947
|
+
"type": "string"
|
|
948
|
+
},
|
|
949
|
+
"endpoints": {
|
|
950
|
+
"type": "array",
|
|
951
|
+
"items": {
|
|
952
|
+
"type": "string"
|
|
953
|
+
}
|
|
954
|
+
},
|
|
955
|
+
"pipelines": {
|
|
956
|
+
"type": "array",
|
|
957
|
+
"items": {
|
|
958
|
+
"type": "string"
|
|
959
|
+
}
|
|
960
|
+
},
|
|
961
|
+
"signals": {
|
|
962
|
+
"type": "array",
|
|
963
|
+
"items": {
|
|
964
|
+
"type": "string"
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
},
|
|
767
972
|
"record": {
|
|
768
973
|
"type": "object",
|
|
769
974
|
"required": [
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant workspace import`: bring an export back into the workspace it came
|
|
3
|
+
* from (#2552, #2524 D10, requirements X3 and X5, ws-004, ws-073).
|
|
4
|
+
*
|
|
5
|
+
* The source is the export member, or a directory holding a copy of an
|
|
6
|
+
* export, such as the repository a hosted copy was kept in. Its
|
|
7
|
+
* `.chant/export.json` names the workspace and the members that went, and the
|
|
8
|
+
* hash each file had on both sides when it went: the merge base.
|
|
9
|
+
*
|
|
10
|
+
* For each file, three hashes decide. A file the copy did not change is left
|
|
11
|
+
* alone. A file the copy changed is written when the workspace did not
|
|
12
|
+
* change it since the export. A file both changed differently is a conflict,
|
|
13
|
+
* and any conflict refuses the whole import, which then writes nothing. The
|
|
14
|
+
* same holds for removed and added files. Only paths inside the members that
|
|
15
|
+
* went, and the record directories of a whole export, are written: a copy
|
|
16
|
+
* that holds anything else is refused, so an import never writes into another
|
|
17
|
+
* member.
|
|
18
|
+
*
|
|
19
|
+
* Host values switch back (X3): the host-bound values the export recorded
|
|
20
|
+
* are put back in the files the lineage lists, and in the lock. Each lineage
|
|
21
|
+
* scope merges the same way as a file, with the scope's digest as the base.
|
|
22
|
+
*
|
|
23
|
+
* Every write is recorded in `.chant/returns/<id>.json` (./returns.ts). When
|
|
24
|
+
* the copy is a git repository of its own, each written file also carries the
|
|
25
|
+
* commit that last changed it there, with the original signature, and the
|
|
26
|
+
* tree objects that tie that commit to the bytes. Records keep their seals,
|
|
27
|
+
* since their bytes are copied as they are.
|
|
28
|
+
*
|
|
29
|
+
* Last, the export member is written again from the workspace as it now
|
|
30
|
+
* stands (the default), or removed with its declaration entry (`--remove`).
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { execFileSync } from "node:child_process";
|
|
34
|
+
import { createHash } from "node:crypto";
|
|
35
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
36
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
37
|
+
import { readDeclaration, type Declaration } from "./declaration";
|
|
38
|
+
import {
|
|
39
|
+
EXPORT_MANIFEST,
|
|
40
|
+
ExportError,
|
|
41
|
+
exportMember,
|
|
42
|
+
exportWorkspace,
|
|
43
|
+
gitTopOf,
|
|
44
|
+
isLockPath,
|
|
45
|
+
lineageDigest,
|
|
46
|
+
listFiles,
|
|
47
|
+
lockDir,
|
|
48
|
+
rawDeclaration,
|
|
49
|
+
readManifestAt,
|
|
50
|
+
scopedPath,
|
|
51
|
+
switchHostValues,
|
|
52
|
+
under,
|
|
53
|
+
type ExportManifest,
|
|
54
|
+
type ExportResult,
|
|
55
|
+
} from "./export";
|
|
56
|
+
import { fileHash, LOCK_FILE, parseLock, renderLock, type LineageLock } from "./lineage-lock";
|
|
57
|
+
import { buildOrigin, originSigners, RETURNS_DIR, writeReturn, type ReturnRecord } from "./returns";
|
|
58
|
+
import { workingTree } from "./tree";
|
|
59
|
+
|
|
60
|
+
export interface ImportOptions {
|
|
61
|
+
/** The workspace root. */
|
|
62
|
+
root: string;
|
|
63
|
+
/** The returned copy's directory. Absent: the export member. */
|
|
64
|
+
from?: string;
|
|
65
|
+
/** Remove the export member and its declaration entry, rather than writing it again. */
|
|
66
|
+
remove?: boolean;
|
|
67
|
+
dryRun?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface ImportConflict {
|
|
71
|
+
path: string;
|
|
72
|
+
reason: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ImportResult {
|
|
76
|
+
/** The export the copy started from. */
|
|
77
|
+
export: string;
|
|
78
|
+
/** The copy's directory, as given or the export member's. */
|
|
79
|
+
from: string;
|
|
80
|
+
written: string[];
|
|
81
|
+
removed: string[];
|
|
82
|
+
/** Locks changed, by path. */
|
|
83
|
+
locks: string[];
|
|
84
|
+
/** Paths whose host-bound values were switched back. */
|
|
85
|
+
hostValues: string[];
|
|
86
|
+
conflicts: ImportConflict[];
|
|
87
|
+
/** Paths in the copy that are outside every member that went. */
|
|
88
|
+
outside: string[];
|
|
89
|
+
/** The return record written, relative to the workspace root, or null when nothing changed or for a dry run. */
|
|
90
|
+
returnRecord: string | null;
|
|
91
|
+
return: ReturnRecord | null;
|
|
92
|
+
/** What happened to the export member: written again, removed, or left (nothing changed, a dry run, or a conflict). */
|
|
93
|
+
member: { name: string; action: "regenerated" | "removed" | "left"; note?: string } | null;
|
|
94
|
+
applied: boolean;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Paths the copy may hold besides member and record files. */
|
|
98
|
+
function ignoredInCopy(path: string): boolean {
|
|
99
|
+
return path === EXPORT_MANIFEST || path === "chant.workspace.json" || path === "chant.workspace.jsonc" || path.startsWith(`${RETURNS_DIR}/`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function readOr(abs: string): Buffer | undefined {
|
|
103
|
+
return existsSync(abs) ? readFileSync(abs) : undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Plan the import, and apply it unless `dryRun` or a conflict. Throws an {@link ExportError}. */
|
|
107
|
+
export async function importWorkspace(o: ImportOptions): Promise<ImportResult> {
|
|
108
|
+
const root = resolve(o.root);
|
|
109
|
+
const decl = readDeclaration(workingTree(root));
|
|
110
|
+
const fromDir = o.from !== undefined ? resolve(o.from) : join(root, exportMember(decl).dir);
|
|
111
|
+
const manifest = readManifestAt(fromDir);
|
|
112
|
+
if (!manifest) throw new ExportError(`${fromDir} holds no ${EXPORT_MANIFEST}: it is not an export`);
|
|
113
|
+
if (manifest.from.workspace !== decl.name) {
|
|
114
|
+
throw new ExportError(`the export came from workspace ${manifest.from.workspace}, and this one is ${decl.name}`);
|
|
115
|
+
}
|
|
116
|
+
const memberDirs = manifest.members.map((name) => {
|
|
117
|
+
const m = decl.members.find((x) => x.name === name);
|
|
118
|
+
if (!m) throw new ExportError(`member ${name} went with the export, and this workspace no longer declares it`);
|
|
119
|
+
return m.dir;
|
|
120
|
+
});
|
|
121
|
+
const allowed = [...memberDirs, ...manifest.dirs];
|
|
122
|
+
const others = decl.members.filter((m) => !manifest.members.includes(m.name) && m.dir !== ".");
|
|
123
|
+
|
|
124
|
+
// The copy's files and locks.
|
|
125
|
+
const copyFiles = new Map<string, Buffer>();
|
|
126
|
+
const copyLocks = new Map<string, LineageLock>();
|
|
127
|
+
const outside: string[] = [];
|
|
128
|
+
for (const path of listFiles(fromDir)) {
|
|
129
|
+
if (ignoredInCopy(path)) continue;
|
|
130
|
+
if (path === LOCK_FILE) {
|
|
131
|
+
copyLocks.set(path, parseLock(readFileSync(join(fromDir, path), "utf-8")));
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (!allowed.some((d) => under(path, d)) || others.some((m) => under(path, m.dir))) {
|
|
135
|
+
outside.push(path);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (isLockPath(path)) copyLocks.set(path, parseLock(readFileSync(join(fromDir, path), "utf-8")));
|
|
139
|
+
else copyFiles.set(path, readFileSync(join(fromDir, path)));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Each scope's digest as the copy holds it, before host values go back.
|
|
143
|
+
const copyDigests = new Map<string, string>();
|
|
144
|
+
for (const [path, lock] of copyLocks) for (const [scope, lineage] of Object.entries(lock.scopes)) copyDigests.set(`${path}#${scope}`, lineageDigest(lineage));
|
|
145
|
+
|
|
146
|
+
// Host values back (X3), in the files and in each lock.
|
|
147
|
+
const exportForm = new Map(copyFiles);
|
|
148
|
+
const hostValues = new Set<string>();
|
|
149
|
+
for (const [path, lock] of copyLocks) {
|
|
150
|
+
for (const [scope, lineage] of Object.entries(lock.scopes)) {
|
|
151
|
+
const values = manifest.hostValues[`${path}#${scope}`];
|
|
152
|
+
if (!values) continue;
|
|
153
|
+
const back = Object.fromEntries(Object.entries(values).map(([n, v]) => [n, { from: v.export, to: v.host }]));
|
|
154
|
+
for (const p of switchHostValues(lineage, lockDir(path), scope, back, copyFiles)) hostValues.add(p);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const conflicts: ImportConflict[] = [];
|
|
159
|
+
const writes = new Map<string, Buffer>();
|
|
160
|
+
const removes: string[] = [];
|
|
161
|
+
for (const path of [...new Set([...Object.keys(manifest.files), ...copyFiles.keys()])].sort()) {
|
|
162
|
+
if (!allowed.some((d) => under(path, d))) continue;
|
|
163
|
+
const base = manifest.files[path];
|
|
164
|
+
const copy = exportForm.get(path);
|
|
165
|
+
const changedThere = copy === undefined ? base !== undefined : base === undefined || fileHash(copy) !== base.export;
|
|
166
|
+
if (!changedThere) continue;
|
|
167
|
+
const here = readOr(join(root, path));
|
|
168
|
+
const changedHere = here === undefined ? base !== undefined : base === undefined || fileHash(here) !== base.host;
|
|
169
|
+
if (copy === undefined) {
|
|
170
|
+
if (here === undefined) continue;
|
|
171
|
+
if (changedHere) conflicts.push({ path, reason: "the copy removed it, and it changed here since the export" });
|
|
172
|
+
else removes.push(path);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const target = copyFiles.get(path)!;
|
|
176
|
+
if (here !== undefined && here.equals(target)) continue;
|
|
177
|
+
if (changedHere) {
|
|
178
|
+
conflicts.push({ path, reason: here === undefined ? "the copy changed it, and it was removed here since the export" : base === undefined ? "the copy added it, and a different file was added here" : "the copy and this workspace both changed it since the export" });
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
writes.set(path, target);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Locks, scope by scope.
|
|
185
|
+
const lockWrites = new Map<string, LineageLock>();
|
|
186
|
+
for (const path of [...new Set([...Object.keys(manifest.locks), ...copyLocks.keys()])].sort()) {
|
|
187
|
+
const there = copyLocks.get(path);
|
|
188
|
+
const hereText = readOr(join(root, path));
|
|
189
|
+
const here: LineageLock | null = hereText ? parseLock(hereText.toString("utf-8")) : null;
|
|
190
|
+
const next: LineageLock = here ? JSON.parse(JSON.stringify(here)) : { lockVersion: 1, scopes: {} };
|
|
191
|
+
let changed = false;
|
|
192
|
+
const recorded = manifest.locks[path] ?? {};
|
|
193
|
+
for (const scope of [...new Set([...Object.keys(recorded), ...Object.keys(there?.scopes ?? {})])].sort()) {
|
|
194
|
+
const base = recorded[scope];
|
|
195
|
+
const theirs = there?.scopes[scope];
|
|
196
|
+
const ours = here?.scopes[scope];
|
|
197
|
+
const theirDigest = copyDigests.get(`${path}#${scope}`);
|
|
198
|
+
const changedThere = theirDigest === undefined ? base !== undefined : base === undefined || theirDigest !== base.export;
|
|
199
|
+
if (!changedThere) continue;
|
|
200
|
+
const changedHere = ours === undefined ? base !== undefined : base === undefined || lineageDigest(ours) !== base.host;
|
|
201
|
+
const where = `${path} scope ${scope}`;
|
|
202
|
+
if (changedHere) {
|
|
203
|
+
conflicts.push({ path: where, reason: "the copy and this workspace both changed the lineage since the export" });
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (!theirs) {
|
|
207
|
+
delete next.scopes[scope];
|
|
208
|
+
changed = true;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (base?.partial && ours) {
|
|
212
|
+
// Only part of the scope went: take the copy's files, and nothing that would move the rest.
|
|
213
|
+
const { files: _f, manualSteps: _m, repinned: _r, ...a } = ours;
|
|
214
|
+
const { files: _f2, manualSteps: _m2, repinned: _r2, ...b } = theirs;
|
|
215
|
+
if (JSON.stringify(a) !== JSON.stringify(b)) {
|
|
216
|
+
conflicts.push({ path: where, reason: "the copy upgraded a scope only part of which went with the export; upgrade it here instead" });
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const dir = lockDir(path);
|
|
220
|
+
const went = (f: string) => manifest.files[scopedPath(dir, scope, f)] !== undefined || theirs.files[f] !== undefined;
|
|
221
|
+
const files = Object.fromEntries(Object.entries(ours.files).filter(([f]) => !went(f)));
|
|
222
|
+
next.scopes[scope] = {
|
|
223
|
+
...ours,
|
|
224
|
+
files: { ...files, ...theirs.files },
|
|
225
|
+
manualSteps: [...ours.manualSteps.filter((s) => !went(s.path)), ...theirs.manualSteps],
|
|
226
|
+
};
|
|
227
|
+
} else {
|
|
228
|
+
next.scopes[scope] = theirs;
|
|
229
|
+
}
|
|
230
|
+
changed = true;
|
|
231
|
+
}
|
|
232
|
+
if (changed) lockWrites.set(path, next);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const result: ImportResult = {
|
|
236
|
+
export: manifest.id,
|
|
237
|
+
from: fromDir,
|
|
238
|
+
written: [...writes.keys()],
|
|
239
|
+
removed: removes,
|
|
240
|
+
locks: [...lockWrites.keys()],
|
|
241
|
+
hostValues: [...hostValues].filter((p) => writes.has(p)).sort(),
|
|
242
|
+
conflicts,
|
|
243
|
+
outside,
|
|
244
|
+
returnRecord: null,
|
|
245
|
+
return: null,
|
|
246
|
+
member: null,
|
|
247
|
+
applied: false,
|
|
248
|
+
};
|
|
249
|
+
if (outside.length > 0 || conflicts.length > 0) return result;
|
|
250
|
+
const nothing = writes.size === 0 && removes.length === 0 && lockWrites.size === 0;
|
|
251
|
+
|
|
252
|
+
// The return record: what came back, and where each file was made.
|
|
253
|
+
const ownRepo = gitTopOf(fromDir);
|
|
254
|
+
const own = ownRepo !== undefined && realpathSync(ownRepo) === realpathSync(fromDir);
|
|
255
|
+
let head: string | null = null;
|
|
256
|
+
if (own) {
|
|
257
|
+
try {
|
|
258
|
+
head = execFileSync("git", ["rev-parse", "HEAD"], { cwd: fromDir, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
259
|
+
} catch {
|
|
260
|
+
head = null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const paths: ReturnRecord["paths"] = {};
|
|
264
|
+
for (const [path, data] of writes) {
|
|
265
|
+
const origin = own && head && !hostValues.has(path) ? buildOrigin(fromDir, path, data) : undefined;
|
|
266
|
+
paths[path] = { sha256: fileHash(data), ...(origin ? { origin } : {}) };
|
|
267
|
+
}
|
|
268
|
+
for (const path of lockWrites.keys()) paths[path] = { sha256: fileHash(renderLock(lockWrites.get(path)!)) };
|
|
269
|
+
const origins = Object.values(paths).flatMap((p) => (p.origin ? [p.origin] : []));
|
|
270
|
+
const idBody = JSON.stringify({ export: manifest.id, head, paths: Object.fromEntries(Object.entries(paths).map(([p, v]) => [p, v.sha256])), removes });
|
|
271
|
+
const ret: ReturnRecord = {
|
|
272
|
+
schema: 1,
|
|
273
|
+
id: `ret-${createHash("sha256").update(idBody).digest("hex").slice(0, 12)}`,
|
|
274
|
+
export: { id: manifest.id, workspace: manifest.from.workspace, revision: manifest.from.revision },
|
|
275
|
+
head,
|
|
276
|
+
paths,
|
|
277
|
+
removed: removes,
|
|
278
|
+
hostValues: result.hostValues,
|
|
279
|
+
signers: originSigners(origins),
|
|
280
|
+
};
|
|
281
|
+
if (!nothing) result.return = ret;
|
|
282
|
+
if (o.dryRun) return result;
|
|
283
|
+
|
|
284
|
+
// Apply.
|
|
285
|
+
for (const [path, data] of writes) {
|
|
286
|
+
const abs = join(root, path);
|
|
287
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
288
|
+
writeFileSync(abs, data);
|
|
289
|
+
}
|
|
290
|
+
for (const path of removes) rmSync(join(root, path), { force: true });
|
|
291
|
+
for (const [path, lock] of lockWrites) {
|
|
292
|
+
const abs = join(root, path);
|
|
293
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
294
|
+
writeFileSync(abs, renderLock(lock));
|
|
295
|
+
}
|
|
296
|
+
if (!nothing) result.returnRecord = writeReturn(root, ret);
|
|
297
|
+
result.applied = true;
|
|
298
|
+
|
|
299
|
+
// The export member: written again from the workspace as it stands, or removed.
|
|
300
|
+
result.member = await settleExportMember(root, decl, manifest, o.remove === true, fromDir);
|
|
301
|
+
return result;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async function settleExportMember(root: string, decl: Declaration, manifest: ExportManifest, remove: boolean, fromDir: string): Promise<ImportResult["member"]> {
|
|
305
|
+
const m = decl.members.find((x) => x.name === manifest.member);
|
|
306
|
+
if (!m) return null;
|
|
307
|
+
if (!remove) {
|
|
308
|
+
const again: ExportResult = await exportWorkspace({
|
|
309
|
+
root,
|
|
310
|
+
to: m.name,
|
|
311
|
+
...(manifest.whole ? {} : { members: manifest.members }),
|
|
312
|
+
params: Object.fromEntries(Object.values(manifest.hostValues).flatMap((v) => Object.entries(v).map(([n, x]) => [n, x.export]))),
|
|
313
|
+
});
|
|
314
|
+
return { name: m.name, action: "regenerated", note: `${again.manifest.id}, ${again.files} file(s)` };
|
|
315
|
+
}
|
|
316
|
+
rmSync(join(root, m.dir), { recursive: true, force: true });
|
|
317
|
+
const { file, obj } = rawDeclaration(root);
|
|
318
|
+
if (file.endsWith(".jsonc")) {
|
|
319
|
+
return { name: m.name, action: "removed", note: `removed ${m.dir}; ${file} keeps comments, so drop the entry for ${m.name} by hand` };
|
|
320
|
+
}
|
|
321
|
+
obj.members = (obj.members as { name: string }[]).filter((e) => e.name !== m.name);
|
|
322
|
+
writeFileSync(join(root, file), JSON.stringify(obj, null, 2) + "\n");
|
|
323
|
+
const inside = resolve(fromDir) === resolve(root, m.dir);
|
|
324
|
+
return { name: m.name, action: "removed", note: `removed ${m.dir} and its entry in ${file}${inside ? "" : `; the copy at ${relative(root, fromDir) || "."} is left as it is`}` };
|
|
325
|
+
}
|
|
@@ -61,10 +61,10 @@ function pkg(name: string, version: string, kinds: unknown[] | null, extra: Reco
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
describe("the built-in kinds (#2535)", () => {
|
|
64
|
-
test("are chant, workspace and other, and the group kind examples", () => {
|
|
65
|
-
expect(BUILTIN_KINDS.map((k) => `${k.name}:${k.shape}`)).toEqual(["chant:member", "workspace:member", "other:member", "examples:group"]);
|
|
64
|
+
test("are chant, workspace, design and other, and the group kind examples", () => {
|
|
65
|
+
expect(BUILTIN_KINDS.map((k) => `${k.name}:${k.shape}`)).toEqual(["chant:member", "workspace:member", "other:member", "design:member", "examples:group"]);
|
|
66
66
|
const registry = builtinKindRegistry();
|
|
67
|
-
expect(registry.names()).toEqual(["chant", "other", "workspace"]);
|
|
67
|
+
expect(registry.names()).toEqual(["chant", "design", "other", "workspace"]);
|
|
68
68
|
expect(registry.get(EXAMPLES_KIND)?.shape).toBe("group");
|
|
69
69
|
});
|
|
70
70
|
|
|
@@ -203,7 +203,7 @@ describe("loadKindRegistry: kinds from the pins", () => {
|
|
|
203
203
|
const { registry, problems } = loadKindRegistry([{ package: "@acme/chant-lexicon-tf", version: "1.2.3", path: null }], root);
|
|
204
204
|
expect(problems).toEqual([]);
|
|
205
205
|
expect(registry.get("terraform")).toMatchObject({ source: "@acme/chant-lexicon-tf", precedence: 400 });
|
|
206
|
-
expect(registry.names()).toEqual(["chant", "other", "terraform", "workspace"]);
|
|
206
|
+
expect(registry.names()).toEqual(["chant", "design", "other", "terraform", "workspace"]);
|
|
207
207
|
expect(existsSync(join(root, "node_modules/@acme/chant-lexicon-tf/IMPORTED"))).toBe(false);
|
|
208
208
|
expect((globalThis as { __chantKindsImported?: boolean }).__chantKindsImported).toBeUndefined();
|
|
209
209
|
});
|
|
@@ -223,7 +223,7 @@ describe("loadKindRegistry: kinds from the pins", () => {
|
|
|
223
223
|
],
|
|
224
224
|
root,
|
|
225
225
|
);
|
|
226
|
-
expect(registry.names()).toEqual(["chant", "other", "workspace"]);
|
|
226
|
+
expect(registry.names()).toEqual(["chant", "design", "other", "workspace"]);
|
|
227
227
|
expect(problems).toEqual([
|
|
228
228
|
{ pin: 1, message: "pinned package absent is not installed; install it to read the kinds it supplies" },
|
|
229
229
|
{ pin: 2, message: "tf-kinds is pinned at 1.0.0, and 2.0.0 is installed" },
|
package/src/workspace/kinds.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* `examples`).
|
|
4
4
|
*
|
|
5
5
|
* A kind is data: a name, a precedence and a probe that says what its
|
|
6
|
-
* directory holds. Probes run no code (K3).
|
|
7
|
-
* member kinds `chant`, `workspace` and `other`, and the group kind
|
|
6
|
+
* directory holds. Probes run no code (K3). Five kinds are built in: the
|
|
7
|
+
* member kinds `chant`, `workspace`, `design` and `other`, and the group kind
|
|
8
8
|
* `examples`. Every other kind comes from a pinned package, which publishes
|
|
9
9
|
* its kinds as a JSON file at a `./workspace-kinds` subpath, the way a
|
|
10
10
|
* lexicon publishes a slim `./detect` entry (#426). chant finds that file
|
|
@@ -23,6 +23,7 @@ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
|
23
23
|
import schema from "./workspace-kinds.schema.json";
|
|
24
24
|
import type { WorkspaceTree } from "./tree";
|
|
25
25
|
import { joinPath, skippedDir } from "./tree";
|
|
26
|
+
import { checkPinIntegrity } from "./pin-integrity";
|
|
26
27
|
|
|
27
28
|
export const KINDS_SCHEMA_ID = schema.$id;
|
|
28
29
|
|
|
@@ -111,6 +112,9 @@ export interface KindRegistry {
|
|
|
111
112
|
/** The group kind (ws-051). An entry of this kind is a group, not a member. */
|
|
112
113
|
export const EXAMPLES_KIND = "examples";
|
|
113
114
|
|
|
115
|
+
/** The data member kind that holds design artifacts (#2549, ws-062). */
|
|
116
|
+
export const DESIGN_KIND = "design";
|
|
117
|
+
|
|
114
118
|
export const BUILTIN_KINDS: readonly MemberKind[] = [
|
|
115
119
|
{
|
|
116
120
|
name: "chant",
|
|
@@ -123,7 +127,7 @@ export const BUILTIN_KINDS: readonly MemberKind[] = [
|
|
|
123
127
|
},
|
|
124
128
|
{
|
|
125
129
|
name: "workspace",
|
|
126
|
-
description: "a nested workspace, with its own chant.workspace.json;
|
|
130
|
+
description: "a nested workspace, with its own chant.workspace.json; the outer one reads it through its own chant workspace graph and never writes inside it",
|
|
127
131
|
probe: { anyFile: ["chant.workspace.json", "chant.workspace.jsonc"] },
|
|
128
132
|
precedence: 1000,
|
|
129
133
|
shape: "member",
|
|
@@ -139,6 +143,15 @@ export const BUILTIN_KINDS: readonly MemberKind[] = [
|
|
|
139
143
|
outputs: { from: "declared", names: [] },
|
|
140
144
|
source: "builtin",
|
|
141
145
|
},
|
|
146
|
+
{
|
|
147
|
+
name: DESIGN_KIND,
|
|
148
|
+
description: "a data member holding the design artifacts the workspace owns; chant reads its files for record pins and builds nothing",
|
|
149
|
+
probe: { directory: true },
|
|
150
|
+
precedence: 0,
|
|
151
|
+
shape: "member",
|
|
152
|
+
outputs: { from: "declared", names: [] },
|
|
153
|
+
source: "builtin",
|
|
154
|
+
},
|
|
142
155
|
{
|
|
143
156
|
name: EXAMPLES_KIND,
|
|
144
157
|
description: "an example group: every directory its glob matches that holds a chant project is built and linted",
|
|
@@ -471,6 +484,8 @@ export interface PinLike {
|
|
|
471
484
|
package: string | null;
|
|
472
485
|
version: string | null;
|
|
473
486
|
path: string | null;
|
|
487
|
+
/** A path pin's content hash (#2547). Checked before anything is read from the directory. */
|
|
488
|
+
integrity?: string | null;
|
|
474
489
|
}
|
|
475
490
|
|
|
476
491
|
export interface KindLoadProblem {
|
|
@@ -521,6 +536,13 @@ export function loadKindRegistry(pins: readonly PinLike[], workspaceRoot: string
|
|
|
521
536
|
} else if (pin.path) {
|
|
522
537
|
source = pin.path;
|
|
523
538
|
dir = join(workspaceRoot, ...pin.path.split("/"));
|
|
539
|
+
if (pin.integrity) {
|
|
540
|
+
const checked = checkPinIntegrity(dir, pin.integrity, pin.path);
|
|
541
|
+
if (!checked.ok) {
|
|
542
|
+
problems.push({ pin: i, message: checked.message });
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
524
546
|
} else {
|
|
525
547
|
return;
|
|
526
548
|
}
|