@intentius/chant 0.124.45 → 0.124.46
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/capabilities/aws.paths +148 -0
- package/dist/capabilities/base-mappings.json +122 -0
- package/dist/capabilities/catalog.d.ts +89 -0
- package/dist/capabilities/catalog.d.ts.map +1 -0
- package/dist/capabilities/catalog.js +204 -0
- package/dist/capabilities/language.d.ts +103 -0
- package/dist/capabilities/language.d.ts.map +1 -0
- package/dist/capabilities/language.js +335 -0
- package/dist/capabilities/matcher.d.ts +65 -0
- package/dist/capabilities/matcher.d.ts.map +1 -0
- package/dist/capabilities/matcher.js +299 -0
- package/dist/capabilities/view.d.ts +207 -0
- package/dist/capabilities/view.d.ts.map +1 -0
- package/dist/capabilities/view.js +484 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +1 -0
- package/dist/cli/handlers/declared-graph.d.ts +44 -0
- package/dist/cli/handlers/declared-graph.d.ts.map +1 -0
- package/dist/cli/handlers/declared-graph.js +87 -0
- package/dist/cli/handlers/graph.d.ts +25 -0
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +93 -87
- package/dist/cli/handlers/pr-env.d.ts.map +1 -1
- package/dist/cli/handlers/pr-env.js +2 -1
- package/dist/cli/handlers/search-count.d.ts +89 -0
- package/dist/cli/handlers/search-count.d.ts.map +1 -0
- package/dist/cli/handlers/search-count.js +132 -0
- package/dist/cli/handlers/search.d.ts +358 -6
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/handlers/search.js +1065 -155
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +23 -3
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/server.js +3 -0
- package/dist/cli/mcp/tools/capabilities.d.ts +7 -0
- package/dist/cli/mcp/tools/capabilities.d.ts.map +1 -0
- package/dist/cli/mcp/tools/capabilities.js +27 -0
- package/dist/cli/mcp/workspace-tools.d.ts +3 -1
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.js +47 -2
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +64 -17
- package/dist/cli/registry.d.ts +22 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/pr-env-ports.d.ts +29 -0
- package/dist/components/pr-env-ports.d.ts.map +1 -0
- package/dist/components/pr-env-ports.js +77 -0
- package/dist/components/pr-env-run.d.ts +5 -21
- package/dist/components/pr-env-run.d.ts.map +1 -1
- package/dist/components/pr-env-run.js +6 -70
- package/dist/components/pr-environment.d.ts +2 -1
- package/dist/components/pr-environment.d.ts.map +1 -1
- package/dist/components/pr-environment.js +1 -1
- package/dist/config-environments.d.ts +92 -0
- package/dist/config-environments.d.ts.map +1 -0
- package/dist/config-environments.js +102 -0
- package/dist/config.d.ts +24 -84
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -96
- package/dist/graph-detail.d.ts.map +1 -1
- package/dist/graph-detail.js +15 -3
- package/dist/graph-effective.d.ts +33 -2
- package/dist/graph-effective.d.ts.map +1 -1
- package/dist/graph-effective.js +215 -68
- package/dist/graph-ir.d.ts +23 -0
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-ir.js +130 -0
- package/dist/graph-sub-template.d.ts +11 -0
- package/dist/graph-sub-template.d.ts.map +1 -0
- package/dist/graph-sub-template.js +19 -0
- package/dist/lexicon.d.ts +9 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/observe.d.ts +28 -0
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/observe.js +23 -1
- package/dist/lint/rule.d.ts +6 -0
- package/dist/lint/rule.d.ts.map +1 -1
- package/dist/live-endpoint.d.ts +1 -1
- package/dist/live-endpoint.d.ts.map +1 -1
- package/dist/live-endpoint.js +1 -1
- package/dist/observation.d.ts +48 -1
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +21 -5
- package/dist/op/activities/activity-contracts.d.ts +4 -4
- package/dist/wave-runtime.mjs +250 -249
- package/dist/workspace/capabilities-contract.d.ts +66 -0
- package/dist/workspace/capabilities-contract.d.ts.map +1 -0
- package/dist/workspace/capabilities-contract.js +107 -0
- package/dist/workspace/capabilities.schema.json +229 -0
- package/dist/workspace/compose-graph.d.ts +9 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/compose-graph.js +3 -1
- package/dist/workspace/conformance/index.d.ts +1 -1
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/index.js +39 -2
- package/dist/workspace/graph-cache.d.ts +8 -0
- package/dist/workspace/graph-cache.d.ts.map +1 -1
- package/dist/workspace/graph-cache.js +27 -3
- package/dist/workspace/graph-cli.d.ts +39 -2
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/graph-cli.js +66 -24
- package/dist/workspace/graph.schema.json +148 -0
- package/dist/workspace/kind-readers.d.ts +3 -0
- package/dist/workspace/kind-readers.d.ts.map +1 -1
- package/dist/workspace/kind-readers.js +7 -1
- package/dist/workspace/member-commands.d.ts +10 -1
- package/dist/workspace/member-commands.d.ts.map +1 -1
- package/dist/workspace/member-commands.js +17 -1
- package/dist/workspace/nested-graph.d.ts +2 -0
- package/dist/workspace/nested-graph.d.ts.map +1 -1
- package/dist/workspace/nested-graph.js +32 -1
- package/dist/workspace/reason-codes.d.ts +10 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +12 -0
- package/dist/workspace/search-cli.d.ts +204 -0
- package/dist/workspace/search-cli.d.ts.map +1 -0
- package/dist/workspace/search-cli.js +466 -0
- package/dist/workspace/search-contract.d.ts +56 -0
- package/dist/workspace/search-contract.d.ts.map +1 -0
- package/dist/workspace/search-contract.js +70 -0
- package/dist/workspace/search.schema.json +822 -0
- package/package.json +1 -1
- package/src/__snapshots__/okf.test.ts.snap +10 -0
- package/src/capabilities/aws.paths +148 -0
- package/src/capabilities/base-mappings.json +122 -0
- package/src/capabilities/catalog.ts +230 -0
- package/src/capabilities/language.test.ts +191 -0
- package/src/capabilities/language.ts +373 -0
- package/src/capabilities/matcher.ts +315 -0
- package/src/capabilities/view-compose.test.ts +76 -0
- package/src/capabilities/view.ts +631 -0
- package/src/cli/commands/lint.ts +1 -0
- package/src/cli/handlers/declared-graph.ts +99 -0
- package/src/cli/handlers/graph.test.ts +94 -3
- package/src/cli/handlers/graph.ts +123 -96
- package/src/cli/handlers/pr-env.ts +1 -2
- package/src/cli/handlers/search-build-roots.test.ts +144 -0
- package/src/cli/handlers/search-count.test.ts +191 -0
- package/src/cli/handlers/search-count.ts +165 -0
- package/src/cli/handlers/search-json.test.ts +333 -0
- package/src/cli/handlers/search-live.test.ts +387 -0
- package/src/cli/handlers/search-paths.test.ts +235 -0
- package/src/cli/handlers/search.ts +1312 -166
- package/src/cli/main.test.ts +28 -3
- package/src/cli/main.ts +23 -3
- package/src/cli/mcp/server.test.ts +3 -3
- package/src/cli/mcp/server.ts +3 -0
- package/src/cli/mcp/tools/capabilities.ts +32 -0
- package/src/cli/mcp/workspace-tools.e2e.test.ts +21 -1
- package/src/cli/mcp/workspace-tools.ts +47 -2
- package/src/cli/parse-args.ts +52 -13
- package/src/cli/registry.ts +22 -0
- package/src/components/pr-env-ports.ts +89 -0
- package/src/components/pr-env-run-bundle.test.ts +54 -0
- package/src/components/pr-env-run.ts +5 -76
- package/src/components/pr-environment.ts +2 -1
- package/src/config-environments.ts +133 -0
- package/src/config.ts +37 -124
- package/src/graph-detail.ts +15 -3
- package/src/graph-effective.ts +226 -75
- package/src/graph-ir.test.ts +163 -0
- package/src/graph-ir.ts +147 -0
- package/src/graph-sub-template.ts +22 -0
- package/src/lexicon.ts +10 -0
- package/src/lifecycle/observe.test.ts +27 -1
- package/src/lifecycle/observe.ts +43 -1
- package/src/lint/rule.ts +6 -0
- package/src/live-endpoint.ts +1 -1
- package/src/observation.test.ts +25 -0
- package/src/observation.ts +69 -5
- package/src/workspace/capabilities-contract.test.ts +88 -0
- package/src/workspace/capabilities-contract.ts +132 -0
- package/src/workspace/capabilities.schema.json +229 -0
- package/src/workspace/compose-graph.ts +10 -1
- package/src/workspace/conformance/conformance.e2e.test.ts +14 -1
- package/src/workspace/conformance/index.ts +31 -2
- package/src/workspace/graph-cache.ts +27 -3
- package/src/workspace/graph-cli.ts +96 -24
- package/src/workspace/graph.schema.json +148 -0
- package/src/workspace/kind-readers.e2e.test.ts +28 -0
- package/src/workspace/kind-readers.test.ts +1 -1
- package/src/workspace/kind-readers.ts +7 -1
- package/src/workspace/member-commands.ts +22 -1
- package/src/workspace/nested-graph.ts +32 -1
- package/src/workspace/reason-codes.test.ts +7 -0
- package/src/workspace/reason-codes.ts +12 -0
- package/src/workspace/search-cli.e2e.test.ts +305 -0
- package/src/workspace/search-cli.ts +591 -0
- package/src/workspace/search-contract.ts +95 -0
- package/src/workspace/search.schema.json +822 -0
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
* - the report and the note ({@link renderPrEnvNote}).
|
|
17
17
|
*
|
|
18
18
|
* `chant components pr-env` (../cli/handlers/pr-env.ts) runs these with the
|
|
19
|
-
* components adapter and the lexicons' teardown
|
|
20
|
-
* chant/lifecycle
|
|
21
|
-
* (
|
|
19
|
+
* components adapter and the lexicons' teardown, and the ports over
|
|
20
|
+
* chant/lifecycle and the gate ledger in ./pr-env-ports.ts
|
|
21
|
+
* (`lifecyclePrEnvPorts`). Those ports load the gate and its schema checks,
|
|
22
|
+
* so they live apart: a product that brings its own ports bundles this
|
|
23
|
+
* module without them (pr-env-run-bundle.test.ts).
|
|
22
24
|
*/
|
|
23
25
|
|
|
24
26
|
import { spawnSync } from "node:child_process";
|
|
@@ -29,19 +31,11 @@ import { withoutForgeTokens } from "../ci-handover";
|
|
|
29
31
|
import type { ChantConfig } from "../config";
|
|
30
32
|
import type { TeardownCandidate, TeardownHole, TeardownOutcome } from "../lexicon";
|
|
31
33
|
import type { ObservationLexicon } from "../lexicon";
|
|
32
|
-
import type { PrForge } from "../pr-forge";
|
|
33
34
|
import {
|
|
34
35
|
PR_ENV_EXIT,
|
|
35
36
|
PR_ENV_GATE,
|
|
36
|
-
PR_ENV_LEDGER_DIR,
|
|
37
|
-
PR_ENV_LEDGER_FILE,
|
|
38
37
|
expired,
|
|
39
38
|
livePrEnvironments,
|
|
40
|
-
parsePrEnvRecords,
|
|
41
|
-
prEnvNoteMarker,
|
|
42
|
-
prEnvStatusContext,
|
|
43
|
-
prEnvStatusDescription,
|
|
44
|
-
prEnvStatusState,
|
|
45
39
|
prEnvironmentName,
|
|
46
40
|
renderPrEnvNote,
|
|
47
41
|
teardownComplete,
|
|
@@ -425,71 +419,6 @@ export async function prEnvSweep(input: PrEnvSweepInput): Promise<{ exit: number
|
|
|
425
419
|
return { exit, results };
|
|
426
420
|
}
|
|
427
421
|
|
|
428
|
-
// ── the ports chant itself uses ────────────────────────────────────────────
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* The note and the `chant/pr-env` status on the forge. A refusal from the
|
|
432
|
-
* forge warns and never fails the run.
|
|
433
|
-
*/
|
|
434
|
-
export function forgePrEnvPublisher(forge: PrForge, warn: (message: string) => void = () => {}): (report: PrEnvReport) => Promise<void> {
|
|
435
|
-
return async (report) => {
|
|
436
|
-
const { forgePrincipalOf } = await import("../pr-forge");
|
|
437
|
-
const note = renderPrEnvNote(report, forgePrincipalOf(forge.kind, forge.host, "<your-login>"));
|
|
438
|
-
if (report.head) {
|
|
439
|
-
try {
|
|
440
|
-
await forge.setStatus(report.head, { context: prEnvStatusContext(report.member), state: prEnvStatusState(report), description: prEnvStatusDescription(report), ...(forge.runUrl ? { url: forge.runUrl } : {}) });
|
|
441
|
-
} catch (err) {
|
|
442
|
-
warn(`could not set the ${prEnvStatusContext(report.member)} status: ${messageOf(err)}`);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
try {
|
|
446
|
-
await forge.upsertNote(report.pr, prEnvNoteMarker(report.env, report.member), note);
|
|
447
|
-
} catch (err) {
|
|
448
|
-
warn(`could not post the note on #${report.pr}: ${messageOf(err)}`);
|
|
449
|
-
}
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* The ports over chant/lifecycle in `cwd` (default the working directory):
|
|
455
|
-
* the records at `_pr-envs/records.jsonl`, appended and pushed, and the gate
|
|
456
|
-
* ledger read without writing a pending fact. With `forge`, the note and
|
|
457
|
-
* status are posted and the sweep asks it whether a pull request is open.
|
|
458
|
-
*/
|
|
459
|
-
export function lifecyclePrEnvPorts(opts: { cwd?: string; forge?: PrForge; log?: (line: string) => void; warn?: (message: string) => void } = {}): PrEnvPorts {
|
|
460
|
-
const at = opts.cwd ? { cwd: opts.cwd } : undefined;
|
|
461
|
-
const warn = opts.warn ?? (() => {});
|
|
462
|
-
const forge = opts.forge;
|
|
463
|
-
return {
|
|
464
|
-
async readRecords() {
|
|
465
|
-
const { fetchLifecycle, readBlobFromPath } = await import("../lifecycle/git");
|
|
466
|
-
await fetchLifecycle(at).catch(() => false);
|
|
467
|
-
return parsePrEnvRecords(await readBlobFromPath(PR_ENV_LEDGER_DIR, PR_ENV_LEDGER_FILE, at));
|
|
468
|
-
},
|
|
469
|
-
async appendRecord(record) {
|
|
470
|
-
const { appendLedgerLine, fetchLifecycle, pushLifecycleReport } = await import("../lifecycle/git");
|
|
471
|
-
try {
|
|
472
|
-
await fetchLifecycle(at).catch(() => false);
|
|
473
|
-
await appendLedgerLine(PR_ENV_LEDGER_DIR, PR_ENV_LEDGER_FILE, JSON.stringify(record), `Pull request environment ${record.env} ${record.kind} (${record.result})`, at);
|
|
474
|
-
const pushed = await pushLifecycleReport(at);
|
|
475
|
-
if (pushed.pushWarning) warn(pushed.pushWarning);
|
|
476
|
-
} catch (err) {
|
|
477
|
-
warn(`could not record ${record.env}'s ${record.kind} on chant/lifecycle: ${messageOf(err)}`);
|
|
478
|
-
}
|
|
479
|
-
},
|
|
480
|
-
async decideGate(input) {
|
|
481
|
-
const { approvalOf, decidePrGate, readOnlyLedger } = await import("./pr-run");
|
|
482
|
-
const { gitGateLedgerPort } = await import("../op/gate");
|
|
483
|
-
const approval = approvalOf(await decidePrGate(readOnlyLedger(gitGateLedgerPort(at)), input));
|
|
484
|
-
return { status: approval.status, ...(approval.approvedBy ? { approvedBy: approval.approvedBy } : {}) };
|
|
485
|
-
},
|
|
486
|
-
...(forge ? { publish: forgePrEnvPublisher(forge, warn) } : {}),
|
|
487
|
-
...(forge?.pullRequestState ? { pullRequestState: (pr: number) => forge.pullRequestState!(pr) } : {}),
|
|
488
|
-
...(opts.log ? { log: opts.log } : {}),
|
|
489
|
-
warn,
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
|
|
493
422
|
// ── a teardown command ─────────────────────────────────────────────────────
|
|
494
423
|
|
|
495
424
|
/** `ci.prEnvironment.teardown` (#4330): a command that tears down what no lexicon does. */
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
* as a lexicon does.
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
|
-
import {
|
|
42
|
+
import type { ChantConfig } from "../config";
|
|
43
|
+
import { environmentEndpoint, environmentName, isEnvironmentPattern, isOwnershipParamRef, matchesDeclaredEnvironment, resolveOwnershipStack, type EnvironmentDeclaration } from "../config-environments";
|
|
43
44
|
import type { OwnershipMarker } from "../ownership";
|
|
44
45
|
import { executeTeardown, planTeardown, type TeardownPlan, type TeardownPlanEntry, type TeardownReport } from "../lifecycle/teardown";
|
|
45
46
|
import type { ObservationLexicon, TeardownCandidate, TeardownEnumeration, TeardownExecution } from "../lexicon";
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pure helpers over a loaded config's `environments` and `ownership`:
|
|
3
|
+
* names, patterns, endpoints and the ownership stack. ./config.ts re-exports
|
|
4
|
+
* them. They import nothing at runtime, so ./components/pr-environment.ts and
|
|
5
|
+
* ./live-endpoint.ts use them without bundling the config loader.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ChantConfig } from "./config";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* One project-declared environment (chant #1166). Historically always a bare
|
|
12
|
+
* name (`"floci"`); an entry can now instead carry the endpoint that
|
|
13
|
+
* environment's `--live` reads should target (`{ name: "floci", endpoint:
|
|
14
|
+
* "http://localhost:4566" }`), so a project pointed at a local emulator is
|
|
15
|
+
* self-sufficient — no ambient `AWS_ENDPOINT_URL` export required to avoid
|
|
16
|
+
* silently querying real AWS. See {@link environmentName}, {@link
|
|
17
|
+
* environmentNames}, {@link environmentEndpoint} below, and
|
|
18
|
+
* `./live-endpoint.ts`'s `applyLiveEndpoint`, the CLI-side consumer.
|
|
19
|
+
*
|
|
20
|
+
* The array's order is the member's promotion order (#4030, see
|
|
21
|
+
* `./workspace/promotion-order.ts`), and `canary: true` marks a step as the
|
|
22
|
+
* canary step of that order. The canary is declared and surfaced; chant does
|
|
23
|
+
* not yet act on it.
|
|
24
|
+
*/
|
|
25
|
+
export type EnvironmentDeclaration = string | { name: string; endpoint?: string; canary?: boolean };
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* `ownership.env`: a literal environment identity, or a reference to a
|
|
29
|
+
* declared build parameter whose resolved value becomes the identity (#1396).
|
|
30
|
+
*/
|
|
31
|
+
export type OwnershipEnv = string | { param: string };
|
|
32
|
+
|
|
33
|
+
/** The declared name of one `environments` entry, whichever form it takes. */
|
|
34
|
+
export function environmentName(entry: EnvironmentDeclaration): string {
|
|
35
|
+
return typeof entry === "string" ? entry : entry.name;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Every declared environment's name, in `environments` order. `undefined` in,
|
|
40
|
+
* `undefined` out — mirrors the field itself being optional, so a caller that
|
|
41
|
+
* already writes `config.environments?.something` can keep doing so:
|
|
42
|
+
* `environmentNames(config.environments)?.includes(name)`.
|
|
43
|
+
*/
|
|
44
|
+
export function environmentNames(environments: EnvironmentDeclaration[] | undefined): string[] | undefined {
|
|
45
|
+
return environments?.map(environmentName);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Whether a declared environment name is a glob pattern rather than a literal
|
|
50
|
+
* (#1221) — it contains at least one `*`.
|
|
51
|
+
*/
|
|
52
|
+
export function isEnvironmentPattern(declaredName: string): boolean {
|
|
53
|
+
return declaredName.includes("*");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Anchored `*`-glob match (#1221): each `*` in `pattern` matches any run of
|
|
58
|
+
* characters (possibly empty); everything else matches itself. Deliberately
|
|
59
|
+
* the whole surface — no `?`, no character classes, no regex. A pattern with
|
|
60
|
+
* no `*` degenerates to string equality.
|
|
61
|
+
*/
|
|
62
|
+
export function matchesEnvironmentPattern(pattern: string, name: string): boolean {
|
|
63
|
+
const parts = pattern.split("*");
|
|
64
|
+
if (parts.length === 1) return pattern === name;
|
|
65
|
+
const first = parts[0];
|
|
66
|
+
const last = parts[parts.length - 1];
|
|
67
|
+
if (!name.startsWith(first)) return false;
|
|
68
|
+
let cursor = first.length;
|
|
69
|
+
for (let i = 1; i < parts.length - 1; i++) {
|
|
70
|
+
const part = parts[i];
|
|
71
|
+
if (part === "") continue;
|
|
72
|
+
const at = name.indexOf(part, cursor);
|
|
73
|
+
if (at === -1) return false;
|
|
74
|
+
cursor = at + part.length;
|
|
75
|
+
}
|
|
76
|
+
return name.length - cursor >= last.length && name.endsWith(last);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Environment-membership test (#1221): is `name` covered by this project's
|
|
81
|
+
* declared `environments`? A literal entry matches by equality; an entry
|
|
82
|
+
* containing `*` matches as a glob pattern (`"pr-*"` covers `pr-42`).
|
|
83
|
+
* Literals are checked first across the whole list, then patterns — so a
|
|
84
|
+
* name that is declared outright never depends on pattern order. `undefined`
|
|
85
|
+
* / empty `environments` returns `false`; callers that treat "no declared
|
|
86
|
+
* environments" as "anything goes" (e.g. {@link unknownEnvError} in
|
|
87
|
+
* `./env.ts`) guard that case themselves.
|
|
88
|
+
*/
|
|
89
|
+
export function matchesDeclaredEnvironment(
|
|
90
|
+
environments: EnvironmentDeclaration[] | undefined,
|
|
91
|
+
name: string,
|
|
92
|
+
): boolean {
|
|
93
|
+
const names = environmentNames(environments) ?? [];
|
|
94
|
+
if (names.includes(name)) return true;
|
|
95
|
+
return names.some((declared) => isEnvironmentPattern(declared) && matchesEnvironmentPattern(declared, name));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The endpoint `name` declares (chant #1166) — `undefined` for a bare-string
|
|
100
|
+
* entry, an entry with no `endpoint` set, or a name this project doesn't
|
|
101
|
+
* declare at all. `./live-endpoint.ts`'s `applyLiveEndpoint` is the consumer:
|
|
102
|
+
* it injects this into the ambient env var each observing lexicon's CLI
|
|
103
|
+
* shell-out reads (e.g. `AWS_ENDPOINT_URL`), unless that var is already set —
|
|
104
|
+
* ambient always wins.
|
|
105
|
+
*
|
|
106
|
+
* A pattern entry (#1221) can carry an endpoint too: `{ name: "pr-*",
|
|
107
|
+
* endpoint }` supplies the endpoint for every `pr-<n>` environment. A literal
|
|
108
|
+
* entry always wins over a pattern — declaring `pr-special` alongside
|
|
109
|
+
* `pr-*` resolves `pr-special` to the literal entry's endpoint (or none).
|
|
110
|
+
*/
|
|
111
|
+
export function environmentEndpoint(environments: EnvironmentDeclaration[] | undefined, name: string): string | undefined {
|
|
112
|
+
const found =
|
|
113
|
+
environments?.find((e) => environmentName(e) === name) ??
|
|
114
|
+
environments?.find((e) => isEnvironmentPattern(environmentName(e)) && matchesEnvironmentPattern(environmentName(e), name));
|
|
115
|
+
return found && typeof found !== "string" ? found.endpoint : undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Whether an `ownership.env` entry is a build-parameter reference rather than a literal. */
|
|
119
|
+
export function isOwnershipParamRef(env: OwnershipEnv | undefined): env is { param: string } {
|
|
120
|
+
return typeof env === "object" && env !== null && typeof env.param === "string";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The ownership stack identity alone — `ownership.stack` when marking is on
|
|
125
|
+
* (no `stack`, or `enabled: false`, means off). For callers that need the
|
|
126
|
+
* stack and nothing else (a field manager, a prune selector) and so have no
|
|
127
|
+
* build parameters to resolve `env` against.
|
|
128
|
+
*/
|
|
129
|
+
export function resolveOwnershipStack(config: Pick<ChantConfig, "ownership">): string | undefined {
|
|
130
|
+
const o = config.ownership;
|
|
131
|
+
if (!o || !o.stack || o.enabled === false) return undefined;
|
|
132
|
+
return o.stack;
|
|
133
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -14,113 +14,22 @@ import { lexiconNames, pathLexiconRoot, registerLexiconDeclarations, type Lexico
|
|
|
14
14
|
import { decideConfigSchema, type DecideConfig } from "./op/decide-config";
|
|
15
15
|
import { PublishDeclarationSchema, type PublishDeclaration } from "./lifecycle/publish-declaration";
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* `ownership.env`: a literal environment identity, or a reference to a
|
|
36
|
-
* declared build parameter whose resolved value becomes the identity (#1396).
|
|
37
|
-
*/
|
|
38
|
-
export type OwnershipEnv = string | { param: string };
|
|
39
|
-
|
|
40
|
-
/** The declared name of one `environments` entry, whichever form it takes. */
|
|
41
|
-
export function environmentName(entry: EnvironmentDeclaration): string {
|
|
42
|
-
return typeof entry === "string" ? entry : entry.name;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Every declared environment's name, in `environments` order. `undefined` in,
|
|
47
|
-
* `undefined` out — mirrors the field itself being optional, so a caller that
|
|
48
|
-
* already writes `config.environments?.something` can keep doing so:
|
|
49
|
-
* `environmentNames(config.environments)?.includes(name)`.
|
|
50
|
-
*/
|
|
51
|
-
export function environmentNames(environments: EnvironmentDeclaration[] | undefined): string[] | undefined {
|
|
52
|
-
return environments?.map(environmentName);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Whether a declared environment name is a glob pattern rather than a literal
|
|
57
|
-
* (#1221) — it contains at least one `*`.
|
|
58
|
-
*/
|
|
59
|
-
export function isEnvironmentPattern(declaredName: string): boolean {
|
|
60
|
-
return declaredName.includes("*");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Anchored `*`-glob match (#1221): each `*` in `pattern` matches any run of
|
|
65
|
-
* characters (possibly empty); everything else matches itself. Deliberately
|
|
66
|
-
* the whole surface — no `?`, no character classes, no regex. A pattern with
|
|
67
|
-
* no `*` degenerates to string equality.
|
|
68
|
-
*/
|
|
69
|
-
export function matchesEnvironmentPattern(pattern: string, name: string): boolean {
|
|
70
|
-
const parts = pattern.split("*");
|
|
71
|
-
if (parts.length === 1) return pattern === name;
|
|
72
|
-
const first = parts[0];
|
|
73
|
-
const last = parts[parts.length - 1];
|
|
74
|
-
if (!name.startsWith(first)) return false;
|
|
75
|
-
let cursor = first.length;
|
|
76
|
-
for (let i = 1; i < parts.length - 1; i++) {
|
|
77
|
-
const part = parts[i];
|
|
78
|
-
if (part === "") continue;
|
|
79
|
-
const at = name.indexOf(part, cursor);
|
|
80
|
-
if (at === -1) return false;
|
|
81
|
-
cursor = at + part.length;
|
|
82
|
-
}
|
|
83
|
-
return name.length - cursor >= last.length && name.endsWith(last);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Environment-membership test (#1221): is `name` covered by this project's
|
|
88
|
-
* declared `environments`? A literal entry matches by equality; an entry
|
|
89
|
-
* containing `*` matches as a glob pattern (`"pr-*"` covers `pr-42`).
|
|
90
|
-
* Literals are checked first across the whole list, then patterns — so a
|
|
91
|
-
* name that is declared outright never depends on pattern order. `undefined`
|
|
92
|
-
* / empty `environments` returns `false`; callers that treat "no declared
|
|
93
|
-
* environments" as "anything goes" (e.g. {@link unknownEnvError} in
|
|
94
|
-
* `./env.ts`) guard that case themselves.
|
|
95
|
-
*/
|
|
96
|
-
export function matchesDeclaredEnvironment(
|
|
97
|
-
environments: EnvironmentDeclaration[] | undefined,
|
|
98
|
-
name: string,
|
|
99
|
-
): boolean {
|
|
100
|
-
const names = environmentNames(environments) ?? [];
|
|
101
|
-
if (names.includes(name)) return true;
|
|
102
|
-
return names.some((declared) => isEnvironmentPattern(declared) && matchesEnvironmentPattern(declared, name));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* The endpoint `name` declares (chant #1166) — `undefined` for a bare-string
|
|
107
|
-
* entry, an entry with no `endpoint` set, or a name this project doesn't
|
|
108
|
-
* declare at all. `./live-endpoint.ts`'s `applyLiveEndpoint` is the consumer:
|
|
109
|
-
* it injects this into the ambient env var each observing lexicon's CLI
|
|
110
|
-
* shell-out reads (e.g. `AWS_ENDPOINT_URL`), unless that var is already set —
|
|
111
|
-
* ambient always wins.
|
|
112
|
-
*
|
|
113
|
-
* A pattern entry (#1221) can carry an endpoint too: `{ name: "pr-*",
|
|
114
|
-
* endpoint }` supplies the endpoint for every `pr-<n>` environment. A literal
|
|
115
|
-
* entry always wins over a pattern — declaring `pr-special` alongside
|
|
116
|
-
* `pr-*` resolves `pr-special` to the literal entry's endpoint (or none).
|
|
117
|
-
*/
|
|
118
|
-
export function environmentEndpoint(environments: EnvironmentDeclaration[] | undefined, name: string): string | undefined {
|
|
119
|
-
const found =
|
|
120
|
-
environments?.find((e) => environmentName(e) === name) ??
|
|
121
|
-
environments?.find((e) => isEnvironmentPattern(environmentName(e)) && matchesEnvironmentPattern(environmentName(e), name));
|
|
122
|
-
return found && typeof found !== "string" ? found.endpoint : undefined;
|
|
123
|
-
}
|
|
17
|
+
// The environment and ownership helpers that read a config already loaded
|
|
18
|
+
// live in ./config-environments.ts, which loads nothing, so a bundle that
|
|
19
|
+
// needs only them stays clear of the loader below (#4356 follow-up).
|
|
20
|
+
export {
|
|
21
|
+
environmentEndpoint,
|
|
22
|
+
environmentName,
|
|
23
|
+
environmentNames,
|
|
24
|
+
isEnvironmentPattern,
|
|
25
|
+
isOwnershipParamRef,
|
|
26
|
+
matchesDeclaredEnvironment,
|
|
27
|
+
matchesEnvironmentPattern,
|
|
28
|
+
resolveOwnershipStack,
|
|
29
|
+
type EnvironmentDeclaration,
|
|
30
|
+
type OwnershipEnv,
|
|
31
|
+
} from "./config-environments";
|
|
32
|
+
import { isOwnershipParamRef, resolveOwnershipStack, type EnvironmentDeclaration, type OwnershipEnv } from "./config-environments";
|
|
124
33
|
|
|
125
34
|
/**
|
|
126
35
|
* Zod schema for ChantConfig validation.
|
|
@@ -157,6 +66,10 @@ export const ChantConfigSchema = z.object({
|
|
|
157
66
|
build: z.object({
|
|
158
67
|
fold: z.boolean().optional(),
|
|
159
68
|
}).optional(),
|
|
69
|
+
// Strict like `run`: a misspelt `lint` would leave the gate on with nothing said.
|
|
70
|
+
graph: z.object({
|
|
71
|
+
lint: z.enum(["gate", "report"]).optional(),
|
|
72
|
+
}).strict().optional(),
|
|
160
73
|
buildParams: z.record(
|
|
161
74
|
z.string(),
|
|
162
75
|
z.object({
|
|
@@ -299,6 +212,23 @@ export interface ChantConfig {
|
|
|
299
212
|
/** Lint configuration (rules, extends, overrides, plugins) */
|
|
300
213
|
lint?: LintConfig;
|
|
301
214
|
|
|
215
|
+
/**
|
|
216
|
+
* How `chant graph` treats lint errors (INTENTIUS/behold#919).
|
|
217
|
+
*
|
|
218
|
+
* `"gate"` (the default) refuses to emit the entity graph for source that
|
|
219
|
+
* fails `chant lint`: the graph stands for source chant will build.
|
|
220
|
+
* `"report"` emits it anyway and carries every lint finding on the IR's
|
|
221
|
+
* `meta.lint`, for a project that only reads source it did not author. The
|
|
222
|
+
* reader project `chant workspace graph` writes for a terraform or choudoufu
|
|
223
|
+
* member sets it, and so does behold's scratch project for a bare Terraform
|
|
224
|
+
* folder: an error-severity finding there describes the estate, and the
|
|
225
|
+
* person needs the picture to look at it. A chant older than the key
|
|
226
|
+
* ignores it and gates as before.
|
|
227
|
+
*/
|
|
228
|
+
graph?: {
|
|
229
|
+
lint?: "gate" | "report";
|
|
230
|
+
};
|
|
231
|
+
|
|
302
232
|
/**
|
|
303
233
|
* Telemetry attribution (#2558, D22): lexicons that declare workloads stamp
|
|
304
234
|
* resource attributes (`chant.workspace`, `chant.member`, `chant.decl`,
|
|
@@ -764,23 +694,6 @@ function warnIfFragmentShadowsProjectConfig(configPath: string): void {
|
|
|
764
694
|
/** Top-level keys that make a config a *project* config rather than a lint fragment. */
|
|
765
695
|
const PROJECT_CONFIG_KEYS = new Set(Object.keys(ChantConfigSchema.shape).filter((k) => k !== "lint"));
|
|
766
696
|
|
|
767
|
-
/** Whether an `ownership.env` entry is a build-parameter reference rather than a literal. */
|
|
768
|
-
export function isOwnershipParamRef(env: OwnershipEnv | undefined): env is { param: string } {
|
|
769
|
-
return typeof env === "object" && env !== null && typeof env.param === "string";
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
/**
|
|
773
|
-
* The ownership stack identity alone — `ownership.stack` when marking is on
|
|
774
|
-
* (no `stack`, or `enabled: false`, means off). For callers that need the
|
|
775
|
-
* stack and nothing else (a field manager, a prune selector) and so have no
|
|
776
|
-
* build parameters to resolve `env` against.
|
|
777
|
-
*/
|
|
778
|
-
export function resolveOwnershipStack(config: ChantConfig): string | undefined {
|
|
779
|
-
const o = config.ownership;
|
|
780
|
-
if (!o || !o.stack || o.enabled === false) return undefined;
|
|
781
|
-
return o.stack;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
697
|
/**
|
|
785
698
|
* Resolve the `env` half of the ownership marker (#1396). A literal is
|
|
786
699
|
* returned as-is. A `{ param }` reference is looked up in this build's resolved
|
package/src/graph-detail.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { GraphIR, IRNode, IREdge } from "./graph-ir";
|
|
2
|
+
import { subTemplateRefs } from "./graph-sub-template";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Detail tiers — the diagram "detail dial". Each level is a pure IR → IR
|
|
@@ -60,13 +61,13 @@ function paintAttrs(attrs: Record<string, unknown>): Record<string, unknown> {
|
|
|
60
61
|
return out;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
/** A `{ $ref: "producer.attribute" }` envelope — a reference, not a property tree. */
|
|
64
|
+
/** A `{ $ref: "producer.attribute" }` or `{ $sub: "..." }` (#4373) envelope — a reference, not a property tree. */
|
|
64
65
|
function isRefEnvelope(v: unknown): boolean {
|
|
65
66
|
return (
|
|
66
67
|
typeof v === "object" &&
|
|
67
68
|
v !== null &&
|
|
68
69
|
!Array.isArray(v) &&
|
|
69
|
-
typeof (v as { $ref?: unknown }).$ref === "string"
|
|
70
|
+
(typeof (v as { $ref?: unknown }).$ref === "string" || typeof (v as { $sub?: unknown }).$sub === "string")
|
|
70
71
|
);
|
|
71
72
|
}
|
|
72
73
|
|
|
@@ -271,7 +272,8 @@ function toAttributes(ir: GraphIR): GraphIR {
|
|
|
271
272
|
return { ...ir, edges };
|
|
272
273
|
}
|
|
273
274
|
|
|
274
|
-
/** Find the attribute in a `{ $ref: "producer.attribute" }` envelope under attrs
|
|
275
|
+
/** Find the attribute in a `{ $ref: "producer.attribute" }` envelope under attrs,
|
|
276
|
+
* or in a `{ $sub }` envelope's `${producer.attribute}` (#4373). */
|
|
275
277
|
function findRefAttr(attrs: Record<string, unknown>, producer: string): string | undefined {
|
|
276
278
|
let found: string | undefined;
|
|
277
279
|
const visit = (v: unknown): void => {
|
|
@@ -288,6 +290,16 @@ function findRefAttr(attrs: Record<string, unknown>, producer: string): string |
|
|
|
288
290
|
return;
|
|
289
291
|
}
|
|
290
292
|
}
|
|
293
|
+
const sub = (v as { $sub?: unknown; $vars?: unknown }).$sub;
|
|
294
|
+
if (typeof sub === "string") {
|
|
295
|
+
const vars = (v as { $vars?: unknown }).$vars;
|
|
296
|
+
const bound = new Set(vars && typeof vars === "object" ? Object.keys(vars) : []);
|
|
297
|
+
const hit = subTemplateRefs(sub, bound).find((r) => r.name === producer && r.attr);
|
|
298
|
+
if (hit) {
|
|
299
|
+
found = hit.attr;
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
291
303
|
for (const val of Object.values(v as Record<string, unknown>)) visit(val);
|
|
292
304
|
};
|
|
293
305
|
visit(attrs);
|