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