@kitsy/cnos 1.5.1 → 1.6.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/dist/build/index.cjs +325 -113
  2. package/dist/build/index.d.cts +3 -2
  3. package/dist/build/index.d.ts +3 -2
  4. package/dist/build/index.js +13 -8
  5. package/dist/{chunk-UWFE4JE2.js → chunk-BMAD24KC.js} +1 -1
  6. package/dist/{chunk-7EI3RFUE.js → chunk-JYWQFMW5.js} +1 -1
  7. package/dist/{chunk-F2ZAIZNH.js → chunk-MW4OVAT3.js} +1 -1
  8. package/dist/chunk-QU5CXL47.js +577 -0
  9. package/dist/{chunk-BS33AW4Y.js → chunk-S7H2UULC.js} +307 -103
  10. package/dist/{chunk-H53ZRQLX.js → chunk-UJBQS7CJ.js} +1 -1
  11. package/dist/{chunk-CMQK2AEF.js → chunk-UOKVLCFL.js} +10 -10
  12. package/dist/{chunk-EJAXWFNT.js → chunk-UR7CHHNN.js} +1 -1
  13. package/dist/{chunk-SZKQVA2M.js → chunk-VGZREX5D.js} +1 -1
  14. package/dist/{chunk-5F2OFKND.js → chunk-XSUP7JKH.js} +23 -1
  15. package/dist/configure/index.cjs +324 -118
  16. package/dist/configure/index.d.cts +3 -3
  17. package/dist/configure/index.d.ts +3 -3
  18. package/dist/configure/index.js +8 -8
  19. package/dist/{envNaming-Dvm_LP2D.d.ts → envNaming-B7Mztkcf.d.ts} +1 -1
  20. package/dist/{envNaming-S4B-dHUx.d.cts → envNaming-gMVnPOfe.d.cts} +1 -1
  21. package/dist/index.cjs +698 -136
  22. package/dist/index.d.cts +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +10 -10
  25. package/dist/internal.cjs +227 -102
  26. package/dist/internal.d.cts +16 -28
  27. package/dist/internal.d.ts +16 -28
  28. package/dist/internal.js +11 -3
  29. package/dist/plugin/basic-schema.cjs +22 -15
  30. package/dist/plugin/basic-schema.d.cts +1 -1
  31. package/dist/plugin/basic-schema.d.ts +1 -1
  32. package/dist/plugin/basic-schema.js +2 -2
  33. package/dist/plugin/cli-args.cjs +27 -18
  34. package/dist/plugin/cli-args.d.cts +1 -1
  35. package/dist/plugin/cli-args.d.ts +1 -1
  36. package/dist/plugin/cli-args.js +2 -2
  37. package/dist/plugin/dotenv.cjs +35 -26
  38. package/dist/plugin/dotenv.d.cts +2 -2
  39. package/dist/plugin/dotenv.d.ts +2 -2
  40. package/dist/plugin/dotenv.js +2 -2
  41. package/dist/plugin/env-export.cjs +28 -19
  42. package/dist/plugin/env-export.d.cts +2 -2
  43. package/dist/plugin/env-export.d.ts +2 -2
  44. package/dist/plugin/env-export.js +2 -2
  45. package/dist/plugin/filesystem.cjs +42 -33
  46. package/dist/plugin/filesystem.d.cts +1 -1
  47. package/dist/plugin/filesystem.d.ts +1 -1
  48. package/dist/plugin/filesystem.js +2 -2
  49. package/dist/plugin/process-env.cjs +24 -17
  50. package/dist/plugin/process-env.d.cts +2 -2
  51. package/dist/plugin/process-env.d.ts +2 -2
  52. package/dist/plugin/process-env.js +2 -2
  53. package/dist/{plugin-B4xwySxw.d.cts → plugin-CKrBlWGI.d.cts} +52 -3
  54. package/dist/{plugin-B4xwySxw.d.ts → plugin-CKrBlWGI.d.ts} +52 -3
  55. package/dist/runtime/index.cjs +696 -136
  56. package/dist/runtime/index.d.cts +7 -1
  57. package/dist/runtime/index.d.ts +7 -1
  58. package/dist/runtime/index.js +10 -10
  59. package/dist/{toPublicEnv-ggmphZFs.d.cts → toPublicEnv-CmBsy53P.d.cts} +1 -1
  60. package/dist/{toPublicEnv-CvhGAfsB.d.ts → toPublicEnv-q6VwWxXZ.d.ts} +1 -1
  61. package/package.json +1 -1
  62. package/dist/chunk-TUMR7JA3.js +0 -234
package/dist/internal.cjs CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var internal_exports = {};
32
32
  __export(internal_exports, {
33
33
  CNOS_GRAPH_ENV_VAR: () => CNOS_GRAPH_ENV_VAR,
34
+ CNOS_PROJECTION_ENV_VAR: () => CNOS_PROJECTION_ENV_VAR,
34
35
  CNOS_SECRET_PAYLOAD_ENV_VAR: () => CNOS_SECRET_PAYLOAD_ENV_VAR,
35
36
  CNOS_SESSION_KEY_ENV_VAR: () => CNOS_SESSION_KEY_ENV_VAR,
36
37
  CnosAuthenticationError: () => CnosAuthenticationError,
@@ -44,6 +45,7 @@ __export(internal_exports, {
44
45
  deleteLocalSecret: () => deleteLocalSecret,
45
46
  deriveVaultKey: () => deriveVaultKey,
46
47
  deserializeRuntimeGraph: () => deserializeRuntimeGraph,
48
+ deserializeServerProjection: () => deserializeServerProjection,
47
49
  detectLegacyVaultFormat: () => detectLegacyVaultFormat,
48
50
  diffGraphs: () => diffGraphs,
49
51
  ensureProjectionAllowed: () => ensureProjectionAllowed,
@@ -64,6 +66,7 @@ __export(internal_exports, {
64
66
  readKeychain: () => readKeychain,
65
67
  readLocalSecret: () => readLocalSecret,
66
68
  readRuntimeGraphFromEnv: () => readRuntimeGraphFromEnv,
69
+ readServerProjectionFromEnv: () => readServerProjectionFromEnv,
67
70
  readVaultMetadata: () => readVaultMetadata,
68
71
  removeLocalVaultFiles: () => removeLocalVaultFiles,
69
72
  resolveCodegenPaths: () => resolveCodegenPaths,
@@ -80,6 +83,7 @@ __export(internal_exports, {
80
83
  scanEnvUsage: () => scanEnvUsage,
81
84
  serializeRuntimeGraph: () => serializeRuntimeGraph,
82
85
  serializeSecretPayload: () => serializeSecretPayload,
86
+ serializeServerProjection: () => serializeServerProjection,
83
87
  stringifyYaml: () => stringifyYaml,
84
88
  validateRuntime: () => validateRuntime,
85
89
  watchFiles: () => watchFiles,
@@ -105,6 +109,11 @@ var CnosManifestError = class extends CnosError {
105
109
  }
106
110
  manifestPath;
107
111
  };
112
+ var CnosDiscoveryError = class extends CnosError {
113
+ constructor(message) {
114
+ super(message);
115
+ }
116
+ };
108
117
  var CnosSecurityError = class extends CnosError {
109
118
  constructor(message) {
110
119
  super(message);
@@ -223,32 +232,110 @@ async function writeKeychain(entry, value) {
223
232
  }
224
233
 
225
234
  // ../core/src/manifest/loadManifest.ts
235
+ var import_promises3 = require("fs/promises");
236
+ var import_node_path3 = __toESM(require("path"), 1);
237
+
238
+ // ../core/src/utils/path.ts
226
239
  var import_promises2 = require("fs/promises");
240
+ var import_node_os = __toESM(require("os"), 1);
227
241
  var import_node_path2 = __toESM(require("path"), 1);
228
242
 
229
- // ../core/src/utils/path.ts
243
+ // ../core/src/discovery/findCnosrc.ts
230
244
  var import_promises = require("fs/promises");
231
- var import_node_os = __toESM(require("os"), 1);
232
245
  var import_node_path = __toESM(require("path"), 1);
246
+
247
+ // ../core/src/utils/yaml.ts
248
+ var import_yaml = require("yaml");
249
+ function parseYaml(source) {
250
+ return (0, import_yaml.parse)(source);
251
+ }
252
+ function stringifyYaml(value) {
253
+ return (0, import_yaml.stringify)(value);
254
+ }
255
+
256
+ // ../core/src/discovery/findCnosrc.ts
257
+ async function exists(targetPath) {
258
+ try {
259
+ await (0, import_promises.access)(targetPath);
260
+ return true;
261
+ } catch {
262
+ return false;
263
+ }
264
+ }
265
+ function validateCnosrc(value, filePath) {
266
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
267
+ throw new CnosManifestError(".cnosrc.yml must be a YAML object", filePath);
268
+ }
269
+ const root = typeof value.root === "string" ? value.root.trim() : "";
270
+ const workspace = typeof value.workspace === "string" ? value.workspace.trim() : void 0;
271
+ if (!root) {
272
+ throw new CnosManifestError(".cnosrc.yml requires root", filePath);
273
+ }
274
+ return {
275
+ root,
276
+ ...workspace ? { workspace } : {}
277
+ };
278
+ }
279
+ async function findCnosrc(startDir = process.cwd(), maxLevels = 3) {
280
+ let current = import_node_path.default.resolve(startDir);
281
+ for (let depth = 0; depth <= maxLevels; depth += 1) {
282
+ const candidate = import_node_path.default.join(current, ".cnosrc.yml");
283
+ if (await exists(candidate)) {
284
+ return candidate;
285
+ }
286
+ const parent = import_node_path.default.dirname(current);
287
+ if (parent === current) {
288
+ break;
289
+ }
290
+ current = parent;
291
+ }
292
+ return void 0;
293
+ }
294
+ async function discoverCnosAnchor(startDir = process.cwd(), maxLevels = 3) {
295
+ const anchorPath = await findCnosrc(startDir, maxLevels);
296
+ if (!anchorPath) {
297
+ throw new CnosDiscoveryError(
298
+ "No .cnosrc.yml found. Run cnos init or create .cnosrc.yml in your package root."
299
+ );
300
+ }
301
+ const source = await (0, import_promises.readFile)(anchorPath, "utf8");
302
+ const parsed = validateCnosrc(parseYaml(source), anchorPath);
303
+ const consumerRoot = import_node_path.default.dirname(anchorPath);
304
+ const manifestRoot = import_node_path.default.resolve(consumerRoot, parsed.root);
305
+ const manifestPath = import_node_path.default.join(manifestRoot, "cnos.yml");
306
+ if (!await exists(manifestPath)) {
307
+ throw new CnosDiscoveryError(
308
+ `.cnosrc.yml points to ${manifestRoot} but no cnos.yml found there.`
309
+ );
310
+ }
311
+ return {
312
+ anchorPath,
313
+ consumerRoot,
314
+ manifestRoot,
315
+ ...parsed.workspace ? { workspace: parsed.workspace } : {}
316
+ };
317
+ }
318
+
319
+ // ../core/src/utils/path.ts
233
320
  var PRIMARY_CNOS_DIR = ".cnos";
234
321
  var LEGACY_CNOS_DIR = "cnos";
235
- async function exists(filePath) {
322
+ async function exists2(filePath) {
236
323
  try {
237
- await (0, import_promises.access)(filePath);
324
+ await (0, import_promises2.access)(filePath);
238
325
  return true;
239
326
  } catch {
240
327
  return false;
241
328
  }
242
329
  }
243
330
  async function resolveCnosRoot(root = process.cwd()) {
244
- const basePath = import_node_path.default.resolve(root);
331
+ const basePath = import_node_path2.default.resolve(root);
245
332
  const candidates = [
246
- import_node_path.default.join(basePath, PRIMARY_CNOS_DIR),
247
- import_node_path.default.join(basePath, LEGACY_CNOS_DIR),
333
+ import_node_path2.default.join(basePath, PRIMARY_CNOS_DIR),
334
+ import_node_path2.default.join(basePath, LEGACY_CNOS_DIR),
248
335
  basePath
249
336
  ];
250
337
  for (const candidate of candidates) {
251
- if (await exists(import_node_path.default.join(candidate, "cnos.yml"))) {
338
+ if (await exists2(import_node_path2.default.join(candidate, "cnos.yml"))) {
252
339
  return candidate;
253
340
  }
254
341
  }
@@ -256,38 +343,44 @@ async function resolveCnosRoot(root = process.cwd()) {
256
343
  `Could not locate .cnos/cnos.yml or cnos/cnos.yml from root: ${basePath}`
257
344
  );
258
345
  }
259
- async function resolveManifestRoot(root = process.cwd()) {
260
- return resolveCnosRoot(root);
346
+ async function resolveManifestRoot(options = {}) {
347
+ if (options.root) {
348
+ const manifestRoot = await resolveCnosRoot(options.root);
349
+ const resolvedRoot = import_node_path2.default.resolve(options.root);
350
+ const consumerRoot = import_node_path2.default.basename(manifestRoot) === PRIMARY_CNOS_DIR || import_node_path2.default.basename(manifestRoot) === LEGACY_CNOS_DIR ? import_node_path2.default.dirname(manifestRoot) : resolvedRoot;
351
+ return {
352
+ manifestRoot,
353
+ consumerRoot
354
+ };
355
+ }
356
+ const discovered = await discoverCnosAnchor(options.cwd ?? process.cwd());
357
+ return {
358
+ manifestRoot: discovered.manifestRoot,
359
+ consumerRoot: discovered.consumerRoot,
360
+ anchorPath: discovered.anchorPath,
361
+ ...discovered.workspace ? { workspace: discovered.workspace } : {}
362
+ };
261
363
  }
262
364
  function expandHomePath(targetPath) {
263
365
  if (targetPath === "~") {
264
366
  return import_node_os.default.homedir();
265
367
  }
266
368
  if (targetPath.startsWith("~/") || targetPath.startsWith("~\\")) {
267
- return import_node_path.default.join(import_node_os.default.homedir(), targetPath.slice(2));
369
+ return import_node_path2.default.join(import_node_os.default.homedir(), targetPath.slice(2));
268
370
  }
269
371
  return targetPath;
270
372
  }
271
373
  function resolveNamespaceDirectory(workspaceRoot, namespace, profile) {
272
374
  const rootFolder = namespace === "value" ? "values" : namespace === "secret" ? "secrets" : namespace;
273
375
  if (profile && profile !== "base") {
274
- return import_node_path.default.resolve(workspaceRoot, "profiles", profile, rootFolder);
376
+ return import_node_path2.default.resolve(workspaceRoot, "profiles", profile, rootFolder);
275
377
  }
276
- return import_node_path.default.resolve(workspaceRoot, rootFolder);
378
+ return import_node_path2.default.resolve(workspaceRoot, rootFolder);
277
379
  }
278
380
  function resolveConfigDocumentPath(workspaceRoot, namespace, configPath, profile) {
279
381
  const namespaceRoot = resolveNamespaceDirectory(workspaceRoot, namespace, profile);
280
382
  const fileName = `${configPath.split(".").shift() ?? "app"}.yml`;
281
- return import_node_path.default.resolve(namespaceRoot, fileName);
282
- }
283
-
284
- // ../core/src/utils/yaml.ts
285
- var import_yaml = require("yaml");
286
- function parseYaml(source) {
287
- return (0, import_yaml.parse)(source);
288
- }
289
- function stringifyYaml(value) {
290
- return (0, import_yaml.stringify)(value);
383
+ return import_node_path2.default.resolve(namespaceRoot, fileName);
291
384
  }
292
385
 
293
386
  // ../core/src/manifest/normalizeManifest.ts
@@ -559,11 +652,15 @@ function normalizeManifest(manifest) {
559
652
 
560
653
  // ../core/src/manifest/loadManifest.ts
561
654
  async function loadManifest(options = {}) {
562
- const manifestRoot = await resolveManifestRoot(options.root);
563
- const manifestPath = import_node_path2.default.join(manifestRoot, "cnos.yml");
655
+ const resolved = await resolveManifestRoot({
656
+ ...options.root ? { root: options.root } : {},
657
+ ...options.cwd ? { cwd: options.cwd } : {}
658
+ });
659
+ const manifestRoot = resolved.manifestRoot;
660
+ const manifestPath = import_node_path3.default.join(manifestRoot, "cnos.yml");
564
661
  let source;
565
662
  try {
566
- source = await (0, import_promises2.readFile)(manifestPath, "utf8");
663
+ source = await (0, import_promises3.readFile)(manifestPath, "utf8");
567
664
  } catch {
568
665
  throw new CnosManifestError("Unable to read CNOS manifest", manifestPath);
569
666
  }
@@ -573,7 +670,10 @@ async function loadManifest(options = {}) {
573
670
  }
574
671
  return {
575
672
  manifestRoot,
576
- repoRoot: import_node_path2.default.dirname(manifestRoot),
673
+ repoRoot: import_node_path3.default.dirname(manifestRoot),
674
+ consumerRoot: resolved.consumerRoot,
675
+ ...resolved.anchorPath ? { anchorPath: resolved.anchorPath } : {},
676
+ ...resolved.workspace ? { anchoredWorkspace: resolved.workspace } : {},
577
677
  manifestPath,
578
678
  manifest: normalizeManifest(rawManifest),
579
679
  rawManifest
@@ -581,13 +681,13 @@ async function loadManifest(options = {}) {
581
681
  }
582
682
 
583
683
  // ../core/src/manifest/loadWorkspaceFile.ts
584
- var import_promises3 = require("fs/promises");
585
- var import_node_path3 = __toESM(require("path"), 1);
586
-
587
- // ../core/src/profiles/expandProfileChain.ts
588
684
  var import_promises4 = require("fs/promises");
589
685
  var import_node_path4 = __toESM(require("path"), 1);
590
686
 
687
+ // ../core/src/profiles/expandProfileChain.ts
688
+ var import_promises5 = require("fs/promises");
689
+ var import_node_path5 = __toESM(require("path"), 1);
690
+
591
691
  // ../core/src/promotions/validatePromotion.ts
592
692
  var DEFAULT_DATA_NAMESPACE = {
593
693
  kind: "data",
@@ -637,42 +737,42 @@ function validateProjectionIssue(manifest, key, target) {
637
737
  }
638
738
 
639
739
  // ../core/src/workspaces/resolveWorkspaceContext.ts
640
- var import_promises5 = require("fs/promises");
641
- var import_node_path5 = __toESM(require("path"), 1);
740
+ var import_promises6 = require("fs/promises");
741
+ var import_node_path6 = __toESM(require("path"), 1);
642
742
 
643
743
  // ../core/src/secrets/auditLog.ts
644
- var import_promises8 = require("fs/promises");
645
- var import_node_path8 = __toESM(require("path"), 1);
744
+ var import_promises9 = require("fs/promises");
745
+ var import_node_path9 = __toESM(require("path"), 1);
646
746
 
647
747
  // ../core/src/utils/secretStore.ts
648
748
  var import_node_crypto = require("crypto");
649
- var import_promises7 = require("fs/promises");
650
- var import_node_path7 = __toESM(require("path"), 1);
749
+ var import_promises8 = require("fs/promises");
750
+ var import_node_path8 = __toESM(require("path"), 1);
651
751
 
652
752
  // ../core/src/secrets/sessionStore.ts
653
- var import_promises6 = require("fs/promises");
654
- var import_node_path6 = __toESM(require("path"), 1);
753
+ var import_promises7 = require("fs/promises");
754
+ var import_node_path7 = __toESM(require("path"), 1);
655
755
  function buildSessionRoot(processEnv = process.env) {
656
- return import_node_path6.default.join(import_node_path6.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
756
+ return import_node_path7.default.join(import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets")), "sessions");
657
757
  }
658
758
  function buildSessionPath(vault, processEnv) {
659
- return import_node_path6.default.join(buildSessionRoot(processEnv), `${vault}.json`);
759
+ return import_node_path7.default.join(buildSessionRoot(processEnv), `${vault}.json`);
660
760
  }
661
761
  async function writeVaultSessionKey(vault, derivedKey, processEnv) {
662
762
  const filePath = buildSessionPath(vault, processEnv);
663
- await (0, import_promises6.mkdir)(import_node_path6.default.dirname(filePath), { recursive: true });
763
+ await (0, import_promises7.mkdir)(import_node_path7.default.dirname(filePath), { recursive: true });
664
764
  const document = {
665
765
  version: 1,
666
766
  vault,
667
767
  derivedKey: derivedKey.toString("hex"),
668
768
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
669
769
  };
670
- await (0, import_promises6.writeFile)(filePath, JSON.stringify(document, null, 2), "utf8");
770
+ await (0, import_promises7.writeFile)(filePath, JSON.stringify(document, null, 2), "utf8");
671
771
  return filePath;
672
772
  }
673
773
  async function readVaultSessionKey(vault, processEnv) {
674
774
  try {
675
- const source = await (0, import_promises6.readFile)(buildSessionPath(vault, processEnv), "utf8");
775
+ const source = await (0, import_promises7.readFile)(buildSessionPath(vault, processEnv), "utf8");
676
776
  const document = JSON.parse(source);
677
777
  if (document.version !== 1 || typeof document.derivedKey !== "string") {
678
778
  return void 0;
@@ -684,13 +784,13 @@ async function readVaultSessionKey(vault, processEnv) {
684
784
  }
685
785
  }
686
786
  async function clearVaultSessionKey(vault, processEnv) {
687
- await (0, import_promises6.rm)(buildSessionPath(vault, processEnv), { force: true });
787
+ await (0, import_promises7.rm)(buildSessionPath(vault, processEnv), { force: true });
688
788
  }
689
789
  async function clearAllVaultSessionKeys(processEnv) {
690
790
  const root = buildSessionRoot(processEnv);
691
791
  try {
692
- const entries = await (0, import_promises6.readdir)(root);
693
- await Promise.all(entries.map((entry) => (0, import_promises6.rm)(import_node_path6.default.join(root, entry), { force: true })));
792
+ const entries = await (0, import_promises7.readdir)(root);
793
+ await Promise.all(entries.map((entry) => (0, import_promises7.rm)(import_node_path7.default.join(root, entry), { force: true })));
694
794
  } catch {
695
795
  }
696
796
  }
@@ -712,7 +812,7 @@ function isSecretReference(value) {
712
812
  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));
713
813
  }
714
814
  function resolveSecretStoreRoot(processEnv = process.env) {
715
- return import_node_path7.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
815
+ return import_node_path8.default.resolve(expandHomePath(processEnv.CNOS_SECRET_HOME ?? "~/.cnos/secrets"));
716
816
  }
717
817
  function normalizeVaultToken(vault = "default") {
718
818
  return vault.replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
@@ -747,19 +847,19 @@ function deriveVaultKey(passphrase, salt, iterations = PBKDF2_ITERATIONS) {
747
847
  return (0, import_node_crypto.pbkdf2Sync)(passphrase, salt, iterations, KEY_LENGTH, "sha512");
748
848
  }
749
849
  function buildMetaPath(storeRoot, vault = "default") {
750
- return import_node_path7.default.join(storeRoot, "vaults", vault, META_FILENAME);
850
+ return import_node_path8.default.join(storeRoot, "vaults", vault, META_FILENAME);
751
851
  }
752
852
  function resolveSecretVaultFile(storeRoot, vault = "default") {
753
853
  return buildMetaPath(storeRoot, vault);
754
854
  }
755
855
  function buildKeystorePath(storeRoot, vault = "default") {
756
- return import_node_path7.default.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
856
+ return import_node_path8.default.join(storeRoot, "vaults", vault, KEYSTORE_FILENAME);
757
857
  }
758
858
  function buildLegacyVaultFile(storeRoot, vault = "default") {
759
- return import_node_path7.default.join(storeRoot, "vaults", `${vault}.json`);
859
+ return import_node_path8.default.join(storeRoot, "vaults", `${vault}.json`);
760
860
  }
761
861
  function buildLegacyVaultStoreRoot(storeRoot, vault = "default") {
762
- return import_node_path7.default.join(storeRoot, "vaults", vault, "store");
862
+ return import_node_path8.default.join(storeRoot, "vaults", vault, "store");
763
863
  }
764
864
  function assertVaultMetadata(value, filePath) {
765
865
  if (!isObject(value)) {
@@ -770,9 +870,9 @@ function assertVaultMetadata(value, filePath) {
770
870
  }
771
871
  return value;
772
872
  }
773
- async function exists2(targetPath) {
873
+ async function exists3(targetPath) {
774
874
  try {
775
- await (0, import_promises7.stat)(targetPath);
875
+ await (0, import_promises8.stat)(targetPath);
776
876
  return true;
777
877
  } catch {
778
878
  return false;
@@ -781,10 +881,10 @@ async function exists2(targetPath) {
781
881
  async function detectLegacyVaultFormat(storeRoot, vault = "default") {
782
882
  const legacyFile = buildLegacyVaultFile(storeRoot, vault);
783
883
  const legacyStore = buildLegacyVaultStoreRoot(storeRoot, vault);
784
- if (await exists2(legacyFile)) {
884
+ if (await exists3(legacyFile)) {
785
885
  return legacyFile;
786
886
  }
787
- if (await exists2(legacyStore)) {
887
+ if (await exists3(legacyStore)) {
788
888
  return legacyStore;
789
889
  }
790
890
  return void 0;
@@ -856,15 +956,15 @@ function buildInitialPayload() {
856
956
  async function writeVaultFiles(storeRoot, vault, meta, payload, key) {
857
957
  const metaPath = buildMetaPath(storeRoot, vault);
858
958
  const keystorePath = buildKeystorePath(storeRoot, vault);
859
- await (0, import_promises7.mkdir)(import_node_path7.default.dirname(metaPath), { recursive: true });
860
- await (0, import_promises7.writeFile)(metaPath, stringifyYaml(meta), "utf8");
861
- await (0, import_promises7.writeFile)(keystorePath, encryptPayload(payload, key));
959
+ await (0, import_promises8.mkdir)(import_node_path8.default.dirname(metaPath), { recursive: true });
960
+ await (0, import_promises8.writeFile)(metaPath, stringifyYaml(meta), "utf8");
961
+ await (0, import_promises8.writeFile)(keystorePath, encryptPayload(payload, key));
862
962
  }
863
963
  async function readVaultMetadata(storeRoot, vault = "default") {
864
964
  await assertNoLegacyVaultFormat(storeRoot, vault);
865
965
  const metaPath = buildMetaPath(storeRoot, vault);
866
966
  try {
867
- const source = await (0, import_promises7.readFile)(metaPath, "utf8");
967
+ const source = await (0, import_promises8.readFile)(metaPath, "utf8");
868
968
  return assertVaultMetadata(parseYaml(source), metaPath);
869
969
  } catch (error) {
870
970
  if (error.code === "ENOENT") {
@@ -874,11 +974,11 @@ async function readVaultMetadata(storeRoot, vault = "default") {
874
974
  }
875
975
  }
876
976
  async function listSecretVaults(storeRoot) {
877
- const vaultRoot = import_node_path7.default.join(storeRoot, "vaults");
977
+ const vaultRoot = import_node_path8.default.join(storeRoot, "vaults");
878
978
  try {
879
- const entries = await (0, import_promises7.readdir)(vaultRoot, { withFileTypes: true });
979
+ const entries = await (0, import_promises8.readdir)(vaultRoot, { withFileTypes: true });
880
980
  const vaults = await Promise.all(
881
- entries.filter((entry) => entry.isDirectory()).map(async (entry) => await exists2(import_node_path7.default.join(vaultRoot, entry.name, META_FILENAME)) ? entry.name : void 0)
981
+ entries.filter((entry) => entry.isDirectory()).map(async (entry) => await exists3(import_node_path8.default.join(vaultRoot, entry.name, META_FILENAME)) ? entry.name : void 0)
882
982
  );
883
983
  return vaults.filter((value) => Boolean(value)).sort((left, right) => left.localeCompare(right));
884
984
  } catch {
@@ -967,7 +1067,7 @@ async function loadVaultPayload(storeRoot, vault, auth) {
967
1067
  if (!key) {
968
1068
  throw new CnosAuthenticationError(`Vault "${vault}" requires authentication before access.`);
969
1069
  }
970
- const buffer = await (0, import_promises7.readFile)(buildKeystorePath(storeRoot, vault));
1070
+ const buffer = await (0, import_promises8.readFile)(buildKeystorePath(storeRoot, vault));
971
1071
  return {
972
1072
  meta,
973
1073
  payload: decryptPayload(buffer, key),
@@ -1040,14 +1140,14 @@ function resolveVaultDefinition(vaults, vault = "default") {
1040
1140
  };
1041
1141
  }
1042
1142
  async function removeLocalVaultFiles(storeRoot, vault = "default") {
1043
- await (0, import_promises7.rm)(import_node_path7.default.join(storeRoot, "vaults", vault), { recursive: true, force: true });
1143
+ await (0, import_promises8.rm)(import_node_path8.default.join(storeRoot, "vaults", vault), { recursive: true, force: true });
1044
1144
  }
1045
1145
 
1046
1146
  // ../core/src/secrets/auditLog.ts
1047
1147
  async function appendAuditEvent(event, processEnv = process.env) {
1048
- const auditFile = processEnv.CNOS_AUDIT_FILE ?? import_node_path8.default.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
1049
- await (0, import_promises8.mkdir)(import_node_path8.default.dirname(auditFile), { recursive: true });
1050
- await (0, import_promises8.appendFile)(
1148
+ const auditFile = processEnv.CNOS_AUDIT_FILE ?? import_node_path9.default.join(resolveSecretStoreRoot(processEnv), "audit", "access.log");
1149
+ await (0, import_promises9.mkdir)(import_node_path9.default.dirname(auditFile), { recursive: true });
1150
+ await (0, import_promises9.appendFile)(
1051
1151
  auditFile,
1052
1152
  `${JSON.stringify({
1053
1153
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1313,9 +1413,12 @@ async function resolveVaultAuth(vaultId, definition, processEnv = process.env) {
1313
1413
  throw toAuthError(vaultId, [getVaultSessionKeyEnvVar(vaultId), ...sources]);
1314
1414
  }
1315
1415
 
1416
+ // ../core/src/runtime/toServerProjection.ts
1417
+ var import_node_crypto2 = require("crypto");
1418
+
1316
1419
  // ../core/src/runtime/dump.ts
1317
- var import_promises9 = require("fs/promises");
1318
- var import_node_path9 = __toESM(require("path"), 1);
1420
+ var import_promises10 = require("fs/promises");
1421
+ var import_node_path10 = __toESM(require("path"), 1);
1319
1422
 
1320
1423
  // ../core/src/utils/envNaming.ts
1321
1424
  function normalizeMappingConfig(config = {}) {
@@ -1327,8 +1430,8 @@ function normalizeMappingConfig(config = {}) {
1327
1430
  function toScreamingSnakeSegment(segment) {
1328
1431
  return segment.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
1329
1432
  }
1330
- function toScreamingSnake(path13) {
1331
- return path13.split(".").map((segment) => toScreamingSnakeSegment(segment)).filter(Boolean).join("_");
1433
+ function toScreamingSnake(path14) {
1434
+ return path14.split(".").map((segment) => toScreamingSnakeSegment(segment)).filter(Boolean).join("_");
1332
1435
  }
1333
1436
  function logicalKeyToEnvVar(key, config = {}) {
1334
1437
  const normalized = normalizeMappingConfig(config);
@@ -1461,10 +1564,21 @@ async function validateRuntime(runtime) {
1461
1564
  }
1462
1565
 
1463
1566
  // src/runtime/bootstrap.ts
1464
- var import_node_crypto2 = require("crypto");
1567
+ var import_node_crypto3 = require("crypto");
1465
1568
  var CNOS_GRAPH_ENV_VAR = "__CNOS_GRAPH__";
1569
+ var CNOS_PROJECTION_ENV_VAR = "__CNOS_PROJECTION__";
1466
1570
  var CNOS_SECRET_PAYLOAD_ENV_VAR = "__CNOS_SECRET_PAYLOAD__";
1467
1571
  var CNOS_SESSION_KEY_ENV_VAR = "__CNOS_SESSION_KEY__";
1572
+ function serializeServerProjection(projection) {
1573
+ return JSON.stringify(projection);
1574
+ }
1575
+ function deserializeServerProjection(source) {
1576
+ const payload = JSON.parse(source);
1577
+ if (!payload || payload.version !== 1 || typeof payload.workspace !== "string" || typeof payload.profile !== "string" || typeof payload.resolvedAt !== "string" || typeof payload.configHash !== "string" || !payload.values || typeof payload.values !== "object" || Array.isArray(payload.values) || !payload.secretRefs || typeof payload.secretRefs !== "object" || Array.isArray(payload.secretRefs) || !Array.isArray(payload.publicKeys) || !payload.meta || typeof payload.meta !== "object") {
1578
+ throw new Error("Invalid CNOS server projection payload");
1579
+ }
1580
+ return payload;
1581
+ }
1468
1582
  function serializeRuntimeGraph(graph) {
1469
1583
  const payload = {
1470
1584
  entries: Array.from(graph.entries.values()),
@@ -1508,15 +1622,15 @@ function decryptSecretPayload(serialized, sessionKey) {
1508
1622
  const iv = Buffer.from(payload.iv, "base64");
1509
1623
  const tag = Buffer.from(payload.tag, "base64");
1510
1624
  const ciphertext = Buffer.from(payload.ciphertext, "base64");
1511
- const decipher = (0, import_node_crypto2.createDecipheriv)("aes-256-gcm", key, iv);
1625
+ const decipher = (0, import_node_crypto3.createDecipheriv)("aes-256-gcm", key, iv);
1512
1626
  decipher.setAuthTag(tag);
1513
1627
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
1514
1628
  return JSON.parse(plaintext);
1515
1629
  }
1516
1630
  function serializeSecretPayload(values) {
1517
- const key = (0, import_node_crypto2.randomBytes)(32);
1518
- const iv = (0, import_node_crypto2.randomBytes)(12);
1519
- const cipher = (0, import_node_crypto2.createCipheriv)("aes-256-gcm", key, iv);
1631
+ const key = (0, import_node_crypto3.randomBytes)(32);
1632
+ const iv = (0, import_node_crypto3.randomBytes)(12);
1633
+ const cipher = (0, import_node_crypto3.createCipheriv)("aes-256-gcm", key, iv);
1520
1634
  const ciphertext = Buffer.concat([cipher.update(JSON.stringify(values), "utf8"), cipher.final()]);
1521
1635
  const tag = cipher.getAuthTag();
1522
1636
  return {
@@ -1547,6 +1661,13 @@ function readRuntimeGraphFromEnv(processEnv = process.env) {
1547
1661
  }
1548
1662
  return graph;
1549
1663
  }
1664
+ function readServerProjectionFromEnv(processEnv = process.env) {
1665
+ const serialized = processEnv[CNOS_PROJECTION_ENV_VAR];
1666
+ if (!serialized) {
1667
+ return void 0;
1668
+ }
1669
+ return deserializeServerProjection(serialized);
1670
+ }
1550
1671
  function graphRequiresSecretHydration(graph) {
1551
1672
  return Array.from(graph.entries.values()).some((entry) => entry.namespace === "secret" && isSecretReference(entry.value));
1552
1673
  }
@@ -1582,10 +1703,10 @@ function buildNamespaceInterfaces(schema) {
1582
1703
  continue;
1583
1704
  }
1584
1705
  const namespace = logicalKey.slice(0, separatorIndex);
1585
- const path13 = logicalKey.slice(separatorIndex + 1);
1706
+ const path14 = logicalKey.slice(separatorIndex + 1);
1586
1707
  const existing = namespaceGroups.get(namespace) ?? [];
1587
1708
  existing.push({
1588
- key: path13,
1709
+ key: path14,
1589
1710
  rule
1590
1711
  });
1591
1712
  namespaceGroups.set(namespace, existing);
@@ -1690,15 +1811,15 @@ function generateCodegenContent(manifest, sourcePath, typeModuleImport = "./cnos
1690
1811
  }
1691
1812
 
1692
1813
  // src/codegen/writeOutput.ts
1693
- var import_promises10 = require("fs/promises");
1694
- var import_node_path10 = __toESM(require("path"), 1);
1814
+ var import_promises11 = require("fs/promises");
1815
+ var import_node_path11 = __toESM(require("path"), 1);
1695
1816
  function stripTsExtension(filePath) {
1696
1817
  return filePath.replace(/(\.d)?\.[cm]?tsx?$/i, "").replace(/\.[cm]?jsx?$/i, "");
1697
1818
  }
1698
1819
  function resolveCodegenPaths(repoRoot, out) {
1699
- const typesPath = out ? import_node_path10.default.resolve(repoRoot, out) : import_node_path10.default.join(repoRoot, ".cnos", "types", "cnos.d.ts");
1700
- const runtimePath = import_node_path10.default.join(import_node_path10.default.dirname(typesPath), "runtime.ts");
1701
- const typeImportPath = `./${import_node_path10.default.basename(stripTsExtension(typesPath))}`;
1820
+ const typesPath = out ? import_node_path11.default.resolve(repoRoot, out) : import_node_path11.default.join(repoRoot, ".cnos", "types", "cnos.d.ts");
1821
+ const runtimePath = import_node_path11.default.join(import_node_path11.default.dirname(typesPath), "runtime.ts");
1822
+ const typeImportPath = `./${import_node_path11.default.basename(stripTsExtension(typesPath))}`;
1702
1823
  return {
1703
1824
  typesPath,
1704
1825
  runtimePath,
@@ -1709,10 +1830,10 @@ async function writeCodegenOutput(options = {}) {
1709
1830
  const loadedManifest = await loadManifest(options.root ? { root: options.root } : {});
1710
1831
  const paths = resolveCodegenPaths(loadedManifest.repoRoot, options.out);
1711
1832
  const generated = generateCodegenContent(loadedManifest.manifest, loadedManifest.manifestPath, paths.typeImportPath);
1712
- await (0, import_promises10.mkdir)(import_node_path10.default.dirname(paths.typesPath), { recursive: true });
1713
- await (0, import_promises10.mkdir)(import_node_path10.default.dirname(paths.runtimePath), { recursive: true });
1714
- await (0, import_promises10.writeFile)(paths.typesPath, generated.typesContent, "utf8");
1715
- await (0, import_promises10.writeFile)(paths.runtimePath, generated.runtimeContent, "utf8");
1833
+ await (0, import_promises11.mkdir)(import_node_path11.default.dirname(paths.typesPath), { recursive: true });
1834
+ await (0, import_promises11.mkdir)(import_node_path11.default.dirname(paths.runtimePath), { recursive: true });
1835
+ await (0, import_promises11.writeFile)(paths.typesPath, generated.typesContent, "utf8");
1836
+ await (0, import_promises11.writeFile)(paths.runtimePath, generated.runtimeContent, "utf8");
1716
1837
  return {
1717
1838
  manifestPath: loadedManifest.manifestPath,
1718
1839
  typesPath: paths.typesPath,
@@ -1898,7 +2019,7 @@ function formatDriftReport(report) {
1898
2019
  }
1899
2020
 
1900
2021
  // src/migrate/applyManifest.ts
1901
- var import_promises11 = require("fs/promises");
2022
+ var import_promises12 = require("fs/promises");
1902
2023
  function sortRecord(record) {
1903
2024
  return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
1904
2025
  }
@@ -1933,7 +2054,7 @@ async function applyManifestMappings(proposals, root) {
1933
2054
  promote: Array.from(promoted).sort((left, right) => left.localeCompare(right))
1934
2055
  };
1935
2056
  }
1936
- await (0, import_promises11.writeFile)(loadedManifest.manifestPath, stringifyYaml(rawManifest), "utf8");
2057
+ await (0, import_promises12.writeFile)(loadedManifest.manifestPath, stringifyYaml(rawManifest), "utf8");
1937
2058
  return {
1938
2059
  manifestPath: loadedManifest.manifestPath,
1939
2060
  appliedMappings,
@@ -1968,7 +2089,7 @@ function proposeMapping(envVar) {
1968
2089
  }
1969
2090
 
1970
2091
  // src/migrate/rewriteSource.ts
1971
- var import_promises12 = require("fs/promises");
2092
+ var import_promises13 = require("fs/promises");
1972
2093
  function importStatementFor(kind) {
1973
2094
  return kind === "import-meta-env" ? "import cnos from '@kitsy/cnos/browser';" : "import cnos from '@kitsy/cnos';";
1974
2095
  }
@@ -1989,7 +2110,7 @@ async function rewriteSourceFiles(usages, proposals) {
1989
2110
  const backupFiles = [];
1990
2111
  const skippedUsages = [];
1991
2112
  for (const [filePath, fileUsages] of fileGroups.entries()) {
1992
- const original = await (0, import_promises12.readFile)(filePath, "utf8");
2113
+ const original = await (0, import_promises13.readFile)(filePath, "utf8");
1993
2114
  let nextSource = original;
1994
2115
  let changed = false;
1995
2116
  const importKinds = /* @__PURE__ */ new Set();
@@ -2016,7 +2137,7 @@ async function rewriteSourceFiles(usages, proposals) {
2016
2137
  continue;
2017
2138
  }
2018
2139
  const backupPath = `${filePath}.bak`;
2019
- await (0, import_promises12.copyFile)(filePath, backupPath);
2140
+ await (0, import_promises13.copyFile)(filePath, backupPath);
2020
2141
  backupFiles.push(backupPath);
2021
2142
  for (const kind of Array.from(importKinds)) {
2022
2143
  const importStatement = importStatementFor(kind);
@@ -2025,7 +2146,7 @@ async function rewriteSourceFiles(usages, proposals) {
2025
2146
  ${nextSource}`;
2026
2147
  }
2027
2148
  }
2028
- await (0, import_promises12.writeFile)(filePath, nextSource, "utf8");
2149
+ await (0, import_promises13.writeFile)(filePath, nextSource, "utf8");
2029
2150
  rewrittenFiles.push(filePath);
2030
2151
  }
2031
2152
  return {
@@ -2036,18 +2157,18 @@ ${nextSource}`;
2036
2157
  }
2037
2158
 
2038
2159
  // src/migrate/scanEnvUsage.ts
2039
- var import_promises13 = require("fs/promises");
2040
- var import_node_path11 = __toESM(require("path"), 1);
2160
+ var import_promises14 = require("fs/promises");
2161
+ var import_node_path12 = __toESM(require("path"), 1);
2041
2162
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"]);
2042
2163
  var PROCESS_ENV_DOT = /process\.env\.([A-Z][A-Z0-9_]*)/g;
2043
2164
  var PROCESS_ENV_BRACKET = /process\.env\[['"]([A-Z][A-Z0-9_]*)['"]\]/g;
2044
2165
  var IMPORT_META_ENV_DOT = /import\.meta\.env\.([A-Z][A-Z0-9_]*)/g;
2045
2166
  var IMPORT_META_ENV_BRACKET = /import\.meta\.env\[['"]([A-Z][A-Z0-9_]*)['"]\]/g;
2046
2167
  async function collectFiles(root) {
2047
- const entries = await (0, import_promises13.readdir)(root, { withFileTypes: true });
2168
+ const entries = await (0, import_promises14.readdir)(root, { withFileTypes: true });
2048
2169
  const files = [];
2049
2170
  for (const entry of entries) {
2050
- const filePath = import_node_path11.default.join(root, entry.name);
2171
+ const filePath = import_node_path12.default.join(root, entry.name);
2051
2172
  if (entry.isDirectory()) {
2052
2173
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git") {
2053
2174
  continue;
@@ -2055,7 +2176,7 @@ async function collectFiles(root) {
2055
2176
  files.push(...await collectFiles(filePath));
2056
2177
  continue;
2057
2178
  }
2058
- if (SOURCE_EXTENSIONS.has(import_node_path11.default.extname(entry.name))) {
2179
+ if (SOURCE_EXTENSIONS.has(import_node_path12.default.extname(entry.name))) {
2059
2180
  files.push(filePath);
2060
2181
  }
2061
2182
  }
@@ -2081,7 +2202,7 @@ async function scanEnvUsage(scanRoot) {
2081
2202
  const files = await collectFiles(scanRoot);
2082
2203
  const usages = [];
2083
2204
  for (const filePath of files) {
2084
- const source = await (0, import_promises13.readFile)(filePath, "utf8");
2205
+ const source = await (0, import_promises14.readFile)(filePath, "utf8");
2085
2206
  usages.push(...collectMatches(filePath, source, PROCESS_ENV_DOT, "process-env"));
2086
2207
  usages.push(...collectMatches(filePath, source, PROCESS_ENV_BRACKET, "process-env"));
2087
2208
  usages.push(...collectMatches(filePath, source, IMPORT_META_ENV_DOT, "import-meta-env"));
@@ -2122,7 +2243,7 @@ function diffGraphs(previous, next) {
2122
2243
  }
2123
2244
 
2124
2245
  // src/watch/watchFiles.ts
2125
- var import_node_path12 = __toESM(require("path"), 1);
2246
+ var import_node_path13 = __toESM(require("path"), 1);
2126
2247
  async function watchFiles(runtime, root) {
2127
2248
  const manifest = await loadManifest(root ? { root } : {});
2128
2249
  const roots = Array.from(
@@ -2130,7 +2251,7 @@ async function watchFiles(runtime, root) {
2130
2251
  ).sort((left, right) => left.localeCompare(right));
2131
2252
  const files = Array.from(
2132
2253
  new Set(
2133
- Array.from(runtime.graph.entries.values()).map((entry) => entry.winner.origin?.file).filter((file) => Boolean(file)).map((file) => import_node_path12.default.resolve(manifest.repoRoot, file))
2254
+ Array.from(runtime.graph.entries.values()).map((entry) => entry.winner.origin?.file).filter((file) => Boolean(file)).map((file) => import_node_path13.default.resolve(manifest.repoRoot, file))
2134
2255
  )
2135
2256
  ).sort((left, right) => left.localeCompare(right));
2136
2257
  return {
@@ -2142,6 +2263,7 @@ async function watchFiles(runtime, root) {
2142
2263
  // Annotate the CommonJS export names for ESM import in node:
2143
2264
  0 && (module.exports = {
2144
2265
  CNOS_GRAPH_ENV_VAR,
2266
+ CNOS_PROJECTION_ENV_VAR,
2145
2267
  CNOS_SECRET_PAYLOAD_ENV_VAR,
2146
2268
  CNOS_SESSION_KEY_ENV_VAR,
2147
2269
  CnosAuthenticationError,
@@ -2155,6 +2277,7 @@ async function watchFiles(runtime, root) {
2155
2277
  deleteLocalSecret,
2156
2278
  deriveVaultKey,
2157
2279
  deserializeRuntimeGraph,
2280
+ deserializeServerProjection,
2158
2281
  detectLegacyVaultFormat,
2159
2282
  diffGraphs,
2160
2283
  ensureProjectionAllowed,
@@ -2175,6 +2298,7 @@ async function watchFiles(runtime, root) {
2175
2298
  readKeychain,
2176
2299
  readLocalSecret,
2177
2300
  readRuntimeGraphFromEnv,
2301
+ readServerProjectionFromEnv,
2178
2302
  readVaultMetadata,
2179
2303
  removeLocalVaultFiles,
2180
2304
  resolveCodegenPaths,
@@ -2191,6 +2315,7 @@ async function watchFiles(runtime, root) {
2191
2315
  scanEnvUsage,
2192
2316
  serializeRuntimeGraph,
2193
2317
  serializeSecretPayload,
2318
+ serializeServerProjection,
2194
2319
  stringifyYaml,
2195
2320
  validateRuntime,
2196
2321
  watchFiles,