@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.
Files changed (2) hide show
  1. package/dist/yamlet.js +499 -221
  2. 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 statSync2, writeFileSync } from "node:fs";
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 Error(`Token endpoint returned a non-JSON response: ${text.slice(0, 200)}`);
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 in response";
311
- throw new Error(`Token request failed: ${err}`);
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 = resolve2(cfg.accessTokenUrl).trim();
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
- const res = await request(url, {
337
- method: "POST",
338
- headers,
339
- body: body.toString(),
340
- dispatcher: opts.dispatcher ?? defaultAgent(),
341
- signal: opts.signal
342
- });
343
- const text = await res.body.text();
344
- if (res.statusCode < 200 || res.statusCode >= 300) {
345
- throw new Error(`Token request failed (${res.statusCode}): ${text.slice(0, 500)}`);
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 NETWORK_HINTS = {
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(NETWORK_HINTS[code] ? `${code}: ${NETWORK_HINTS[code]}` : code);
1511
- else if (code && NETWORK_HINTS[code]) add(`(${NETWORK_HINTS[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
- function slugify(name, fallback = "untitled") {
2031
- if (!name?.trim()) return fallback;
2032
- let out = "";
2033
- let lastHyphen = false;
2034
- for (const ch of name.trim().toLowerCase()) {
2035
- if (/[a-z0-9]/.test(ch)) {
2036
- out += ch;
2037
- lastHyphen = false;
2038
- } else if (" -_./".includes(ch)) {
2039
- if (!lastHyphen && out.length > 0) {
2040
- out += "-";
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 !== except; n++) {
2060
- candidate = path2.join(parent, `${name}-${n}`);
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, stringify } from "yaml";
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 STRINGIFY_OPTS = { lineWidth: 0 };
2091
- function dumpYaml(value) {
2092
- return stringify(value, STRINGIFY_OPTS);
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 dumpYamlPreserving(value, original, known) {
2095
- if (!original?.trim()) return dumpYaml(value);
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.toString(STRINGIFY_OPTS);
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 typed = (name) => raw[name];
2262
- a.token = credential(typed("bearer"), "token") ?? str(raw.token);
2263
- a.username = credential(typed("basic"), "username") ?? str(raw.username);
2264
- a.password = credential(typed("basic"), "password") ?? str(raw.password);
2265
- a.apiKeyName = credential(typed("apikey"), "key") ?? str(raw.key);
2266
- a.apiKeyValue = credential(typed("apikey"), "value") ?? str(raw.value);
2267
- a.apiKeyIn = (credential(typed("apikey"), "in") ?? str(raw.in)).toLowerCase() === "query" ? "query" : "header";
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
- if (o.addTokenTo === "query") out.addTokenTo = "queryParams";
2292
- if (o.clientAuthentication === "body") out.client_authentication = "body";
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) out[k] = v;
2477
+ if (v) creds[k] = v;
2300
2478
  };
2301
- put("token", a.token);
2302
- put("username", a.username);
2303
- put("password", a.password);
2304
- put("key", a.apiKeyName);
2305
- put("value", a.apiKeyValue);
2306
- if (a.type === "apikey") out.in = a.apiKeyIn === "query" ? "query" : "header";
2307
- put("cookie", a.cookie);
2308
- if (a.type === "oauth2") out.credentials = writeOAuth2(a.oauth2);
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
- function writeScripts(pre, post) {
2523
+ var SCRIPT_LANGUAGE = "text/javascript";
2524
+ function writeScripts(pre, post, prefix = "") {
2333
2525
  const list = [];
2334
- if (pre) list.push({ type: "preRequest", code: pre });
2335
- if (post) list.push({ type: "afterResponse", code: post });
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
- b.binaryFile = str(raw.file ?? raw.binaryFile ?? (b.type === "binary" && typeof raw.src === "string" ? raw.src : ""));
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: "raw",
2601
+ raw: "text",
2409
2602
  json: "json",
2410
2603
  xml: "xml",
2411
2604
  text: "text",
2412
2605
  html: "html",
2413
- "form-data": "form-data",
2414
- urlencoded: "x-www-form-urlencoded",
2606
+ "form-data": "formdata",
2607
+ urlencoded: "urlencoded",
2415
2608
  graphql: "graphql",
2416
- binary: "binary"
2609
+ binary: "file"
2417
2610
  };
2418
2611
  function writeBody(b) {
2419
2612
  const out = { type: BODY_TYPE_NAMES[b.type] ?? "none" };
2420
- if (b.raw) out.raw = b.raw;
2421
- const fields = b.fields?.filter((f) => f.key?.trim() || f.value);
2422
- if (fields?.length) out.content = fields.map(writeField);
2423
- if (b.graphqlQuery || b.graphqlVariables) {
2424
- const g = {};
2425
- if (b.graphqlQuery) g.query = b.graphqlQuery;
2426
- if (b.graphqlVariables) g.variables = b.graphqlVariables;
2427
- out.graphql = g;
2428
- }
2429
- if (b.binaryFile) out.file = b.binaryFile;
2430
- if (b.type === "none" && Object.keys(out).length === 1) return void 0;
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 = { id: r.id, name: r.name };
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
- function requestToYaml(r, original) {
2561
- return dumpYamlPreserving(requestToDto(r), original, REQUEST_KEYS);
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 vars = readVariables(raw.variables);
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 = { id: c.id, name: c.name };
2847
+ const o = { $kind: COLLECTION_KIND, name: c.name };
2621
2848
  if (c.description) o.description = c.description;
2622
- o.order = c.order ?? 0;
2623
- const vars = writeVariables(c.variables);
2624
- if (vars) o.variables = vars;
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
- return dumpYamlPreserving(collectionToDto(c), original, COLLECTION_KEYS);
2632
- }
2633
- var FOLDER_KEYS = /* @__PURE__ */ new Set(["id", "name", "description", "order"]);
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 = { id: f.id, name: f.name };
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
- const o = { id: e.id, name: e.name };
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({ variables: writeVariables(vars) ?? [] }, original, /* @__PURE__ */ new Set(["variables", "values"]));
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 REQUEST_EXTENSION = ".yaml";
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 statSync(p).isDirectory();
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) + 1), 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
- constructor(workspace) {
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 dir = uniqueDirectoryPath(ws.collectionsPath, slugify(name, "collection"));
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: name.trim() || "New Collection",
3117
+ name: display,
2877
3118
  directoryPath: dir,
2878
- filePath: path3.join(dir, COLLECTION_FILE),
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() && patch.name !== c.name) {
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, slugify(name, "collection"));
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, COLLECTION_FILE);
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, slugify(c.name, "collection"));
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, COLLECTION_FILE);
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, slugify(r.name, "request") + REQUEST_EXTENSION);
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, slugify(f.name, "folder"));
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, FOLDER_FILE), folderToYaml(f));
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 folderDir = uniqueDirectoryPath(dir, slugify(name, "folder"));
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: name.trim() || "New Folder", directoryPath: folderDir, order: nextOrder(node.folders) });
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() && patch.name !== f.name) {
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), slugify(name, "folder"));
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, slugify(name, "request") + REQUEST_EXTENSION);
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 when the name changed. */
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
- let file = existing.sourceFilePath;
3042
- if (!file) throw new Error("Request has no file path");
3043
- if (updated.name !== existing.name && updated.name.trim()) {
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), slugify(r.name, "request") + REQUEST_EXTENSION);
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, path3.basename(r.sourceFilePath));
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
- if (r.order === i && existsSync3(r.sourceFilePath ?? "")) continue;
3132
- r.order = i;
3133
- if (r.sourceFilePath) await writeFileAtomic(r.sourceFilePath, requestToYaml(r, await readIfExists(r.sourceFilePath)));
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
- if (f.order === i && existsSync3(path3.join(f.directoryPath ?? "", FOLDER_FILE))) continue;
3137
- f.order = i;
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 file = uniqueFilePath(ws.environmentsPath, slugify(name, "environment") + ".yaml");
3146
- const env = newEnvironment({ name: name.trim() || "New Environment", filePath: file });
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, slugify(env.name, "environment") + ".yaml");
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
- let file = existing.filePath ?? uniqueFilePath(this.workspace.environmentsPath, slugify(env.name, "environment") + ".yaml");
3169
- if (env.name !== existing.name && env.name.trim() && existing.filePath) {
3170
- const target = uniqueFilePath(path3.dirname(file), slugify(env.name, "environment") + ".yaml", file);
3171
- if (target !== file) {
3172
- if (existsSync3(file)) await fs2.rename(file, target);
3173
- file = target;
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, slugify(env.name, "environment") + ".yaml");
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
- await writeFileAtomic(file, globalsToYaml(vars, await readIfExists(file)));
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.filePath = path3.join(c.directoryPath, COLLECTION_FILE);
3232
- await writeFileAtomic(c.filePath, collectionToYaml(c, await readIfExists(c.filePath)));
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
- const file = path3.join(f.directoryPath, FOLDER_FILE);
3237
- await writeFileAtomic(file, folderToYaml(f, await readIfExists(file)));
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, COLLECTION_FILE)
3560
+ filePath: path3.join(dir, DEFINITION_FILE)
3298
3561
  });
3299
- const definition = await readIfExists(path3.join(dir, DEFINITION_FILE));
3562
+ const definition = await readIfExists(c.filePath);
3300
3563
  if (definition) applyCollectionDefinition(c, loadYaml(definition));
3301
- const metadata = await readIfExists(c.filePath);
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 meta = await readIfExists(path3.join(dir, FOLDER_FILE));
3314
- if (meta) {
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 === COLLECTION_FILE || lower === FOLDER_FILE) continue;
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.1.14" : "0.0.0-dev";
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) && statSync2(path4).isFile()) return environmentFromYaml(readFileSync(path4, "utf8"), 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) || !statSync2(dir).isDirectory()) throw new CliError(`Workspace folder not found: ${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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piyushdoorwar/yamlet",
3
- "version": "1.1.14",
3
+ "version": "1.13.0",
4
4
  "description": "Run Yamlet API collections (Git-friendly YAML) from the command line or CI.",
5
5
  "type": "module",
6
6
  "bin": {