@intentius/chant 0.27.0 → 0.29.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.
Files changed (106) hide show
  1. package/dist/cli/commands/build.d.ts.map +1 -1
  2. package/dist/cli/commands/lint.d.ts.map +1 -1
  3. package/dist/cli/handlers/components.d.ts.map +1 -1
  4. package/dist/cli/handlers/graph.d.ts.map +1 -1
  5. package/dist/cli/handlers/lifecycle.d.ts +5 -3
  6. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  7. package/dist/config.d.ts +46 -4
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/discovery/fold-import.d.ts +153 -17
  10. package/dist/discovery/fold-import.d.ts.map +1 -1
  11. package/dist/discovery/sandbox/config-run.d.ts.map +1 -1
  12. package/dist/discovery/sandbox/config-wire.d.ts +3 -2
  13. package/dist/discovery/sandbox/config-wire.d.ts.map +1 -1
  14. package/dist/discovery/sandbox/fork.d.ts +25 -0
  15. package/dist/discovery/sandbox/fork.d.ts.map +1 -1
  16. package/dist/discovery/sandbox/policy-run.d.ts.map +1 -1
  17. package/dist/discovery/sandbox/run.d.ts.map +1 -1
  18. package/dist/env.d.ts +5 -2
  19. package/dist/env.d.ts.map +1 -1
  20. package/dist/fold/fold.d.ts +12 -0
  21. package/dist/fold/fold.d.ts.map +1 -1
  22. package/dist/graph-ir.d.ts +29 -4
  23. package/dist/graph-ir.d.ts.map +1 -1
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/kubectl-context.d.ts +100 -0
  27. package/dist/kubectl-context.d.ts.map +1 -0
  28. package/dist/lexicon.d.ts +31 -6
  29. package/dist/lexicon.d.ts.map +1 -1
  30. package/dist/lifecycle/change-set.d.ts +26 -5
  31. package/dist/lifecycle/change-set.d.ts.map +1 -1
  32. package/dist/lifecycle/live-diff.d.ts +25 -1
  33. package/dist/lifecycle/live-diff.d.ts.map +1 -1
  34. package/dist/lifecycle/observe.d.ts +4 -2
  35. package/dist/lifecycle/observe.d.ts.map +1 -1
  36. package/dist/lifecycle/snapshot.d.ts.map +1 -1
  37. package/dist/lifecycle/status.d.ts +26 -1
  38. package/dist/lifecycle/status.d.ts.map +1 -1
  39. package/dist/lifecycle/types.d.ts +8 -0
  40. package/dist/lifecycle/types.d.ts.map +1 -1
  41. package/dist/lint/config.d.ts +80 -0
  42. package/dist/lint/config.d.ts.map +1 -1
  43. package/dist/lint/policy.d.ts +8 -2
  44. package/dist/lint/policy.d.ts.map +1 -1
  45. package/dist/lint/post-synth.d.ts +18 -1
  46. package/dist/lint/post-synth.d.ts.map +1 -1
  47. package/dist/live-endpoint.d.ts +92 -0
  48. package/dist/live-endpoint.d.ts.map +1 -0
  49. package/dist/observation.d.ts +123 -0
  50. package/dist/observation.d.ts.map +1 -0
  51. package/dist/stack-output.d.ts +9 -4
  52. package/dist/stack-output.d.ts.map +1 -1
  53. package/package.json +1 -1
  54. package/src/cli/commands/build.test.ts +190 -0
  55. package/src/cli/commands/build.ts +34 -2
  56. package/src/cli/commands/lint.ts +17 -25
  57. package/src/cli/handlers/components.test.ts +63 -4
  58. package/src/cli/handlers/components.ts +78 -35
  59. package/src/cli/handlers/graph.test.ts +69 -6
  60. package/src/cli/handlers/graph.ts +61 -27
  61. package/src/cli/handlers/lifecycle.test.ts +285 -6
  62. package/src/cli/handlers/lifecycle.ts +297 -185
  63. package/src/config.test.ts +75 -0
  64. package/src/config.ts +61 -3
  65. package/src/discovery/fold-composite.test.ts +594 -0
  66. package/src/discovery/fold-import.ts +987 -43
  67. package/src/discovery/sandbox/config-boundary.test.ts +55 -1
  68. package/src/discovery/sandbox/config-run.ts +3 -0
  69. package/src/discovery/sandbox/config-wire.ts +3 -2
  70. package/src/discovery/sandbox/fork.ts +75 -1
  71. package/src/discovery/sandbox/policy-boundary.test.ts +56 -1
  72. package/src/discovery/sandbox/policy-run.ts +15 -1
  73. package/src/discovery/sandbox/run.test.ts +85 -1
  74. package/src/discovery/sandbox/run.ts +3 -0
  75. package/src/env.test.ts +12 -0
  76. package/src/env.ts +12 -4
  77. package/src/fold/fold.ts +12 -2
  78. package/src/graph-ir-live.test.ts +28 -1
  79. package/src/graph-ir.ts +68 -12
  80. package/src/index.ts +1 -0
  81. package/src/kubectl-context.test.ts +94 -0
  82. package/src/kubectl-context.ts +207 -0
  83. package/src/lexicon.ts +41 -6
  84. package/src/lifecycle/change-set.test.ts +93 -1
  85. package/src/lifecycle/change-set.ts +65 -13
  86. package/src/lifecycle/live-diff.test.ts +39 -0
  87. package/src/lifecycle/live-diff.ts +51 -5
  88. package/src/lifecycle/observe.test.ts +74 -3
  89. package/src/lifecycle/observe.ts +82 -22
  90. package/src/lifecycle/snapshot.test.ts +39 -1
  91. package/src/lifecycle/snapshot.ts +34 -9
  92. package/src/lifecycle/status.test.ts +89 -8
  93. package/src/lifecycle/status.ts +53 -3
  94. package/src/lifecycle/types.ts +8 -0
  95. package/src/lint/config.test.ts +93 -1
  96. package/src/lint/config.ts +108 -0
  97. package/src/lint/policy.test.ts +90 -0
  98. package/src/lint/policy.ts +17 -5
  99. package/src/lint/post-synth.test.ts +4 -0
  100. package/src/lint/post-synth.ts +30 -1
  101. package/src/live-endpoint.test.ts +115 -0
  102. package/src/live-endpoint.ts +148 -0
  103. package/src/observation.test.ts +96 -0
  104. package/src/observation.ts +213 -0
  105. package/src/stack-output.test.ts +76 -3
  106. package/src/stack-output.ts +59 -26
@@ -5,6 +5,9 @@ import {
5
5
  resolveAutoReleaseDisabled,
6
6
  resolveFoldEnabled,
7
7
  resolveSbomFormat,
8
+ environmentName,
9
+ environmentNames,
10
+ environmentEndpoint,
8
11
  } from "./config";
9
12
  import { writeFileSync, mkdirSync, rmSync } from "fs";
10
13
  import { join } from "path";
@@ -134,6 +137,78 @@ describe("loadChantConfig", () => {
134
137
  });
135
138
  });
136
139
 
140
+ // #1166 — `environments` accepts either a bare name (unchanged) or
141
+ // `{ name, endpoint }`, so a declared environment can be self-sufficient for
142
+ // `--live` reads without an ambient AWS_ENDPOINT_URL export.
143
+ describe("environments (#1166 — string or { name, endpoint })", () => {
144
+ test("bare string environments load exactly as before", async () => {
145
+ writeFileSync(
146
+ join(TEST_DIR, "chant.config.json"),
147
+ JSON.stringify({ environments: ["dev", "prod"] }),
148
+ );
149
+
150
+ const result = await loadChantConfig(TEST_DIR);
151
+ expect(result.config.environments).toEqual(["dev", "prod"]);
152
+ });
153
+
154
+ test("an object entry with a declared endpoint loads alongside bare strings", async () => {
155
+ writeFileSync(
156
+ join(TEST_DIR, "chant.config.json"),
157
+ JSON.stringify({
158
+ environments: ["prod", { name: "floci", endpoint: "http://localhost:4566" }],
159
+ }),
160
+ );
161
+
162
+ const result = await loadChantConfig(TEST_DIR);
163
+ expect(result.config.environments).toEqual([
164
+ "prod",
165
+ { name: "floci", endpoint: "http://localhost:4566" },
166
+ ]);
167
+ });
168
+
169
+ test("an object entry with no endpoint is legal (name-only, same as a bare string)", async () => {
170
+ writeFileSync(
171
+ join(TEST_DIR, "chant.config.json"),
172
+ JSON.stringify({ environments: [{ name: "staging" }] }),
173
+ );
174
+
175
+ const result = await loadChantConfig(TEST_DIR);
176
+ expect(result.config.environments).toEqual([{ name: "staging" }]);
177
+ });
178
+
179
+ test("rejects an environments entry that is neither a string nor { name }", async () => {
180
+ writeFileSync(
181
+ join(TEST_DIR, "chant.config.json"),
182
+ JSON.stringify({ environments: [{ endpoint: "http://localhost:4566" }] }),
183
+ );
184
+
185
+ await expect(loadChantConfig(TEST_DIR)).rejects.toThrow(/environments/);
186
+ });
187
+ });
188
+
189
+ describe("environmentName / environmentNames / environmentEndpoint (#1166)", () => {
190
+ test("environmentName reduces either form to its name", () => {
191
+ expect(environmentName("prod")).toBe("prod");
192
+ expect(environmentName({ name: "floci", endpoint: "http://localhost:4566" })).toBe("floci");
193
+ expect(environmentName({ name: "staging" })).toBe("staging");
194
+ });
195
+
196
+ test("environmentNames maps a mixed list, and passes undefined through", () => {
197
+ expect(environmentNames(["prod", { name: "floci", endpoint: "http://x" }])).toEqual(["prod", "floci"]);
198
+ expect(environmentNames(undefined)).toBeUndefined();
199
+ expect(environmentNames([])).toEqual([]);
200
+ });
201
+
202
+ test("environmentEndpoint resolves the declared endpoint, or undefined otherwise", () => {
203
+ const environments = ["prod", { name: "floci", endpoint: "http://localhost:4566" }, { name: "staging" }];
204
+ expect(environmentEndpoint(environments, "floci")).toBe("http://localhost:4566");
205
+ expect(environmentEndpoint(environments, "prod")).toBeUndefined(); // bare string, no endpoint
206
+ expect(environmentEndpoint(environments, "staging")).toBeUndefined(); // object, but no endpoint set
207
+ expect(environmentEndpoint(environments, "unknown")).toBeUndefined(); // not declared at all
208
+ expect(environmentEndpoint(undefined, "floci")).toBeUndefined();
209
+ });
210
+ });
211
+
137
212
  describe("resolveFoldEnabled (#1134 — fold is the default build path)", () => {
138
213
  test("default (no flag, no config) → fold ON", () => {
139
214
  expect(resolveFoldEnabled({})).toBe(true);
package/src/config.ts CHANGED
@@ -10,13 +10,61 @@ import type { BuildParamsConfig } from "./build-params";
10
10
  import { findProjectConfig } from "./project-root";
11
11
  import { evaluateProjectConfig } from "./config-sandbox";
12
12
 
13
+ /**
14
+ * One project-declared environment (chant #1166). Historically always a bare
15
+ * name (`"floci"`); an entry can now instead carry the endpoint that
16
+ * environment's `--live` reads should target (`{ name: "floci", endpoint:
17
+ * "http://localhost:4566" }`), so a project pointed at a local emulator is
18
+ * self-sufficient — no ambient `AWS_ENDPOINT_URL` export required to avoid
19
+ * silently querying real AWS. See {@link environmentName}, {@link
20
+ * environmentNames}, {@link environmentEndpoint} below, and
21
+ * `./live-endpoint.ts`'s `applyLiveEndpoint`, the CLI-side consumer.
22
+ */
23
+ export type EnvironmentDeclaration = string | { name: string; endpoint?: string };
24
+
25
+ /** The declared name of one `environments` entry, whichever form it takes. */
26
+ export function environmentName(entry: EnvironmentDeclaration): string {
27
+ return typeof entry === "string" ? entry : entry.name;
28
+ }
29
+
30
+ /**
31
+ * Every declared environment's name, in `environments` order. `undefined` in,
32
+ * `undefined` out — mirrors the field itself being optional, so a caller that
33
+ * already writes `config.environments?.something` can keep doing so:
34
+ * `environmentNames(config.environments)?.includes(name)`.
35
+ */
36
+ export function environmentNames(environments: EnvironmentDeclaration[] | undefined): string[] | undefined {
37
+ return environments?.map(environmentName);
38
+ }
39
+
40
+ /**
41
+ * The endpoint `name` declares (chant #1166) — `undefined` for a bare-string
42
+ * entry, an entry with no `endpoint` set, or a name this project doesn't
43
+ * declare at all. `./live-endpoint.ts`'s `applyLiveEndpoint` is the consumer:
44
+ * it injects this into the ambient env var each observing lexicon's CLI
45
+ * shell-out reads (e.g. `AWS_ENDPOINT_URL`), unless that var is already set —
46
+ * ambient always wins.
47
+ */
48
+ export function environmentEndpoint(environments: EnvironmentDeclaration[] | undefined, name: string): string | undefined {
49
+ const found = environments?.find((e) => environmentName(e) === name);
50
+ return found && typeof found !== "string" ? found.endpoint : undefined;
51
+ }
52
+
13
53
  /**
14
54
  * Zod schema for ChantConfig validation.
15
55
  */
56
+ const EnvironmentEntrySchema = z.union([
57
+ z.string().min(1),
58
+ z.object({
59
+ name: z.string().min(1),
60
+ endpoint: z.string().min(1).optional(),
61
+ }),
62
+ ]);
63
+
16
64
  export const ChantConfigSchema = z.object({
17
65
  lexicons: z.array(z.string().min(1)).optional(),
18
66
  capabilities: z.array(z.string().min(1)).optional(),
19
- environments: z.array(z.string().min(1)).optional(),
67
+ environments: z.array(EnvironmentEntrySchema).optional(),
20
68
  sourceDir: z.string().min(1).optional(),
21
69
  lint: z.record(z.string(), z.unknown()).optional(),
22
70
  ownership: z.object({
@@ -87,8 +135,18 @@ export interface ChantConfig {
87
135
  */
88
136
  capabilities?: string[];
89
137
 
90
- /** Environment names (e.g. ["staging", "prod"]) */
91
- environments?: string[];
138
+ /**
139
+ * Declared environments (e.g. `["staging", "prod"]`). An entry is either a
140
+ * bare name (unchanged since always) or `{ name, endpoint }` (#1166) when
141
+ * that environment's `--live` reads should target a specific endpoint —
142
+ * a local emulator like Floci (`{ name: "floci", endpoint:
143
+ * "http://localhost:4566" }`) chief among them. See {@link
144
+ * environmentEndpoint} and `./live-endpoint.ts`'s `applyLiveEndpoint`, which
145
+ * injects the declared endpoint into the ambient env var each observing
146
+ * lexicon's CLI shell-out reads (e.g. `AWS_ENDPOINT_URL`) — unless that var
147
+ * is already set, in which case the ambient value always wins.
148
+ */
149
+ environments?: EnvironmentDeclaration[];
92
150
 
93
151
  /**
94
152
  * Directory (relative to the project root) that holds the chant infrastructure