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