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