@piyushdoorwar/yamlet 1.1.14 → 1.13.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/dist/yamlet.js +499 -221
- package/package.json +1 -1
package/dist/yamlet.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// cli/src/index.ts
|
|
4
|
-
import { existsSync as existsSync4, readFileSync, statSync as
|
|
4
|
+
import { existsSync as existsSync4, readFileSync, statSync as statSync3, writeFileSync } from "node:fs";
|
|
5
5
|
import { basename, resolve } from "node:path";
|
|
6
6
|
|
|
7
7
|
// core/src/requestExecutor.ts
|
|
@@ -293,6 +293,71 @@ function yamletUserAgent() {
|
|
|
293
293
|
|
|
294
294
|
// core/src/oauth2.ts
|
|
295
295
|
import { request } from "undici";
|
|
296
|
+
var OAuth2Error = class extends Error {
|
|
297
|
+
constructor(kind, message, status) {
|
|
298
|
+
super(message);
|
|
299
|
+
this.kind = kind;
|
|
300
|
+
this.status = status;
|
|
301
|
+
this.name = "OAuth2Error";
|
|
302
|
+
}
|
|
303
|
+
kind;
|
|
304
|
+
status;
|
|
305
|
+
};
|
|
306
|
+
var UNRESOLVED = /\{\{\s*([^{}\s]+)\s*\}\}/;
|
|
307
|
+
function resolveUrl(label, raw, resolve2) {
|
|
308
|
+
const url = resolve2(raw).trim();
|
|
309
|
+
if (!url) throw new OAuth2Error("config", `${label} is not set`);
|
|
310
|
+
const missing = UNRESOLVED.exec(url);
|
|
311
|
+
if (missing) throw new OAuth2Error("config", `${label} uses {{${missing[1]}}}, which is not defined in any active scope`);
|
|
312
|
+
let parsed;
|
|
313
|
+
try {
|
|
314
|
+
parsed = new URL(url);
|
|
315
|
+
} catch {
|
|
316
|
+
throw new OAuth2Error("config", `${label} is not a valid URL: ${url}`);
|
|
317
|
+
}
|
|
318
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new OAuth2Error("config", `${label} must start with http:// or https://`);
|
|
319
|
+
return url;
|
|
320
|
+
}
|
|
321
|
+
var NETWORK_HINTS = {
|
|
322
|
+
ENOTFOUND: "the host name could not be found",
|
|
323
|
+
EAI_AGAIN: "the host name could not be resolved",
|
|
324
|
+
ECONNREFUSED: "the connection was refused",
|
|
325
|
+
ECONNRESET: "the connection was reset",
|
|
326
|
+
ETIMEDOUT: "the connection timed out",
|
|
327
|
+
UND_ERR_CONNECT_TIMEOUT: "the connection timed out",
|
|
328
|
+
UND_ERR_HEADERS_TIMEOUT: "the server took too long to answer",
|
|
329
|
+
UND_ERR_BODY_TIMEOUT: "the server took too long to answer",
|
|
330
|
+
CERT_HAS_EXPIRED: "the server's TLS certificate has expired",
|
|
331
|
+
DEPTH_ZERO_SELF_SIGNED_CERT: "the server uses a self-signed TLS certificate",
|
|
332
|
+
SELF_SIGNED_CERT_IN_CHAIN: "the server's TLS certificate chain is self-signed",
|
|
333
|
+
UNABLE_TO_VERIFY_LEAF_SIGNATURE: "the server's TLS certificate could not be verified"
|
|
334
|
+
};
|
|
335
|
+
function networkError(url, err) {
|
|
336
|
+
const e = err;
|
|
337
|
+
const code = e?.cause?.code ?? e?.code;
|
|
338
|
+
const reason = (code && NETWORK_HINTS[code]) ?? e?.cause?.message ?? e?.message ?? String(err);
|
|
339
|
+
let host = url;
|
|
340
|
+
try {
|
|
341
|
+
host = new URL(url).host;
|
|
342
|
+
} catch {
|
|
343
|
+
}
|
|
344
|
+
return new OAuth2Error("network", `Could not reach the token URL (${host}): ${reason}`);
|
|
345
|
+
}
|
|
346
|
+
function providerError(status, text) {
|
|
347
|
+
let detail = "";
|
|
348
|
+
try {
|
|
349
|
+
const o = JSON.parse(text);
|
|
350
|
+
const err = typeof o.error === "string" ? o.error : typeof o.message === "string" ? o.message : "";
|
|
351
|
+
const desc = typeof o.error_description === "string" ? o.error_description : "";
|
|
352
|
+
detail = [err, desc].filter(Boolean).join(": ");
|
|
353
|
+
} catch {
|
|
354
|
+
const form = new URLSearchParams(text);
|
|
355
|
+
if (form.get("error")) detail = [form.get("error"), form.get("error_description")].filter(Boolean).join(": ");
|
|
356
|
+
}
|
|
357
|
+
if (!detail) detail = text.trim().replace(/\s+/g, " ").slice(0, 300) || "empty response";
|
|
358
|
+
const hint = /invalid_client/i.test(detail) ? " Check the client ID and secret, and whether the provider expects them in the Basic auth header or the request body." : /invalid_grant/i.test(detail) ? " The username, password, code or refresh token was rejected." : /invalid_scope/i.test(detail) ? " Check the scope value." : "";
|
|
359
|
+
return new OAuth2Error("provider", `The token endpoint answered ${status}: ${detail}.${hint}`, status);
|
|
360
|
+
}
|
|
296
361
|
function parseTokenBody(text, contentType) {
|
|
297
362
|
let raw;
|
|
298
363
|
if (contentType.includes("x-www-form-urlencoded") || !text.trim().startsWith("{") && text.includes("access_token=")) {
|
|
@@ -301,14 +366,14 @@ function parseTokenBody(text, contentType) {
|
|
|
301
366
|
try {
|
|
302
367
|
raw = JSON.parse(text);
|
|
303
368
|
} catch {
|
|
304
|
-
throw new
|
|
369
|
+
throw new OAuth2Error("provider", `The token endpoint did not return JSON: ${text.trim().slice(0, 200) || "empty response"}`);
|
|
305
370
|
}
|
|
306
371
|
}
|
|
307
372
|
const o = raw ?? {};
|
|
308
373
|
const accessToken = typeof o.access_token === "string" ? o.access_token : "";
|
|
309
374
|
if (!accessToken) {
|
|
310
|
-
const err = o.error ? `${o.error}${o.error_description ? `: ${o.error_description}` : ""}` : "no access_token
|
|
311
|
-
throw new
|
|
375
|
+
const err = o.error ? `${o.error}${o.error_description ? `: ${o.error_description}` : ""}` : "the response has no access_token";
|
|
376
|
+
throw new OAuth2Error("provider", `Token request failed: ${err}`);
|
|
312
377
|
}
|
|
313
378
|
const expires = Number(o.expires_in);
|
|
314
379
|
const result = { accessToken, raw };
|
|
@@ -318,8 +383,7 @@ function parseTokenBody(text, contentType) {
|
|
|
318
383
|
return result;
|
|
319
384
|
}
|
|
320
385
|
async function requestToken(cfg, resolve2, form, opts = {}) {
|
|
321
|
-
const url =
|
|
322
|
-
if (!url) throw new Error("OAuth 2.0 access token URL is not set");
|
|
386
|
+
const url = resolveUrl("Token URL", cfg.accessTokenUrl, resolve2);
|
|
323
387
|
const clientId = resolve2(cfg.clientId);
|
|
324
388
|
const clientSecret = resolve2(cfg.clientSecret);
|
|
325
389
|
const headers = {
|
|
@@ -333,17 +397,22 @@ async function requestToken(cfg, resolve2, form, opts = {}) {
|
|
|
333
397
|
} else if (clientId || clientSecret) {
|
|
334
398
|
headers.authorization = `Basic ${Buffer.from(`${clientId}:${clientSecret}`, "utf8").toString("base64")}`;
|
|
335
399
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
400
|
+
let res;
|
|
401
|
+
let text;
|
|
402
|
+
try {
|
|
403
|
+
res = await request(url, {
|
|
404
|
+
method: "POST",
|
|
405
|
+
headers,
|
|
406
|
+
body: body.toString(),
|
|
407
|
+
dispatcher: opts.dispatcher ?? defaultAgent(),
|
|
408
|
+
signal: opts.signal
|
|
409
|
+
});
|
|
410
|
+
text = await res.body.text();
|
|
411
|
+
} catch (err) {
|
|
412
|
+
if (opts.signal?.aborted) throw err;
|
|
413
|
+
throw networkError(url, err);
|
|
346
414
|
}
|
|
415
|
+
if (res.statusCode < 200 || res.statusCode >= 300) throw providerError(res.statusCode, text);
|
|
347
416
|
const ct = res.headers["content-type"];
|
|
348
417
|
return parseTokenBody(text, String(Array.isArray(ct) ? ct[0] : ct ?? ""));
|
|
349
418
|
}
|
|
@@ -1473,7 +1542,7 @@ ${opts.script}
|
|
|
1473
1542
|
|
|
1474
1543
|
// core/src/requestExecutor.ts
|
|
1475
1544
|
var MAX_REDIRECTS = 10;
|
|
1476
|
-
var
|
|
1545
|
+
var NETWORK_HINTS2 = {
|
|
1477
1546
|
ENOTFOUND: "host name could not be resolved",
|
|
1478
1547
|
EAI_AGAIN: "DNS lookup timed out",
|
|
1479
1548
|
ECONNREFUSED: "connection refused",
|
|
@@ -1507,8 +1576,8 @@ var errMessage2 = (e) => {
|
|
|
1507
1576
|
if (msg && msg !== "fetch failed") add(msg);
|
|
1508
1577
|
const hasInner = Array.isArray(err.errors) && err.errors.length > 0;
|
|
1509
1578
|
if (code && !msg && hasInner) {
|
|
1510
|
-
} else if (code && !msg.includes(code)) add(
|
|
1511
|
-
else if (code &&
|
|
1579
|
+
} else if (code && !msg.includes(code)) add(NETWORK_HINTS2[code] ? `${code}: ${NETWORK_HINTS2[code]}` : code);
|
|
1580
|
+
else if (code && NETWORK_HINTS2[code]) add(`(${NETWORK_HINTS2[code]})`);
|
|
1512
1581
|
if (Array.isArray(err.errors)) for (const inner of err.errors.slice(0, 3)) walk2(inner, depth + 1);
|
|
1513
1582
|
walk2(err.cause, depth + 1);
|
|
1514
1583
|
};
|
|
@@ -2021,49 +2090,53 @@ function parseDataFile(text, filename) {
|
|
|
2021
2090
|
}
|
|
2022
2091
|
|
|
2023
2092
|
// core/src/workspaceStore.ts
|
|
2024
|
-
import { existsSync as existsSync3, promises as fs2, statSync } from "node:fs";
|
|
2093
|
+
import { existsSync as existsSync3, promises as fs2, statSync as statSync2 } from "node:fs";
|
|
2025
2094
|
import path3 from "node:path";
|
|
2026
2095
|
|
|
2096
|
+
// core/src/localValues.ts
|
|
2097
|
+
var environmentScope = (id) => `environment:${id}`;
|
|
2098
|
+
var collectionScope = (id) => `collection:${id}`;
|
|
2099
|
+
var GLOBALS_SCOPE = "globals";
|
|
2100
|
+
|
|
2027
2101
|
// core/src/pathNaming.ts
|
|
2028
|
-
import { existsSync as existsSync2 } from "node:fs";
|
|
2102
|
+
import { existsSync as existsSync2, statSync } from "node:fs";
|
|
2029
2103
|
import path2 from "node:path";
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
let out = "";
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
lastHyphen = true;
|
|
2042
|
-
}
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
out = out.replace(/^-+|-+$/g, "");
|
|
2046
|
-
return out || fallback;
|
|
2047
|
-
}
|
|
2048
|
-
function uniqueFilePath(directory, fileName, except) {
|
|
2049
|
-
const ext = path2.extname(fileName);
|
|
2050
|
-
const base = fileName.slice(0, fileName.length - ext.length);
|
|
2051
|
-
let candidate = path2.join(directory, fileName);
|
|
2052
|
-
for (let n = 2; existsSync2(candidate) && candidate !== except; n++) {
|
|
2053
|
-
candidate = path2.join(directory, `${base}-${n}${ext}`);
|
|
2104
|
+
var RESERVED = /^(con|prn|aux|nul|com\d|lpt\d)$/i;
|
|
2105
|
+
function fileSafeName(name, fallback = "Untitled") {
|
|
2106
|
+
let out = (name ?? "").replace(/[<>:"/\\|?*\u0000-\u001f\u007f]/g, "-").replace(/\s+/g, " ").trim().replace(/^\.+/, "").replace(/[. ]+$/, "");
|
|
2107
|
+
if (out.length > 120) out = out.slice(0, 120).trimEnd();
|
|
2108
|
+
if (!out) return fallback;
|
|
2109
|
+
return RESERVED.test(out) ? `${out}_` : out;
|
|
2110
|
+
}
|
|
2111
|
+
function uniqueFilePath(directory, base, suffix, except) {
|
|
2112
|
+
let candidate = path2.join(directory, base + suffix);
|
|
2113
|
+
for (let n = 2; existsSync2(candidate) && !samePath(candidate, except); n++) {
|
|
2114
|
+
candidate = path2.join(directory, `${base} ${n}${suffix}`);
|
|
2054
2115
|
}
|
|
2055
2116
|
return candidate;
|
|
2056
2117
|
}
|
|
2057
2118
|
function uniqueDirectoryPath(parent, name, except) {
|
|
2058
2119
|
let candidate = path2.join(parent, name);
|
|
2059
|
-
for (let n = 2; existsSync2(candidate) && candidate
|
|
2060
|
-
candidate = path2.join(parent, `${name}
|
|
2120
|
+
for (let n = 2; existsSync2(candidate) && !samePath(candidate, except); n++) {
|
|
2121
|
+
candidate = path2.join(parent, `${name} ${n}`);
|
|
2061
2122
|
}
|
|
2062
2123
|
return candidate;
|
|
2063
2124
|
}
|
|
2125
|
+
function samePath(a, b) {
|
|
2126
|
+
if (!b) return false;
|
|
2127
|
+
if (a === b) return true;
|
|
2128
|
+
if (a.toLowerCase() !== b.toLowerCase()) return false;
|
|
2129
|
+
try {
|
|
2130
|
+
const x = statSync(a);
|
|
2131
|
+
const y = statSync(b);
|
|
2132
|
+
return x.ino === y.ino && x.dev === y.dev;
|
|
2133
|
+
} catch {
|
|
2134
|
+
return false;
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2064
2137
|
|
|
2065
2138
|
// core/src/yamlDtos.ts
|
|
2066
|
-
import { Document, isAlias, isMap, isScalar, isSeq, parseDocument,
|
|
2139
|
+
import { Document, isAlias, isMap, isScalar, isSeq, parseDocument, Scalar, visit } from "yaml";
|
|
2067
2140
|
function loadYaml(text) {
|
|
2068
2141
|
if (!text?.trim()) return {};
|
|
2069
2142
|
const doc = parseDocument(text, { uniqueKeys: false });
|
|
@@ -2087,26 +2160,57 @@ function loadYaml(text) {
|
|
|
2087
2160
|
};
|
|
2088
2161
|
return toPlain(doc.contents) ?? {};
|
|
2089
2162
|
}
|
|
2090
|
-
var
|
|
2091
|
-
function
|
|
2092
|
-
|
|
2163
|
+
var OPTS = { lineWidth: 0 };
|
|
2164
|
+
function styled(doc, style) {
|
|
2165
|
+
if (style?.quoteValues) {
|
|
2166
|
+
visit(doc, {
|
|
2167
|
+
Pair(_, pair) {
|
|
2168
|
+
if (isScalar(pair.key) && pair.key.value === "value" && isScalar(pair.value) && typeof pair.value.value === "string") {
|
|
2169
|
+
pair.value.type = Scalar.QUOTE_SINGLE;
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
});
|
|
2173
|
+
}
|
|
2174
|
+
return doc.toString(OPTS);
|
|
2093
2175
|
}
|
|
2094
|
-
function
|
|
2095
|
-
|
|
2176
|
+
function dumpYaml(value, style) {
|
|
2177
|
+
return styled(new Document(value), style);
|
|
2178
|
+
}
|
|
2179
|
+
function dumpYamlPreserving(value, original, known, style) {
|
|
2180
|
+
if (!original?.trim()) return dumpYaml(value, style);
|
|
2096
2181
|
try {
|
|
2097
2182
|
const orig = parseDocument(original);
|
|
2098
|
-
if (orig.errors.length || !isMap(orig.contents)) return dumpYaml(value);
|
|
2183
|
+
if (orig.errors.length || !isMap(orig.contents)) return dumpYaml(value, style);
|
|
2099
2184
|
const doc = new Document(value);
|
|
2100
2185
|
const target = doc.contents;
|
|
2101
|
-
if (!isMap(target)) return dumpYaml(value);
|
|
2186
|
+
if (!isMap(target)) return dumpYaml(value, style);
|
|
2102
2187
|
for (const pair of orig.contents.items) {
|
|
2103
2188
|
const key = isScalar(pair.key) ? String(pair.key.value) : void 0;
|
|
2104
2189
|
if (!key || known.has(key) || isDropped(key) || doc.has(key)) continue;
|
|
2105
2190
|
target.items.push(pair);
|
|
2106
2191
|
}
|
|
2107
|
-
return doc
|
|
2192
|
+
return styled(doc, style);
|
|
2108
2193
|
} catch {
|
|
2109
|
-
return dumpYaml(value);
|
|
2194
|
+
return dumpYaml(value, style);
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
function keepEquivalent(out, original, readers) {
|
|
2198
|
+
if (!original) return;
|
|
2199
|
+
for (const [key, read] of Object.entries(readers)) {
|
|
2200
|
+
if (!(key in out) || !(key in original)) continue;
|
|
2201
|
+
try {
|
|
2202
|
+
if (JSON.stringify(read(original[key])) === JSON.stringify(read(out[key]))) out[key] = original[key];
|
|
2203
|
+
} catch {
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
function parsedObj(text) {
|
|
2208
|
+
if (!text?.trim()) return void 0;
|
|
2209
|
+
try {
|
|
2210
|
+
const o = loadYaml(text);
|
|
2211
|
+
return isObj(o) && o.$kind !== void 0 ? o : void 0;
|
|
2212
|
+
} catch {
|
|
2213
|
+
return void 0;
|
|
2110
2214
|
}
|
|
2111
2215
|
}
|
|
2112
2216
|
var isDropped = (key) => /^_[a-z0-9]+_[a-z_]+$/i.test(key);
|
|
@@ -2158,6 +2262,7 @@ function readVariables(raw) {
|
|
|
2158
2262
|
return readRows(raw).map((r) => {
|
|
2159
2263
|
const v = { key: r.key, value: r.value, enabled: r.enabled };
|
|
2160
2264
|
if (str(r.o.type).toLowerCase() === "secret" || bool(r.o.secret) === true) v.secret = true;
|
|
2265
|
+
if (bool(r.o.local) === true) v.local = true;
|
|
2161
2266
|
return v;
|
|
2162
2267
|
});
|
|
2163
2268
|
}
|
|
@@ -2165,20 +2270,47 @@ function writeKeyValues(list) {
|
|
|
2165
2270
|
if (!list?.length) return void 0;
|
|
2166
2271
|
return list.map((kv) => {
|
|
2167
2272
|
const o = { key: kv.key, value: kv.value ?? "" };
|
|
2273
|
+
if (kv.enabled === false) o.disabled = true;
|
|
2168
2274
|
if (kv.description) o.description = kv.description;
|
|
2169
|
-
if (kv.enabled === false) o.enabled = false;
|
|
2170
2275
|
return o;
|
|
2171
2276
|
});
|
|
2172
2277
|
}
|
|
2173
2278
|
function writeVariables(list) {
|
|
2174
2279
|
if (!list?.length) return void 0;
|
|
2175
2280
|
return list.map((v) => {
|
|
2176
|
-
const o = { key: v.key, value: v.value ?? "" };
|
|
2281
|
+
const o = { key: v.key, value: v.local ? "" : v.value ?? "" };
|
|
2177
2282
|
if (v.secret) o.type = "secret";
|
|
2178
2283
|
if (v.enabled === false) o.enabled = false;
|
|
2284
|
+
if (v.local) o.local = true;
|
|
2179
2285
|
return o;
|
|
2180
2286
|
});
|
|
2181
2287
|
}
|
|
2288
|
+
function writeVariableMap(list) {
|
|
2289
|
+
const values = {};
|
|
2290
|
+
const settings = {};
|
|
2291
|
+
for (const v of list) {
|
|
2292
|
+
values[v.key] = v.local ? "" : v.value ?? "";
|
|
2293
|
+
const flags = {};
|
|
2294
|
+
if (v.enabled === false) flags.enabled = false;
|
|
2295
|
+
if (v.secret) flags.secret = true;
|
|
2296
|
+
if (v.local) flags.local = true;
|
|
2297
|
+
if (Object.keys(flags).length) settings[v.key] = flags;
|
|
2298
|
+
else delete settings[v.key];
|
|
2299
|
+
}
|
|
2300
|
+
return Object.keys(settings).length ? { values, settings } : { values };
|
|
2301
|
+
}
|
|
2302
|
+
function applyVariableSettings(vars, raw) {
|
|
2303
|
+
if (!isObj(raw)) return vars;
|
|
2304
|
+
return vars.map((v) => {
|
|
2305
|
+
const flags = raw[v.key];
|
|
2306
|
+
if (!isObj(flags)) return v;
|
|
2307
|
+
const out = { ...v };
|
|
2308
|
+
if (bool(flags.enabled) === false) out.enabled = false;
|
|
2309
|
+
if (bool(flags.secret) === true) out.secret = true;
|
|
2310
|
+
if (bool(flags.local) === true) out.local = true;
|
|
2311
|
+
return out;
|
|
2312
|
+
});
|
|
2313
|
+
}
|
|
2182
2314
|
function authType(raw) {
|
|
2183
2315
|
switch (str(raw).trim().toLowerCase()) {
|
|
2184
2316
|
case "bearer":
|
|
@@ -2258,15 +2390,15 @@ function readAuth(raw, fallback = "none") {
|
|
|
2258
2390
|
if (!isObj(raw)) return defaultAuth(fallback);
|
|
2259
2391
|
const type = raw.type === void 0 ? fallback : authType(raw.type);
|
|
2260
2392
|
const a = defaultAuth(type);
|
|
2261
|
-
const
|
|
2262
|
-
a.token =
|
|
2263
|
-
a.username =
|
|
2264
|
-
a.password =
|
|
2265
|
-
a.apiKeyName = credential(
|
|
2266
|
-
a.apiKeyValue = credential(
|
|
2267
|
-
a.apiKeyIn = (credential(
|
|
2268
|
-
a.cookie = str(raw.cookie);
|
|
2269
|
-
if (isObj(raw.credentials)) a.oauth2 = readOAuth2(raw.credentials);
|
|
2393
|
+
const cred = (block, key) => credential(raw[block], key) ?? credential(raw.credentials, key) ?? str(raw[key]);
|
|
2394
|
+
a.token = cred("bearer", "token");
|
|
2395
|
+
a.username = cred("basic", "username");
|
|
2396
|
+
a.password = cred("basic", "password");
|
|
2397
|
+
a.apiKeyName = credential(raw.apikey, "key") ?? (type === "apikey" ? credential(raw.credentials, "key") : void 0) ?? str(raw.key);
|
|
2398
|
+
a.apiKeyValue = credential(raw.apikey, "value") ?? (type === "apikey" ? credential(raw.credentials, "value") : void 0) ?? str(raw.value);
|
|
2399
|
+
a.apiKeyIn = (credential(raw.apikey, "in") ?? credential(raw.credentials, "in") ?? str(raw.in)).toLowerCase() === "query" ? "query" : "header";
|
|
2400
|
+
a.cookie = credential(raw.credentials, "cookie") ?? str(raw.cookie);
|
|
2401
|
+
if (raw.credentials && (type === "oauth2" || isObj(raw.credentials))) a.oauth2 = readOAuth2(raw.credentials);
|
|
2270
2402
|
else if (raw.oauth2 !== void 0) a.oauth2 = readOAuth2(raw.oauth2);
|
|
2271
2403
|
return a;
|
|
2272
2404
|
}
|
|
@@ -2288,24 +2420,83 @@ function writeOAuth2(o) {
|
|
|
2288
2420
|
put("accessToken", o.accessToken);
|
|
2289
2421
|
put("refreshToken", o.refreshToken);
|
|
2290
2422
|
if (o.headerPrefix && o.headerPrefix !== "Bearer") out.tokenType = o.headerPrefix;
|
|
2291
|
-
|
|
2292
|
-
|
|
2423
|
+
out.addTokenTo = o.addTokenTo === "query" ? "queryParams" : "header";
|
|
2424
|
+
out.client_authentication = o.clientAuthentication === "body" ? "body" : "header";
|
|
2293
2425
|
if (o.challengeAlgorithm === "plain") out.challengeAlgorithm = "plain";
|
|
2294
2426
|
return out;
|
|
2295
2427
|
}
|
|
2428
|
+
var MODELED_CREDENTIALS = new Set(
|
|
2429
|
+
[
|
|
2430
|
+
"token",
|
|
2431
|
+
"username",
|
|
2432
|
+
"password",
|
|
2433
|
+
"key",
|
|
2434
|
+
"value",
|
|
2435
|
+
"in",
|
|
2436
|
+
"cookie",
|
|
2437
|
+
"grant_type",
|
|
2438
|
+
"grantType",
|
|
2439
|
+
"accessToken",
|
|
2440
|
+
"access_token",
|
|
2441
|
+
"refreshToken",
|
|
2442
|
+
"refresh_token",
|
|
2443
|
+
"headerPrefix",
|
|
2444
|
+
"tokenType",
|
|
2445
|
+
"accessTokenUrl",
|
|
2446
|
+
"tokenUrl",
|
|
2447
|
+
"access_token_url",
|
|
2448
|
+
"authUrl",
|
|
2449
|
+
"authorizationUrl",
|
|
2450
|
+
"auth_url",
|
|
2451
|
+
"clientId",
|
|
2452
|
+
"client_id",
|
|
2453
|
+
"clientSecret",
|
|
2454
|
+
"client_secret",
|
|
2455
|
+
"scope",
|
|
2456
|
+
"redirect_uri",
|
|
2457
|
+
"redirectUri",
|
|
2458
|
+
"redirect_url",
|
|
2459
|
+
"callbackUrl",
|
|
2460
|
+
"addTokenTo",
|
|
2461
|
+
"client_authentication",
|
|
2462
|
+
"clientAuthentication",
|
|
2463
|
+
"challengeAlgorithm"
|
|
2464
|
+
].map((k) => k.toLowerCase())
|
|
2465
|
+
);
|
|
2466
|
+
var normalizedAuth = (v) => writeAuth(readAuth(v, "none"));
|
|
2467
|
+
function mergeAuth(ours, original) {
|
|
2468
|
+
if (!isObj(original) || str(original.type).toLowerCase() !== str(ours.type).toLowerCase() || !isObj(original.credentials)) return ours;
|
|
2469
|
+
const extra = Object.entries(original.credentials).filter(([k]) => !MODELED_CREDENTIALS.has(k.toLowerCase()));
|
|
2470
|
+
if (!extra.length) return ours;
|
|
2471
|
+
return { ...ours, credentials: { ...isObj(ours.credentials) ? ours.credentials : {}, ...Object.fromEntries(extra) } };
|
|
2472
|
+
}
|
|
2296
2473
|
function writeAuth(a) {
|
|
2297
2474
|
const out = { type: a.type === "none" ? "noauth" : a.type };
|
|
2475
|
+
const creds = {};
|
|
2298
2476
|
const put = (k, v) => {
|
|
2299
|
-
if (v)
|
|
2477
|
+
if (v) creds[k] = v;
|
|
2300
2478
|
};
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2479
|
+
switch (a.type) {
|
|
2480
|
+
case "bearer":
|
|
2481
|
+
put("token", a.token);
|
|
2482
|
+
break;
|
|
2483
|
+
case "basic":
|
|
2484
|
+
put("username", a.username);
|
|
2485
|
+
put("password", a.password);
|
|
2486
|
+
break;
|
|
2487
|
+
case "apikey":
|
|
2488
|
+
put("key", a.apiKeyName);
|
|
2489
|
+
put("value", a.apiKeyValue);
|
|
2490
|
+
creds.in = a.apiKeyIn === "query" ? "query" : "header";
|
|
2491
|
+
break;
|
|
2492
|
+
case "cookie":
|
|
2493
|
+
put("cookie", a.cookie);
|
|
2494
|
+
break;
|
|
2495
|
+
case "oauth2":
|
|
2496
|
+
Object.assign(creds, writeOAuth2(a.oauth2));
|
|
2497
|
+
break;
|
|
2498
|
+
}
|
|
2499
|
+
if (Object.keys(creds).length) out.credentials = creds;
|
|
2309
2500
|
return out;
|
|
2310
2501
|
}
|
|
2311
2502
|
function isPreScriptType(type) {
|
|
@@ -2329,10 +2520,11 @@ function readScripts(raw) {
|
|
|
2329
2520
|
}
|
|
2330
2521
|
return { pre: pre.join("\n\n"), post: post.join("\n\n") };
|
|
2331
2522
|
}
|
|
2332
|
-
|
|
2523
|
+
var SCRIPT_LANGUAGE = "text/javascript";
|
|
2524
|
+
function writeScripts(pre, post, prefix = "") {
|
|
2333
2525
|
const list = [];
|
|
2334
|
-
if (pre) list.push({ type:
|
|
2335
|
-
if (post) list.push({ type:
|
|
2526
|
+
if (pre) list.push({ type: `${prefix}beforeRequest`, code: pre, language: SCRIPT_LANGUAGE });
|
|
2527
|
+
if (post) list.push({ type: `${prefix}afterResponse`, code: post, language: SCRIPT_LANGUAGE });
|
|
2336
2528
|
return list.length ? list : void 0;
|
|
2337
2529
|
}
|
|
2338
2530
|
function bodyType(raw) {
|
|
@@ -2387,47 +2579,58 @@ function readBody(raw) {
|
|
|
2387
2579
|
if (fieldsRaw) b.fields = fieldsRaw.filter(isObj).map(readField);
|
|
2388
2580
|
else if (isObj(content) && (b.type === "form-data" || b.type === "urlencoded"))
|
|
2389
2581
|
b.fields = Object.entries(content).map(([key, value]) => ({ key, value: str(value), enabled: true }));
|
|
2390
|
-
const gql = isObj(raw.graphql) ? raw.graphql : raw;
|
|
2582
|
+
const gql = isObj(raw.graphql) ? raw.graphql : b.type === "graphql" && isObj(content) ? content : raw;
|
|
2391
2583
|
b.graphqlQuery = str(gql.query);
|
|
2392
2584
|
b.graphqlVariables = gql.variables === void 0 || gql.variables === null ? "" : typeof gql.variables === "string" ? gql.variables : JSON.stringify(gql.variables, null, 2);
|
|
2393
2585
|
if (b.type === "graphql" && !b.graphqlQuery && b.raw) b.graphqlQuery = b.raw;
|
|
2394
|
-
|
|
2586
|
+
const src = Array.isArray(raw.src) ? raw.src[0] : raw.src;
|
|
2587
|
+
b.binaryFile = str(raw.file ?? raw.binaryFile ?? (b.type === "binary" && typeof src === "string" ? src : ""));
|
|
2395
2588
|
if (b.type === "binary" && !b.binaryFile && b.raw) b.binaryFile = b.raw;
|
|
2396
2589
|
return b;
|
|
2397
2590
|
}
|
|
2398
|
-
function writeField(f) {
|
|
2399
|
-
const o = { type: f.isFile ? "file" : "text", key: f.key };
|
|
2400
|
-
if (f.isFile) o.src = f.value.startsWith("@") ? f.value.slice(1) : f.value;
|
|
2591
|
+
function writeField(f, multipart) {
|
|
2592
|
+
const o = multipart ? { type: f.isFile ? "file" : "text", key: f.key } : { key: f.key };
|
|
2593
|
+
if (multipart && f.isFile) o.src = [f.value.startsWith("@") ? f.value.slice(1) : f.value];
|
|
2401
2594
|
else o.value = f.value ?? "";
|
|
2595
|
+
if (f.enabled === false) o.disabled = true;
|
|
2402
2596
|
if (f.description) o.description = f.description;
|
|
2403
|
-
if (f.enabled === false) o.enabled = false;
|
|
2404
2597
|
return o;
|
|
2405
2598
|
}
|
|
2406
2599
|
var BODY_TYPE_NAMES = {
|
|
2407
2600
|
none: "none",
|
|
2408
|
-
raw: "
|
|
2601
|
+
raw: "text",
|
|
2409
2602
|
json: "json",
|
|
2410
2603
|
xml: "xml",
|
|
2411
2604
|
text: "text",
|
|
2412
2605
|
html: "html",
|
|
2413
|
-
"form-data": "
|
|
2414
|
-
urlencoded: "
|
|
2606
|
+
"form-data": "formdata",
|
|
2607
|
+
urlencoded: "urlencoded",
|
|
2415
2608
|
graphql: "graphql",
|
|
2416
|
-
binary: "
|
|
2609
|
+
binary: "file"
|
|
2417
2610
|
};
|
|
2418
2611
|
function writeBody(b) {
|
|
2419
2612
|
const out = { type: BODY_TYPE_NAMES[b.type] ?? "none" };
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2613
|
+
switch (b.type) {
|
|
2614
|
+
case "none":
|
|
2615
|
+
return void 0;
|
|
2616
|
+
case "form-data":
|
|
2617
|
+
case "urlencoded": {
|
|
2618
|
+
const fields = b.fields?.filter((f) => f.key?.trim() || f.value);
|
|
2619
|
+
out.content = (fields ?? []).map((f) => writeField(f, b.type === "form-data"));
|
|
2620
|
+
break;
|
|
2621
|
+
}
|
|
2622
|
+
case "graphql": {
|
|
2623
|
+
const g = { query: b.graphqlQuery ?? "" };
|
|
2624
|
+
if (b.graphqlVariables) g.variables = b.graphqlVariables;
|
|
2625
|
+
out.content = g;
|
|
2626
|
+
break;
|
|
2627
|
+
}
|
|
2628
|
+
case "binary":
|
|
2629
|
+
out.src = b.binaryFile ?? "";
|
|
2630
|
+
break;
|
|
2631
|
+
default:
|
|
2632
|
+
out.content = b.raw ?? "";
|
|
2633
|
+
}
|
|
2431
2634
|
return out;
|
|
2432
2635
|
}
|
|
2433
2636
|
function readSettings(o) {
|
|
@@ -2477,6 +2680,7 @@ function writeExamples(list) {
|
|
|
2477
2680
|
});
|
|
2478
2681
|
}
|
|
2479
2682
|
var REQUEST_KEYS = /* @__PURE__ */ new Set([
|
|
2683
|
+
"$kind",
|
|
2480
2684
|
"id",
|
|
2481
2685
|
"name",
|
|
2482
2686
|
"description",
|
|
@@ -2496,6 +2700,8 @@ var REQUEST_KEYS = /* @__PURE__ */ new Set([
|
|
|
2496
2700
|
"ssl",
|
|
2497
2701
|
"protocolProfileBehavior"
|
|
2498
2702
|
]);
|
|
2703
|
+
var REQUEST_KIND = "http-request";
|
|
2704
|
+
var COLLECTION_KIND = "collection";
|
|
2499
2705
|
function deriveNameFromFile(filePath) {
|
|
2500
2706
|
let name = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
2501
2707
|
name = name.replace(/\.ya?ml$/i, "");
|
|
@@ -2530,12 +2736,12 @@ function requestFromDto(raw, sourceFilePath) {
|
|
|
2530
2736
|
if (sourceFilePath) req.sourceFilePath = sourceFilePath;
|
|
2531
2737
|
return req;
|
|
2532
2738
|
}
|
|
2533
|
-
function requestToDto(r) {
|
|
2534
|
-
const o = {
|
|
2739
|
+
function requestToDto(r, filePath) {
|
|
2740
|
+
const o = { $kind: REQUEST_KIND };
|
|
2741
|
+
if (!filePath || deriveNameFromFile(filePath) !== r.name) o.name = r.name;
|
|
2535
2742
|
if (r.description) o.description = r.description;
|
|
2536
|
-
o.order = r.order ?? 0;
|
|
2537
|
-
o.method = (r.method || "GET").toUpperCase();
|
|
2538
2743
|
o.url = r.url ?? "";
|
|
2744
|
+
o.method = (r.method || "GET").toUpperCase();
|
|
2539
2745
|
const put = (k, v) => {
|
|
2540
2746
|
if (v !== void 0) o[k] = v;
|
|
2541
2747
|
};
|
|
@@ -2549,23 +2755,41 @@ function requestToDto(r) {
|
|
|
2549
2755
|
return x;
|
|
2550
2756
|
}) : void 0
|
|
2551
2757
|
);
|
|
2552
|
-
put("variables", writeVariables(r.variables));
|
|
2553
2758
|
if (r.auth && r.auth.type !== "inherit") o.auth = writeAuth(r.auth);
|
|
2554
2759
|
put("body", r.body ? writeBody(r.body) : void 0);
|
|
2555
2760
|
put("scripts", writeScripts(r.preRequestScript, r.postResponseScript));
|
|
2761
|
+
o.order = r.order ?? 0;
|
|
2762
|
+
o.id = r.id;
|
|
2763
|
+
put("variables", writeVariables(r.variables));
|
|
2556
2764
|
put("settings", writeSettings(r.settings));
|
|
2557
2765
|
put("examples", writeExamples(r.examples));
|
|
2558
2766
|
return o;
|
|
2559
2767
|
}
|
|
2560
|
-
|
|
2561
|
-
|
|
2768
|
+
var sameRows = (v) => readRows(v).map(({ o: _o, ...row }) => row);
|
|
2769
|
+
var REQUEST_READERS = {
|
|
2770
|
+
headers: sameRows,
|
|
2771
|
+
queryParams: sameRows,
|
|
2772
|
+
pathVariables: sameRows,
|
|
2773
|
+
variables: readVariables,
|
|
2774
|
+
auth: normalizedAuth,
|
|
2775
|
+
body: (v) => writeBody(readBody(v)),
|
|
2776
|
+
scripts: readScripts
|
|
2777
|
+
};
|
|
2778
|
+
function requestToYaml(r, original, filePath) {
|
|
2779
|
+
const out = requestToDto(r, filePath);
|
|
2780
|
+
const prev = parsedObj(original);
|
|
2781
|
+
if (isObj(out.auth)) out.auth = mergeAuth(out.auth, prev?.auth);
|
|
2782
|
+
keepEquivalent(out, prev, REQUEST_READERS);
|
|
2783
|
+
return dumpYamlPreserving(out, original, REQUEST_KEYS);
|
|
2562
2784
|
}
|
|
2563
2785
|
var COLLECTION_KEYS = /* @__PURE__ */ new Set([
|
|
2786
|
+
"$kind",
|
|
2564
2787
|
"id",
|
|
2565
2788
|
"name",
|
|
2566
2789
|
"description",
|
|
2567
2790
|
"order",
|
|
2568
2791
|
"variables",
|
|
2792
|
+
"variableSettings",
|
|
2569
2793
|
"auth",
|
|
2570
2794
|
"scripts",
|
|
2571
2795
|
// Read-only aliases from the collection v2.1 shape; migrated on save.
|
|
@@ -2606,9 +2830,12 @@ function applyCollectionMetadata(c, raw) {
|
|
|
2606
2830
|
}
|
|
2607
2831
|
function applyCollectionDefinition(c, raw) {
|
|
2608
2832
|
if (!isObj(raw)) return;
|
|
2833
|
+
if (str(raw.id).trim()) c.id = str(raw.id);
|
|
2609
2834
|
if (str(raw.name).trim()) c.name = str(raw.name);
|
|
2610
2835
|
if (raw.description) c.description = str(raw.description);
|
|
2611
|
-
const
|
|
2836
|
+
const order = int(raw.order);
|
|
2837
|
+
if (order !== void 0) c.order = order;
|
|
2838
|
+
const vars = applyVariableSettings(readVariables(raw.variables), raw.variableSettings);
|
|
2612
2839
|
if (vars.length) c.variables = vars;
|
|
2613
2840
|
const auth = Array.isArray(raw.auth) ? raw.auth.find(isObj) : isObj(raw.auth) ? raw.auth : void 0;
|
|
2614
2841
|
if (auth) c.auth = readAuth(auth, "none");
|
|
@@ -2617,20 +2844,27 @@ function applyCollectionDefinition(c, raw) {
|
|
|
2617
2844
|
if (scripts.post) c.postResponseScript = scripts.post;
|
|
2618
2845
|
}
|
|
2619
2846
|
function collectionToDto(c) {
|
|
2620
|
-
const o = {
|
|
2847
|
+
const o = { $kind: COLLECTION_KIND, name: c.name };
|
|
2621
2848
|
if (c.description) o.description = c.description;
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
if (c.auth && c.auth.type !== "none" && c.auth.type !== "inherit") o.auth = writeAuth(c.auth);
|
|
2626
|
-
const scripts = writeScripts(c.preRequestScript, c.postResponseScript);
|
|
2849
|
+
const vars = c.variables?.length ? writeVariableMap(c.variables) : void 0;
|
|
2850
|
+
if (vars) o.variables = vars.values;
|
|
2851
|
+
const scripts = writeScripts(c.preRequestScript, c.postResponseScript, "http:");
|
|
2627
2852
|
if (scripts) o.scripts = scripts;
|
|
2853
|
+
if (c.auth && c.auth.type !== "none" && c.auth.type !== "inherit") o.auth = writeAuth(c.auth);
|
|
2854
|
+
o.order = c.order ?? 0;
|
|
2855
|
+
o.id = c.id;
|
|
2856
|
+
if (vars?.settings) o.variableSettings = vars.settings;
|
|
2628
2857
|
return o;
|
|
2629
2858
|
}
|
|
2630
2859
|
function collectionToYaml(c, original) {
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2860
|
+
const out = collectionToDto(c);
|
|
2861
|
+
const prev = parsedObj(original);
|
|
2862
|
+
const prevAuth = Array.isArray(prev?.auth) ? prev.auth.find(isObj) : prev?.auth;
|
|
2863
|
+
if (isObj(out.auth)) out.auth = mergeAuth(out.auth, prevAuth);
|
|
2864
|
+
keepEquivalent(out, prev && { ...prev, auth: prevAuth }, { auth: normalizedAuth, scripts: readScripts });
|
|
2865
|
+
return dumpYamlPreserving(out, original, COLLECTION_KEYS);
|
|
2866
|
+
}
|
|
2867
|
+
var FOLDER_KEYS = /* @__PURE__ */ new Set(["$kind", "id", "name", "description", "order"]);
|
|
2634
2868
|
function applyFolderMetadata(f, raw) {
|
|
2635
2869
|
if (!isObj(raw)) return;
|
|
2636
2870
|
if (str(raw.id).trim()) f.id = str(raw.id);
|
|
@@ -2639,14 +2873,16 @@ function applyFolderMetadata(f, raw) {
|
|
|
2639
2873
|
const order = int(raw.order);
|
|
2640
2874
|
if (order !== void 0) f.order = order;
|
|
2641
2875
|
}
|
|
2642
|
-
function folderToDto(f) {
|
|
2643
|
-
const o = {
|
|
2876
|
+
function folderToDto(f, directoryName) {
|
|
2877
|
+
const o = { $kind: COLLECTION_KIND };
|
|
2878
|
+
if (directoryName === void 0 || directoryName !== f.name) o.name = f.name;
|
|
2644
2879
|
if (f.description) o.description = f.description;
|
|
2645
2880
|
o.order = f.order ?? 0;
|
|
2881
|
+
o.id = f.id;
|
|
2646
2882
|
return o;
|
|
2647
2883
|
}
|
|
2648
|
-
function folderToYaml(f, original) {
|
|
2649
|
-
return dumpYamlPreserving(folderToDto(f), original, FOLDER_KEYS);
|
|
2884
|
+
function folderToYaml(f, original, directoryName) {
|
|
2885
|
+
return dumpYamlPreserving(folderToDto(f, directoryName), original, FOLDER_KEYS);
|
|
2650
2886
|
}
|
|
2651
2887
|
var ENVIRONMENT_KEYS = /* @__PURE__ */ new Set(["id", "name", "variables", "values"]);
|
|
2652
2888
|
function deriveEnvironmentName(filePath) {
|
|
@@ -2664,23 +2900,20 @@ function environmentFromDto(raw, filePath) {
|
|
|
2664
2900
|
return env;
|
|
2665
2901
|
}
|
|
2666
2902
|
function environmentToDto(e) {
|
|
2667
|
-
|
|
2668
|
-
const vars = writeVariables(e.variables);
|
|
2669
|
-
if (vars) o.variables = vars;
|
|
2670
|
-
return o;
|
|
2903
|
+
return { name: e.name, values: writeVariables(e.variables) ?? [], id: e.id };
|
|
2671
2904
|
}
|
|
2672
2905
|
function environmentFromYaml(text, filePath) {
|
|
2673
2906
|
return environmentFromDto(loadYaml(text), filePath);
|
|
2674
2907
|
}
|
|
2675
2908
|
function environmentToYaml(e, original) {
|
|
2676
|
-
return dumpYamlPreserving(environmentToDto(e), original, ENVIRONMENT_KEYS);
|
|
2909
|
+
return dumpYamlPreserving(environmentToDto(e), original, ENVIRONMENT_KEYS, { quoteValues: true });
|
|
2677
2910
|
}
|
|
2678
2911
|
function globalsFromYaml(text) {
|
|
2679
2912
|
const o = loadYaml(text);
|
|
2680
2913
|
return isObj(o) ? readVariables(o.variables ?? o.values) : [];
|
|
2681
2914
|
}
|
|
2682
2915
|
function globalsToYaml(vars, original) {
|
|
2683
|
-
return dumpYamlPreserving({
|
|
2916
|
+
return dumpYamlPreserving({ name: "Globals", values: writeVariables(vars) ?? [] }, original, /* @__PURE__ */ new Set(["name", "variables", "values"]), { quoteValues: true });
|
|
2684
2917
|
}
|
|
2685
2918
|
|
|
2686
2919
|
// core/src/workspaceStore.ts
|
|
@@ -2688,14 +2921,17 @@ var ROOT_DIR_NAME = "yamlet";
|
|
|
2688
2921
|
var COLLECTIONS_DIR = "collections";
|
|
2689
2922
|
var ENVIRONMENTS_DIR = "environments";
|
|
2690
2923
|
var GLOBALS_DIR = "globals";
|
|
2691
|
-
var GLOBALS_FILE = "globals.yaml";
|
|
2692
|
-
var COLLECTION_FILE = "collection.yaml";
|
|
2693
|
-
var FOLDER_FILE = "folder.yaml";
|
|
2924
|
+
var GLOBALS_FILE = "workspace.globals.yaml";
|
|
2694
2925
|
var DEFINITION_FILE = path3.join(".resources", "definition.yaml");
|
|
2695
|
-
var
|
|
2926
|
+
var REQUEST_SUFFIX = ".request.yaml";
|
|
2927
|
+
var ENVIRONMENT_SUFFIX = ".environment.yaml";
|
|
2928
|
+
var LEGACY_COLLECTION_FILE = "collection.yaml";
|
|
2929
|
+
var LEGACY_FOLDER_FILE = "folder.yaml";
|
|
2930
|
+
var LEGACY_GLOBALS_FILE = "globals.yaml";
|
|
2931
|
+
var ORDER_STEP = 1e3;
|
|
2696
2932
|
var isDir = (p) => {
|
|
2697
2933
|
try {
|
|
2698
|
-
return
|
|
2934
|
+
return statSync2(p).isDirectory();
|
|
2699
2935
|
} catch {
|
|
2700
2936
|
return false;
|
|
2701
2937
|
}
|
|
@@ -2711,7 +2947,7 @@ function stableId(prefix, key) {
|
|
|
2711
2947
|
}
|
|
2712
2948
|
var ordinal = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
2713
2949
|
var byOrder = (list) => list.sort((a, b) => a.order - b.order);
|
|
2714
|
-
var nextOrder = (list) => list.reduce((m, x) => Math.max(m, (x.order ?? 0) +
|
|
2950
|
+
var nextOrder = (list) => list.reduce((m, x) => Math.max(m, (x.order ?? 0) + ORDER_STEP), ORDER_STEP);
|
|
2715
2951
|
async function readIfExists(p) {
|
|
2716
2952
|
if (!p) return void 0;
|
|
2717
2953
|
try {
|
|
@@ -2759,8 +2995,11 @@ function copyName(name) {
|
|
|
2759
2995
|
var WorkspaceStore = class _WorkspaceStore {
|
|
2760
2996
|
workspace;
|
|
2761
2997
|
queue = Promise.resolve();
|
|
2762
|
-
|
|
2998
|
+
local;
|
|
2999
|
+
constructor(workspace, options) {
|
|
2763
3000
|
this.workspace = workspace;
|
|
3001
|
+
this.local = options.localValues;
|
|
3002
|
+
if (this.local) applyLocalValues(workspace, this.local);
|
|
2764
3003
|
}
|
|
2765
3004
|
// ---- Opening -------------------------------------------------------------
|
|
2766
3005
|
/** The picked folder if it already holds collections/ + environments/ (or is named `yamlet`), else `<dir>/yamlet`. */
|
|
@@ -2774,22 +3013,22 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
2774
3013
|
return isDir(path3.join(_WorkspaceStore.resolveRoot(dir), COLLECTIONS_DIR));
|
|
2775
3014
|
}
|
|
2776
3015
|
/** Opens an existing workspace. Does not create anything on disk. */
|
|
2777
|
-
static async open(dir) {
|
|
3016
|
+
static async open(dir, options = {}) {
|
|
2778
3017
|
const root = _WorkspaceStore.resolveRoot(dir);
|
|
2779
3018
|
if (!isDir(root)) throw new Error(`No Yamlet workspace found at ${dir}`);
|
|
2780
|
-
return new _WorkspaceStore(await loadWorkspace(root));
|
|
3019
|
+
return new _WorkspaceStore(await loadWorkspace(root), options);
|
|
2781
3020
|
}
|
|
2782
3021
|
/** Creates the workspace skeleton (seeding globals) where missing, then opens it. */
|
|
2783
|
-
static async create(dir) {
|
|
3022
|
+
static async create(dir, options = {}) {
|
|
2784
3023
|
const root = _WorkspaceStore.resolveRoot(dir);
|
|
2785
3024
|
await fs2.mkdir(path3.join(root, COLLECTIONS_DIR), { recursive: true });
|
|
2786
3025
|
await fs2.mkdir(path3.join(root, ENVIRONMENTS_DIR), { recursive: true });
|
|
2787
3026
|
await fs2.mkdir(path3.join(root, GLOBALS_DIR), { recursive: true });
|
|
2788
3027
|
const globals = path3.join(root, GLOBALS_DIR, GLOBALS_FILE);
|
|
2789
|
-
if (!existsSync3(globals)) {
|
|
3028
|
+
if (!existsSync3(globals) && !existsSync3(path3.join(root, GLOBALS_DIR, LEGACY_GLOBALS_FILE))) {
|
|
2790
3029
|
await writeFileAtomic(globals, globalsToYaml([{ key: "appName", value: "Yamlet", enabled: true }]));
|
|
2791
3030
|
}
|
|
2792
|
-
return _WorkspaceStore.open(root);
|
|
3031
|
+
return _WorkspaceStore.open(root, options);
|
|
2793
3032
|
}
|
|
2794
3033
|
get rootPath() {
|
|
2795
3034
|
return this.workspace.rootPath;
|
|
@@ -2797,6 +3036,7 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
2797
3036
|
async reload() {
|
|
2798
3037
|
return this.enqueue(async () => {
|
|
2799
3038
|
this.workspace = await loadWorkspace(this.workspace.rootPath);
|
|
3039
|
+
if (this.local) applyLocalValues(this.workspace, this.local);
|
|
2800
3040
|
return this.workspace;
|
|
2801
3041
|
});
|
|
2802
3042
|
}
|
|
@@ -2870,12 +3110,13 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
2870
3110
|
return this.enqueue(async () => {
|
|
2871
3111
|
const ws = this.workspace;
|
|
2872
3112
|
await fs2.mkdir(ws.collectionsPath, { recursive: true });
|
|
2873
|
-
const
|
|
3113
|
+
const display = name.trim() || "New Collection";
|
|
3114
|
+
const dir = uniqueDirectoryPath(ws.collectionsPath, fileSafeName(display, "New Collection"));
|
|
2874
3115
|
await fs2.mkdir(dir, { recursive: true });
|
|
2875
3116
|
const c = newCollection({
|
|
2876
|
-
name:
|
|
3117
|
+
name: display,
|
|
2877
3118
|
directoryPath: dir,
|
|
2878
|
-
filePath: path3.join(dir,
|
|
3119
|
+
filePath: path3.join(dir, DEFINITION_FILE),
|
|
2879
3120
|
order: nextOrder(ws.collections)
|
|
2880
3121
|
});
|
|
2881
3122
|
await this.writeCollection(c);
|
|
@@ -2886,10 +3127,7 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
2886
3127
|
async updateCollection(id, patch) {
|
|
2887
3128
|
return this.enqueue(async () => {
|
|
2888
3129
|
const c = this.requireCollection(id);
|
|
2889
|
-
if (patch.name !== void 0 && patch.name.trim()
|
|
2890
|
-
c.name = patch.name;
|
|
2891
|
-
await this.renameNodeDir(c, this.workspace.collectionsPath, slugify(c.name, "collection"));
|
|
2892
|
-
}
|
|
3130
|
+
if (patch.name !== void 0 && patch.name.trim()) c.name = patch.name;
|
|
2893
3131
|
if (patch.description !== void 0) c.description = patch.description;
|
|
2894
3132
|
if (patch.variables !== void 0) c.variables = patch.variables;
|
|
2895
3133
|
if (patch.auth !== void 0) c.auth = patch.auth;
|
|
@@ -2903,6 +3141,7 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
2903
3141
|
return this.enqueue(async () => {
|
|
2904
3142
|
const c = this.requireCollection(id);
|
|
2905
3143
|
if (c.directoryPath) await fs2.rm(c.directoryPath, { recursive: true, force: true });
|
|
3144
|
+
await this.local?.set(collectionScope(c.id), void 0);
|
|
2906
3145
|
this.workspace.collections = this.workspace.collections.filter((x) => x !== c);
|
|
2907
3146
|
return c;
|
|
2908
3147
|
});
|
|
@@ -2912,13 +3151,13 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
2912
3151
|
const src = this.requireCollection(id);
|
|
2913
3152
|
if (!src.directoryPath) throw new Error("Collection has no directory");
|
|
2914
3153
|
const name = copyName(src.name);
|
|
2915
|
-
const dir = uniqueDirectoryPath(this.workspace.collectionsPath,
|
|
3154
|
+
const dir = uniqueDirectoryPath(this.workspace.collectionsPath, fileSafeName(name, "Collection"));
|
|
2916
3155
|
await fs2.cp(src.directoryPath, dir, { recursive: true });
|
|
2917
3156
|
const c = structuredClone(src);
|
|
2918
3157
|
repathTree(c, src.directoryPath, dir);
|
|
2919
3158
|
freshIds(c);
|
|
2920
3159
|
c.name = name;
|
|
2921
|
-
c.filePath = path3.join(dir,
|
|
3160
|
+
c.filePath = path3.join(dir, DEFINITION_FILE);
|
|
2922
3161
|
c.order = nextOrder(this.workspace.collections);
|
|
2923
3162
|
await this.writeTree(c);
|
|
2924
3163
|
this.workspace.collections.push(c);
|
|
@@ -2932,26 +3171,27 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
2932
3171
|
const c = structuredClone(imported);
|
|
2933
3172
|
freshIds(c);
|
|
2934
3173
|
c.name = c.name?.trim() || "Imported Collection";
|
|
2935
|
-
const dir = uniqueDirectoryPath(ws.collectionsPath,
|
|
3174
|
+
const dir = uniqueDirectoryPath(ws.collectionsPath, fileSafeName(c.name, "Imported Collection"));
|
|
2936
3175
|
await fs2.mkdir(dir, { recursive: true });
|
|
2937
3176
|
c.directoryPath = dir;
|
|
2938
|
-
c.filePath = path3.join(dir,
|
|
3177
|
+
c.filePath = path3.join(dir, DEFINITION_FILE);
|
|
2939
3178
|
c.order = nextOrder(ws.collections);
|
|
2940
3179
|
const placeAndWrite = async (node, nodeDir) => {
|
|
2941
3180
|
for (const [i, r] of node.requests.entries()) {
|
|
2942
|
-
r.order = i;
|
|
2943
|
-
r.sourceFilePath = uniqueFilePath(nodeDir,
|
|
2944
|
-
await writeFileAtomic(r.sourceFilePath, requestToYaml(r));
|
|
3181
|
+
r.order = (i + 1) * ORDER_STEP;
|
|
3182
|
+
r.sourceFilePath = uniqueFilePath(nodeDir, fileSafeName(r.name, "New Request"), REQUEST_SUFFIX);
|
|
3183
|
+
await writeFileAtomic(r.sourceFilePath, requestToYaml(r, void 0, r.sourceFilePath));
|
|
2945
3184
|
}
|
|
2946
3185
|
for (const [i, f] of node.folders.entries()) {
|
|
2947
|
-
f.order = i;
|
|
2948
|
-
f.directoryPath = uniqueDirectoryPath(nodeDir,
|
|
3186
|
+
f.order = (i + 1) * ORDER_STEP;
|
|
3187
|
+
f.directoryPath = uniqueDirectoryPath(nodeDir, fileSafeName(f.name, "New Folder"));
|
|
2949
3188
|
await fs2.mkdir(f.directoryPath, { recursive: true });
|
|
2950
|
-
await writeFileAtomic(path3.join(f.directoryPath,
|
|
3189
|
+
await writeFileAtomic(path3.join(f.directoryPath, DEFINITION_FILE), folderToYaml(f, void 0, path3.basename(f.directoryPath)));
|
|
2951
3190
|
await placeAndWrite(f, f.directoryPath);
|
|
2952
3191
|
}
|
|
2953
3192
|
};
|
|
2954
3193
|
await placeAndWrite(c, dir);
|
|
3194
|
+
await this.saveLocal(collectionScope(c.id), c.variables);
|
|
2955
3195
|
await writeFileAtomic(c.filePath, collectionToYaml(c));
|
|
2956
3196
|
ws.collections.push(c);
|
|
2957
3197
|
return c;
|
|
@@ -2961,9 +3201,10 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
2961
3201
|
async createFolder(collectionId, parentFolderId, name) {
|
|
2962
3202
|
return this.enqueue(async () => {
|
|
2963
3203
|
const { node, dir } = this.container(collectionId, parentFolderId);
|
|
2964
|
-
const
|
|
3204
|
+
const display = name.trim() || "New Folder";
|
|
3205
|
+
const folderDir = uniqueDirectoryPath(dir, fileSafeName(display, "New Folder"));
|
|
2965
3206
|
await fs2.mkdir(folderDir, { recursive: true });
|
|
2966
|
-
const f = newFolder({ name:
|
|
3207
|
+
const f = newFolder({ name: display, directoryPath: folderDir, order: nextOrder(node.folders) });
|
|
2967
3208
|
await this.writeFolder(f);
|
|
2968
3209
|
node.folders.push(f);
|
|
2969
3210
|
return f;
|
|
@@ -2974,11 +3215,7 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
2974
3215
|
const hit = this.locateFolder(id);
|
|
2975
3216
|
if (!hit) throw new Error(`Folder not found: ${id}`);
|
|
2976
3217
|
const f = hit.folder;
|
|
2977
|
-
if (patch.name !== void 0 && patch.name.trim()
|
|
2978
|
-
f.name = patch.name;
|
|
2979
|
-
const parentDir = path3.dirname(f.directoryPath);
|
|
2980
|
-
await this.renameNodeDir(f, parentDir, slugify(f.name, "folder"));
|
|
2981
|
-
}
|
|
3218
|
+
if (patch.name !== void 0 && patch.name.trim()) f.name = patch.name;
|
|
2982
3219
|
if (patch.description !== void 0) f.description = patch.description;
|
|
2983
3220
|
await this.writeFolder(f);
|
|
2984
3221
|
return f;
|
|
@@ -3000,7 +3237,7 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3000
3237
|
const src = hit.folder;
|
|
3001
3238
|
const srcDir = src.directoryPath;
|
|
3002
3239
|
const name = copyName(src.name);
|
|
3003
|
-
const dir = uniqueDirectoryPath(path3.dirname(srcDir),
|
|
3240
|
+
const dir = uniqueDirectoryPath(path3.dirname(srcDir), fileSafeName(name, "Folder"));
|
|
3004
3241
|
await fs2.cp(srcDir, dir, { recursive: true });
|
|
3005
3242
|
const f = structuredClone(src);
|
|
3006
3243
|
repathTree(f, srcDir, dir);
|
|
@@ -3017,16 +3254,16 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3017
3254
|
return this.enqueue(async () => {
|
|
3018
3255
|
const { node, dir } = this.container(collectionId, parentFolderId);
|
|
3019
3256
|
const name = init?.name?.trim() ? init.name : "New Request";
|
|
3020
|
-
const file = uniqueFilePath(dir,
|
|
3257
|
+
const file = uniqueFilePath(dir, fileSafeName(name, "New Request"), REQUEST_SUFFIX);
|
|
3021
3258
|
const id = init?.id && !this.idInUse(init.id) ? init.id : newId();
|
|
3022
3259
|
const r = newRequest({ ...init, id, name, sourceFilePath: file, order: nextOrder(node.requests) });
|
|
3023
3260
|
r.method = (r.method || "GET").toUpperCase();
|
|
3024
|
-
await writeFileAtomic(file, requestToYaml(r));
|
|
3261
|
+
await writeFileAtomic(file, requestToYaml(r, void 0, file));
|
|
3025
3262
|
node.requests.push(r);
|
|
3026
3263
|
return r;
|
|
3027
3264
|
});
|
|
3028
3265
|
}
|
|
3029
|
-
/** Replaces a request by id and rewrites its file, renaming the file
|
|
3266
|
+
/** Replaces a request by id and rewrites its file, renaming the file to match the name. */
|
|
3030
3267
|
async saveRequest(request3) {
|
|
3031
3268
|
return this.enqueue(async () => {
|
|
3032
3269
|
const hit = this.locateRequest(request3.id);
|
|
@@ -3038,17 +3275,9 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3038
3275
|
order: existing.order,
|
|
3039
3276
|
sourceFilePath: existing.sourceFilePath
|
|
3040
3277
|
};
|
|
3041
|
-
|
|
3042
|
-
if (!
|
|
3043
|
-
|
|
3044
|
-
const target = uniqueFilePath(path3.dirname(file), slugify(updated.name, "request") + REQUEST_EXTENSION, file);
|
|
3045
|
-
if (target !== file) {
|
|
3046
|
-
if (existsSync3(file)) await fs2.rename(file, target);
|
|
3047
|
-
file = target;
|
|
3048
|
-
}
|
|
3049
|
-
}
|
|
3050
|
-
updated.sourceFilePath = file;
|
|
3051
|
-
await writeFileAtomic(file, requestToYaml(updated, await readIfExists(file)));
|
|
3278
|
+
if (!existing.sourceFilePath) throw new Error("Request has no file path");
|
|
3279
|
+
if (!updated.name.trim()) updated.name = existing.name;
|
|
3280
|
+
await this.writeRequest(updated);
|
|
3052
3281
|
hit.list[hit.list.indexOf(existing)] = updated;
|
|
3053
3282
|
return updated;
|
|
3054
3283
|
});
|
|
@@ -3071,8 +3300,8 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3071
3300
|
r.id = newId();
|
|
3072
3301
|
for (const e of r.examples ?? []) e.id = newId();
|
|
3073
3302
|
r.name = copyName(src.name);
|
|
3074
|
-
r.sourceFilePath = uniqueFilePath(path3.dirname(src.sourceFilePath),
|
|
3075
|
-
await writeFileAtomic(r.sourceFilePath, requestToYaml(r, await readIfExists(src.sourceFilePath)));
|
|
3303
|
+
r.sourceFilePath = uniqueFilePath(path3.dirname(src.sourceFilePath), fileSafeName(r.name, "New Request"), REQUEST_SUFFIX);
|
|
3304
|
+
await writeFileAtomic(r.sourceFilePath, requestToYaml(r, await readIfExists(src.sourceFilePath), r.sourceFilePath));
|
|
3076
3305
|
hit.list.splice(hit.list.indexOf(src) + 1, 0, r);
|
|
3077
3306
|
await this.saveContainerOrder(hit.collection, hit.folders.at(-1) ?? null);
|
|
3078
3307
|
return r;
|
|
@@ -3094,7 +3323,7 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3094
3323
|
const r = hit2.request;
|
|
3095
3324
|
hit2.list.splice(hit2.list.indexOf(r), 1);
|
|
3096
3325
|
if (r.sourceFilePath && path3.dirname(r.sourceFilePath) !== target.dir) {
|
|
3097
|
-
const dest = uniqueFilePath(target.dir,
|
|
3326
|
+
const dest = uniqueFilePath(target.dir, fileSafeName(r.name, "New Request"), REQUEST_SUFFIX);
|
|
3098
3327
|
await fs2.rename(r.sourceFilePath, dest);
|
|
3099
3328
|
r.sourceFilePath = dest;
|
|
3100
3329
|
}
|
|
@@ -3128,13 +3357,15 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3128
3357
|
async saveContainerOrder(collection, folder) {
|
|
3129
3358
|
const node = folder ?? collection;
|
|
3130
3359
|
for (const [i, r] of node.requests.entries()) {
|
|
3131
|
-
|
|
3132
|
-
r.order
|
|
3133
|
-
|
|
3360
|
+
const order = (i + 1) * ORDER_STEP;
|
|
3361
|
+
if (r.order === order && existsSync3(r.sourceFilePath ?? "")) continue;
|
|
3362
|
+
r.order = order;
|
|
3363
|
+
if (r.sourceFilePath) await this.writeRequest(r);
|
|
3134
3364
|
}
|
|
3135
3365
|
for (const [i, f] of node.folders.entries()) {
|
|
3136
|
-
|
|
3137
|
-
f.order
|
|
3366
|
+
const order = (i + 1) * ORDER_STEP;
|
|
3367
|
+
if (f.order === order && existsSync3(path3.join(f.directoryPath ?? "", DEFINITION_FILE))) continue;
|
|
3368
|
+
f.order = order;
|
|
3138
3369
|
await this.writeFolder(f);
|
|
3139
3370
|
}
|
|
3140
3371
|
}
|
|
@@ -3142,8 +3373,9 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3142
3373
|
async createEnvironment(name) {
|
|
3143
3374
|
return this.enqueue(async () => {
|
|
3144
3375
|
const ws = this.workspace;
|
|
3145
|
-
const
|
|
3146
|
-
const
|
|
3376
|
+
const display = name.trim() || "New Environment";
|
|
3377
|
+
const file = uniqueFilePath(ws.environmentsPath, fileSafeName(display, "New Environment"), ENVIRONMENT_SUFFIX);
|
|
3378
|
+
const env = newEnvironment({ name: display, filePath: file });
|
|
3147
3379
|
await writeFileAtomic(file, environmentToYaml(env));
|
|
3148
3380
|
ws.environments.push(env);
|
|
3149
3381
|
return env;
|
|
@@ -3153,7 +3385,8 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3153
3385
|
return this.enqueue(async () => {
|
|
3154
3386
|
const ws = this.workspace;
|
|
3155
3387
|
const env = { ...structuredClone(imported), id: newId(), name: imported.name?.trim() || "Imported Environment" };
|
|
3156
|
-
env.filePath = uniqueFilePath(ws.environmentsPath,
|
|
3388
|
+
env.filePath = uniqueFilePath(ws.environmentsPath, fileSafeName(env.name, "Imported Environment"), ENVIRONMENT_SUFFIX);
|
|
3389
|
+
await this.saveLocal(environmentScope(env.id), env.variables);
|
|
3157
3390
|
await writeFileAtomic(env.filePath, environmentToYaml(env));
|
|
3158
3391
|
ws.environments.push(env);
|
|
3159
3392
|
return env;
|
|
@@ -3165,16 +3398,14 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3165
3398
|
const idx = list.findIndex((e) => e.id === env.id);
|
|
3166
3399
|
if (idx < 0) throw new Error(`Environment not found: ${env.id}`);
|
|
3167
3400
|
const existing = list[idx];
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
const updated = { ...structuredClone(env), filePath: file };
|
|
3177
|
-
await writeFileAtomic(file, environmentToYaml(updated, await readIfExists(file)));
|
|
3401
|
+
const name = env.name.trim() ? env.name : existing.name;
|
|
3402
|
+
const dir = existing.filePath ? path3.dirname(existing.filePath) : this.workspace.environmentsPath;
|
|
3403
|
+
const target = uniqueFilePath(dir, fileSafeName(name, "New Environment"), ENVIRONMENT_SUFFIX, existing.filePath);
|
|
3404
|
+
const original = await readIfExists(existing.filePath);
|
|
3405
|
+
if (existing.filePath && target !== existing.filePath && existsSync3(existing.filePath)) await fs2.rename(existing.filePath, target);
|
|
3406
|
+
const updated = { ...structuredClone(env), name, filePath: target };
|
|
3407
|
+
await this.saveLocal(environmentScope(updated.id), updated.variables);
|
|
3408
|
+
await writeFileAtomic(target, environmentToYaml(updated, original));
|
|
3178
3409
|
list[idx] = updated;
|
|
3179
3410
|
return updated;
|
|
3180
3411
|
});
|
|
@@ -3185,6 +3416,7 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3185
3416
|
const env = list.find((e) => e.id === id);
|
|
3186
3417
|
if (!env) throw new Error(`Environment not found: ${id}`);
|
|
3187
3418
|
if (env.filePath) await fs2.rm(env.filePath, { force: true });
|
|
3419
|
+
await this.local?.set(environmentScope(env.id), void 0);
|
|
3188
3420
|
list.splice(list.indexOf(env), 1);
|
|
3189
3421
|
return env;
|
|
3190
3422
|
});
|
|
@@ -3195,7 +3427,8 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3195
3427
|
const src = list.find((e) => e.id === id);
|
|
3196
3428
|
if (!src) throw new Error(`Environment not found: ${id}`);
|
|
3197
3429
|
const env = { ...structuredClone(src), id: newId(), name: copyName(src.name) };
|
|
3198
|
-
env.filePath = uniqueFilePath(this.workspace.environmentsPath,
|
|
3430
|
+
env.filePath = uniqueFilePath(this.workspace.environmentsPath, fileSafeName(env.name, "Environment"), ENVIRONMENT_SUFFIX);
|
|
3431
|
+
await this.saveLocal(environmentScope(env.id), env.variables);
|
|
3199
3432
|
await writeFileAtomic(env.filePath, environmentToYaml(env, await readIfExists(src.filePath)));
|
|
3200
3433
|
list.splice(list.indexOf(src) + 1, 0, env);
|
|
3201
3434
|
return env;
|
|
@@ -3204,7 +3437,10 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3204
3437
|
async saveGlobals(vars) {
|
|
3205
3438
|
return this.enqueue(async () => {
|
|
3206
3439
|
const file = path3.join(this.workspace.globalsPath, GLOBALS_FILE);
|
|
3207
|
-
|
|
3440
|
+
const legacy = path3.join(this.workspace.globalsPath, LEGACY_GLOBALS_FILE);
|
|
3441
|
+
await this.saveLocal(GLOBALS_SCOPE, vars);
|
|
3442
|
+
await writeFileAtomic(file, globalsToYaml(vars, await readIfExists(file) ?? await readIfExists(legacy)));
|
|
3443
|
+
await fs2.rm(legacy, { force: true });
|
|
3208
3444
|
this.workspace.globals = structuredClone(vars);
|
|
3209
3445
|
return this.workspace.globals;
|
|
3210
3446
|
});
|
|
@@ -3226,23 +3462,50 @@ var WorkspaceStore = class _WorkspaceStore {
|
|
|
3226
3462
|
await fs2.rename(from, to);
|
|
3227
3463
|
repathTree(node, from, to);
|
|
3228
3464
|
}
|
|
3465
|
+
/** Stores a scope's local values (the YAML gets blanks for them). */
|
|
3466
|
+
async saveLocal(scope, vars) {
|
|
3467
|
+
if (!this.local) return;
|
|
3468
|
+
const values = {};
|
|
3469
|
+
for (const v of vars) if (v.local && v.key) values[v.key] = v.value ?? "";
|
|
3470
|
+
await this.local.set(scope, values);
|
|
3471
|
+
}
|
|
3472
|
+
/** Writes a request, first renaming its file to `<name>.request.yaml` (older names move over). */
|
|
3473
|
+
async writeRequest(r) {
|
|
3474
|
+
const file = r.sourceFilePath;
|
|
3475
|
+
if (!file) return;
|
|
3476
|
+
const original = await readIfExists(file);
|
|
3477
|
+
const target = uniqueFilePath(path3.dirname(file), fileSafeName(r.name, "New Request"), REQUEST_SUFFIX, file);
|
|
3478
|
+
if (target !== file && existsSync3(file)) await fs2.rename(file, target);
|
|
3479
|
+
r.sourceFilePath = target;
|
|
3480
|
+
await writeFileAtomic(target, requestToYaml(r, original, target));
|
|
3481
|
+
}
|
|
3482
|
+
/** Writes `.resources/definition.yaml` (renaming the directory to match the name) and drops a legacy collection.yaml. */
|
|
3229
3483
|
async writeCollection(c) {
|
|
3230
3484
|
if (!c.directoryPath) throw new Error("Collection has no directory");
|
|
3231
|
-
c.
|
|
3232
|
-
|
|
3485
|
+
await this.renameNodeDir(c, this.workspace.collectionsPath, fileSafeName(c.name, "New Collection"));
|
|
3486
|
+
const file = path3.join(c.directoryPath, DEFINITION_FILE);
|
|
3487
|
+
const legacy = path3.join(c.directoryPath, LEGACY_COLLECTION_FILE);
|
|
3488
|
+
const original = await readIfExists(file) ?? await readIfExists(legacy);
|
|
3489
|
+
c.filePath = file;
|
|
3490
|
+
await this.saveLocal(collectionScope(c.id), c.variables);
|
|
3491
|
+
await writeFileAtomic(file, collectionToYaml(c, original));
|
|
3492
|
+
await fs2.rm(legacy, { force: true });
|
|
3233
3493
|
}
|
|
3234
3494
|
async writeFolder(f) {
|
|
3235
3495
|
if (!f.directoryPath) return;
|
|
3236
|
-
|
|
3237
|
-
|
|
3496
|
+
await this.renameNodeDir(f, path3.dirname(f.directoryPath), fileSafeName(f.name, "New Folder"));
|
|
3497
|
+
const dir = f.directoryPath;
|
|
3498
|
+
const file = path3.join(dir, DEFINITION_FILE);
|
|
3499
|
+
const legacy = path3.join(dir, LEGACY_FOLDER_FILE);
|
|
3500
|
+
const original = await readIfExists(file) ?? await readIfExists(legacy);
|
|
3501
|
+
await writeFileAtomic(file, folderToYaml(f, original, path3.basename(dir)));
|
|
3502
|
+
await fs2.rm(legacy, { force: true });
|
|
3238
3503
|
}
|
|
3239
3504
|
/** Rewrites every metadata and request file under a node (used after copying a directory). */
|
|
3240
3505
|
async writeTree(node) {
|
|
3241
3506
|
if ("variables" in node) await this.writeCollection(node);
|
|
3242
3507
|
else await this.writeFolder(node);
|
|
3243
|
-
for (const r of node.requests)
|
|
3244
|
-
if (r.sourceFilePath) await writeFileAtomic(r.sourceFilePath, requestToYaml(r, await readIfExists(r.sourceFilePath)));
|
|
3245
|
-
}
|
|
3508
|
+
for (const r of node.requests) await this.writeRequest(r);
|
|
3246
3509
|
for (const f of node.folders) await this.writeTree(f);
|
|
3247
3510
|
}
|
|
3248
3511
|
};
|
|
@@ -3278,7 +3541,7 @@ async function loadWorkspace(root) {
|
|
|
3278
3541
|
} catch {
|
|
3279
3542
|
}
|
|
3280
3543
|
}
|
|
3281
|
-
const globalsText = await readIfExists(path3.join(ws.globalsPath, GLOBALS_FILE));
|
|
3544
|
+
const globalsText = await readIfExists(path3.join(ws.globalsPath, GLOBALS_FILE)) ?? await readIfExists(path3.join(ws.globalsPath, LEGACY_GLOBALS_FILE));
|
|
3282
3545
|
if (globalsText) {
|
|
3283
3546
|
try {
|
|
3284
3547
|
ws.globals = globalsFromYaml(globalsText);
|
|
@@ -3294,11 +3557,11 @@ async function loadCollection(dir, rel) {
|
|
|
3294
3557
|
id: stableId("c", rel(dir)),
|
|
3295
3558
|
name: path3.basename(dir),
|
|
3296
3559
|
directoryPath: dir,
|
|
3297
|
-
filePath: path3.join(dir,
|
|
3560
|
+
filePath: path3.join(dir, DEFINITION_FILE)
|
|
3298
3561
|
});
|
|
3299
|
-
const definition = await readIfExists(
|
|
3562
|
+
const definition = await readIfExists(c.filePath);
|
|
3300
3563
|
if (definition) applyCollectionDefinition(c, loadYaml(definition));
|
|
3301
|
-
const metadata = await readIfExists(
|
|
3564
|
+
const metadata = await readIfExists(path3.join(dir, LEGACY_COLLECTION_FILE));
|
|
3302
3565
|
if (metadata) applyCollectionMetadata(c, loadYaml(metadata));
|
|
3303
3566
|
c.requests = await loadRequests(dir, rel);
|
|
3304
3567
|
c.folders = await loadFolders(dir, rel);
|
|
@@ -3310,8 +3573,9 @@ async function loadFolders(parentDir, rel) {
|
|
|
3310
3573
|
if (!entry.dir || entry.name.startsWith(".")) continue;
|
|
3311
3574
|
const dir = path3.join(parentDir, entry.name);
|
|
3312
3575
|
const f = newFolder({ id: stableId("f", rel(dir)), name: entry.name, directoryPath: dir });
|
|
3313
|
-
const
|
|
3314
|
-
|
|
3576
|
+
for (const file of [DEFINITION_FILE, LEGACY_FOLDER_FILE]) {
|
|
3577
|
+
const meta = await readIfExists(path3.join(dir, file));
|
|
3578
|
+
if (!meta) continue;
|
|
3315
3579
|
try {
|
|
3316
3580
|
applyFolderMetadata(f, loadYaml(meta));
|
|
3317
3581
|
} catch {
|
|
@@ -3328,11 +3592,12 @@ async function loadRequests(dir, rel) {
|
|
|
3328
3592
|
for (const entry of await listDir(dir)) {
|
|
3329
3593
|
if (entry.dir || !isYamlFile(entry.name)) continue;
|
|
3330
3594
|
const lower = entry.name.toLowerCase();
|
|
3331
|
-
if (lower ===
|
|
3595
|
+
if (lower === LEGACY_COLLECTION_FILE || lower === LEGACY_FOLDER_FILE) continue;
|
|
3332
3596
|
const file = path3.join(dir, entry.name);
|
|
3333
3597
|
try {
|
|
3334
3598
|
const raw = loadYaml(await fs2.readFile(file, "utf8"));
|
|
3335
3599
|
if (!isObj(raw)) continue;
|
|
3600
|
+
if (raw.$kind !== void 0 && String(raw.$kind) !== REQUEST_KIND) continue;
|
|
3336
3601
|
const r = requestFromDto(raw, file);
|
|
3337
3602
|
if (!String(raw.id ?? "").trim()) r.id = stableId("r", rel(file));
|
|
3338
3603
|
requests.push(r);
|
|
@@ -3362,9 +3627,22 @@ function ensureUniqueIds(ws, rel) {
|
|
|
3362
3627
|
}
|
|
3363
3628
|
for (const e of ws.environments) e.id = claim(e.id, "e", rel(e.filePath ?? e.name));
|
|
3364
3629
|
}
|
|
3630
|
+
function applyLocalValues(ws, local) {
|
|
3631
|
+
const overlay = (vars, stored) => {
|
|
3632
|
+
if (!stored) return;
|
|
3633
|
+
for (const v of vars) {
|
|
3634
|
+
if (!Object.hasOwn(stored, v.key) || !v.local && v.value) continue;
|
|
3635
|
+
v.value = stored[v.key];
|
|
3636
|
+
v.local = true;
|
|
3637
|
+
}
|
|
3638
|
+
};
|
|
3639
|
+
for (const e of ws.environments) overlay(e.variables, local.get(environmentScope(e.id)));
|
|
3640
|
+
for (const c of ws.collections) overlay(c.variables, local.get(collectionScope(c.id)));
|
|
3641
|
+
overlay(ws.globals, local.get(GLOBALS_SCOPE));
|
|
3642
|
+
}
|
|
3365
3643
|
|
|
3366
3644
|
// cli/src/index.ts
|
|
3367
|
-
var VERSION = true ? "1.
|
|
3645
|
+
var VERSION = true ? "1.13.0" : "0.0.0-dev";
|
|
3368
3646
|
setYamletVersion(VERSION);
|
|
3369
3647
|
var CliError = class extends Error {
|
|
3370
3648
|
};
|
|
@@ -3464,7 +3742,7 @@ function parseArgs(argv) {
|
|
|
3464
3742
|
function resolveEnvironment(ws, arg) {
|
|
3465
3743
|
if (!arg) return void 0;
|
|
3466
3744
|
const path4 = resolve(arg);
|
|
3467
|
-
if (existsSync4(path4) &&
|
|
3745
|
+
if (existsSync4(path4) && statSync3(path4).isFile()) return environmentFromYaml(readFileSync(path4, "utf8"), path4);
|
|
3468
3746
|
const stem = basename(arg).replace(/\.(environment\.)?ya?ml$/i, "");
|
|
3469
3747
|
const match = ws.environments.find((e) => e.name.toLowerCase() === arg.toLowerCase() || e.name.toLowerCase() === stem.toLowerCase());
|
|
3470
3748
|
if (!match) {
|
|
@@ -3562,7 +3840,7 @@ function formatJson(summary) {
|
|
|
3562
3840
|
}
|
|
3563
3841
|
async function run(args) {
|
|
3564
3842
|
const dir = resolve(args.workspace);
|
|
3565
|
-
if (!existsSync4(dir) || !
|
|
3843
|
+
if (!existsSync4(dir) || !statSync3(dir).isDirectory()) throw new CliError(`Workspace folder not found: ${dir}`);
|
|
3566
3844
|
if (!await WorkspaceStore.isWorkspace(dir)) throw new CliError(`Not a Yamlet workspace (no collections/ and environments/): ${dir}`);
|
|
3567
3845
|
const store = await WorkspaceStore.open(dir);
|
|
3568
3846
|
const ws = store.workspace;
|