@verentis/cli 0.2.6 → 0.2.8
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/dist/index.js +75 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -246,15 +246,16 @@ ${truncate(text)}` : ""}`);
|
|
|
246
246
|
}
|
|
247
247
|
const headers = { Authorization: `Bearer ${token}` };
|
|
248
248
|
let body;
|
|
249
|
+
const isFormData = options.rawBody instanceof FormData;
|
|
249
250
|
if (options.rawBody !== void 0) {
|
|
250
251
|
body = options.rawBody;
|
|
251
|
-
headers["Content-Type"] = options.contentType ?? "application/octet-stream";
|
|
252
|
+
if (!isFormData) headers["Content-Type"] = options.contentType ?? "application/octet-stream";
|
|
252
253
|
} else if (options.body !== void 0) {
|
|
253
254
|
body = JSON.stringify(options.body);
|
|
254
255
|
headers["Content-Type"] = "application/json";
|
|
255
256
|
}
|
|
256
257
|
for (const [key, value] of Object.entries(options.headers ?? {})) headers[key] = value;
|
|
257
|
-
const res = await fetch(url, { method, headers, body, ...insecureFetchOptions(insecure), ...options.rawBody !== void 0 ? { duplex: "half" } : {} });
|
|
258
|
+
const res = await fetch(url, { method, headers, body, ...insecureFetchOptions(insecure), ...options.rawBody !== void 0 && !isFormData ? { duplex: "half" } : {} });
|
|
258
259
|
if (!res.ok) {
|
|
259
260
|
const text = await res.text().catch(() => "");
|
|
260
261
|
throw new ApiError(res.status, `${method} ${path} failed (${res.status}). ${hintFor(res.status)}${formatErrorBody(text)}`);
|
|
@@ -1038,6 +1039,7 @@ function validateManifest(manifest) {
|
|
|
1038
1039
|
validateApplicationLaunch(spec, issues);
|
|
1039
1040
|
}
|
|
1040
1041
|
validateInstallDirectives(spec.install, issues);
|
|
1042
|
+
validateSettingDeclarations(kind, spec, issues);
|
|
1041
1043
|
for (const message of validateMarketplaceSection(manifest.marketplace)) error(message);
|
|
1042
1044
|
const packaging = manifest.packaging ?? {};
|
|
1043
1045
|
if (!packaging.publisher) warning("packaging.publisher is not set \u2014 `verentis pack`/`publish` will refuse the package");
|
|
@@ -1120,6 +1122,72 @@ function validateInstallDirectives(install, issues) {
|
|
|
1120
1122
|
}
|
|
1121
1123
|
});
|
|
1122
1124
|
}
|
|
1125
|
+
var SETTING_KEY_PATTERN = /^[a-z0-9]+([.-][a-z0-9]+)*$/;
|
|
1126
|
+
var ENV_VAR_PATTERN = /^[A-Z][A-Z0-9_]*$/;
|
|
1127
|
+
var SETTING_TYPES = ["string", "number", "boolean", "select"];
|
|
1128
|
+
function validateSettingDeclarations(kind, spec, issues) {
|
|
1129
|
+
const settings2 = spec.settings;
|
|
1130
|
+
if (settings2 === void 0) return;
|
|
1131
|
+
const error = (message) => issues.push({ level: "error", message });
|
|
1132
|
+
if (!Array.isArray(settings2)) {
|
|
1133
|
+
error("spec.settings must be a list of setting declarations");
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1137
|
+
settings2.forEach((declaration, index) => {
|
|
1138
|
+
const at = `spec.settings[${index}]`;
|
|
1139
|
+
if (!declaration || typeof declaration !== "object" || Array.isArray(declaration)) {
|
|
1140
|
+
error(`${at} must be an object with at least a 'key'`);
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
if (!declaration.key || typeof declaration.key !== "string") {
|
|
1144
|
+
error(`${at}.key is required (short key, stored namespaced as '{metadata.name}.{key}')`);
|
|
1145
|
+
} else if (!SETTING_KEY_PATTERN.test(declaration.key)) {
|
|
1146
|
+
error(`${at}.key '${declaration.key}' must be lowercase kebab/dot notation (e.g. 'api-key' or 'smtp.host')`);
|
|
1147
|
+
} else if (seen.has(declaration.key)) {
|
|
1148
|
+
error(`${at}.key '${declaration.key}' is declared more than once`);
|
|
1149
|
+
} else {
|
|
1150
|
+
seen.add(declaration.key);
|
|
1151
|
+
}
|
|
1152
|
+
if (declaration.env !== void 0) {
|
|
1153
|
+
if (typeof declaration.env !== "string" || !ENV_VAR_PATTERN.test(declaration.env)) {
|
|
1154
|
+
error(`${at}.env must be an environment variable name (UPPER_SNAKE_CASE, e.g. 'MY_API_KEY')`);
|
|
1155
|
+
} else if (declaration.env.startsWith("VERENTIS_")) {
|
|
1156
|
+
error(`${at}.env must not use the reserved 'VERENTIS_' prefix`);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
for (const flag of ["secret", "required"]) {
|
|
1160
|
+
if (declaration[flag] !== void 0 && typeof declaration[flag] !== "boolean") {
|
|
1161
|
+
error(`${at}.${flag} must be a boolean`);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
const type = declaration.type ?? "string";
|
|
1165
|
+
if (typeof type !== "string" || !SETTING_TYPES.includes(type)) {
|
|
1166
|
+
error(`${at}.type must be one of ${SETTING_TYPES.join(", ")} (got '${String(declaration.type)}')`);
|
|
1167
|
+
}
|
|
1168
|
+
if (declaration.secret === true && declaration.default !== void 0) {
|
|
1169
|
+
error(`${at} declares a default for a secret \u2014 secrets must never ship default values`);
|
|
1170
|
+
}
|
|
1171
|
+
if (type === "select") {
|
|
1172
|
+
if (!Array.isArray(declaration.options) || declaration.options.length === 0) {
|
|
1173
|
+
error(`${at}.options is required for type 'select'`);
|
|
1174
|
+
}
|
|
1175
|
+
} else if (declaration.options !== void 0) {
|
|
1176
|
+
error(`${at}.options is only allowed for type 'select'`);
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
if (kind === "ExecutionEngine") {
|
|
1180
|
+
const declaresSecret = settings2.some(
|
|
1181
|
+
(declaration) => declaration && typeof declaration === "object" && declaration.secret === true
|
|
1182
|
+
);
|
|
1183
|
+
const runtimes = spec.runtimes ?? {};
|
|
1184
|
+
if (declaresSecret && !runtimes["docker"]) {
|
|
1185
|
+
error(
|
|
1186
|
+
"spec.settings declares secrets but the engine has no docker runtime \u2014 secrets are server-only and cannot be provisioned into browser (wasm) runs"
|
|
1187
|
+
);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1123
1191
|
|
|
1124
1192
|
// src/commands/dev.ts
|
|
1125
1193
|
async function resolveDevScopes(flags) {
|
|
@@ -1479,15 +1547,15 @@ var normalizeRemote = (path) => {
|
|
|
1479
1547
|
return trimmed;
|
|
1480
1548
|
};
|
|
1481
1549
|
async function uploadWithHeaders(api, workspaceId, remotePath, localPath, options) {
|
|
1482
|
-
const
|
|
1550
|
+
const buffer = await readFile(localPath);
|
|
1551
|
+
const form = new FormData();
|
|
1552
|
+
form.append("file", new Blob([buffer], { type: options.contentType ?? "application/octet-stream" }), basename(localPath));
|
|
1553
|
+
form.append("metadata", JSON.stringify({ branch: options.branch, contentType: options.contentType }));
|
|
1483
1554
|
const res = await api.requestRaw("POST", fillPath(files.upload.path, { path: normalizeRemote(remotePath) }), {
|
|
1484
1555
|
scopes: [...files.upload.scopes],
|
|
1485
1556
|
tokenWorkspaceId: workspaceId,
|
|
1486
|
-
rawBody:
|
|
1487
|
-
contentType: options.contentType ?? "application/octet-stream",
|
|
1557
|
+
rawBody: form,
|
|
1488
1558
|
headers: {
|
|
1489
|
-
"X-Branch": options.branch,
|
|
1490
|
-
"Content-Disposition": `attachment; filename="${basename(localPath)}"`,
|
|
1491
1559
|
...options.autoExtract ? { "X-Auto-Extract": "true" } : {}
|
|
1492
1560
|
}
|
|
1493
1561
|
});
|