@intentius/chant 0.30.0 → 0.31.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.
@@ -23,6 +23,15 @@ export interface GenerateResult {
23
23
  file: string;
24
24
  error: string;
25
25
  }>;
26
+ /**
27
+ * Additional generated files, keyed by filename, produced by the optional
28
+ * {@link GeneratePipelineConfig.generateExtraArtifacts} hook. They come out
29
+ * of the same parse a lexicon's types and registry come out of, which is the
30
+ * point: an artifact derived here cannot drift from the types, the way a
31
+ * hand-maintained table beside them can (chant #1074's operation surface is
32
+ * the first of these).
33
+ */
34
+ extraArtifacts?: Record<string, string>;
26
35
  }
27
36
  /**
28
37
  * A parsed result with enough structure for the pipeline to count stats.
@@ -62,6 +71,13 @@ export interface GeneratePipelineConfig<T extends ParsedResult> {
62
71
  generateTypes: (results: T[], naming: NamingStrategy) => string;
63
72
  /** Generate runtime index with factory exports. */
64
73
  generateRuntimeIndex: (results: T[], naming: NamingStrategy) => string;
74
+ /**
75
+ * Optional extra artifacts from the same parsed results — filename → content.
76
+ * Used when a lexicon needs a second derived table alongside the registry and
77
+ * the types, and needs it to come from the same pass so the three cannot
78
+ * skew.
79
+ */
80
+ generateExtraArtifacts?: (results: T[], naming: NamingStrategy) => Record<string, string>;
65
81
  /** Optional pre-parse hook (patches, overlays, extra resources, etc.). */
66
82
  augmentSchemas?: (schemas: Map<string, Buffer>, opts: GenerateOptions, log: (msg: string) => void) => Promise<AugmentResult<T>>;
67
83
  /** Optional post-parse hook (add synthetic resources, fallbacks, etc.). */
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/codegen/generate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/codegen/generate.ts"],"names":[],"mappings":"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"}
@@ -52,6 +52,23 @@ export interface ResolvedClusterTarget {
52
52
  /** Where the target came from. */
53
53
  source: "bound" | "ambient";
54
54
  }
55
+ /**
56
+ * How the resolver learns which context is ambient. The default shells
57
+ * `kubectl config current-context`; the k8s lexicon's typed API client
58
+ * (chant #1074) supplies one that reads the parsed kubeconfig instead, so a
59
+ * client that never needs the `kubectl` binary does not acquire a dependency
60
+ * on it just to check the binding. Both answer the same question, so the
61
+ * refusal semantics below are identical either way.
62
+ */
63
+ export type AmbientContextReader = () => Promise<string | undefined>;
64
+ /** Options for {@link resolveClusterTarget}. */
65
+ export interface ResolveClusterTargetOptions {
66
+ /**
67
+ * Override how the ambient context is read. Defaults to
68
+ * `kubectl config current-context`.
69
+ */
70
+ ambientContext?: AmbientContextReader;
71
+ }
55
72
  /**
56
73
  * Resolve the kubectl context an environment should be observed/applied
57
74
  * against, reading `k8s.profiles.<environment>.context` from `chant.config.ts`
@@ -70,7 +87,7 @@ export interface ResolvedClusterTarget {
70
87
  * {@link ClusterBindingMismatchError} — a loud refusal instead of quietly
71
88
  * reading the wrong cluster.
72
89
  */
73
- export declare function resolveClusterTarget(config: Record<string, unknown>, environment: string, lexiconName: string): Promise<ResolvedClusterTarget>;
90
+ export declare function resolveClusterTarget(config: Record<string, unknown>, environment: string, lexiconName: string, options?: ResolveClusterTargetOptions): Promise<ResolvedClusterTarget>;
74
91
  /**
75
92
  * What a failed `kubectl get` actually proved. Shared by the k8s and gcp
76
93
  * lexicons, which read through the same kubectl path and used to collapse every
@@ -1 +1 @@
1
- {"version":3,"file":"kubectl-context.d.ts","sourceRoot":"","sources":["../src/kubectl-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItD,wFAAwF;AACxF,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wFAAwF;AACxF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;aAElC,WAAW,EAAE,MAAM;aACnB,eAAe,EAAE,MAAM;aACvB,cAAc,EAAE,MAAM;gBAFtB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM;CAWzC;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;CAC7B;AAaD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,qBAAqB,CAAC,CAmBhC;AAID;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB;AAC5B,kFAAkF;AAChF;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE;AACpB,4DAA4D;GAC1D;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAmBrE;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,kBAAkB,CAuCvE"}
1
+ {"version":3,"file":"kubectl-context.d.ts","sourceRoot":"","sources":["../src/kubectl-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItD,wFAAwF;AACxF,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wFAAwF;AACxF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;aAElC,WAAW,EAAE,MAAM;aACnB,eAAe,EAAE,MAAM;aACvB,cAAc,EAAE,MAAM;gBAFtB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM;CAWzC;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AAarE,gDAAgD;AAChD,MAAM,WAAW,2BAA2B;IAC1C;;;OAGG;IACH,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,qBAAqB,CAAC,CAmBhC;AAID;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB;AAC5B,kFAAkF;AAChF;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE;AACpB,4DAA4D;GAC1D;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAmBrE;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,kBAAkB,CAuCvE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.30.0",
3
+ "version": "0.31.0",
4
4
  "description": "Declarative infrastructure-as-code toolkit — TypeScript on Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -27,6 +27,15 @@ export interface GenerateResult {
27
27
  properties: number;
28
28
  enums: number;
29
29
  warnings: Array<{ file: string; error: string }>;
30
+ /**
31
+ * Additional generated files, keyed by filename, produced by the optional
32
+ * {@link GeneratePipelineConfig.generateExtraArtifacts} hook. They come out
33
+ * of the same parse a lexicon's types and registry come out of, which is the
34
+ * point: an artifact derived here cannot drift from the types, the way a
35
+ * hand-maintained table beside them can (chant #1074's operation surface is
36
+ * the first of these).
37
+ */
38
+ extraArtifacts?: Record<string, string>;
30
39
  }
31
40
 
32
41
  /**
@@ -68,6 +77,14 @@ export interface GeneratePipelineConfig<T extends ParsedResult> {
68
77
  /** Generate runtime index with factory exports. */
69
78
  generateRuntimeIndex: (results: T[], naming: NamingStrategy) => string;
70
79
 
80
+ /**
81
+ * Optional extra artifacts from the same parsed results — filename → content.
82
+ * Used when a lexicon needs a second derived table alongside the registry and
83
+ * the types, and needs it to come from the same pass so the three cannot
84
+ * skew.
85
+ */
86
+ generateExtraArtifacts?: (results: T[], naming: NamingStrategy) => Record<string, string>;
87
+
71
88
  /** Optional pre-parse hook (patches, overlays, extra resources, etc.). */
72
89
  augmentSchemas?: (
73
90
  schemas: Map<string, Buffer>,
@@ -161,6 +178,13 @@ export async function generatePipeline<T extends ParsedResult>(
161
178
  log("Generating runtime index...");
162
179
  const indexTS = config.generateRuntimeIndex(results, naming);
163
180
 
181
+ let extraArtifacts: Record<string, string> | undefined;
182
+ if (config.generateExtraArtifacts) {
183
+ log("Generating extra artifacts...");
184
+ extraArtifacts = config.generateExtraArtifacts(results, naming);
185
+ log(`Generated ${Object.keys(extraArtifacts).length} extra artifact(s)`);
186
+ }
187
+
164
188
  // Count stats
165
189
  let resourceCount = 0;
166
190
  let propertyCount = 0;
@@ -179,6 +203,7 @@ export async function generatePipeline<T extends ParsedResult>(
179
203
  properties: propertyCount,
180
204
  enums: enumCount,
181
205
  warnings,
206
+ ...(extraArtifacts ? { extraArtifacts } : {}),
182
207
  };
183
208
  }
184
209
 
@@ -72,8 +72,18 @@ export interface ResolvedClusterTarget {
72
72
  source: "bound" | "ambient";
73
73
  }
74
74
 
75
+ /**
76
+ * How the resolver learns which context is ambient. The default shells
77
+ * `kubectl config current-context`; the k8s lexicon's typed API client
78
+ * (chant #1074) supplies one that reads the parsed kubeconfig instead, so a
79
+ * client that never needs the `kubectl` binary does not acquire a dependency
80
+ * on it just to check the binding. Both answer the same question, so the
81
+ * refusal semantics below are identical either way.
82
+ */
83
+ export type AmbientContextReader = () => Promise<string | undefined>;
84
+
75
85
  /** Reads `kubectl config current-context`. Returns undefined if unset or kubectl fails. */
76
- async function currentAmbientContext(): Promise<string | undefined> {
86
+ const currentAmbientContext: AmbientContextReader = async () => {
77
87
  try {
78
88
  const { stdout } = await execAsync("kubectl config current-context");
79
89
  const trimmed = stdout.trim();
@@ -81,6 +91,15 @@ async function currentAmbientContext(): Promise<string | undefined> {
81
91
  } catch {
82
92
  return undefined;
83
93
  }
94
+ };
95
+
96
+ /** Options for {@link resolveClusterTarget}. */
97
+ export interface ResolveClusterTargetOptions {
98
+ /**
99
+ * Override how the ambient context is read. Defaults to
100
+ * `kubectl config current-context`.
101
+ */
102
+ ambientContext?: AmbientContextReader;
84
103
  }
85
104
 
86
105
  /**
@@ -105,6 +124,7 @@ export async function resolveClusterTarget(
105
124
  config: Record<string, unknown>,
106
125
  environment: string,
107
126
  lexiconName: string,
127
+ options: ResolveClusterTargetOptions = {},
108
128
  ): Promise<ResolvedClusterTarget> {
109
129
  const k8sConfig = config.k8s as K8sConfigShape | undefined;
110
130
  const bound = k8sConfig?.profiles?.[environment]?.context;
@@ -118,7 +138,7 @@ export async function resolveClusterTarget(
118
138
  return { source: "ambient" };
119
139
  }
120
140
 
121
- const ambient = await currentAmbientContext();
141
+ const ambient = await (options.ambientContext ?? currentAmbientContext)();
122
142
  if (ambient && ambient !== bound) {
123
143
  throw new ClusterBindingMismatchError(environment, bound, ambient);
124
144
  }