@kitsy/cnos 1.1.1 → 1.2.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 (62) hide show
  1. package/README.md +4 -1
  2. package/dist/browser/index.cjs +94 -0
  3. package/dist/browser/index.d.cts +16 -0
  4. package/dist/browser/index.d.ts +16 -0
  5. package/dist/browser/index.js +67 -0
  6. package/dist/build/index.cjs +2100 -0
  7. package/dist/build/index.d.cts +5 -0
  8. package/dist/build/index.d.ts +5 -0
  9. package/dist/build/index.js +14 -0
  10. package/dist/{chunk-JPJ3S3CO.js → chunk-APCTXRUN.js} +620 -426
  11. package/dist/{chunk-PBU5NAX4.js → chunk-EIN55XXA.js} +1 -1
  12. package/dist/chunk-JUHPBAEH.js +20 -0
  13. package/dist/{chunk-L3HOQHCH.js → chunk-MLQGYCO7.js} +1 -1
  14. package/dist/chunk-PQ4KSV76.js +50 -0
  15. package/dist/{chunk-7GNXYEO6.js → chunk-RD5WMHPM.js} +1 -1
  16. package/dist/chunk-SO5XREEU.js +179 -0
  17. package/dist/{chunk-QKJ6QLRS.js → chunk-SXTMTACL.js} +2 -2
  18. package/dist/{chunk-X4GOXEKX.js → chunk-WHUGFPE4.js} +1 -1
  19. package/dist/{chunk-M4S6PYM5.js → chunk-ZA74BO47.js} +1 -1
  20. package/dist/{envNaming-BrOk5ndZ.d.cts → envNaming-BTJpH93W.d.cts} +1 -1
  21. package/dist/{envNaming-DCaNdnrF.d.ts → envNaming-CcsqAel3.d.ts} +1 -1
  22. package/dist/index.cjs +294 -133
  23. package/dist/index.d.cts +4 -3
  24. package/dist/index.d.ts +4 -3
  25. package/dist/index.js +14 -132
  26. package/dist/internal.cjs +479 -61
  27. package/dist/internal.d.cts +29 -3
  28. package/dist/internal.d.ts +29 -3
  29. package/dist/internal.js +27 -1
  30. package/dist/plugin/basic-schema.cjs +3 -3
  31. package/dist/plugin/basic-schema.d.cts +1 -1
  32. package/dist/plugin/basic-schema.d.ts +1 -1
  33. package/dist/plugin/basic-schema.js +2 -2
  34. package/dist/plugin/cli-args.cjs +3 -3
  35. package/dist/plugin/cli-args.d.cts +1 -1
  36. package/dist/plugin/cli-args.d.ts +1 -1
  37. package/dist/plugin/cli-args.js +2 -2
  38. package/dist/plugin/dotenv.cjs +9 -9
  39. package/dist/plugin/dotenv.d.cts +2 -2
  40. package/dist/plugin/dotenv.d.ts +2 -2
  41. package/dist/plugin/dotenv.js +2 -2
  42. package/dist/plugin/env-export.cjs +46 -64
  43. package/dist/plugin/env-export.d.cts +2 -2
  44. package/dist/plugin/env-export.d.ts +2 -2
  45. package/dist/plugin/env-export.js +2 -2
  46. package/dist/plugin/filesystem.cjs +10 -10
  47. package/dist/plugin/filesystem.d.cts +1 -1
  48. package/dist/plugin/filesystem.d.ts +1 -1
  49. package/dist/plugin/filesystem.js +2 -2
  50. package/dist/plugin/process-env.cjs +9 -9
  51. package/dist/plugin/process-env.d.cts +2 -2
  52. package/dist/plugin/process-env.d.ts +2 -2
  53. package/dist/plugin/process-env.js +2 -2
  54. package/dist/{plugin-BVNEHj19.d.cts → plugin-DkOIT5uI.d.cts} +30 -2
  55. package/dist/{plugin-BVNEHj19.d.ts → plugin-DkOIT5uI.d.ts} +30 -2
  56. package/dist/runtime/index.cjs +2288 -0
  57. package/dist/runtime/index.d.cts +23 -0
  58. package/dist/runtime/index.d.ts +23 -0
  59. package/dist/runtime/index.js +190 -0
  60. package/dist/{toPublicEnv-Gwz3xTK0.d.ts → toPublicEnv-C9clvXLo.d.ts} +1 -1
  61. package/dist/{toPublicEnv-Dd152fFy.d.cts → toPublicEnv-DvFeV3qG.d.cts} +1 -1
  62. package/package.json +16 -1
@@ -1,8 +1,3 @@
1
- // ../core/src/utils/path.ts
2
- import { access } from "fs/promises";
3
- import os from "os";
4
- import path from "path";
5
-
6
1
  // ../core/src/errors.ts
7
2
  var CnosError = class extends Error {
8
3
  constructor(message) {
@@ -17,6 +12,11 @@ var CnosManifestError = class extends CnosError {
17
12
  }
18
13
  manifestPath;
19
14
  };
15
+ var CnosSecurityError = class extends CnosError {
16
+ constructor(message) {
17
+ super(message);
18
+ }
19
+ };
20
20
  var CnosKeyNotFoundError = class extends CnosError {
21
21
  constructor(key) {
22
22
  super(`Missing required CNOS config key: ${key}`);
@@ -25,7 +25,43 @@ var CnosKeyNotFoundError = class extends CnosError {
25
25
  key;
26
26
  };
27
27
 
28
+ // ../core/src/runtime/inspect.ts
29
+ function inspectValue(graph, key) {
30
+ const entry = graph.entries.get(key);
31
+ if (!entry) {
32
+ throw new CnosKeyNotFoundError(key);
33
+ }
34
+ return {
35
+ key: entry.key,
36
+ value: entry.value,
37
+ namespace: entry.namespace,
38
+ profile: graph.profile,
39
+ profileSource: graph.profileSource,
40
+ workspace: {
41
+ id: graph.workspace.workspaceId,
42
+ source: graph.workspace.workspaceSource,
43
+ chain: graph.workspace.workspaceChain
44
+ },
45
+ winner: {
46
+ sourceId: entry.winner.sourceId,
47
+ pluginId: entry.winner.pluginId,
48
+ workspaceId: entry.winner.workspaceId,
49
+ ...entry.winner.origin ? { origin: entry.winner.origin } : {}
50
+ },
51
+ overridden: entry.overridden.map((override) => ({
52
+ sourceId: override.sourceId,
53
+ pluginId: override.pluginId,
54
+ workspaceId: override.workspaceId,
55
+ value: override.value,
56
+ ...override.origin ? { origin: override.origin } : {}
57
+ }))
58
+ };
59
+ }
60
+
28
61
  // ../core/src/utils/path.ts
62
+ import { access } from "fs/promises";
63
+ import os from "os";
64
+ import path from "path";
29
65
  var PRIMARY_CNOS_DIR = ".cnos";
30
66
  var LEGACY_CNOS_DIR = "cnos";
31
67
  async function exists(filePath) {
@@ -120,158 +156,282 @@ function stringifyYaml(value) {
120
156
  return stringify(value);
121
157
  }
122
158
 
123
- // ../core/src/utils/envNaming.ts
124
- function normalizeMappingConfig(config = {}) {
125
- return {
126
- convention: config.convention,
127
- explicit: config.explicit ?? {}
128
- };
129
- }
130
- function toScreamingSnakeSegment(segment) {
131
- return segment.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
132
- }
133
- function toScreamingSnake(path8) {
134
- return path8.split(".").map((segment) => toScreamingSnakeSegment(segment)).filter(Boolean).join("_");
135
- }
136
- function fromScreamingSnake(path8) {
137
- return path8.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
138
- }
139
- function logicalKeyToEnvVar(key, config = {}) {
140
- const normalized = normalizeMappingConfig(config);
141
- const explicitEntry = Object.entries(normalized.explicit).find(([, logicalKey]) => logicalKey === key);
142
- if (explicitEntry) {
143
- return explicitEntry[0];
144
- }
145
- if (normalized.convention !== "SCREAMING_SNAKE") {
146
- return void 0;
147
- }
148
- if (key.startsWith("value.")) {
149
- return toScreamingSnake(key.slice("value.".length));
150
- }
151
- if (key.startsWith("secret.")) {
152
- return `SECRET_${toScreamingSnake(key.slice("secret.".length))}`;
153
- }
154
- return void 0;
155
- }
156
- function envVarToLogicalKey(envVar, config = {}) {
157
- const normalized = normalizeMappingConfig(config);
158
- const explicitMatch = normalized.explicit[envVar];
159
- if (explicitMatch) {
160
- return explicitMatch;
161
- }
162
- if (normalized.convention !== "SCREAMING_SNAKE") {
163
- return void 0;
159
+ // ../core/src/manifest/loadManifest.ts
160
+ import { readFile } from "fs/promises";
161
+ import path2 from "path";
162
+
163
+ // ../core/src/manifest/normalizeManifest.ts
164
+ var DEFAULT_RESOLVE_FROM = ["cli.profile", "env.CNOS_PROFILE", "default"];
165
+ var DEFAULT_LOADERS = [
166
+ "filesystem-values",
167
+ "filesystem-secrets",
168
+ "dotenv",
169
+ "process-env",
170
+ "cli-args"
171
+ ];
172
+ var DEFAULT_VALIDATORS = ["basic-schema"];
173
+ var DEFAULT_EXPORTERS = ["env", "public-env"];
174
+ var DEFAULT_INSPECTORS = ["provenance"];
175
+ var DEFAULT_FRAMEWORK_PREFIXES = {
176
+ next: "NEXT_PUBLIC_",
177
+ vite: "VITE_",
178
+ nuxt: "NUXT_PUBLIC_"
179
+ };
180
+ var DEFAULT_NAMESPACES = {
181
+ value: {
182
+ kind: "data",
183
+ shareable: true
184
+ },
185
+ secret: {
186
+ kind: "data",
187
+ shareable: false,
188
+ sensitive: true
189
+ },
190
+ meta: {
191
+ kind: "system",
192
+ shareable: false,
193
+ readonly: true
194
+ },
195
+ public: {
196
+ kind: "projection",
197
+ source: "promote",
198
+ shareable: true,
199
+ readonly: true
200
+ },
201
+ env: {
202
+ kind: "projection",
203
+ source: "envMapping",
204
+ shareable: true,
205
+ readonly: true
164
206
  }
165
- if (envVar.startsWith("SECRET_")) {
166
- const stripped = envVar.slice("SECRET_".length);
167
- if (!stripped) {
168
- return void 0;
207
+ };
208
+ function validateResolveFrom(resolveFrom) {
209
+ const validValues = ["cli.profile", "env.CNOS_PROFILE", "default"];
210
+ for (const entry of resolveFrom) {
211
+ if (!validValues.includes(entry)) {
212
+ throw new CnosManifestError(`Unsupported profiles.resolveFrom entry: ${entry}`);
169
213
  }
170
- return `secret.${fromScreamingSnake(stripped)}`;
171
214
  }
172
- if (!/^[A-Z][A-Z0-9_]*$/.test(envVar)) {
173
- return void 0;
174
- }
175
- return `value.${fromScreamingSnake(envVar)}`;
215
+ return resolveFrom;
176
216
  }
177
-
178
- // ../core/src/runtime/toEnv.ts
179
- function fallbackLogicalKeyToEnvVar(key) {
180
- if (key.startsWith("value.")) {
181
- return key.slice("value.".length).replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
182
- }
183
- if (key.startsWith("secret.")) {
184
- const normalized = key.slice("secret.".length).replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
185
- return `SECRET_${normalized}`;
186
- }
187
- return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
217
+ function normalizeWorkspaceItems(items) {
218
+ return Object.fromEntries(
219
+ Object.entries(items ?? {}).map(([workspaceId, item]) => [
220
+ workspaceId,
221
+ {
222
+ extends: Array.isArray(item?.extends) ? item.extends.map((entry) => entry.trim()).filter(Boolean) : item?.extends ? [item.extends.trim()].filter(Boolean) : [],
223
+ ...item?.globalId?.trim() ? { globalId: item.globalId.trim() } : {}
224
+ }
225
+ ])
226
+ );
188
227
  }
189
- function normalizeEnvValue(value) {
190
- if (value === void 0 || value === null) {
191
- return "";
192
- }
193
- if (typeof value === "string") {
194
- return value;
195
- }
196
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
197
- return String(value);
198
- }
199
- return JSON.stringify(value);
228
+ function normalizeNamespaces(namespaces) {
229
+ const normalized = Object.fromEntries(
230
+ Object.entries(namespaces ?? {}).map(([namespace, definition]) => [
231
+ namespace,
232
+ {
233
+ kind: definition.kind ?? "data",
234
+ shareable: definition.shareable ?? false,
235
+ ...definition.sensitive !== void 0 ? { sensitive: definition.sensitive } : {},
236
+ ...definition.readonly !== void 0 ? { readonly: definition.readonly } : {},
237
+ ...definition.source ? { source: definition.source } : {}
238
+ }
239
+ ])
240
+ );
241
+ return {
242
+ ...DEFAULT_NAMESPACES,
243
+ ...normalized
244
+ };
200
245
  }
201
- function toEnv(graph, manifest, options = {}) {
202
- const includeSecrets = options.includeSecrets ?? true;
203
- const output = {};
204
- const resolvedEntries = Array.from(graph.entries.values()).sort(
205
- (left, right) => left.key.localeCompare(right.key)
246
+ function normalizeVaults(vaults) {
247
+ return Object.fromEntries(
248
+ Object.entries(vaults ?? {}).map(([name, definition]) => {
249
+ const provider = definition.provider?.trim();
250
+ if (!provider) {
251
+ throw new CnosManifestError(`Vault "${name}" requires a provider`);
252
+ }
253
+ return [
254
+ name,
255
+ {
256
+ provider,
257
+ ...definition.passphrase?.trim() ? {
258
+ passphrase: definition.passphrase.trim()
259
+ } : {}
260
+ }
261
+ ];
262
+ })
206
263
  );
207
- for (const entry of resolvedEntries) {
208
- if (entry.namespace === "meta") {
209
- continue;
210
- }
211
- if (!includeSecrets && entry.namespace === "secret") {
212
- continue;
213
- }
214
- const envVar = logicalKeyToEnvVar(entry.key, manifest.envMapping) ?? fallbackLogicalKeyToEnvVar(entry.key);
215
- output[envVar] = normalizeEnvValue(entry.value);
264
+ }
265
+ function normalizeManifest(manifest) {
266
+ const version = manifest.version ?? 1;
267
+ if (version !== 1) {
268
+ throw new CnosManifestError(`Unsupported CNOS manifest version: ${version}`);
216
269
  }
217
- return output;
270
+ const projectName = manifest.project?.name?.trim();
271
+ if (!projectName) {
272
+ throw new CnosManifestError("Manifest requires project.name");
273
+ }
274
+ const defaultProfile = manifest.profiles?.default?.trim() || "base";
275
+ const workspaceItems = normalizeWorkspaceItems(manifest.workspaces?.items);
276
+ const resolveFrom = validateResolveFrom(manifest.profiles?.resolveFrom ?? DEFAULT_RESOLVE_FROM);
277
+ const filesystemValues = {
278
+ root: "./",
279
+ format: "yaml",
280
+ ...manifest.sources?.["filesystem-values"] ?? {}
281
+ };
282
+ const filesystemSecrets = {
283
+ root: "./",
284
+ format: "yaml",
285
+ ...manifest.sources?.["filesystem-secrets"] ?? {}
286
+ };
287
+ const dotenv = {
288
+ root: "./env",
289
+ ...manifest.sources?.dotenv ?? {}
290
+ };
291
+ return {
292
+ version: 1,
293
+ project: {
294
+ name: projectName
295
+ },
296
+ workspaces: {
297
+ ...manifest.workspaces?.default?.trim() ? {
298
+ default: manifest.workspaces.default.trim()
299
+ } : {},
300
+ global: {
301
+ enabled: manifest.workspaces?.global?.enabled ?? false,
302
+ ...manifest.workspaces?.global?.root?.trim() ? {
303
+ root: manifest.workspaces.global.root.trim()
304
+ } : {},
305
+ allowWrite: manifest.workspaces?.global?.allowWrite ?? false
306
+ },
307
+ items: workspaceItems
308
+ },
309
+ profiles: {
310
+ default: defaultProfile,
311
+ resolveFrom
312
+ },
313
+ plugins: {
314
+ loaders: manifest.plugins?.loaders ?? DEFAULT_LOADERS,
315
+ resolver: manifest.plugins?.resolver ?? "profile-aware",
316
+ validators: manifest.plugins?.validators ?? DEFAULT_VALIDATORS,
317
+ exporters: manifest.plugins?.exporters ?? DEFAULT_EXPORTERS,
318
+ inspectors: manifest.plugins?.inspectors ?? DEFAULT_INSPECTORS
319
+ },
320
+ sources: {
321
+ ...manifest.sources ?? {},
322
+ "filesystem-values": filesystemValues,
323
+ "filesystem-secrets": filesystemSecrets,
324
+ dotenv
325
+ },
326
+ resolution: {
327
+ precedence: manifest.resolution?.precedence ?? [
328
+ "filesystem-values",
329
+ "filesystem-secrets",
330
+ "dotenv",
331
+ "process-env",
332
+ "cli-args"
333
+ ],
334
+ arrayPolicy: manifest.resolution?.arrayPolicy ?? "replace"
335
+ },
336
+ envMapping: {
337
+ ...manifest.envMapping?.convention ? {
338
+ convention: manifest.envMapping.convention
339
+ } : {},
340
+ explicit: manifest.envMapping?.explicit ?? {}
341
+ },
342
+ public: {
343
+ promote: manifest.public?.promote ?? [],
344
+ frameworks: {
345
+ ...DEFAULT_FRAMEWORK_PREFIXES,
346
+ ...manifest.public?.frameworks ?? {}
347
+ }
348
+ },
349
+ namespaces: normalizeNamespaces(manifest.namespaces),
350
+ vaults: normalizeVaults(manifest.vaults),
351
+ writePolicy: {
352
+ define: {
353
+ defaultProfile: manifest.writePolicy?.define?.defaultProfile ?? defaultProfile,
354
+ targets: {
355
+ value: manifest.writePolicy?.define?.targets?.value ?? "./values/app.yml",
356
+ secret: manifest.writePolicy?.define?.targets?.secret ?? "./secrets/app.yml"
357
+ }
358
+ }
359
+ },
360
+ schema: manifest.schema ?? {}
361
+ };
218
362
  }
219
363
 
220
- // ../core/src/runtime/toPublicEnv.ts
221
- function fallbackValueEnvVar(key) {
222
- return key.replace(/^value\./, "").replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
223
- }
224
- function normalizeEnvValue2(value) {
225
- if (value === void 0 || value === null) {
226
- return "";
227
- }
228
- if (typeof value === "string") {
229
- return value;
364
+ // ../core/src/manifest/loadManifest.ts
365
+ async function loadManifest(options = {}) {
366
+ const manifestRoot = await resolveManifestRoot(options.root);
367
+ const manifestPath = path2.join(manifestRoot, "cnos.yml");
368
+ let source;
369
+ try {
370
+ source = await readFile(manifestPath, "utf8");
371
+ } catch {
372
+ throw new CnosManifestError("Unable to read CNOS manifest", manifestPath);
230
373
  }
231
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
232
- return String(value);
374
+ const rawManifest = parseYaml(source);
375
+ if (!rawManifest || typeof rawManifest !== "object") {
376
+ throw new CnosManifestError("CNOS manifest must be a YAML object", manifestPath);
233
377
  }
234
- return JSON.stringify(value);
378
+ return {
379
+ manifestRoot,
380
+ repoRoot: path2.dirname(manifestRoot),
381
+ manifestPath,
382
+ manifest: normalizeManifest(rawManifest),
383
+ rawManifest
384
+ };
235
385
  }
236
- function resolvePublicPrefix(manifest, options) {
237
- if (options.prefix) {
238
- return options.prefix;
386
+
387
+ // ../core/src/promotions/validatePromotion.ts
388
+ var DEFAULT_DATA_NAMESPACE = {
389
+ kind: "data",
390
+ shareable: false
391
+ };
392
+ function getNamespaceNameForKey(key) {
393
+ const [namespace] = key.split(".");
394
+ if (!namespace || !key.includes(".")) {
395
+ throw new CnosManifestError(`Logical key must be namespace-qualified: ${key}`);
239
396
  }
240
- if (!options.framework) {
241
- return "";
397
+ return namespace;
398
+ }
399
+ function getNamespaceDefinition(manifest, namespaceOrKey) {
400
+ const namespace = namespaceOrKey.includes(".") ? getNamespaceNameForKey(namespaceOrKey) : namespaceOrKey;
401
+ return manifest.namespaces[namespace] ?? DEFAULT_DATA_NAMESPACE;
402
+ }
403
+ function ensureProjectionAllowed(manifest, key, target) {
404
+ const namespace = getNamespaceNameForKey(key);
405
+ const definition = getNamespaceDefinition(manifest, namespace);
406
+ if (definition.kind !== "data") {
407
+ throw new CnosManifestError(
408
+ `Cannot promote ${key} to ${target} because namespace "${namespace}" is not a data namespace.`
409
+ );
242
410
  }
243
- const configuredPrefix = manifest.public.frameworks[options.framework];
244
- if (!configuredPrefix) {
245
- throw new CnosManifestError(`Unknown public framework prefix: ${options.framework}`);
411
+ if (definition.sensitive) {
412
+ throw new CnosSecurityError(
413
+ `Cannot promote ${key} to ${target} because namespace "${namespace}" is sensitive.`
414
+ );
246
415
  }
247
- return configuredPrefix;
248
- }
249
- function ensurePublicPromotionKey(key) {
250
- if (!key.startsWith("value.")) {
251
- throw new CnosManifestError(`public.promote may only contain value.* keys: ${key}`);
416
+ if (!definition.shareable) {
417
+ throw new CnosSecurityError(
418
+ `Cannot promote ${key} to ${target} because namespace "${namespace}" is not shareable.`
419
+ );
252
420
  }
253
421
  }
254
- function toPublicEnv(graph, manifest, options = {}) {
255
- const prefix = resolvePublicPrefix(manifest, options);
256
- const output = {};
257
- const promotions = [...manifest.public.promote].sort((left, right) => left.localeCompare(right));
258
- for (const key of promotions) {
259
- ensurePublicPromotionKey(key);
260
- const resolved = graph.entries.get(key);
261
- if (!resolved) {
262
- continue;
263
- }
264
- const baseEnvVar = logicalKeyToEnvVar(key, manifest.envMapping) ?? fallbackValueEnvVar(key);
265
- const envVar = prefix && !baseEnvVar.startsWith(prefix) ? `${prefix}${baseEnvVar}` : baseEnvVar;
266
- output[envVar] = normalizeEnvValue2(resolved.value);
422
+ function validateProjectionIssue(manifest, key, target) {
423
+ try {
424
+ ensureProjectionAllowed(manifest, key, target);
425
+ return void 0;
426
+ } catch (error) {
427
+ return {
428
+ code: target === "public" ? "public.invalid-promotion" : "env.invalid-mapping",
429
+ key,
430
+ message: error instanceof Error ? error.message : String(error)
431
+ };
267
432
  }
268
- return output;
269
433
  }
270
434
 
271
- // ../core/src/runtime/dump.ts
272
- import { mkdir, writeFile } from "fs/promises";
273
- import path2 from "path";
274
-
275
435
  // ../core/src/runtime/projection.ts
276
436
  function setNestedValue(target, pathSegments, value) {
277
437
  const [head, ...tail] = pathSegments;
@@ -302,66 +462,29 @@ function toNamespaceObject(graph, namespace) {
302
462
  return output;
303
463
  }
304
464
 
305
- // ../core/src/runtime/dump.ts
306
- function buildDumpFiles(graph, options = {}) {
307
- const basePath = options.flatten ? "" : path2.posix.join("workspaces", graph.workspace.workspaceId);
308
- const values = toNamespaceObject(graph, "value");
309
- const secrets = toNamespaceObject(graph, "secret");
310
- const files = [];
311
- if (Object.keys(values).length > 0) {
312
- files.push({
313
- path: path2.posix.join(basePath, "values", graph.profile, "app.yml"),
314
- namespace: "value",
315
- content: stringifyYaml(values)
316
- });
317
- }
318
- if (Object.keys(secrets).length > 0) {
319
- files.push({
320
- path: path2.posix.join(basePath, "secrets", graph.profile, "app.yml"),
321
- namespace: "secret",
322
- content: stringifyYaml(secrets)
323
- });
324
- }
325
- return files.sort((left, right) => left.path.localeCompare(right.path));
326
- }
327
- function planDump(graph, options = {}) {
328
- return {
329
- workspaceId: graph.workspace.workspaceId,
330
- profile: graph.profile,
331
- flatten: options.flatten ?? false,
332
- files: buildDumpFiles(graph, options)
333
- };
465
+ // ../core/src/runtime/read.ts
466
+ function readValue(graph, key) {
467
+ return graph.entries.get(key)?.value;
334
468
  }
335
- async function writeDump(graph, options) {
336
- const root = path2.resolve(options.to);
337
- const plan = planDump(graph, options);
338
- for (const file of plan.files) {
339
- const destination = path2.join(root, file.path);
340
- await mkdir(path2.dirname(destination), { recursive: true });
341
- await writeFile(destination, file.content, "utf8");
342
- }
343
- return {
344
- ...plan,
345
- root
346
- };
469
+
470
+ // ../core/src/runtime/readOr.ts
471
+ function readOrValue(graph, key, fallback) {
472
+ const value = readValue(graph, key);
473
+ return value === void 0 ? fallback : value;
347
474
  }
348
475
 
349
- // ../core/src/utils/flatten.ts
350
- function flattenObject(value, prefix = "") {
351
- return Object.entries(value).reduce((accumulator, [key, nestedValue]) => {
352
- const nextKey = prefix ? `${prefix}.${key}` : key;
353
- if (nestedValue && typeof nestedValue === "object" && !Array.isArray(nestedValue)) {
354
- Object.assign(accumulator, flattenObject(nestedValue, nextKey));
355
- return accumulator;
356
- }
357
- accumulator[nextKey] = nestedValue;
358
- return accumulator;
359
- }, {});
476
+ // ../core/src/runtime/require.ts
477
+ function requireValue(graph, key) {
478
+ const value = readValue(graph, key);
479
+ if (value === void 0) {
480
+ throw new CnosKeyNotFoundError(key);
481
+ }
482
+ return value;
360
483
  }
361
484
 
362
485
  // ../core/src/utils/secretStore.ts
363
486
  import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
364
- import { mkdir as mkdir2, readdir, readFile, writeFile as writeFile2 } from "fs/promises";
487
+ import { mkdir, readdir, readFile as readFile2, writeFile } from "fs/promises";
365
488
  import path3 from "path";
366
489
  function isObject(value) {
367
490
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -385,6 +508,38 @@ function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
385
508
  const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
386
509
  return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
387
510
  }
511
+ function getVaultPassphraseEnvVar(vault = "default") {
512
+ const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
513
+ return vaultToken && vaultToken !== "DEFAULT" ? `CNOS_SECRET_PASSPHRASE_${vaultToken}` : "CNOS_SECRET_PASSPHRASE";
514
+ }
515
+ function isPassphraseEnvRef(value) {
516
+ return typeof value === "string" && value.startsWith("env:") && value.length > 4;
517
+ }
518
+ function resolveConfiguredVaultPassphrase(definition, vault = "default", processEnv = process.env) {
519
+ if (definition?.provider !== "local") {
520
+ return void 0;
521
+ }
522
+ const passphraseRef = definition.passphrase;
523
+ if (typeof passphraseRef === "string" && passphraseRef.startsWith("env:") && passphraseRef.length > 4) {
524
+ return processEnv[passphraseRef.slice(4)];
525
+ }
526
+ if (passphraseRef) {
527
+ return passphraseRef;
528
+ }
529
+ return resolveSecretPassphrase(vault, processEnv);
530
+ }
531
+ function resolveVaultDefinition(vaults, vault = "default") {
532
+ const definition = vaults?.[vault];
533
+ const provider = definition?.provider ?? "local";
534
+ return {
535
+ name: vault,
536
+ provider,
537
+ ...definition?.passphrase ? {
538
+ passphrase: definition.passphrase
539
+ } : {},
540
+ requiresPassphrase: provider === "local"
541
+ };
542
+ }
388
543
  function encryptDocument(value, passphrase) {
389
544
  const salt = randomBytes(16);
390
545
  const iv = randomBytes(12);
@@ -415,21 +570,21 @@ function decryptDocument(document, passphrase) {
415
570
  async function createSecretVault(storeRoot, vault, passphrase) {
416
571
  const normalizedVault = vault.trim() || "default";
417
572
  const filePath = resolveSecretVaultFile(storeRoot, normalizedVault);
418
- await mkdir2(path3.dirname(filePath), { recursive: true });
573
+ await mkdir(path3.dirname(filePath), { recursive: true });
419
574
  const document = {
420
575
  version: 1,
421
576
  name: normalizedVault,
422
577
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
423
578
  verifier: encryptDocument(`cnos-vault:${normalizedVault}`, passphrase)
424
579
  };
425
- await writeFile2(filePath, JSON.stringify(document, null, 2), "utf8");
580
+ await writeFile(filePath, JSON.stringify(document, null, 2), "utf8");
426
581
  return filePath;
427
582
  }
428
583
  async function ensureSecretVault(storeRoot, vault, passphrase) {
429
584
  const normalizedVault = vault.trim() || "default";
430
585
  const filePath = resolveSecretVaultFile(storeRoot, normalizedVault);
431
586
  try {
432
- await readFile(filePath, "utf8");
587
+ await readFile2(filePath, "utf8");
433
588
  return filePath;
434
589
  } catch (error) {
435
590
  if (error.code !== "ENOENT") {
@@ -450,8 +605,8 @@ async function listSecretVaults(storeRoot) {
450
605
  async function writeLocalSecret(storeRoot, ref, value, passphrase, vault = "default") {
451
606
  await ensureSecretVault(storeRoot, vault, passphrase);
452
607
  const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
453
- await mkdir2(path3.dirname(filePath), { recursive: true });
454
- await writeFile2(filePath, JSON.stringify(encryptDocument(value, passphrase), null, 2), "utf8");
608
+ await mkdir(path3.dirname(filePath), { recursive: true });
609
+ await writeFile(filePath, JSON.stringify(encryptDocument(value, passphrase), null, 2), "utf8");
455
610
  return filePath;
456
611
  }
457
612
  async function readLocalSecret(storeRoot, ref, passphrase, vault = "default") {
@@ -461,16 +616,210 @@ async function readLocalSecret(storeRoot, ref, passphrase, vault = "default") {
461
616
  );
462
617
  }
463
618
  const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
464
- const source = await readFile(filePath, "utf8");
619
+ const source = await readFile2(filePath, "utf8");
465
620
  const document = JSON.parse(source);
466
621
  if (document.version !== 1 || document.algorithm !== "aes-256-gcm" || typeof document.salt !== "string" || typeof document.iv !== "string" || typeof document.tag !== "string" || typeof document.ciphertext !== "string") {
467
622
  throw new CnosManifestError("Invalid local secret document", filePath);
468
623
  }
469
- return decryptDocument(document, passphrase);
624
+ return decryptDocument(document, passphrase);
625
+ }
626
+
627
+ // ../core/src/runtime/toEnv.ts
628
+ function normalizeEnvValue(value) {
629
+ if (value === void 0 || value === null) {
630
+ return "";
631
+ }
632
+ if (typeof value === "string") {
633
+ return value;
634
+ }
635
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
636
+ return String(value);
637
+ }
638
+ return JSON.stringify(value);
639
+ }
640
+ function toEnv(graph, manifest, options = {}) {
641
+ const includeSecrets = options.includeSecrets ?? true;
642
+ const output = {};
643
+ const mappedEntries = Object.entries(manifest.envMapping.explicit).sort(
644
+ ([left], [right]) => left.localeCompare(right)
645
+ );
646
+ for (const [envVar, logicalKey] of mappedEntries) {
647
+ const entry = graph.entries.get(logicalKey);
648
+ if (!entry) {
649
+ continue;
650
+ }
651
+ const namespaceDefinition = getNamespaceDefinition(manifest, entry.namespace);
652
+ if (namespaceDefinition.kind !== "data" || !namespaceDefinition.shareable || namespaceDefinition.sensitive) {
653
+ continue;
654
+ }
655
+ if (entry.namespace === "secret" && !includeSecrets) {
656
+ continue;
657
+ }
658
+ if (isSecretReference(entry.value)) {
659
+ continue;
660
+ }
661
+ output[envVar] = normalizeEnvValue(entry.value);
662
+ }
663
+ return output;
664
+ }
665
+
666
+ // ../core/src/runtime/toPublicEnv.ts
667
+ function fallbackPublicEnvVar(valuePath) {
668
+ return valuePath.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
669
+ }
670
+ function normalizeEnvValue2(value) {
671
+ if (value === void 0 || value === null) {
672
+ return "";
673
+ }
674
+ if (typeof value === "string") {
675
+ return value;
676
+ }
677
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
678
+ return String(value);
679
+ }
680
+ return JSON.stringify(value);
681
+ }
682
+ function resolvePublicPrefix(manifest, options) {
683
+ if (options.prefix) {
684
+ return options.prefix;
685
+ }
686
+ if (!options.framework) {
687
+ return "";
688
+ }
689
+ const configuredPrefix = manifest.public.frameworks[options.framework];
690
+ if (!configuredPrefix) {
691
+ throw new CnosManifestError(`Unknown public framework prefix: ${options.framework}`);
692
+ }
693
+ return configuredPrefix;
694
+ }
695
+ function toPublicEnv(graph, manifest, options = {}) {
696
+ const prefix = resolvePublicPrefix(manifest, options);
697
+ const output = {};
698
+ const promotions = Array.from(graph.entries.values()).filter((entry) => entry.namespace === "public").sort((left, right) => left.key.localeCompare(right.key));
699
+ for (const resolved of promotions) {
700
+ const baseEnvVar = fallbackPublicEnvVar(stripNamespace(resolved.key));
701
+ const envVar = prefix && !baseEnvVar.startsWith(prefix) ? `${prefix}${baseEnvVar}` : baseEnvVar;
702
+ output[envVar] = normalizeEnvValue2(resolved.value);
703
+ }
704
+ return output;
705
+ }
706
+
707
+ // ../core/src/runtime/dump.ts
708
+ import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
709
+ import path4 from "path";
710
+ function buildDumpFiles(graph, options = {}) {
711
+ const basePath = options.flatten ? "" : path4.posix.join("workspaces", graph.workspace.workspaceId);
712
+ const values = toNamespaceObject(graph, "value");
713
+ const secrets = toNamespaceObject(graph, "secret");
714
+ const files = [];
715
+ if (Object.keys(values).length > 0) {
716
+ files.push({
717
+ path: path4.posix.join(basePath, "values", graph.profile, "app.yml"),
718
+ namespace: "value",
719
+ content: stringifyYaml(values)
720
+ });
721
+ }
722
+ if (Object.keys(secrets).length > 0) {
723
+ files.push({
724
+ path: path4.posix.join(basePath, "secrets", graph.profile, "app.yml"),
725
+ namespace: "secret",
726
+ content: stringifyYaml(secrets)
727
+ });
728
+ }
729
+ return files.sort((left, right) => left.path.localeCompare(right.path));
730
+ }
731
+ function planDump(graph, options = {}) {
732
+ return {
733
+ workspaceId: graph.workspace.workspaceId,
734
+ profile: graph.profile,
735
+ flatten: options.flatten ?? false,
736
+ files: buildDumpFiles(graph, options)
737
+ };
738
+ }
739
+ async function writeDump(graph, options) {
740
+ const root = path4.resolve(options.to);
741
+ const plan = planDump(graph, options);
742
+ for (const file of plan.files) {
743
+ const destination = path4.join(root, file.path);
744
+ await mkdir2(path4.dirname(destination), { recursive: true });
745
+ await writeFile2(destination, file.content, "utf8");
746
+ }
747
+ return {
748
+ ...plan,
749
+ root
750
+ };
751
+ }
752
+
753
+ // ../core/src/utils/flatten.ts
754
+ function flattenObject(value, prefix = "") {
755
+ return Object.entries(value).reduce((accumulator, [key, nestedValue]) => {
756
+ const nextKey = prefix ? `${prefix}.${key}` : key;
757
+ if (nestedValue && typeof nestedValue === "object" && !Array.isArray(nestedValue)) {
758
+ Object.assign(accumulator, flattenObject(nestedValue, nextKey));
759
+ return accumulator;
760
+ }
761
+ accumulator[nextKey] = nestedValue;
762
+ return accumulator;
763
+ }, {});
764
+ }
765
+
766
+ // ../core/src/utils/envNaming.ts
767
+ function normalizeMappingConfig(config = {}) {
768
+ return {
769
+ convention: config.convention,
770
+ explicit: config.explicit ?? {}
771
+ };
772
+ }
773
+ function toScreamingSnakeSegment(segment) {
774
+ return segment.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
775
+ }
776
+ function toScreamingSnake(path8) {
777
+ return path8.split(".").map((segment) => toScreamingSnakeSegment(segment)).filter(Boolean).join("_");
778
+ }
779
+ function fromScreamingSnake(path8) {
780
+ return path8.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
781
+ }
782
+ function logicalKeyToEnvVar(key, config = {}) {
783
+ const normalized = normalizeMappingConfig(config);
784
+ const explicitEntry = Object.entries(normalized.explicit).find(([, logicalKey]) => logicalKey === key);
785
+ if (explicitEntry) {
786
+ return explicitEntry[0];
787
+ }
788
+ if (normalized.convention !== "SCREAMING_SNAKE") {
789
+ return void 0;
790
+ }
791
+ if (key.startsWith("value.")) {
792
+ return toScreamingSnake(key.slice("value.".length));
793
+ }
794
+ if (key.startsWith("secret.")) {
795
+ return `SECRET_${toScreamingSnake(key.slice("secret.".length))}`;
796
+ }
797
+ return void 0;
798
+ }
799
+ function envVarToLogicalKey(envVar, config = {}) {
800
+ const normalized = normalizeMappingConfig(config);
801
+ const explicitMatch = normalized.explicit[envVar];
802
+ if (explicitMatch) {
803
+ return explicitMatch;
804
+ }
805
+ if (normalized.convention !== "SCREAMING_SNAKE") {
806
+ return void 0;
807
+ }
808
+ if (envVar.startsWith("SECRET_")) {
809
+ const stripped = envVar.slice("SECRET_".length);
810
+ if (!stripped) {
811
+ return void 0;
812
+ }
813
+ return `secret.${fromScreamingSnake(stripped)}`;
814
+ }
815
+ if (!/^[A-Z][A-Z0-9_]*$/.test(envVar)) {
816
+ return void 0;
817
+ }
818
+ return `value.${fromScreamingSnake(envVar)}`;
470
819
  }
471
820
 
472
821
  // ../core/src/validation/envMapping.ts
473
- function fallbackLogicalKeyToEnvVar2(key) {
822
+ function fallbackLogicalKeyToEnvVar(key) {
474
823
  return key.replace(/^(value|secret)\./, "").replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
475
824
  }
476
825
  function validateEnvMappingCollisions(manifest, graph) {
@@ -482,10 +831,11 @@ function validateEnvMappingCollisions(manifest, graph) {
482
831
  ]);
483
832
  const collisions = /* @__PURE__ */ new Map();
484
833
  for (const key of candidates) {
485
- if (key.startsWith("meta.")) {
834
+ const definition = getNamespaceDefinition(manifest, key);
835
+ if (definition.kind !== "data") {
486
836
  continue;
487
837
  }
488
- const envVar = logicalKeyToEnvVar(key, manifest.envMapping) ?? (key.startsWith("value.") || key.startsWith("secret.") ? fallbackLogicalKeyToEnvVar2(key) : void 0);
838
+ const envVar = logicalKeyToEnvVar(key, manifest.envMapping) ?? (key.startsWith("value.") || key.startsWith("secret.") ? fallbackLogicalKeyToEnvVar(key) : void 0);
489
839
  if (!envVar) {
490
840
  continue;
491
841
  }
@@ -501,11 +851,7 @@ function validateEnvMappingCollisions(manifest, graph) {
501
851
 
502
852
  // ../core/src/validation/publicSafety.ts
503
853
  function validatePublicSafety(manifest) {
504
- return manifest.public.promote.filter((key) => !key.startsWith("value.")).map((key) => ({
505
- code: "public.invalid-promotion",
506
- key,
507
- message: `public.promote may only include value.* keys: ${key}`
508
- }));
854
+ return manifest.public.promote.map((key) => validateProjectionIssue(manifest, key, "public")).filter((issue) => Boolean(issue));
509
855
  }
510
856
 
511
857
  // ../core/src/validation/workspaceSafety.ts
@@ -571,39 +917,6 @@ async function validateRuntime(runtime) {
571
917
  };
572
918
  }
573
919
 
574
- // ../core/src/runtime/inspect.ts
575
- function inspectValue(graph, key) {
576
- const entry = graph.entries.get(key);
577
- if (!entry) {
578
- throw new CnosKeyNotFoundError(key);
579
- }
580
- return {
581
- key: entry.key,
582
- value: entry.value,
583
- namespace: entry.namespace,
584
- profile: graph.profile,
585
- profileSource: graph.profileSource,
586
- workspace: {
587
- id: graph.workspace.workspaceId,
588
- source: graph.workspace.workspaceSource,
589
- chain: graph.workspace.workspaceChain
590
- },
591
- winner: {
592
- sourceId: entry.winner.sourceId,
593
- pluginId: entry.winner.pluginId,
594
- workspaceId: entry.winner.workspaceId,
595
- ...entry.winner.origin ? { origin: entry.winner.origin } : {}
596
- },
597
- overridden: entry.overridden.map((override) => ({
598
- sourceId: override.sourceId,
599
- pluginId: override.pluginId,
600
- workspaceId: override.workspaceId,
601
- value: override.value,
602
- ...override.origin ? { origin: override.origin } : {}
603
- }))
604
- };
605
- }
606
-
607
920
  // ../core/src/inspectors/provenance.ts
608
921
  function createProvenanceInspector() {
609
922
  return {
@@ -615,167 +928,6 @@ function createProvenanceInspector() {
615
928
  };
616
929
  }
617
930
 
618
- // ../core/src/manifest/loadManifest.ts
619
- import { readFile as readFile2 } from "fs/promises";
620
- import path4 from "path";
621
-
622
- // ../core/src/manifest/normalizeManifest.ts
623
- var DEFAULT_RESOLVE_FROM = ["cli.profile", "env.CNOS_PROFILE", "default"];
624
- var DEFAULT_LOADERS = [
625
- "filesystem-values",
626
- "filesystem-secrets",
627
- "dotenv",
628
- "process-env",
629
- "cli-args"
630
- ];
631
- var DEFAULT_VALIDATORS = ["basic-schema"];
632
- var DEFAULT_EXPORTERS = ["env", "public-env"];
633
- var DEFAULT_INSPECTORS = ["provenance"];
634
- var DEFAULT_FRAMEWORK_PREFIXES = {
635
- next: "NEXT_PUBLIC_",
636
- vite: "VITE_",
637
- nuxt: "NUXT_PUBLIC_"
638
- };
639
- function validateResolveFrom(resolveFrom) {
640
- const validValues = ["cli.profile", "env.CNOS_PROFILE", "default"];
641
- for (const entry of resolveFrom) {
642
- if (!validValues.includes(entry)) {
643
- throw new CnosManifestError(`Unsupported profiles.resolveFrom entry: ${entry}`);
644
- }
645
- }
646
- return resolveFrom;
647
- }
648
- function normalizeWorkspaceItems(items) {
649
- return Object.fromEntries(
650
- Object.entries(items ?? {}).map(([workspaceId, item]) => [
651
- workspaceId,
652
- {
653
- extends: Array.isArray(item?.extends) ? item.extends.map((entry) => entry.trim()).filter(Boolean) : item?.extends ? [item.extends.trim()].filter(Boolean) : [],
654
- ...item?.globalId?.trim() ? { globalId: item.globalId.trim() } : {}
655
- }
656
- ])
657
- );
658
- }
659
- function normalizeManifest(manifest) {
660
- const version = manifest.version ?? 1;
661
- if (version !== 1) {
662
- throw new CnosManifestError(`Unsupported CNOS manifest version: ${version}`);
663
- }
664
- const projectName = manifest.project?.name?.trim();
665
- if (!projectName) {
666
- throw new CnosManifestError("Manifest requires project.name");
667
- }
668
- const defaultProfile = manifest.profiles?.default?.trim() || "base";
669
- const workspaceItems = normalizeWorkspaceItems(manifest.workspaces?.items);
670
- const resolveFrom = validateResolveFrom(manifest.profiles?.resolveFrom ?? DEFAULT_RESOLVE_FROM);
671
- const filesystemValues = {
672
- root: "./",
673
- format: "yaml",
674
- ...manifest.sources?.["filesystem-values"] ?? {}
675
- };
676
- const filesystemSecrets = {
677
- root: "./",
678
- format: "yaml",
679
- ...manifest.sources?.["filesystem-secrets"] ?? {}
680
- };
681
- const dotenv = {
682
- root: "./env",
683
- ...manifest.sources?.dotenv ?? {}
684
- };
685
- return {
686
- version: 1,
687
- project: {
688
- name: projectName
689
- },
690
- workspaces: {
691
- ...manifest.workspaces?.default?.trim() ? {
692
- default: manifest.workspaces.default.trim()
693
- } : {},
694
- global: {
695
- enabled: manifest.workspaces?.global?.enabled ?? false,
696
- ...manifest.workspaces?.global?.root?.trim() ? {
697
- root: manifest.workspaces.global.root.trim()
698
- } : {},
699
- allowWrite: manifest.workspaces?.global?.allowWrite ?? false
700
- },
701
- items: workspaceItems
702
- },
703
- profiles: {
704
- default: defaultProfile,
705
- resolveFrom
706
- },
707
- plugins: {
708
- loaders: manifest.plugins?.loaders ?? DEFAULT_LOADERS,
709
- resolver: manifest.plugins?.resolver ?? "profile-aware",
710
- validators: manifest.plugins?.validators ?? DEFAULT_VALIDATORS,
711
- exporters: manifest.plugins?.exporters ?? DEFAULT_EXPORTERS,
712
- inspectors: manifest.plugins?.inspectors ?? DEFAULT_INSPECTORS
713
- },
714
- sources: {
715
- ...manifest.sources ?? {},
716
- "filesystem-values": filesystemValues,
717
- "filesystem-secrets": filesystemSecrets,
718
- dotenv
719
- },
720
- resolution: {
721
- precedence: manifest.resolution?.precedence ?? [
722
- "filesystem-values",
723
- "filesystem-secrets",
724
- "dotenv",
725
- "process-env",
726
- "cli-args"
727
- ],
728
- arrayPolicy: manifest.resolution?.arrayPolicy ?? "replace"
729
- },
730
- envMapping: {
731
- ...manifest.envMapping?.convention ? {
732
- convention: manifest.envMapping.convention
733
- } : {},
734
- explicit: manifest.envMapping?.explicit ?? {}
735
- },
736
- public: {
737
- promote: manifest.public?.promote ?? [],
738
- frameworks: {
739
- ...DEFAULT_FRAMEWORK_PREFIXES,
740
- ...manifest.public?.frameworks ?? {}
741
- }
742
- },
743
- writePolicy: {
744
- define: {
745
- defaultProfile: manifest.writePolicy?.define?.defaultProfile ?? defaultProfile,
746
- targets: {
747
- value: manifest.writePolicy?.define?.targets?.value ?? "./values/app.yml",
748
- secret: manifest.writePolicy?.define?.targets?.secret ?? "./secrets/app.yml"
749
- }
750
- }
751
- },
752
- schema: manifest.schema ?? {}
753
- };
754
- }
755
-
756
- // ../core/src/manifest/loadManifest.ts
757
- async function loadManifest(options = {}) {
758
- const manifestRoot = await resolveManifestRoot(options.root);
759
- const manifestPath = path4.join(manifestRoot, "cnos.yml");
760
- let source;
761
- try {
762
- source = await readFile2(manifestPath, "utf8");
763
- } catch {
764
- throw new CnosManifestError("Unable to read CNOS manifest", manifestPath);
765
- }
766
- const rawManifest = parseYaml(source);
767
- if (!rawManifest || typeof rawManifest !== "object") {
768
- throw new CnosManifestError("CNOS manifest must be a YAML object", manifestPath);
769
- }
770
- return {
771
- manifestRoot,
772
- repoRoot: path4.dirname(manifestRoot),
773
- manifestPath,
774
- manifest: normalizeManifest(rawManifest),
775
- rawManifest
776
- };
777
- }
778
-
779
931
  // ../core/src/manifest/loadWorkspaceFile.ts
780
932
  import { readFile as readFile3 } from "fs/promises";
781
933
  import path5 from "path";
@@ -943,6 +1095,50 @@ async function expandProfileChain(activeProfile, options = {}) {
943
1095
  };
944
1096
  }
945
1097
 
1098
+ // ../core/src/promotions/promoteToPublic.ts
1099
+ function toPublicKey(key) {
1100
+ const namespace = getNamespaceNameForKey(key);
1101
+ return namespace === "value" ? `public.${stripNamespace(key)}` : `public.${key}`;
1102
+ }
1103
+ function toPromotedConfigEntry(entry, key, promotedFrom) {
1104
+ return {
1105
+ ...entry,
1106
+ key,
1107
+ namespace: "public",
1108
+ sourceId: "public-promote",
1109
+ pluginId: "core",
1110
+ metadata: {
1111
+ ...entry.metadata ?? {},
1112
+ promotedFrom
1113
+ }
1114
+ };
1115
+ }
1116
+ function toPromotedResolvedEntry(entry) {
1117
+ const key = toPublicKey(entry.key);
1118
+ return {
1119
+ key,
1120
+ value: entry.value,
1121
+ namespace: "public",
1122
+ winner: toPromotedConfigEntry(entry.winner, key, entry.key),
1123
+ overridden: entry.overridden.map((override) => toPromotedConfigEntry(override, key, entry.key))
1124
+ };
1125
+ }
1126
+ function promoteToPublic(graph, manifest) {
1127
+ const entries = new Map(graph.entries);
1128
+ for (const key of manifest.public.promote) {
1129
+ ensureProjectionAllowed(manifest, key, "public");
1130
+ const resolved = graph.entries.get(key);
1131
+ if (!resolved) {
1132
+ continue;
1133
+ }
1134
+ entries.set(toPublicKey(key), toPromotedResolvedEntry(resolved));
1135
+ }
1136
+ return {
1137
+ ...graph,
1138
+ entries
1139
+ };
1140
+ }
1141
+
946
1142
  // ../core/src/profiles/resolveActiveProfile.ts
947
1143
  function resolveActiveProfile(manifest, options = {}) {
948
1144
  for (const source of manifest.profiles.resolveFrom) {
@@ -1377,26 +1573,6 @@ async function runPipeline(options) {
1377
1573
  return collectedEntries.flat();
1378
1574
  }
1379
1575
 
1380
- // ../core/src/runtime/read.ts
1381
- function readValue(graph, key) {
1382
- return graph.entries.get(key)?.value;
1383
- }
1384
-
1385
- // ../core/src/runtime/readOr.ts
1386
- function readOrValue(graph, key, fallback) {
1387
- const value = readValue(graph, key);
1388
- return value === void 0 ? fallback : value;
1389
- }
1390
-
1391
- // ../core/src/runtime/require.ts
1392
- function requireValue(graph, key) {
1393
- const value = readValue(graph, key);
1394
- if (value === void 0) {
1395
- throw new CnosKeyNotFoundError(key);
1396
- }
1397
- return value;
1398
- }
1399
-
1400
1576
  // ../core/src/orchestrator/runtime.ts
1401
1577
  function createRuntime(manifest, graph, plugins = []) {
1402
1578
  return {
@@ -1534,6 +1710,9 @@ function appendMetaEntries(graph, cnosVersion) {
1534
1710
  }
1535
1711
  async function createCnos(options = {}) {
1536
1712
  const loadedManifest = await loadManifest(options.root ? { root: options.root } : {});
1713
+ for (const key of loadedManifest.manifest.public.promote) {
1714
+ ensureProjectionAllowed(loadedManifest.manifest, key, "public");
1715
+ }
1537
1716
  const workspaceFile = await loadWorkspaceFile(loadedManifest.repoRoot);
1538
1717
  const workspace = await resolveWorkspaceContext(loadedManifest.manifest, {
1539
1718
  manifestRoot: loadedManifest.manifestRoot,
@@ -1573,10 +1752,11 @@ async function createCnos(options = {}) {
1573
1752
  workspace
1574
1753
  });
1575
1754
  const schemaApplied = applySchemaRules(graph, loadedManifest.manifest.schema);
1755
+ const promotedGraph = promoteToPublic(schemaApplied.graph, loadedManifest.manifest);
1576
1756
  return createRuntime(
1577
1757
  loadedManifest.manifest,
1578
1758
  appendMetaEntries({
1579
- ...schemaApplied.graph,
1759
+ ...promotedGraph,
1580
1760
  profileSource: activeProfile.source
1581
1761
  }, options.cnosVersion),
1582
1762
  plugins
@@ -1585,28 +1765,42 @@ async function createCnos(options = {}) {
1585
1765
 
1586
1766
  export {
1587
1767
  CnosManifestError,
1768
+ CnosSecurityError,
1769
+ inspectValue,
1588
1770
  createProvenanceInspector,
1771
+ resolveManifestRoot,
1589
1772
  resolveWorkspaceScopedPath,
1590
1773
  resolveConfigDocumentPath,
1591
1774
  toPortablePath,
1592
1775
  joinConfigPath,
1776
+ toLogicalKey,
1593
1777
  parseYaml,
1594
1778
  stringifyYaml,
1779
+ loadManifest,
1780
+ ensureProjectionAllowed,
1595
1781
  applySchemaRules,
1596
- envVarToLogicalKey,
1597
- toEnv,
1598
- toPublicEnv,
1599
- createCnos,
1600
- planDump,
1601
- writeDump,
1602
- flattenObject,
1782
+ toNamespaceObject,
1783
+ readValue,
1784
+ readOrValue,
1785
+ requireValue,
1603
1786
  isSecretReference,
1604
1787
  resolveSecretStoreRoot,
1605
1788
  resolveSecretVaultFile,
1606
1789
  resolveSecretPassphrase,
1790
+ getVaultPassphraseEnvVar,
1791
+ isPassphraseEnvRef,
1792
+ resolveConfiguredVaultPassphrase,
1793
+ resolveVaultDefinition,
1607
1794
  createSecretVault,
1608
1795
  listSecretVaults,
1609
1796
  writeLocalSecret,
1610
1797
  readLocalSecret,
1798
+ toEnv,
1799
+ toPublicEnv,
1800
+ createCnos,
1801
+ planDump,
1802
+ writeDump,
1803
+ envVarToLogicalKey,
1804
+ flattenObject,
1611
1805
  validateRuntime
1612
1806
  };