@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
package/dist/internal.cjs CHANGED
@@ -30,20 +30,52 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/internal.ts
31
31
  var internal_exports = {};
32
32
  __export(internal_exports, {
33
+ CNOS_GRAPH_ENV_VAR: () => CNOS_GRAPH_ENV_VAR,
34
+ CnosSecurityError: () => CnosSecurityError,
33
35
  createSecretVault: () => createSecretVault,
36
+ deserializeRuntimeGraph: () => deserializeRuntimeGraph,
37
+ ensureProjectionAllowed: () => ensureProjectionAllowed,
34
38
  flattenObject: () => flattenObject,
39
+ getVaultPassphraseEnvVar: () => getVaultPassphraseEnvVar,
40
+ isPassphraseEnvRef: () => isPassphraseEnvRef,
35
41
  listSecretVaults: () => listSecretVaults,
42
+ loadManifest: () => loadManifest,
36
43
  parseYaml: () => parseYaml,
44
+ readRuntimeGraphFromEnv: () => readRuntimeGraphFromEnv,
37
45
  resolveConfigDocumentPath: () => resolveConfigDocumentPath,
46
+ resolveConfiguredVaultPassphrase: () => resolveConfiguredVaultPassphrase,
47
+ resolveManifestRoot: () => resolveManifestRoot,
38
48
  resolveSecretPassphrase: () => resolveSecretPassphrase,
39
49
  resolveSecretStoreRoot: () => resolveSecretStoreRoot,
40
50
  resolveSecretVaultFile: () => resolveSecretVaultFile,
51
+ resolveVaultDefinition: () => resolveVaultDefinition,
52
+ serializeRuntimeGraph: () => serializeRuntimeGraph,
41
53
  stringifyYaml: () => stringifyYaml,
42
54
  validateRuntime: () => validateRuntime,
43
55
  writeLocalSecret: () => writeLocalSecret
44
56
  });
45
57
  module.exports = __toCommonJS(internal_exports);
46
58
 
59
+ // ../core/src/errors.ts
60
+ var CnosError = class extends Error {
61
+ constructor(message) {
62
+ super(message);
63
+ this.name = new.target.name;
64
+ }
65
+ };
66
+ var CnosManifestError = class extends CnosError {
67
+ constructor(message, manifestPath) {
68
+ super(manifestPath ? `${message} (${manifestPath})` : message);
69
+ this.manifestPath = manifestPath;
70
+ }
71
+ manifestPath;
72
+ };
73
+ var CnosSecurityError = class extends CnosError {
74
+ constructor(message) {
75
+ super(message);
76
+ }
77
+ };
78
+
47
79
  // ../core/src/manifest/loadManifest.ts
48
80
  var import_promises2 = require("fs/promises");
49
81
  var import_node_path2 = __toESM(require("path"), 1);
@@ -52,6 +84,35 @@ var import_node_path2 = __toESM(require("path"), 1);
52
84
  var import_promises = require("fs/promises");
53
85
  var import_node_os = __toESM(require("os"), 1);
54
86
  var import_node_path = __toESM(require("path"), 1);
87
+ var PRIMARY_CNOS_DIR = ".cnos";
88
+ var LEGACY_CNOS_DIR = "cnos";
89
+ async function exists(filePath) {
90
+ try {
91
+ await (0, import_promises.access)(filePath);
92
+ return true;
93
+ } catch {
94
+ return false;
95
+ }
96
+ }
97
+ async function resolveCnosRoot(root = process.cwd()) {
98
+ const basePath = import_node_path.default.resolve(root);
99
+ const candidates = [
100
+ import_node_path.default.join(basePath, PRIMARY_CNOS_DIR),
101
+ import_node_path.default.join(basePath, LEGACY_CNOS_DIR),
102
+ basePath
103
+ ];
104
+ for (const candidate of candidates) {
105
+ if (await exists(import_node_path.default.join(candidate, "cnos.yml"))) {
106
+ return candidate;
107
+ }
108
+ }
109
+ throw new CnosManifestError(
110
+ `Could not locate .cnos/cnos.yml or cnos/cnos.yml from root: ${basePath}`
111
+ );
112
+ }
113
+ async function resolveManifestRoot(root = process.cwd()) {
114
+ return resolveCnosRoot(root);
115
+ }
55
116
  function expandHomePath(targetPath) {
56
117
  if (targetPath === "~") {
57
118
  return import_node_os.default.homedir();
@@ -83,6 +144,230 @@ function stringifyYaml(value) {
83
144
  return (0, import_yaml.stringify)(value);
84
145
  }
85
146
 
147
+ // ../core/src/manifest/normalizeManifest.ts
148
+ var DEFAULT_RESOLVE_FROM = ["cli.profile", "env.CNOS_PROFILE", "default"];
149
+ var DEFAULT_LOADERS = [
150
+ "filesystem-values",
151
+ "filesystem-secrets",
152
+ "dotenv",
153
+ "process-env",
154
+ "cli-args"
155
+ ];
156
+ var DEFAULT_VALIDATORS = ["basic-schema"];
157
+ var DEFAULT_EXPORTERS = ["env", "public-env"];
158
+ var DEFAULT_INSPECTORS = ["provenance"];
159
+ var DEFAULT_FRAMEWORK_PREFIXES = {
160
+ next: "NEXT_PUBLIC_",
161
+ vite: "VITE_",
162
+ nuxt: "NUXT_PUBLIC_"
163
+ };
164
+ var DEFAULT_NAMESPACES = {
165
+ value: {
166
+ kind: "data",
167
+ shareable: true
168
+ },
169
+ secret: {
170
+ kind: "data",
171
+ shareable: false,
172
+ sensitive: true
173
+ },
174
+ meta: {
175
+ kind: "system",
176
+ shareable: false,
177
+ readonly: true
178
+ },
179
+ public: {
180
+ kind: "projection",
181
+ source: "promote",
182
+ shareable: true,
183
+ readonly: true
184
+ },
185
+ env: {
186
+ kind: "projection",
187
+ source: "envMapping",
188
+ shareable: true,
189
+ readonly: true
190
+ }
191
+ };
192
+ function validateResolveFrom(resolveFrom) {
193
+ const validValues = ["cli.profile", "env.CNOS_PROFILE", "default"];
194
+ for (const entry of resolveFrom) {
195
+ if (!validValues.includes(entry)) {
196
+ throw new CnosManifestError(`Unsupported profiles.resolveFrom entry: ${entry}`);
197
+ }
198
+ }
199
+ return resolveFrom;
200
+ }
201
+ function normalizeWorkspaceItems(items) {
202
+ return Object.fromEntries(
203
+ Object.entries(items ?? {}).map(([workspaceId, item]) => [
204
+ workspaceId,
205
+ {
206
+ extends: Array.isArray(item?.extends) ? item.extends.map((entry) => entry.trim()).filter(Boolean) : item?.extends ? [item.extends.trim()].filter(Boolean) : [],
207
+ ...item?.globalId?.trim() ? { globalId: item.globalId.trim() } : {}
208
+ }
209
+ ])
210
+ );
211
+ }
212
+ function normalizeNamespaces(namespaces) {
213
+ const normalized = Object.fromEntries(
214
+ Object.entries(namespaces ?? {}).map(([namespace, definition]) => [
215
+ namespace,
216
+ {
217
+ kind: definition.kind ?? "data",
218
+ shareable: definition.shareable ?? false,
219
+ ...definition.sensitive !== void 0 ? { sensitive: definition.sensitive } : {},
220
+ ...definition.readonly !== void 0 ? { readonly: definition.readonly } : {},
221
+ ...definition.source ? { source: definition.source } : {}
222
+ }
223
+ ])
224
+ );
225
+ return {
226
+ ...DEFAULT_NAMESPACES,
227
+ ...normalized
228
+ };
229
+ }
230
+ function normalizeVaults(vaults) {
231
+ return Object.fromEntries(
232
+ Object.entries(vaults ?? {}).map(([name, definition]) => {
233
+ const provider = definition.provider?.trim();
234
+ if (!provider) {
235
+ throw new CnosManifestError(`Vault "${name}" requires a provider`);
236
+ }
237
+ return [
238
+ name,
239
+ {
240
+ provider,
241
+ ...definition.passphrase?.trim() ? {
242
+ passphrase: definition.passphrase.trim()
243
+ } : {}
244
+ }
245
+ ];
246
+ })
247
+ );
248
+ }
249
+ function normalizeManifest(manifest) {
250
+ const version = manifest.version ?? 1;
251
+ if (version !== 1) {
252
+ throw new CnosManifestError(`Unsupported CNOS manifest version: ${version}`);
253
+ }
254
+ const projectName = manifest.project?.name?.trim();
255
+ if (!projectName) {
256
+ throw new CnosManifestError("Manifest requires project.name");
257
+ }
258
+ const defaultProfile = manifest.profiles?.default?.trim() || "base";
259
+ const workspaceItems = normalizeWorkspaceItems(manifest.workspaces?.items);
260
+ const resolveFrom = validateResolveFrom(manifest.profiles?.resolveFrom ?? DEFAULT_RESOLVE_FROM);
261
+ const filesystemValues = {
262
+ root: "./",
263
+ format: "yaml",
264
+ ...manifest.sources?.["filesystem-values"] ?? {}
265
+ };
266
+ const filesystemSecrets = {
267
+ root: "./",
268
+ format: "yaml",
269
+ ...manifest.sources?.["filesystem-secrets"] ?? {}
270
+ };
271
+ const dotenv = {
272
+ root: "./env",
273
+ ...manifest.sources?.dotenv ?? {}
274
+ };
275
+ return {
276
+ version: 1,
277
+ project: {
278
+ name: projectName
279
+ },
280
+ workspaces: {
281
+ ...manifest.workspaces?.default?.trim() ? {
282
+ default: manifest.workspaces.default.trim()
283
+ } : {},
284
+ global: {
285
+ enabled: manifest.workspaces?.global?.enabled ?? false,
286
+ ...manifest.workspaces?.global?.root?.trim() ? {
287
+ root: manifest.workspaces.global.root.trim()
288
+ } : {},
289
+ allowWrite: manifest.workspaces?.global?.allowWrite ?? false
290
+ },
291
+ items: workspaceItems
292
+ },
293
+ profiles: {
294
+ default: defaultProfile,
295
+ resolveFrom
296
+ },
297
+ plugins: {
298
+ loaders: manifest.plugins?.loaders ?? DEFAULT_LOADERS,
299
+ resolver: manifest.plugins?.resolver ?? "profile-aware",
300
+ validators: manifest.plugins?.validators ?? DEFAULT_VALIDATORS,
301
+ exporters: manifest.plugins?.exporters ?? DEFAULT_EXPORTERS,
302
+ inspectors: manifest.plugins?.inspectors ?? DEFAULT_INSPECTORS
303
+ },
304
+ sources: {
305
+ ...manifest.sources ?? {},
306
+ "filesystem-values": filesystemValues,
307
+ "filesystem-secrets": filesystemSecrets,
308
+ dotenv
309
+ },
310
+ resolution: {
311
+ precedence: manifest.resolution?.precedence ?? [
312
+ "filesystem-values",
313
+ "filesystem-secrets",
314
+ "dotenv",
315
+ "process-env",
316
+ "cli-args"
317
+ ],
318
+ arrayPolicy: manifest.resolution?.arrayPolicy ?? "replace"
319
+ },
320
+ envMapping: {
321
+ ...manifest.envMapping?.convention ? {
322
+ convention: manifest.envMapping.convention
323
+ } : {},
324
+ explicit: manifest.envMapping?.explicit ?? {}
325
+ },
326
+ public: {
327
+ promote: manifest.public?.promote ?? [],
328
+ frameworks: {
329
+ ...DEFAULT_FRAMEWORK_PREFIXES,
330
+ ...manifest.public?.frameworks ?? {}
331
+ }
332
+ },
333
+ namespaces: normalizeNamespaces(manifest.namespaces),
334
+ vaults: normalizeVaults(manifest.vaults),
335
+ writePolicy: {
336
+ define: {
337
+ defaultProfile: manifest.writePolicy?.define?.defaultProfile ?? defaultProfile,
338
+ targets: {
339
+ value: manifest.writePolicy?.define?.targets?.value ?? "./values/app.yml",
340
+ secret: manifest.writePolicy?.define?.targets?.secret ?? "./secrets/app.yml"
341
+ }
342
+ }
343
+ },
344
+ schema: manifest.schema ?? {}
345
+ };
346
+ }
347
+
348
+ // ../core/src/manifest/loadManifest.ts
349
+ async function loadManifest(options = {}) {
350
+ const manifestRoot = await resolveManifestRoot(options.root);
351
+ const manifestPath = import_node_path2.default.join(manifestRoot, "cnos.yml");
352
+ let source;
353
+ try {
354
+ source = await (0, import_promises2.readFile)(manifestPath, "utf8");
355
+ } catch {
356
+ throw new CnosManifestError("Unable to read CNOS manifest", manifestPath);
357
+ }
358
+ const rawManifest = parseYaml(source);
359
+ if (!rawManifest || typeof rawManifest !== "object") {
360
+ throw new CnosManifestError("CNOS manifest must be a YAML object", manifestPath);
361
+ }
362
+ return {
363
+ manifestRoot,
364
+ repoRoot: import_node_path2.default.dirname(manifestRoot),
365
+ manifestPath,
366
+ manifest: normalizeManifest(rawManifest),
367
+ rawManifest
368
+ };
369
+ }
370
+
86
371
  // ../core/src/manifest/loadWorkspaceFile.ts
87
372
  var import_promises3 = require("fs/promises");
88
373
  var import_node_path3 = __toESM(require("path"), 1);
@@ -91,70 +376,70 @@ var import_node_path3 = __toESM(require("path"), 1);
91
376
  var import_promises4 = require("fs/promises");
92
377
  var import_node_path4 = __toESM(require("path"), 1);
93
378
 
94
- // ../core/src/workspaces/resolveWorkspaceContext.ts
95
- var import_promises5 = require("fs/promises");
96
- var import_node_path5 = __toESM(require("path"), 1);
97
-
98
- // ../core/src/utils/envNaming.ts
99
- function normalizeMappingConfig(config = {}) {
100
- return {
101
- convention: config.convention,
102
- explicit: config.explicit ?? {}
103
- };
104
- }
105
- function toScreamingSnakeSegment(segment) {
106
- return segment.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
379
+ // ../core/src/promotions/validatePromotion.ts
380
+ var DEFAULT_DATA_NAMESPACE = {
381
+ kind: "data",
382
+ shareable: false
383
+ };
384
+ function getNamespaceNameForKey(key) {
385
+ const [namespace] = key.split(".");
386
+ if (!namespace || !key.includes(".")) {
387
+ throw new CnosManifestError(`Logical key must be namespace-qualified: ${key}`);
388
+ }
389
+ return namespace;
107
390
  }
108
- function toScreamingSnake(path8) {
109
- return path8.split(".").map((segment) => toScreamingSnakeSegment(segment)).filter(Boolean).join("_");
391
+ function getNamespaceDefinition(manifest, namespaceOrKey) {
392
+ const namespace = namespaceOrKey.includes(".") ? getNamespaceNameForKey(namespaceOrKey) : namespaceOrKey;
393
+ return manifest.namespaces[namespace] ?? DEFAULT_DATA_NAMESPACE;
110
394
  }
111
- function logicalKeyToEnvVar(key, config = {}) {
112
- const normalized = normalizeMappingConfig(config);
113
- const explicitEntry = Object.entries(normalized.explicit).find(([, logicalKey]) => logicalKey === key);
114
- if (explicitEntry) {
115
- return explicitEntry[0];
395
+ function ensureProjectionAllowed(manifest, key, target) {
396
+ const namespace = getNamespaceNameForKey(key);
397
+ const definition = getNamespaceDefinition(manifest, namespace);
398
+ if (definition.kind !== "data") {
399
+ throw new CnosManifestError(
400
+ `Cannot promote ${key} to ${target} because namespace "${namespace}" is not a data namespace.`
401
+ );
116
402
  }
117
- if (normalized.convention !== "SCREAMING_SNAKE") {
118
- return void 0;
403
+ if (definition.sensitive) {
404
+ throw new CnosSecurityError(
405
+ `Cannot promote ${key} to ${target} because namespace "${namespace}" is sensitive.`
406
+ );
119
407
  }
120
- if (key.startsWith("value.")) {
121
- return toScreamingSnake(key.slice("value.".length));
408
+ if (!definition.shareable) {
409
+ throw new CnosSecurityError(
410
+ `Cannot promote ${key} to ${target} because namespace "${namespace}" is not shareable.`
411
+ );
122
412
  }
123
- if (key.startsWith("secret.")) {
124
- return `SECRET_${toScreamingSnake(key.slice("secret.".length))}`;
413
+ }
414
+ function validateProjectionIssue(manifest, key, target) {
415
+ try {
416
+ ensureProjectionAllowed(manifest, key, target);
417
+ return void 0;
418
+ } catch (error) {
419
+ return {
420
+ code: target === "public" ? "public.invalid-promotion" : "env.invalid-mapping",
421
+ key,
422
+ message: error instanceof Error ? error.message : String(error)
423
+ };
125
424
  }
126
- return void 0;
127
425
  }
128
426
 
129
- // ../core/src/runtime/dump.ts
130
- var import_promises6 = require("fs/promises");
131
- var import_node_path6 = __toESM(require("path"), 1);
132
-
133
- // ../core/src/utils/flatten.ts
134
- function flattenObject(value, prefix = "") {
135
- return Object.entries(value).reduce((accumulator, [key, nestedValue]) => {
136
- const nextKey = prefix ? `${prefix}.${key}` : key;
137
- if (nestedValue && typeof nestedValue === "object" && !Array.isArray(nestedValue)) {
138
- Object.assign(accumulator, flattenObject(nestedValue, nextKey));
139
- return accumulator;
140
- }
141
- accumulator[nextKey] = nestedValue;
142
- return accumulator;
143
- }, {});
144
- }
427
+ // ../core/src/workspaces/resolveWorkspaceContext.ts
428
+ var import_promises5 = require("fs/promises");
429
+ var import_node_path5 = __toESM(require("path"), 1);
145
430
 
146
431
  // ../core/src/utils/secretStore.ts
147
432
  var import_node_crypto = require("crypto");
148
- var import_promises7 = require("fs/promises");
149
- var import_node_path7 = __toESM(require("path"), 1);
433
+ var import_promises6 = require("fs/promises");
434
+ var import_node_path6 = __toESM(require("path"), 1);
150
435
  function resolveSecretStoreRoot(processEnv = process.env) {
151
- return import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
436
+ return import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
152
437
  }
153
438
  function resolveSecretVaultFile(storeRoot, vault = "default") {
154
- return import_node_path7.default.join(storeRoot, "vaults", `${vault}.json`);
439
+ return import_node_path6.default.join(storeRoot, "vaults", `${vault}.json`);
155
440
  }
156
441
  function resolveSecretStoreFile(storeRoot, ref, vault = "default") {
157
- return import_node_path7.default.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
442
+ return import_node_path6.default.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
158
443
  }
159
444
  function deriveKey(passphrase, salt) {
160
445
  return (0, import_node_crypto.scryptSync)(passphrase, salt, 32);
@@ -163,6 +448,38 @@ function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
163
448
  const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
164
449
  return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
165
450
  }
451
+ function getVaultPassphraseEnvVar(vault = "default") {
452
+ const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
453
+ return vaultToken && vaultToken !== "DEFAULT" ? `CNOS_SECRET_PASSPHRASE_${vaultToken}` : "CNOS_SECRET_PASSPHRASE";
454
+ }
455
+ function isPassphraseEnvRef(value) {
456
+ return typeof value === "string" && value.startsWith("env:") && value.length > 4;
457
+ }
458
+ function resolveConfiguredVaultPassphrase(definition, vault = "default", processEnv = process.env) {
459
+ if (definition?.provider !== "local") {
460
+ return void 0;
461
+ }
462
+ const passphraseRef = definition.passphrase;
463
+ if (typeof passphraseRef === "string" && passphraseRef.startsWith("env:") && passphraseRef.length > 4) {
464
+ return processEnv[passphraseRef.slice(4)];
465
+ }
466
+ if (passphraseRef) {
467
+ return passphraseRef;
468
+ }
469
+ return resolveSecretPassphrase(vault, processEnv);
470
+ }
471
+ function resolveVaultDefinition(vaults, vault = "default") {
472
+ const definition = vaults?.[vault];
473
+ const provider = definition?.provider ?? "local";
474
+ return {
475
+ name: vault,
476
+ provider,
477
+ ...definition?.passphrase ? {
478
+ passphrase: definition.passphrase
479
+ } : {},
480
+ requiresPassphrase: provider === "local"
481
+ };
482
+ }
166
483
  function encryptDocument(value, passphrase) {
167
484
  const salt = (0, import_node_crypto.randomBytes)(16);
168
485
  const iv = (0, import_node_crypto.randomBytes)(12);
@@ -182,21 +499,21 @@ function encryptDocument(value, passphrase) {
182
499
  async function createSecretVault(storeRoot, vault, passphrase) {
183
500
  const normalizedVault = vault.trim() || "default";
184
501
  const filePath = resolveSecretVaultFile(storeRoot, normalizedVault);
185
- await (0, import_promises7.mkdir)(import_node_path7.default.dirname(filePath), { recursive: true });
502
+ await (0, import_promises6.mkdir)(import_node_path6.default.dirname(filePath), { recursive: true });
186
503
  const document = {
187
504
  version: 1,
188
505
  name: normalizedVault,
189
506
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
190
507
  verifier: encryptDocument(`cnos-vault:${normalizedVault}`, passphrase)
191
508
  };
192
- await (0, import_promises7.writeFile)(filePath, JSON.stringify(document, null, 2), "utf8");
509
+ await (0, import_promises6.writeFile)(filePath, JSON.stringify(document, null, 2), "utf8");
193
510
  return filePath;
194
511
  }
195
512
  async function ensureSecretVault(storeRoot, vault, passphrase) {
196
513
  const normalizedVault = vault.trim() || "default";
197
514
  const filePath = resolveSecretVaultFile(storeRoot, normalizedVault);
198
515
  try {
199
- await (0, import_promises7.readFile)(filePath, "utf8");
516
+ await (0, import_promises6.readFile)(filePath, "utf8");
200
517
  return filePath;
201
518
  } catch (error) {
202
519
  if (error.code !== "ENOENT") {
@@ -206,9 +523,9 @@ async function ensureSecretVault(storeRoot, vault, passphrase) {
206
523
  return createSecretVault(storeRoot, normalizedVault, passphrase);
207
524
  }
208
525
  async function listSecretVaults(storeRoot) {
209
- const vaultRoot = import_node_path7.default.join(storeRoot, "vaults");
526
+ const vaultRoot = import_node_path6.default.join(storeRoot, "vaults");
210
527
  try {
211
- const entries = await (0, import_promises7.readdir)(vaultRoot, { withFileTypes: true });
528
+ const entries = await (0, import_promises6.readdir)(vaultRoot, { withFileTypes: true });
212
529
  return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.replace(/\.json$/, "")).sort((left, right) => left.localeCompare(right));
213
530
  } catch {
214
531
  return [];
@@ -217,11 +534,59 @@ async function listSecretVaults(storeRoot) {
217
534
  async function writeLocalSecret(storeRoot, ref, value, passphrase, vault = "default") {
218
535
  await ensureSecretVault(storeRoot, vault, passphrase);
219
536
  const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
220
- await (0, import_promises7.mkdir)(import_node_path7.default.dirname(filePath), { recursive: true });
221
- await (0, import_promises7.writeFile)(filePath, JSON.stringify(encryptDocument(value, passphrase), null, 2), "utf8");
537
+ await (0, import_promises6.mkdir)(import_node_path6.default.dirname(filePath), { recursive: true });
538
+ await (0, import_promises6.writeFile)(filePath, JSON.stringify(encryptDocument(value, passphrase), null, 2), "utf8");
222
539
  return filePath;
223
540
  }
224
541
 
542
+ // ../core/src/runtime/dump.ts
543
+ var import_promises7 = require("fs/promises");
544
+ var import_node_path7 = __toESM(require("path"), 1);
545
+
546
+ // ../core/src/utils/envNaming.ts
547
+ function normalizeMappingConfig(config = {}) {
548
+ return {
549
+ convention: config.convention,
550
+ explicit: config.explicit ?? {}
551
+ };
552
+ }
553
+ function toScreamingSnakeSegment(segment) {
554
+ return segment.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
555
+ }
556
+ function toScreamingSnake(path8) {
557
+ return path8.split(".").map((segment) => toScreamingSnakeSegment(segment)).filter(Boolean).join("_");
558
+ }
559
+ function logicalKeyToEnvVar(key, config = {}) {
560
+ const normalized = normalizeMappingConfig(config);
561
+ const explicitEntry = Object.entries(normalized.explicit).find(([, logicalKey]) => logicalKey === key);
562
+ if (explicitEntry) {
563
+ return explicitEntry[0];
564
+ }
565
+ if (normalized.convention !== "SCREAMING_SNAKE") {
566
+ return void 0;
567
+ }
568
+ if (key.startsWith("value.")) {
569
+ return toScreamingSnake(key.slice("value.".length));
570
+ }
571
+ if (key.startsWith("secret.")) {
572
+ return `SECRET_${toScreamingSnake(key.slice("secret.".length))}`;
573
+ }
574
+ return void 0;
575
+ }
576
+
577
+ // ../core/src/utils/flatten.ts
578
+ function flattenObject(value, prefix = "") {
579
+ return Object.entries(value).reduce((accumulator, [key, nestedValue]) => {
580
+ const nextKey = prefix ? `${prefix}.${key}` : key;
581
+ if (nestedValue && typeof nestedValue === "object" && !Array.isArray(nestedValue)) {
582
+ Object.assign(accumulator, flattenObject(nestedValue, nextKey));
583
+ return accumulator;
584
+ }
585
+ accumulator[nextKey] = nestedValue;
586
+ return accumulator;
587
+ }, {});
588
+ }
589
+
225
590
  // ../core/src/validation/envMapping.ts
226
591
  function fallbackLogicalKeyToEnvVar(key) {
227
592
  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();
@@ -235,7 +600,8 @@ function validateEnvMappingCollisions(manifest, graph) {
235
600
  ]);
236
601
  const collisions = /* @__PURE__ */ new Map();
237
602
  for (const key of candidates) {
238
- if (key.startsWith("meta.")) {
603
+ const definition = getNamespaceDefinition(manifest, key);
604
+ if (definition.kind !== "data") {
239
605
  continue;
240
606
  }
241
607
  const envVar = logicalKeyToEnvVar(key, manifest.envMapping) ?? (key.startsWith("value.") || key.startsWith("secret.") ? fallbackLogicalKeyToEnvVar(key) : void 0);
@@ -254,11 +620,7 @@ function validateEnvMappingCollisions(manifest, graph) {
254
620
 
255
621
  // ../core/src/validation/publicSafety.ts
256
622
  function validatePublicSafety(manifest) {
257
- return manifest.public.promote.filter((key) => !key.startsWith("value.")).map((key) => ({
258
- code: "public.invalid-promotion",
259
- key,
260
- message: `public.promote may only include value.* keys: ${key}`
261
- }));
623
+ return manifest.public.promote.map((key) => validateProjectionIssue(manifest, key, "public")).filter((issue) => Boolean(issue));
262
624
  }
263
625
 
264
626
  // ../core/src/validation/workspaceSafety.ts
@@ -323,16 +685,72 @@ async function validateRuntime(runtime) {
323
685
  results
324
686
  };
325
687
  }
688
+
689
+ // src/runtime/bootstrap.ts
690
+ var CNOS_GRAPH_ENV_VAR = "__CNOS_GRAPH__";
691
+ function serializeRuntimeGraph(graph) {
692
+ const payload = {
693
+ entries: Array.from(graph.entries.values()),
694
+ profile: graph.profile,
695
+ resolvedAt: graph.resolvedAt,
696
+ profileSource: graph.profileSource,
697
+ workspace: graph.workspace
698
+ };
699
+ return JSON.stringify(payload);
700
+ }
701
+ function deserializeRuntimeGraph(source) {
702
+ const payload = JSON.parse(source);
703
+ if (!payload || !Array.isArray(payload.entries) || typeof payload.profile !== "string" || typeof payload.resolvedAt !== "string" || !payload.profileSource || !payload.workspace || typeof payload.workspace.workspaceId !== "string" || !Array.isArray(payload.workspace.workspaceChain) || !Array.isArray(payload.workspace.workspaceRoots)) {
704
+ throw new Error("Invalid CNOS runtime bootstrap payload");
705
+ }
706
+ return {
707
+ entries: new Map(
708
+ payload.entries.map((entry) => [
709
+ entry.key,
710
+ {
711
+ key: entry.key,
712
+ value: entry.value,
713
+ namespace: entry.namespace,
714
+ winner: entry.winner,
715
+ overridden: entry.overridden ?? []
716
+ }
717
+ ])
718
+ ),
719
+ profile: payload.profile,
720
+ resolvedAt: payload.resolvedAt,
721
+ profileSource: payload.profileSource,
722
+ workspace: payload.workspace
723
+ };
724
+ }
725
+ function readRuntimeGraphFromEnv(processEnv = process.env) {
726
+ const serialized = processEnv[CNOS_GRAPH_ENV_VAR];
727
+ if (!serialized) {
728
+ return void 0;
729
+ }
730
+ return deserializeRuntimeGraph(serialized);
731
+ }
326
732
  // Annotate the CommonJS export names for ESM import in node:
327
733
  0 && (module.exports = {
734
+ CNOS_GRAPH_ENV_VAR,
735
+ CnosSecurityError,
328
736
  createSecretVault,
737
+ deserializeRuntimeGraph,
738
+ ensureProjectionAllowed,
329
739
  flattenObject,
740
+ getVaultPassphraseEnvVar,
741
+ isPassphraseEnvRef,
330
742
  listSecretVaults,
743
+ loadManifest,
331
744
  parseYaml,
745
+ readRuntimeGraphFromEnv,
332
746
  resolveConfigDocumentPath,
747
+ resolveConfiguredVaultPassphrase,
748
+ resolveManifestRoot,
333
749
  resolveSecretPassphrase,
334
750
  resolveSecretStoreRoot,
335
751
  resolveSecretVaultFile,
752
+ resolveVaultDefinition,
753
+ serializeRuntimeGraph,
336
754
  stringifyYaml,
337
755
  validateRuntime,
338
756
  writeLocalSecret