@kitsy/cnos 1.2.0 → 1.4.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 (65) hide show
  1. package/README.md +3 -3
  2. package/dist/build/index.cjs +1003 -121
  3. package/dist/build/index.d.cts +1 -1
  4. package/dist/build/index.d.ts +1 -1
  5. package/dist/build/index.js +22 -10
  6. package/dist/{chunk-APCTXRUN.js → chunk-APIU4GTB.js} +1012 -195
  7. package/dist/chunk-EQSKV3DP.js +105 -0
  8. package/dist/{chunk-MLQGYCO7.js → chunk-FWJC4Y2D.js} +1 -1
  9. package/dist/{chunk-RD5WMHPM.js → chunk-HMM76UYZ.js} +1 -1
  10. package/dist/{chunk-EIN55XXA.js → chunk-J4K4JUJL.js} +1 -1
  11. package/dist/{chunk-SO5XREEU.js → chunk-JSBVYK2T.js} +32 -11
  12. package/dist/chunk-LJD4SM32.js +189 -0
  13. package/dist/{chunk-SXTMTACL.js → chunk-T6Y57KTT.js} +20 -31
  14. package/dist/chunk-WCHX2QFY.js +115 -0
  15. package/dist/{chunk-ZA74BO47.js → chunk-ZTPSFXWP.js} +1 -1
  16. package/dist/configure/index.cjs +3021 -0
  17. package/dist/configure/index.d.cts +12 -0
  18. package/dist/configure/index.d.ts +12 -0
  19. package/dist/configure/index.js +24 -0
  20. package/dist/{envNaming-CcsqAel3.d.ts → envNaming-Dvm_LP2D.d.ts} +1 -1
  21. package/dist/{envNaming-BTJpH93W.d.cts → envNaming-S4B-dHUx.d.cts} +1 -1
  22. package/dist/index.cjs +1243 -186
  23. package/dist/index.d.cts +2 -13
  24. package/dist/index.d.ts +2 -13
  25. package/dist/index.js +13 -25
  26. package/dist/internal.cjs +1525 -81
  27. package/dist/internal.d.cts +171 -14
  28. package/dist/internal.d.ts +171 -14
  29. package/dist/internal.js +652 -5
  30. package/dist/plugin/basic-schema.cjs +29 -2
  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 +29 -2
  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 +36 -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 +31 -2
  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 +65 -91
  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 +105 -11
  51. package/dist/plugin/process-env.d.cts +4 -3
  52. package/dist/plugin/process-env.d.ts +4 -3
  53. package/dist/plugin/process-env.js +6 -4
  54. package/dist/{plugin-DkOIT5uI.d.cts → plugin-B4xwySxw.d.cts} +15 -2
  55. package/dist/{plugin-DkOIT5uI.d.ts → plugin-B4xwySxw.d.ts} +15 -2
  56. package/dist/runtime/index.cjs +1057 -136
  57. package/dist/runtime/index.d.cts +1 -1
  58. package/dist/runtime/index.d.ts +1 -1
  59. package/dist/runtime/index.js +11 -186
  60. package/dist/{toPublicEnv-C9clvXLo.d.ts → toPublicEnv-CvhGAfsB.d.ts} +1 -1
  61. package/dist/{toPublicEnv-DvFeV3qG.d.cts → toPublicEnv-ggmphZFs.d.cts} +1 -1
  62. package/package.json +11 -1
  63. package/dist/chunk-JUHPBAEH.js +0 -20
  64. package/dist/chunk-PQ4KSV76.js +0 -50
  65. package/dist/chunk-WHUGFPE4.js +0 -49
package/dist/internal.js CHANGED
@@ -1,52 +1,699 @@
1
1
  import {
2
2
  CNOS_GRAPH_ENV_VAR,
3
+ CNOS_SECRET_PAYLOAD_ENV_VAR,
4
+ CNOS_SESSION_KEY_ENV_VAR,
3
5
  deserializeRuntimeGraph,
6
+ graphRequiresSecretHydration,
4
7
  readRuntimeGraphFromEnv,
5
- serializeRuntimeGraph
6
- } from "./chunk-PQ4KSV76.js";
8
+ serializeRuntimeGraph,
9
+ serializeSecretPayload
10
+ } from "./chunk-EQSKV3DP.js";
7
11
  import {
12
+ CnosAuthenticationError,
8
13
  CnosSecurityError,
14
+ clearAllVaultSessionKeys,
15
+ clearVaultSessionKey,
9
16
  createSecretVault,
17
+ createSecretVaultProvider,
18
+ deleteLocalSecret,
19
+ deriveVaultKey,
20
+ detectLegacyVaultFormat,
10
21
  ensureProjectionAllowed,
11
22
  flattenObject,
23
+ getNamespaceDefinition,
12
24
  getVaultPassphraseEnvVar,
25
+ getVaultSessionKeyEnvVar,
13
26
  isPassphraseEnvRef,
27
+ isSecretReference,
28
+ listLocalSecrets,
14
29
  listSecretVaults,
15
30
  loadManifest,
16
31
  parseYaml,
32
+ readKeychain,
33
+ readLocalSecret,
34
+ readVaultMetadata,
35
+ removeLocalVaultFiles,
17
36
  resolveConfigDocumentPath,
18
37
  resolveConfiguredVaultPassphrase,
19
38
  resolveManifestRoot,
20
39
  resolveSecretPassphrase,
21
40
  resolveSecretStoreRoot,
22
41
  resolveSecretVaultFile,
42
+ resolveVaultAccessKey,
43
+ resolveVaultAuth,
23
44
  resolveVaultDefinition,
24
45
  stringifyYaml,
25
46
  validateRuntime,
26
- writeLocalSecret
27
- } from "./chunk-APCTXRUN.js";
47
+ writeKeychain,
48
+ writeLocalSecret,
49
+ writeVaultSessionKey
50
+ } from "./chunk-APIU4GTB.js";
51
+
52
+ // src/codegen/generateTypes.ts
53
+ function toPascalCase(value) {
54
+ return value.split(/[^A-Za-z0-9]+/).filter((segment) => segment.length > 0).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join("");
55
+ }
56
+ function mapSchemaType(rule) {
57
+ switch (rule?.type) {
58
+ case "number":
59
+ return "number";
60
+ case "string":
61
+ return "string";
62
+ case "boolean":
63
+ return "boolean";
64
+ case "object":
65
+ return "Record<string, unknown>";
66
+ case "array":
67
+ return "unknown[]";
68
+ default:
69
+ return "unknown";
70
+ }
71
+ }
72
+ function isOptional(rule) {
73
+ return !(rule?.required ?? false) && rule?.default === void 0;
74
+ }
75
+ function buildNamespaceInterfaces(schema) {
76
+ const namespaceGroups = /* @__PURE__ */ new Map();
77
+ for (const [logicalKey, rule] of Object.entries(schema).sort(([left], [right]) => left.localeCompare(right))) {
78
+ const separatorIndex = logicalKey.indexOf(".");
79
+ if (separatorIndex <= 0 || separatorIndex >= logicalKey.length - 1) {
80
+ continue;
81
+ }
82
+ const namespace = logicalKey.slice(0, separatorIndex);
83
+ const path4 = logicalKey.slice(separatorIndex + 1);
84
+ const existing = namespaceGroups.get(namespace) ?? [];
85
+ existing.push({
86
+ key: path4,
87
+ rule
88
+ });
89
+ namespaceGroups.set(namespace, existing);
90
+ }
91
+ const orderedNamespaces = Array.from(namespaceGroups.keys()).sort((left, right) => {
92
+ if (left === "value") {
93
+ return -1;
94
+ }
95
+ if (right === "value") {
96
+ return 1;
97
+ }
98
+ if (left === "secret") {
99
+ return -1;
100
+ }
101
+ if (right === "secret") {
102
+ return 1;
103
+ }
104
+ return left.localeCompare(right);
105
+ });
106
+ if (orderedNamespaces.length === 0) {
107
+ return [
108
+ "export interface CnosValueConfig {}",
109
+ "export interface CnosSecretConfig {}",
110
+ "",
111
+ "export interface CnosConfig {",
112
+ " value: CnosValueConfig;",
113
+ " secret: CnosSecretConfig;",
114
+ "}"
115
+ ];
116
+ }
117
+ const blocks = [];
118
+ for (const namespace of orderedNamespaces) {
119
+ const entries = namespaceGroups.get(namespace) ?? [];
120
+ const interfaceName = namespace === "value" ? "CnosValueConfig" : namespace === "secret" ? "CnosSecretConfig" : `Cnos${toPascalCase(namespace)}Config`;
121
+ blocks.push(`export interface ${interfaceName} {`);
122
+ for (const entry of entries) {
123
+ const optional = isOptional(entry.rule) ? "?" : "";
124
+ blocks.push(` "${entry.key}"${optional}: ${mapSchemaType(entry.rule)};`);
125
+ }
126
+ blocks.push("}");
127
+ blocks.push("");
128
+ }
129
+ const configEntries = orderedNamespaces.map((namespace) => {
130
+ const interfaceName = namespace === "value" ? "CnosValueConfig" : namespace === "secret" ? "CnosSecretConfig" : `Cnos${toPascalCase(namespace)}Config`;
131
+ return ` ${namespace}: ${interfaceName};`;
132
+ });
133
+ blocks.push("export interface CnosConfig {");
134
+ blocks.push(...configEntries);
135
+ blocks.push("}");
136
+ if (!orderedNamespaces.includes("value")) {
137
+ blocks.push("");
138
+ blocks.push("export interface CnosValueConfig {}");
139
+ }
140
+ if (!orderedNamespaces.includes("secret")) {
141
+ blocks.push("");
142
+ blocks.push("export interface CnosSecretConfig {}");
143
+ }
144
+ return blocks;
145
+ }
146
+ function generateCodegenContent(manifest, sourcePath, typeModuleImport = "./cnos") {
147
+ const schema = manifest.schema ?? {};
148
+ const hasSchema = Object.keys(schema).length > 0;
149
+ const interfaceBlocks = buildNamespaceInterfaces(schema);
150
+ const typesLines = [
151
+ "// Auto-generated by cnos codegen. Do not edit.",
152
+ `// Source: ${sourcePath}`,
153
+ "",
154
+ ...interfaceBlocks,
155
+ "",
156
+ 'import type { CnosRuntime } from "@kitsy/cnos";',
157
+ "",
158
+ "export interface TypedCnosRuntime extends CnosRuntime {",
159
+ " value<K extends keyof CnosValueConfig>(path: K): CnosValueConfig[K] | undefined;",
160
+ " secret<K extends keyof CnosSecretConfig>(path: K): CnosSecretConfig[K] | undefined;",
161
+ " require(key: `value.${keyof CnosValueConfig}`): CnosValueConfig[keyof CnosValueConfig];",
162
+ " require(key: `secret.${keyof CnosSecretConfig}`): CnosSecretConfig[keyof CnosSecretConfig];",
163
+ "}",
164
+ ""
165
+ ];
166
+ if (!hasSchema) {
167
+ typesLines.push("// Hint: add a schema section to .cnos/cnos.yml for typed key generation.");
168
+ typesLines.push("");
169
+ }
170
+ const runtimeLines = [
171
+ "// Auto-generated by cnos codegen. Do not edit.",
172
+ `// Source: ${sourcePath}`,
173
+ "",
174
+ 'import { createCnos as _createCnos, type CnosCreateOptions } from "@kitsy/cnos/configure";',
175
+ `import type { TypedCnosRuntime } from "${typeModuleImport}";`,
176
+ "",
177
+ "export async function createCnos(options: CnosCreateOptions = {}): Promise<TypedCnosRuntime> {",
178
+ " return (await _createCnos(options)) as TypedCnosRuntime;",
179
+ "}",
180
+ ""
181
+ ];
182
+ return {
183
+ typesContent: typesLines.join("\n"),
184
+ runtimeContent: runtimeLines.join("\n"),
185
+ schemaEntryCount: Object.keys(schema).length,
186
+ hasSchema
187
+ };
188
+ }
189
+
190
+ // src/codegen/writeOutput.ts
191
+ import { mkdir, writeFile } from "fs/promises";
192
+ import path from "path";
193
+ function stripTsExtension(filePath) {
194
+ return filePath.replace(/(\.d)?\.[cm]?tsx?$/i, "").replace(/\.[cm]?jsx?$/i, "");
195
+ }
196
+ function resolveCodegenPaths(repoRoot, out) {
197
+ const typesPath = out ? path.resolve(repoRoot, out) : path.join(repoRoot, ".cnos", "types", "cnos.d.ts");
198
+ const runtimePath = path.join(path.dirname(typesPath), "runtime.ts");
199
+ const typeImportPath = `./${path.basename(stripTsExtension(typesPath))}`;
200
+ return {
201
+ typesPath,
202
+ runtimePath,
203
+ typeImportPath
204
+ };
205
+ }
206
+ async function writeCodegenOutput(options = {}) {
207
+ const loadedManifest = await loadManifest(options.root ? { root: options.root } : {});
208
+ const paths = resolveCodegenPaths(loadedManifest.repoRoot, options.out);
209
+ const generated = generateCodegenContent(loadedManifest.manifest, loadedManifest.manifestPath, paths.typeImportPath);
210
+ await mkdir(path.dirname(paths.typesPath), { recursive: true });
211
+ await mkdir(path.dirname(paths.runtimePath), { recursive: true });
212
+ await writeFile(paths.typesPath, generated.typesContent, "utf8");
213
+ await writeFile(paths.runtimePath, generated.runtimeContent, "utf8");
214
+ return {
215
+ manifestPath: loadedManifest.manifestPath,
216
+ typesPath: paths.typesPath,
217
+ runtimePath: paths.runtimePath,
218
+ schemaEntryCount: generated.schemaEntryCount,
219
+ hasSchema: generated.hasSchema
220
+ };
221
+ }
222
+
223
+ // src/codegen/watchSchema.ts
224
+ import { watch } from "fs";
225
+ async function watchSchema(options = {}) {
226
+ const loadedManifest = await loadManifest(options.root ? { root: options.root } : {});
227
+ let timeout;
228
+ const debounceMs = options.debounceMs ?? 300;
229
+ const runWrite = async () => {
230
+ try {
231
+ const result = await writeCodegenOutput(options);
232
+ await options.onWrite?.(result);
233
+ } catch (error) {
234
+ await options.onError?.(error);
235
+ }
236
+ };
237
+ await runWrite();
238
+ const watcher = watch(loadedManifest.manifestPath, () => {
239
+ if (timeout) {
240
+ clearTimeout(timeout);
241
+ }
242
+ timeout = setTimeout(() => {
243
+ void runWrite();
244
+ }, debounceMs);
245
+ });
246
+ watcher.on("close", () => {
247
+ if (timeout) {
248
+ clearTimeout(timeout);
249
+ }
250
+ });
251
+ return watcher;
252
+ }
253
+
254
+ // src/drift/compareSchemaToGraph.ts
255
+ function describeValueType(value) {
256
+ if (Array.isArray(value)) {
257
+ return "array";
258
+ }
259
+ if (value === null) {
260
+ return "null";
261
+ }
262
+ return typeof value;
263
+ }
264
+ function matchesType(value, type) {
265
+ if (!type) {
266
+ return true;
267
+ }
268
+ switch (type) {
269
+ case "array":
270
+ return Array.isArray(value);
271
+ case "object":
272
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
273
+ default:
274
+ return typeof value === type;
275
+ }
276
+ }
277
+ function isSchemaDefault(entry) {
278
+ return entry.winner.metadata?.schemaDefault === true;
279
+ }
280
+ function shouldTrackKey(key) {
281
+ return key.startsWith("value.") || key.startsWith("secret.");
282
+ }
283
+ function isTransientRuntimeSource(entry) {
284
+ return entry.winner.sourceId === "process-env" || entry.winner.sourceId === "cli-args";
285
+ }
286
+ function compareSchemaToGraph(runtime) {
287
+ const schema = runtime.manifest.schema;
288
+ const missing = [];
289
+ const mismatches = [];
290
+ const defaultsApplied = [];
291
+ for (const [key, rule] of Object.entries(schema).sort(([left], [right]) => left.localeCompare(right))) {
292
+ const entry = runtime.graph.entries.get(key);
293
+ if (!entry) {
294
+ if (rule.required && rule.default === void 0) {
295
+ missing.push({
296
+ key,
297
+ ...rule.type ? {
298
+ expectedType: rule.type
299
+ } : {}
300
+ });
301
+ }
302
+ continue;
303
+ }
304
+ if (isSchemaDefault(entry)) {
305
+ defaultsApplied.push({
306
+ key,
307
+ value: entry.value
308
+ });
309
+ }
310
+ const actualValue = entry.winner.value;
311
+ if (!matchesType(actualValue, rule.type)) {
312
+ mismatches.push({
313
+ key,
314
+ ...rule.type ? {
315
+ expectedType: rule.type
316
+ } : {},
317
+ actualType: describeValueType(actualValue),
318
+ value: actualValue,
319
+ ...entry.winner.origin?.file ? {
320
+ sourceFile: entry.winner.origin.file
321
+ } : {}
322
+ });
323
+ }
324
+ }
325
+ const undeclared = Array.from(runtime.graph.entries.values()).filter(
326
+ (entry) => shouldTrackKey(entry.key) && !schema[entry.key] && !isSchemaDefault(entry) && !isTransientRuntimeSource(entry)
327
+ ).map((entry) => {
328
+ const issue = {
329
+ key: entry.key,
330
+ value: entry.winner.value,
331
+ actualType: describeValueType(entry.winner.value)
332
+ };
333
+ if (entry.winner.origin?.file) {
334
+ issue.sourceFile = entry.winner.origin.file;
335
+ }
336
+ return issue;
337
+ }).sort((left, right) => left.key.localeCompare(right.key));
338
+ return {
339
+ profile: runtime.graph.profile,
340
+ workspace: runtime.graph.workspace.workspaceId,
341
+ missing,
342
+ undeclared,
343
+ mismatches,
344
+ defaultsApplied
345
+ };
346
+ }
347
+
348
+ // src/drift/formatDriftReport.ts
349
+ function formatIssueList(title, marker, issues, formatter) {
350
+ if (issues.length === 0) {
351
+ return [];
352
+ }
353
+ return [
354
+ `${title}:`,
355
+ ...issues.map((issue) => ` ${marker} ${formatter(issue)}`),
356
+ ""
357
+ ];
358
+ }
359
+ function formatDriftReport(report) {
360
+ const lines = [
361
+ `Schema vs resolved config (${report.workspace} / ${report.profile}):`,
362
+ ""
363
+ ];
364
+ lines.push(
365
+ ...formatIssueList("Missing (required, not defined)", "x", report.missing, (issue) => issue.key)
366
+ );
367
+ lines.push(
368
+ ...formatIssueList(
369
+ "Undeclared (defined, not in schema)",
370
+ "?",
371
+ report.undeclared,
372
+ (issue) => issue.sourceFile ? `${issue.key} (found in ${issue.sourceFile})` : issue.key
373
+ )
374
+ );
375
+ lines.push(
376
+ ...formatIssueList("Type mismatches", "x", report.mismatches, (issue) => {
377
+ const actual = issue.value === void 0 ? issue.actualType : `${issue.actualType} ${JSON.stringify(issue.value)}`;
378
+ return `${issue.key} (schema: ${issue.expectedType}, actual: ${actual})`;
379
+ })
380
+ );
381
+ lines.push(
382
+ ...formatIssueList(
383
+ "Defaults applied",
384
+ "i",
385
+ report.defaultsApplied,
386
+ (issue) => `${issue.key} (using default: ${JSON.stringify(issue.value)})`
387
+ )
388
+ );
389
+ if (lines[lines.length - 1] === "") {
390
+ lines.pop();
391
+ }
392
+ if (lines.length === 2) {
393
+ lines.push("No drift detected.");
394
+ }
395
+ return lines.join("\n");
396
+ }
397
+
398
+ // src/migrate/applyManifest.ts
399
+ import { writeFile as writeFile2 } from "fs/promises";
400
+ function sortRecord(record) {
401
+ return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
402
+ }
403
+ async function applyManifestMappings(proposals, root) {
404
+ const loadedManifest = await loadManifest(root ? { root } : {});
405
+ const rawManifest = {
406
+ ...loadedManifest.rawManifest
407
+ };
408
+ const explicit = {
409
+ ...rawManifest.envMapping?.explicit ?? {}
410
+ };
411
+ const promoted = new Set(rawManifest.public?.promote ?? []);
412
+ let appliedMappings = 0;
413
+ let appliedPromotions = 0;
414
+ for (const proposal of proposals) {
415
+ if (explicit[proposal.envVar] !== proposal.logicalKey) {
416
+ explicit[proposal.envVar] = proposal.logicalKey;
417
+ appliedMappings += 1;
418
+ }
419
+ if (proposal.public && !promoted.has(proposal.logicalKey)) {
420
+ promoted.add(proposal.logicalKey);
421
+ appliedPromotions += 1;
422
+ }
423
+ }
424
+ rawManifest.envMapping = {
425
+ ...rawManifest.envMapping ?? {},
426
+ explicit: sortRecord(explicit)
427
+ };
428
+ if (promoted.size > 0) {
429
+ rawManifest.public = {
430
+ ...rawManifest.public ?? {},
431
+ promote: Array.from(promoted).sort((left, right) => left.localeCompare(right))
432
+ };
433
+ }
434
+ await writeFile2(loadedManifest.manifestPath, stringifyYaml(rawManifest), "utf8");
435
+ return {
436
+ manifestPath: loadedManifest.manifestPath,
437
+ appliedMappings,
438
+ appliedPromotions
439
+ };
440
+ }
441
+
442
+ // src/migrate/proposeMapping.ts
443
+ var SECRET_TOKENS = ["PASSWORD", "SECRET", "KEY", "TOKEN"];
444
+ function normalizeSegments(value) {
445
+ return value.split("_").map((segment) => segment.trim().toLowerCase()).filter((segment) => segment.length > 0);
446
+ }
447
+ function isSecretEnvVar(value) {
448
+ return SECRET_TOKENS.some((token) => value.includes(`_${token}`) || value.endsWith(token));
449
+ }
450
+ function proposeMapping(envVar) {
451
+ const framework = envVar.startsWith("VITE_") ? "vite" : envVar.startsWith("NEXT_PUBLIC_") ? "next" : void 0;
452
+ const strippedEnvVar = framework === "vite" ? envVar.slice("VITE_".length) : framework === "next" ? envVar.slice("NEXT_PUBLIC_".length) : envVar;
453
+ const namespace = isSecretEnvVar(strippedEnvVar) && !framework ? "secret" : "value";
454
+ const segments = normalizeSegments(strippedEnvVar);
455
+ const logicalPath = segments.join(".");
456
+ return {
457
+ envVar,
458
+ namespace,
459
+ logicalPath,
460
+ logicalKey: `${namespace}.${logicalPath}`,
461
+ public: Boolean(framework),
462
+ ...framework ? {
463
+ framework
464
+ } : {}
465
+ };
466
+ }
467
+
468
+ // src/migrate/rewriteSource.ts
469
+ import { copyFile, readFile, writeFile as writeFile3 } from "fs/promises";
470
+ function importStatementFor(kind) {
471
+ return kind === "import-meta-env" ? "import cnos from '@kitsy/cnos/browser';" : "import cnos from '@kitsy/cnos';";
472
+ }
473
+ function replacementFor(proposal) {
474
+ if (proposal.public) {
475
+ return `cnos.read(${JSON.stringify(`public.${proposal.logicalPath}`)})`;
476
+ }
477
+ return proposal.namespace === "secret" ? `cnos.secret(${JSON.stringify(proposal.logicalPath)})` : `cnos.value(${JSON.stringify(proposal.logicalPath)})`;
478
+ }
479
+ async function rewriteSourceFiles(usages, proposals) {
480
+ const fileGroups = /* @__PURE__ */ new Map();
481
+ for (const usage of usages) {
482
+ const existing = fileGroups.get(usage.filePath) ?? [];
483
+ existing.push(usage);
484
+ fileGroups.set(usage.filePath, existing);
485
+ }
486
+ const rewrittenFiles = [];
487
+ const backupFiles = [];
488
+ const skippedUsages = [];
489
+ for (const [filePath, fileUsages] of fileGroups.entries()) {
490
+ const original = await readFile(filePath, "utf8");
491
+ let nextSource = original;
492
+ let changed = false;
493
+ const importKinds = /* @__PURE__ */ new Set();
494
+ for (const usage of fileUsages) {
495
+ const proposal = proposals.get(usage.envVar);
496
+ if (!proposal) {
497
+ skippedUsages.push(`${filePath}:${usage.source}`);
498
+ continue;
499
+ }
500
+ if (usage.kind === "import-meta-env" && proposal.namespace === "secret") {
501
+ skippedUsages.push(`${filePath}:${usage.source}`);
502
+ continue;
503
+ }
504
+ const replacement = replacementFor(proposal);
505
+ if (!nextSource.includes(usage.source)) {
506
+ skippedUsages.push(`${filePath}:${usage.source}`);
507
+ continue;
508
+ }
509
+ nextSource = nextSource.split(usage.source).join(replacement);
510
+ changed = true;
511
+ importKinds.add(usage.kind);
512
+ }
513
+ if (!changed) {
514
+ continue;
515
+ }
516
+ const backupPath = `${filePath}.bak`;
517
+ await copyFile(filePath, backupPath);
518
+ backupFiles.push(backupPath);
519
+ for (const kind of Array.from(importKinds)) {
520
+ const importStatement = importStatementFor(kind);
521
+ if (!nextSource.includes(importStatement)) {
522
+ nextSource = `${importStatement}
523
+ ${nextSource}`;
524
+ }
525
+ }
526
+ await writeFile3(filePath, nextSource, "utf8");
527
+ rewrittenFiles.push(filePath);
528
+ }
529
+ return {
530
+ rewrittenFiles: rewrittenFiles.sort((left, right) => left.localeCompare(right)),
531
+ backupFiles: backupFiles.sort((left, right) => left.localeCompare(right)),
532
+ skippedUsages: skippedUsages.sort((left, right) => left.localeCompare(right))
533
+ };
534
+ }
535
+
536
+ // src/migrate/scanEnvUsage.ts
537
+ import { readdir, readFile as readFile2 } from "fs/promises";
538
+ import path2 from "path";
539
+ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"]);
540
+ var PROCESS_ENV_DOT = /process\.env\.([A-Z][A-Z0-9_]*)/g;
541
+ var PROCESS_ENV_BRACKET = /process\.env\[['"]([A-Z][A-Z0-9_]*)['"]\]/g;
542
+ var IMPORT_META_ENV_DOT = /import\.meta\.env\.([A-Z][A-Z0-9_]*)/g;
543
+ var IMPORT_META_ENV_BRACKET = /import\.meta\.env\[['"]([A-Z][A-Z0-9_]*)['"]\]/g;
544
+ async function collectFiles(root) {
545
+ const entries = await readdir(root, { withFileTypes: true });
546
+ const files = [];
547
+ for (const entry of entries) {
548
+ const filePath = path2.join(root, entry.name);
549
+ if (entry.isDirectory()) {
550
+ if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git") {
551
+ continue;
552
+ }
553
+ files.push(...await collectFiles(filePath));
554
+ continue;
555
+ }
556
+ if (SOURCE_EXTENSIONS.has(path2.extname(entry.name))) {
557
+ files.push(filePath);
558
+ }
559
+ }
560
+ return files;
561
+ }
562
+ function collectMatches(filePath, source, pattern, kind) {
563
+ const matches = [];
564
+ for (const match of source.matchAll(pattern)) {
565
+ const envVar = match[1];
566
+ if (!envVar) {
567
+ continue;
568
+ }
569
+ matches.push({
570
+ filePath,
571
+ envVar,
572
+ source: match[0],
573
+ kind
574
+ });
575
+ }
576
+ return matches;
577
+ }
578
+ async function scanEnvUsage(scanRoot) {
579
+ const files = await collectFiles(scanRoot);
580
+ const usages = [];
581
+ for (const filePath of files) {
582
+ const source = await readFile2(filePath, "utf8");
583
+ usages.push(...collectMatches(filePath, source, PROCESS_ENV_DOT, "process-env"));
584
+ usages.push(...collectMatches(filePath, source, PROCESS_ENV_BRACKET, "process-env"));
585
+ usages.push(...collectMatches(filePath, source, IMPORT_META_ENV_DOT, "import-meta-env"));
586
+ usages.push(...collectMatches(filePath, source, IMPORT_META_ENV_BRACKET, "import-meta-env"));
587
+ }
588
+ return usages.sort((left, right) => {
589
+ const byFile = left.filePath.localeCompare(right.filePath);
590
+ if (byFile !== 0) {
591
+ return byFile;
592
+ }
593
+ return left.envVar.localeCompare(right.envVar);
594
+ });
595
+ }
596
+
597
+ // src/watch/diffGraphs.ts
598
+ function stableStringify(value) {
599
+ if (Array.isArray(value)) {
600
+ return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
601
+ }
602
+ if (value && typeof value === "object") {
603
+ return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`).join(",")}}`;
604
+ }
605
+ return JSON.stringify(value);
606
+ }
607
+ function diffGraphs(previous, next) {
608
+ const keys = /* @__PURE__ */ new Set([
609
+ ...Array.from(previous.entries.keys()),
610
+ ...Array.from(next.entries.keys())
611
+ ]);
612
+ return Array.from(keys).filter((key) => !key.startsWith("meta.")).filter((key) => {
613
+ const previousEntry = previous.entries.get(key);
614
+ const nextEntry = next.entries.get(key);
615
+ if (!previousEntry || !nextEntry) {
616
+ return previousEntry?.namespace !== "meta" && nextEntry?.namespace !== "meta";
617
+ }
618
+ return stableStringify(previousEntry.value) !== stableStringify(nextEntry.value);
619
+ }).sort((left, right) => left.localeCompare(right));
620
+ }
621
+
622
+ // src/watch/watchFiles.ts
623
+ import path3 from "path";
624
+ async function watchFiles(runtime, root) {
625
+ const manifest = await loadManifest(root ? { root } : {});
626
+ const roots = Array.from(
627
+ new Set(runtime.graph.workspace.workspaceRoots.map((workspaceRoot) => workspaceRoot.path))
628
+ ).sort((left, right) => left.localeCompare(right));
629
+ const files = Array.from(
630
+ new Set(
631
+ Array.from(runtime.graph.entries.values()).map((entry) => entry.winner.origin?.file).filter((file) => Boolean(file)).map((file) => path3.resolve(manifest.repoRoot, file))
632
+ )
633
+ ).sort((left, right) => left.localeCompare(right));
634
+ return {
635
+ manifestPath: manifest.manifestPath,
636
+ roots,
637
+ files
638
+ };
639
+ }
28
640
  export {
29
641
  CNOS_GRAPH_ENV_VAR,
642
+ CNOS_SECRET_PAYLOAD_ENV_VAR,
643
+ CNOS_SESSION_KEY_ENV_VAR,
644
+ CnosAuthenticationError,
30
645
  CnosSecurityError,
646
+ applyManifestMappings,
647
+ clearAllVaultSessionKeys,
648
+ clearVaultSessionKey,
649
+ compareSchemaToGraph,
31
650
  createSecretVault,
651
+ createSecretVaultProvider,
652
+ deleteLocalSecret,
653
+ deriveVaultKey,
32
654
  deserializeRuntimeGraph,
655
+ detectLegacyVaultFormat,
656
+ diffGraphs,
33
657
  ensureProjectionAllowed,
34
658
  flattenObject,
659
+ formatDriftReport,
660
+ generateCodegenContent,
661
+ getNamespaceDefinition,
35
662
  getVaultPassphraseEnvVar,
663
+ getVaultSessionKeyEnvVar,
664
+ graphRequiresSecretHydration,
36
665
  isPassphraseEnvRef,
666
+ isSecretReference,
667
+ listLocalSecrets,
37
668
  listSecretVaults,
38
669
  loadManifest,
39
670
  parseYaml,
671
+ proposeMapping,
672
+ readKeychain,
673
+ readLocalSecret,
40
674
  readRuntimeGraphFromEnv,
675
+ readVaultMetadata,
676
+ removeLocalVaultFiles,
677
+ resolveCodegenPaths,
41
678
  resolveConfigDocumentPath,
42
679
  resolveConfiguredVaultPassphrase,
43
680
  resolveManifestRoot,
44
681
  resolveSecretPassphrase,
45
682
  resolveSecretStoreRoot,
46
683
  resolveSecretVaultFile,
684
+ resolveVaultAccessKey,
685
+ resolveVaultAuth,
47
686
  resolveVaultDefinition,
687
+ rewriteSourceFiles,
688
+ scanEnvUsage,
48
689
  serializeRuntimeGraph,
690
+ serializeSecretPayload,
49
691
  stringifyYaml,
50
692
  validateRuntime,
51
- writeLocalSecret
693
+ watchFiles,
694
+ watchSchema,
695
+ writeCodegenOutput,
696
+ writeKeychain,
697
+ writeLocalSecret,
698
+ writeVaultSessionKey
52
699
  };