@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/index.cjs CHANGED
@@ -33,6 +33,7 @@ __export(index_exports, {
33
33
  createCnos: () => createCnos2,
34
34
  defaultPlugins: () => defaultPlugins,
35
35
  planDump: () => planDump,
36
+ resolveBrowserData: () => resolveBrowserData,
36
37
  toEnv: () => toEnv,
37
38
  toPublicEnv: () => toPublicEnv,
38
39
  writeDump: () => writeDump
@@ -53,6 +54,11 @@ var CnosManifestError = class extends CnosError {
53
54
  }
54
55
  manifestPath;
55
56
  };
57
+ var CnosSecurityError = class extends CnosError {
58
+ constructor(message) {
59
+ super(message);
60
+ }
61
+ };
56
62
  var CnosKeyNotFoundError = class extends CnosError {
57
63
  constructor(key) {
58
64
  super(`Missing required CNOS config key: ${key}`);
@@ -212,6 +218,34 @@ var DEFAULT_FRAMEWORK_PREFIXES = {
212
218
  vite: "VITE_",
213
219
  nuxt: "NUXT_PUBLIC_"
214
220
  };
221
+ var DEFAULT_NAMESPACES = {
222
+ value: {
223
+ kind: "data",
224
+ shareable: true
225
+ },
226
+ secret: {
227
+ kind: "data",
228
+ shareable: false,
229
+ sensitive: true
230
+ },
231
+ meta: {
232
+ kind: "system",
233
+ shareable: false,
234
+ readonly: true
235
+ },
236
+ public: {
237
+ kind: "projection",
238
+ source: "promote",
239
+ shareable: true,
240
+ readonly: true
241
+ },
242
+ env: {
243
+ kind: "projection",
244
+ source: "envMapping",
245
+ shareable: true,
246
+ readonly: true
247
+ }
248
+ };
215
249
  function validateResolveFrom(resolveFrom) {
216
250
  const validValues = ["cli.profile", "env.CNOS_PROFILE", "default"];
217
251
  for (const entry of resolveFrom) {
@@ -232,6 +266,43 @@ function normalizeWorkspaceItems(items) {
232
266
  ])
233
267
  );
234
268
  }
269
+ function normalizeNamespaces(namespaces) {
270
+ const normalized = Object.fromEntries(
271
+ Object.entries(namespaces ?? {}).map(([namespace, definition]) => [
272
+ namespace,
273
+ {
274
+ kind: definition.kind ?? "data",
275
+ shareable: definition.shareable ?? false,
276
+ ...definition.sensitive !== void 0 ? { sensitive: definition.sensitive } : {},
277
+ ...definition.readonly !== void 0 ? { readonly: definition.readonly } : {},
278
+ ...definition.source ? { source: definition.source } : {}
279
+ }
280
+ ])
281
+ );
282
+ return {
283
+ ...DEFAULT_NAMESPACES,
284
+ ...normalized
285
+ };
286
+ }
287
+ function normalizeVaults(vaults) {
288
+ return Object.fromEntries(
289
+ Object.entries(vaults ?? {}).map(([name, definition]) => {
290
+ const provider = definition.provider?.trim();
291
+ if (!provider) {
292
+ throw new CnosManifestError(`Vault "${name}" requires a provider`);
293
+ }
294
+ return [
295
+ name,
296
+ {
297
+ provider,
298
+ ...definition.passphrase?.trim() ? {
299
+ passphrase: definition.passphrase.trim()
300
+ } : {}
301
+ }
302
+ ];
303
+ })
304
+ );
305
+ }
235
306
  function normalizeManifest(manifest) {
236
307
  const version = manifest.version ?? 1;
237
308
  if (version !== 1) {
@@ -316,6 +387,8 @@ function normalizeManifest(manifest) {
316
387
  ...manifest.public?.frameworks ?? {}
317
388
  }
318
389
  },
390
+ namespaces: normalizeNamespaces(manifest.namespaces),
391
+ vaults: normalizeVaults(manifest.vaults),
319
392
  writePolicy: {
320
393
  define: {
321
394
  defaultProfile: manifest.writePolicy?.define?.defaultProfile ?? defaultProfile,
@@ -519,6 +592,86 @@ async function expandProfileChain(activeProfile, options = {}) {
519
592
  };
520
593
  }
521
594
 
595
+ // ../core/src/promotions/validatePromotion.ts
596
+ var DEFAULT_DATA_NAMESPACE = {
597
+ kind: "data",
598
+ shareable: false
599
+ };
600
+ function getNamespaceNameForKey(key) {
601
+ const [namespace] = key.split(".");
602
+ if (!namespace || !key.includes(".")) {
603
+ throw new CnosManifestError(`Logical key must be namespace-qualified: ${key}`);
604
+ }
605
+ return namespace;
606
+ }
607
+ function getNamespaceDefinition(manifest, namespaceOrKey) {
608
+ const namespace = namespaceOrKey.includes(".") ? getNamespaceNameForKey(namespaceOrKey) : namespaceOrKey;
609
+ return manifest.namespaces[namespace] ?? DEFAULT_DATA_NAMESPACE;
610
+ }
611
+ function ensureProjectionAllowed(manifest, key, target) {
612
+ const namespace = getNamespaceNameForKey(key);
613
+ const definition = getNamespaceDefinition(manifest, namespace);
614
+ if (definition.kind !== "data") {
615
+ throw new CnosManifestError(
616
+ `Cannot promote ${key} to ${target} because namespace "${namespace}" is not a data namespace.`
617
+ );
618
+ }
619
+ if (definition.sensitive) {
620
+ throw new CnosSecurityError(
621
+ `Cannot promote ${key} to ${target} because namespace "${namespace}" is sensitive.`
622
+ );
623
+ }
624
+ if (!definition.shareable) {
625
+ throw new CnosSecurityError(
626
+ `Cannot promote ${key} to ${target} because namespace "${namespace}" is not shareable.`
627
+ );
628
+ }
629
+ }
630
+
631
+ // ../core/src/promotions/promoteToPublic.ts
632
+ function toPublicKey(key) {
633
+ const namespace = getNamespaceNameForKey(key);
634
+ return namespace === "value" ? `public.${stripNamespace(key)}` : `public.${key}`;
635
+ }
636
+ function toPromotedConfigEntry(entry, key, promotedFrom) {
637
+ return {
638
+ ...entry,
639
+ key,
640
+ namespace: "public",
641
+ sourceId: "public-promote",
642
+ pluginId: "core",
643
+ metadata: {
644
+ ...entry.metadata ?? {},
645
+ promotedFrom
646
+ }
647
+ };
648
+ }
649
+ function toPromotedResolvedEntry(entry) {
650
+ const key = toPublicKey(entry.key);
651
+ return {
652
+ key,
653
+ value: entry.value,
654
+ namespace: "public",
655
+ winner: toPromotedConfigEntry(entry.winner, key, entry.key),
656
+ overridden: entry.overridden.map((override) => toPromotedConfigEntry(override, key, entry.key))
657
+ };
658
+ }
659
+ function promoteToPublic(graph, manifest) {
660
+ const entries = new Map(graph.entries);
661
+ for (const key of manifest.public.promote) {
662
+ ensureProjectionAllowed(manifest, key, "public");
663
+ const resolved = graph.entries.get(key);
664
+ if (!resolved) {
665
+ continue;
666
+ }
667
+ entries.set(toPublicKey(key), toPromotedResolvedEntry(resolved));
668
+ }
669
+ return {
670
+ ...graph,
671
+ entries
672
+ };
673
+ }
674
+
522
675
  // ../core/src/profiles/resolveActiveProfile.ts
523
676
  function resolveActiveProfile(manifest, options = {}) {
524
677
  for (const source of manifest.profiles.resolveFrom) {
@@ -1003,72 +1156,56 @@ function requireValue(graph, key) {
1003
1156
  return value;
1004
1157
  }
1005
1158
 
1006
- // ../core/src/utils/envNaming.ts
1007
- function normalizeMappingConfig(config = {}) {
1008
- return {
1009
- convention: config.convention,
1010
- explicit: config.explicit ?? {}
1011
- };
1159
+ // ../core/src/utils/secretStore.ts
1160
+ var import_node_crypto = require("crypto");
1161
+ var import_promises6 = require("fs/promises");
1162
+ var import_node_path6 = __toESM(require("path"), 1);
1163
+ function isObject(value) {
1164
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1012
1165
  }
1013
- function toScreamingSnakeSegment(segment) {
1014
- return segment.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1166
+ function isSecretReference(value) {
1167
+ return isObject(value) && typeof value.provider === "string" && value.provider.trim().length > 0 && typeof value.ref === "string" && value.ref.trim().length > 0 && (value.vault === void 0 && true || typeof value.vault === "string" && value.vault.trim().length > 0) && Object.keys(value).every((key) => ["provider", "ref", "vault"].includes(key));
1015
1168
  }
1016
- function toScreamingSnake(path10) {
1017
- return path10.split(".").map((segment) => toScreamingSnakeSegment(segment)).filter(Boolean).join("_");
1169
+ function resolveSecretStoreRoot(processEnv = process.env) {
1170
+ return import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1018
1171
  }
1019
- function fromScreamingSnake(path10) {
1020
- return path10.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
1172
+ function resolveSecretStoreFile(storeRoot, ref, vault = "default") {
1173
+ return import_node_path6.default.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
1021
1174
  }
1022
- function logicalKeyToEnvVar(key, config = {}) {
1023
- const normalized = normalizeMappingConfig(config);
1024
- const explicitEntry = Object.entries(normalized.explicit).find(([, logicalKey]) => logicalKey === key);
1025
- if (explicitEntry) {
1026
- return explicitEntry[0];
1027
- }
1028
- if (normalized.convention !== "SCREAMING_SNAKE") {
1029
- return void 0;
1030
- }
1031
- if (key.startsWith("value.")) {
1032
- return toScreamingSnake(key.slice("value.".length));
1033
- }
1034
- if (key.startsWith("secret.")) {
1035
- return `SECRET_${toScreamingSnake(key.slice("secret.".length))}`;
1036
- }
1037
- return void 0;
1175
+ function deriveKey(passphrase, salt) {
1176
+ return (0, import_node_crypto.scryptSync)(passphrase, salt, 32);
1038
1177
  }
1039
- function envVarToLogicalKey(envVar, config = {}) {
1040
- const normalized = normalizeMappingConfig(config);
1041
- const explicitMatch = normalized.explicit[envVar];
1042
- if (explicitMatch) {
1043
- return explicitMatch;
1044
- }
1045
- if (normalized.convention !== "SCREAMING_SNAKE") {
1046
- return void 0;
1047
- }
1048
- if (envVar.startsWith("SECRET_")) {
1049
- const stripped = envVar.slice("SECRET_".length);
1050
- if (!stripped) {
1051
- return void 0;
1052
- }
1053
- return `secret.${fromScreamingSnake(stripped)}`;
1178
+ function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
1179
+ const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1180
+ return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
1181
+ }
1182
+ function decryptDocument(document, passphrase) {
1183
+ const salt = Buffer.from(document.salt, "base64");
1184
+ const iv = Buffer.from(document.iv, "base64");
1185
+ const tag = Buffer.from(document.tag, "base64");
1186
+ const ciphertext = Buffer.from(document.ciphertext, "base64");
1187
+ const key = deriveKey(passphrase, salt);
1188
+ const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
1189
+ decipher.setAuthTag(tag);
1190
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
1191
+ return plaintext.toString("utf8");
1192
+ }
1193
+ async function readLocalSecret(storeRoot, ref, passphrase, vault = "default") {
1194
+ if (!passphrase) {
1195
+ throw new CnosManifestError(
1196
+ `Missing CNOS secret passphrase for local secret ref "${ref}". Set CNOS_SECRET_PASSPHRASE or pass processEnv explicitly.`
1197
+ );
1054
1198
  }
1055
- if (!/^[A-Z][A-Z0-9_]*$/.test(envVar)) {
1056
- return void 0;
1199
+ const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
1200
+ const source = await (0, import_promises6.readFile)(filePath, "utf8");
1201
+ const document = JSON.parse(source);
1202
+ 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") {
1203
+ throw new CnosManifestError("Invalid local secret document", filePath);
1057
1204
  }
1058
- return `value.${fromScreamingSnake(envVar)}`;
1205
+ return decryptDocument(document, passphrase);
1059
1206
  }
1060
1207
 
1061
1208
  // ../core/src/runtime/toEnv.ts
1062
- function fallbackLogicalKeyToEnvVar(key) {
1063
- if (key.startsWith("value.")) {
1064
- 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();
1065
- }
1066
- if (key.startsWith("secret.")) {
1067
- 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();
1068
- return `SECRET_${normalized}`;
1069
- }
1070
- return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1071
- }
1072
1209
  function normalizeEnvValue(value) {
1073
1210
  if (value === void 0 || value === null) {
1074
1211
  return "";
@@ -1084,25 +1221,32 @@ function normalizeEnvValue(value) {
1084
1221
  function toEnv(graph, manifest, options = {}) {
1085
1222
  const includeSecrets = options.includeSecrets ?? true;
1086
1223
  const output = {};
1087
- const resolvedEntries = Array.from(graph.entries.values()).sort(
1088
- (left, right) => left.key.localeCompare(right.key)
1224
+ const mappedEntries = Object.entries(manifest.envMapping.explicit).sort(
1225
+ ([left], [right]) => left.localeCompare(right)
1089
1226
  );
1090
- for (const entry of resolvedEntries) {
1091
- if (entry.namespace === "meta") {
1227
+ for (const [envVar, logicalKey] of mappedEntries) {
1228
+ const entry = graph.entries.get(logicalKey);
1229
+ if (!entry) {
1230
+ continue;
1231
+ }
1232
+ const namespaceDefinition = getNamespaceDefinition(manifest, entry.namespace);
1233
+ if (namespaceDefinition.kind !== "data" || !namespaceDefinition.shareable || namespaceDefinition.sensitive) {
1234
+ continue;
1235
+ }
1236
+ if (entry.namespace === "secret" && !includeSecrets) {
1092
1237
  continue;
1093
1238
  }
1094
- if (!includeSecrets && entry.namespace === "secret") {
1239
+ if (isSecretReference(entry.value)) {
1095
1240
  continue;
1096
1241
  }
1097
- const envVar = logicalKeyToEnvVar(entry.key, manifest.envMapping) ?? fallbackLogicalKeyToEnvVar(entry.key);
1098
1242
  output[envVar] = normalizeEnvValue(entry.value);
1099
1243
  }
1100
1244
  return output;
1101
1245
  }
1102
1246
 
1103
1247
  // ../core/src/runtime/toPublicEnv.ts
1104
- function fallbackValueEnvVar(key) {
1105
- return key.replace(/^value\./, "").replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1248
+ function fallbackPublicEnvVar(valuePath) {
1249
+ return valuePath.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1106
1250
  }
1107
1251
  function normalizeEnvValue2(value) {
1108
1252
  if (value === void 0 || value === null) {
@@ -1129,22 +1273,12 @@ function resolvePublicPrefix(manifest, options) {
1129
1273
  }
1130
1274
  return configuredPrefix;
1131
1275
  }
1132
- function ensurePublicPromotionKey(key) {
1133
- if (!key.startsWith("value.")) {
1134
- throw new CnosManifestError(`public.promote may only contain value.* keys: ${key}`);
1135
- }
1136
- }
1137
1276
  function toPublicEnv(graph, manifest, options = {}) {
1138
1277
  const prefix = resolvePublicPrefix(manifest, options);
1139
1278
  const output = {};
1140
- const promotions = [...manifest.public.promote].sort((left, right) => left.localeCompare(right));
1141
- for (const key of promotions) {
1142
- ensurePublicPromotionKey(key);
1143
- const resolved = graph.entries.get(key);
1144
- if (!resolved) {
1145
- continue;
1146
- }
1147
- const baseEnvVar = logicalKeyToEnvVar(key, manifest.envMapping) ?? fallbackValueEnvVar(key);
1279
+ const promotions = Array.from(graph.entries.values()).filter((entry) => entry.namespace === "public").sort((left, right) => left.key.localeCompare(right.key));
1280
+ for (const resolved of promotions) {
1281
+ const baseEnvVar = fallbackPublicEnvVar(stripNamespace(resolved.key));
1148
1282
  const envVar = prefix && !baseEnvVar.startsWith(prefix) ? `${prefix}${baseEnvVar}` : baseEnvVar;
1149
1283
  output[envVar] = normalizeEnvValue2(resolved.value);
1150
1284
  }
@@ -1288,6 +1422,9 @@ function appendMetaEntries(graph, cnosVersion) {
1288
1422
  }
1289
1423
  async function createCnos(options = {}) {
1290
1424
  const loadedManifest = await loadManifest(options.root ? { root: options.root } : {});
1425
+ for (const key of loadedManifest.manifest.public.promote) {
1426
+ ensureProjectionAllowed(loadedManifest.manifest, key, "public");
1427
+ }
1291
1428
  const workspaceFile = await loadWorkspaceFile(loadedManifest.repoRoot);
1292
1429
  const workspace = await resolveWorkspaceContext(loadedManifest.manifest, {
1293
1430
  manifestRoot: loadedManifest.manifestRoot,
@@ -1327,10 +1464,11 @@ async function createCnos(options = {}) {
1327
1464
  workspace
1328
1465
  });
1329
1466
  const schemaApplied = applySchemaRules(graph, loadedManifest.manifest.schema);
1467
+ const promotedGraph = promoteToPublic(schemaApplied.graph, loadedManifest.manifest);
1330
1468
  return createRuntime(
1331
1469
  loadedManifest.manifest,
1332
1470
  appendMetaEntries({
1333
- ...schemaApplied.graph,
1471
+ ...promotedGraph,
1334
1472
  profileSource: activeProfile.source
1335
1473
  }, options.cnosVersion),
1336
1474
  plugins
@@ -1338,23 +1476,23 @@ async function createCnos(options = {}) {
1338
1476
  }
1339
1477
 
1340
1478
  // ../core/src/runtime/dump.ts
1341
- var import_promises6 = require("fs/promises");
1342
- var import_node_path6 = __toESM(require("path"), 1);
1479
+ var import_promises7 = require("fs/promises");
1480
+ var import_node_path7 = __toESM(require("path"), 1);
1343
1481
  function buildDumpFiles(graph, options = {}) {
1344
- const basePath = options.flatten ? "" : import_node_path6.default.posix.join("workspaces", graph.workspace.workspaceId);
1482
+ const basePath = options.flatten ? "" : import_node_path7.default.posix.join("workspaces", graph.workspace.workspaceId);
1345
1483
  const values = toNamespaceObject(graph, "value");
1346
1484
  const secrets = toNamespaceObject(graph, "secret");
1347
1485
  const files = [];
1348
1486
  if (Object.keys(values).length > 0) {
1349
1487
  files.push({
1350
- path: import_node_path6.default.posix.join(basePath, "values", graph.profile, "app.yml"),
1488
+ path: import_node_path7.default.posix.join(basePath, "values", graph.profile, "app.yml"),
1351
1489
  namespace: "value",
1352
1490
  content: stringifyYaml(values)
1353
1491
  });
1354
1492
  }
1355
1493
  if (Object.keys(secrets).length > 0) {
1356
1494
  files.push({
1357
- path: import_node_path6.default.posix.join(basePath, "secrets", graph.profile, "app.yml"),
1495
+ path: import_node_path7.default.posix.join(basePath, "secrets", graph.profile, "app.yml"),
1358
1496
  namespace: "secret",
1359
1497
  content: stringifyYaml(secrets)
1360
1498
  });
@@ -1370,12 +1508,12 @@ function planDump(graph, options = {}) {
1370
1508
  };
1371
1509
  }
1372
1510
  async function writeDump(graph, options) {
1373
- const root = import_node_path6.default.resolve(options.to);
1511
+ const root = import_node_path7.default.resolve(options.to);
1374
1512
  const plan = planDump(graph, options);
1375
1513
  for (const file of plan.files) {
1376
- const destination = import_node_path6.default.join(root, file.path);
1377
- await (0, import_promises6.mkdir)(import_node_path6.default.dirname(destination), { recursive: true });
1378
- await (0, import_promises6.writeFile)(destination, file.content, "utf8");
1514
+ const destination = import_node_path7.default.join(root, file.path);
1515
+ await (0, import_promises7.mkdir)(import_node_path7.default.dirname(destination), { recursive: true });
1516
+ await (0, import_promises7.writeFile)(destination, file.content, "utf8");
1379
1517
  }
1380
1518
  return {
1381
1519
  ...plan,
@@ -1383,59 +1521,42 @@ async function writeDump(graph, options) {
1383
1521
  };
1384
1522
  }
1385
1523
 
1386
- // ../core/src/utils/secretStore.ts
1387
- var import_node_crypto = require("crypto");
1388
- var import_promises7 = require("fs/promises");
1389
- var import_node_path7 = __toESM(require("path"), 1);
1390
- function isObject(value) {
1391
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1392
- }
1393
- function isSecretReference(value) {
1394
- return isObject(value) && typeof value.provider === "string" && value.provider.trim().length > 0 && typeof value.ref === "string" && value.ref.trim().length > 0 && (value.vault === void 0 && true || typeof value.vault === "string" && value.vault.trim().length > 0) && Object.keys(value).every((key) => ["provider", "ref", "vault"].includes(key));
1395
- }
1396
- function resolveSecretStoreRoot(processEnv = process.env) {
1397
- return import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
1398
- }
1399
- function resolveSecretStoreFile(storeRoot, ref, vault = "default") {
1400
- return import_node_path7.default.join(storeRoot, "vaults", vault, "store", ...ref.split("/")).concat(".json");
1401
- }
1402
- function deriveKey(passphrase, salt) {
1403
- return (0, import_node_crypto.scryptSync)(passphrase, salt, 32);
1404
- }
1405
- function resolveSecretPassphrase(vault = "default", processEnv = process.env) {
1406
- const vaultToken = vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1407
- return processEnv[`CNOS_SECRET_PASSPHRASE_${vaultToken}`] ?? processEnv.CNOS_SECRET_PASSPHRASE;
1524
+ // ../core/src/utils/envNaming.ts
1525
+ function normalizeMappingConfig(config = {}) {
1526
+ return {
1527
+ convention: config.convention,
1528
+ explicit: config.explicit ?? {}
1529
+ };
1408
1530
  }
1409
- function decryptDocument(document, passphrase) {
1410
- const salt = Buffer.from(document.salt, "base64");
1411
- const iv = Buffer.from(document.iv, "base64");
1412
- const tag = Buffer.from(document.tag, "base64");
1413
- const ciphertext = Buffer.from(document.ciphertext, "base64");
1414
- const key = deriveKey(passphrase, salt);
1415
- const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-gcm", key, iv);
1416
- decipher.setAuthTag(tag);
1417
- const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
1418
- return plaintext.toString("utf8");
1531
+ function fromScreamingSnake(path10) {
1532
+ return path10.split("_").map((segment) => segment.trim().toLowerCase()).filter(Boolean).join(".");
1419
1533
  }
1420
- async function readLocalSecret(storeRoot, ref, passphrase, vault = "default") {
1421
- if (!passphrase) {
1422
- throw new CnosManifestError(
1423
- `Missing CNOS secret passphrase for local secret ref "${ref}". Set CNOS_SECRET_PASSPHRASE or pass processEnv explicitly.`
1424
- );
1534
+ function envVarToLogicalKey(envVar, config = {}) {
1535
+ const normalized = normalizeMappingConfig(config);
1536
+ const explicitMatch = normalized.explicit[envVar];
1537
+ if (explicitMatch) {
1538
+ return explicitMatch;
1425
1539
  }
1426
- const filePath = resolveSecretStoreFile(storeRoot, ref, vault);
1427
- const source = await (0, import_promises7.readFile)(filePath, "utf8");
1428
- const document = JSON.parse(source);
1429
- 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") {
1430
- throw new CnosManifestError("Invalid local secret document", filePath);
1540
+ if (normalized.convention !== "SCREAMING_SNAKE") {
1541
+ return void 0;
1431
1542
  }
1432
- return decryptDocument(document, passphrase);
1543
+ if (envVar.startsWith("SECRET_")) {
1544
+ const stripped = envVar.slice("SECRET_".length);
1545
+ if (!stripped) {
1546
+ return void 0;
1547
+ }
1548
+ return `secret.${fromScreamingSnake(stripped)}`;
1549
+ }
1550
+ if (!/^[A-Z][A-Z0-9_]*$/.test(envVar)) {
1551
+ return void 0;
1552
+ }
1553
+ return `value.${fromScreamingSnake(envVar)}`;
1433
1554
  }
1434
1555
 
1435
1556
  // package.json
1436
1557
  var package_default = {
1437
1558
  name: "@kitsy/cnos",
1438
- version: "1.1.1",
1559
+ version: "1.2.0",
1439
1560
  description: "Batteries-included CNOS runtime package wired with the official plugins.",
1440
1561
  type: "module",
1441
1562
  main: "./dist/index.cjs",
@@ -1452,6 +1573,21 @@ var package_default = {
1452
1573
  import: "./dist/internal.js",
1453
1574
  require: "./dist/internal.cjs"
1454
1575
  },
1576
+ "./runtime": {
1577
+ types: "./dist/runtime/index.d.ts",
1578
+ import: "./dist/runtime/index.js",
1579
+ require: "./dist/runtime/index.cjs"
1580
+ },
1581
+ "./browser": {
1582
+ types: "./dist/browser/index.d.ts",
1583
+ import: "./dist/browser/index.js",
1584
+ require: "./dist/browser/index.cjs"
1585
+ },
1586
+ "./build": {
1587
+ types: "./dist/build/index.d.ts",
1588
+ import: "./dist/build/index.js",
1589
+ require: "./dist/build/index.cjs"
1590
+ },
1455
1591
  "./plugins/filesystem": {
1456
1592
  types: "./dist/plugin/filesystem.d.ts",
1457
1593
  import: "./dist/plugin/filesystem.js",
@@ -1841,7 +1977,7 @@ async function resolveSecretValue(value, processEnv) {
1841
1977
  value.vault
1842
1978
  );
1843
1979
  }
1844
- if (value.provider === "env") {
1980
+ if (value.provider === "env" || value.provider === "github-secrets") {
1845
1981
  const resolved = processEnv?.[value.ref];
1846
1982
  if (resolved === void 0) {
1847
1983
  return value;
@@ -1977,19 +2113,44 @@ function defaultPlugins() {
1977
2113
  ];
1978
2114
  }
1979
2115
 
2116
+ // src/runtime/state.ts
2117
+ var singletonRuntime;
2118
+ var singletonReady;
2119
+ function setSingletonRuntime(runtime) {
2120
+ singletonRuntime = runtime;
2121
+ singletonReady = Promise.resolve(runtime);
2122
+ return runtime;
2123
+ }
2124
+
1980
2125
  // src/createCnos.ts
1981
2126
  async function createCnos2(options = {}) {
1982
- return createCnos({
2127
+ const runtime = await createCnos({
1983
2128
  ...options,
1984
2129
  cnosVersion: package_default.version,
1985
2130
  plugins: [...defaultPlugins(), ...options.plugins ?? []]
1986
2131
  });
2132
+ setSingletonRuntime(runtime);
2133
+ return runtime;
2134
+ }
2135
+
2136
+ // src/build/index.ts
2137
+ async function resolveBrowserData(options = {}) {
2138
+ const runtime = await createCnos2(options);
2139
+ const browserData = {};
2140
+ for (const [key, entry] of runtime.graph.entries) {
2141
+ if (!key.startsWith("public.")) {
2142
+ continue;
2143
+ }
2144
+ browserData[key] = entry.value;
2145
+ }
2146
+ return browserData;
1987
2147
  }
1988
2148
  // Annotate the CommonJS export names for ESM import in node:
1989
2149
  0 && (module.exports = {
1990
2150
  createCnos,
1991
2151
  defaultPlugins,
1992
2152
  planDump,
2153
+ resolveBrowserData,
1993
2154
  toEnv,
1994
2155
  toPublicEnv,
1995
2156
  writeDump
package/dist/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
- import { R as ResolvedGraph, D as DumpPlanOptions, a as DumpPlan, b as DumpOptions, c as DumpResult, C as CnosCreateOptions, d as CnosRuntime, e as CnosPlugin } from './plugin-BVNEHj19.cjs';
2
- export { f as ConfigEntry, I as InspectResult, L as LoaderPlugin, g as LogicalKey, M as ManifestFile, N as NormalizedManifest, T as ToEnvOptions, h as ToPublicEnvOptions } from './plugin-BVNEHj19.cjs';
3
- export { t as toEnv, a as toPublicEnv } from './toPublicEnv-Dd152fFy.cjs';
1
+ import { R as ResolvedGraph, D as DumpPlanOptions, a as DumpPlan, b as DumpOptions, c as DumpResult, C as CnosCreateOptions, d as CnosRuntime, e as CnosPlugin } from './plugin-DkOIT5uI.cjs';
2
+ export { f as ConfigEntry, I as InspectResult, L as LoaderPlugin, g as LogicalKey, M as ManifestFile, N as NormalizedManifest, T as ToEnvOptions, h as ToPublicEnvOptions } from './plugin-DkOIT5uI.cjs';
3
+ export { t as toEnv, a as toPublicEnv } from './toPublicEnv-DvFeV3qG.cjs';
4
+ export { resolveBrowserData } from './build/index.cjs';
4
5
 
5
6
  declare function planDump(graph: ResolvedGraph, options?: DumpPlanOptions): DumpPlan;
6
7
  declare function writeDump(graph: ResolvedGraph, options: DumpOptions): Promise<DumpResult>;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { R as ResolvedGraph, D as DumpPlanOptions, a as DumpPlan, b as DumpOptions, c as DumpResult, C as CnosCreateOptions, d as CnosRuntime, e as CnosPlugin } from './plugin-BVNEHj19.js';
2
- export { f as ConfigEntry, I as InspectResult, L as LoaderPlugin, g as LogicalKey, M as ManifestFile, N as NormalizedManifest, T as ToEnvOptions, h as ToPublicEnvOptions } from './plugin-BVNEHj19.js';
3
- export { t as toEnv, a as toPublicEnv } from './toPublicEnv-Gwz3xTK0.js';
1
+ import { R as ResolvedGraph, D as DumpPlanOptions, a as DumpPlan, b as DumpOptions, c as DumpResult, C as CnosCreateOptions, d as CnosRuntime, e as CnosPlugin } from './plugin-DkOIT5uI.js';
2
+ export { f as ConfigEntry, I as InspectResult, L as LoaderPlugin, g as LogicalKey, M as ManifestFile, N as NormalizedManifest, T as ToEnvOptions, h as ToPublicEnvOptions } from './plugin-DkOIT5uI.js';
3
+ export { t as toEnv, a as toPublicEnv } from './toPublicEnv-C9clvXLo.js';
4
+ export { resolveBrowserData } from './build/index.js';
4
5
 
5
6
  declare function planDump(graph: ResolvedGraph, options?: DumpPlanOptions): DumpPlan;
6
7
  declare function writeDump(graph: ResolvedGraph, options: DumpOptions): Promise<DumpResult>;