@apicircle/core 1.0.8 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/{chunk-L5DQT7V6.js → chunk-T6A4ICRL.js} +5 -5
- package/dist/chunk-T6A4ICRL.js.map +1 -0
- package/dist/index.cjs +995 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +219 -8
- package/dist/index.d.ts +219 -8
- package/dist/index.js +965 -77
- package/dist/index.js.map +1 -1
- package/dist/{patches-ysO3y8pG.d.cts → patches-B3VGNVgf.d.cts} +74 -1
- package/dist/{patches-ysO3y8pG.d.ts → patches-B3VGNVgf.d.ts} +74 -1
- package/dist/workspace/file-backed.cjs +4 -4
- package/dist/workspace/file-backed.cjs.map +1 -1
- package/dist/workspace/file-backed.d.cts +5 -1
- package/dist/workspace/file-backed.d.ts +5 -1
- package/dist/workspace/file-backed.js +1 -1
- package/dist/workspace/registry.cjs +13 -33
- package/dist/workspace/registry.cjs.map +1 -1
- package/dist/workspace/registry.d.cts +13 -26
- package/dist/workspace/registry.d.ts +13 -26
- package/dist/workspace/registry.js +9 -30
- package/dist/workspace/registry.js.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-L5DQT7V6.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1054,8 +1054,7 @@ async function importPkcs8(pem, algorithm) {
|
|
|
1054
1054
|
"JWT: PKCS#1 RSA PEM (`BEGIN RSA PRIVATE KEY`) is not supported. Convert with `openssl pkcs8 -topk8 -in key.pem -out pkcs8.pem -nocrypt`."
|
|
1055
1055
|
);
|
|
1056
1056
|
}
|
|
1057
|
-
const
|
|
1058
|
-
const body = envelope ? envelope[1] : pem;
|
|
1057
|
+
const body = extractPemBody(pem);
|
|
1059
1058
|
const stripped = body.replace(/\s+/g, "");
|
|
1060
1059
|
if (!stripped) {
|
|
1061
1060
|
throw new Error("JWT: PEM key is empty after stripping headers/whitespace");
|
|
@@ -1076,6 +1075,19 @@ async function importPkcs8(pem, algorithm) {
|
|
|
1076
1075
|
["sign"]
|
|
1077
1076
|
);
|
|
1078
1077
|
}
|
|
1078
|
+
function extractPemBody(pem) {
|
|
1079
|
+
const BEGIN = "-----BEGIN ";
|
|
1080
|
+
const END = "-----END ";
|
|
1081
|
+
const FENCE = "-----";
|
|
1082
|
+
const beginAt = pem.indexOf(BEGIN);
|
|
1083
|
+
if (beginAt === -1) return pem;
|
|
1084
|
+
const beginHeaderEnd = pem.indexOf(FENCE, beginAt + BEGIN.length);
|
|
1085
|
+
if (beginHeaderEnd === -1) return pem;
|
|
1086
|
+
const bodyStart = beginHeaderEnd + FENCE.length;
|
|
1087
|
+
const endAt = pem.indexOf(END, bodyStart);
|
|
1088
|
+
if (endAt === -1) return pem;
|
|
1089
|
+
return pem.slice(bodyStart, endAt);
|
|
1090
|
+
}
|
|
1079
1091
|
function base64UrlEncode(bytes) {
|
|
1080
1092
|
let s = "";
|
|
1081
1093
|
for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
|
|
@@ -1530,6 +1542,10 @@ async function fetchOAuth2Token(args) {
|
|
|
1530
1542
|
if (args.extraParams) {
|
|
1531
1543
|
for (const [k, v] of Object.entries(args.extraParams)) body.set(k, v);
|
|
1532
1544
|
}
|
|
1545
|
+
const tokenUrlParsed = new URL(args.tokenUrl);
|
|
1546
|
+
if (tokenUrlParsed.protocol !== "https:" && tokenUrlParsed.protocol !== "http:") {
|
|
1547
|
+
throw new Error(`Token URL must use HTTP or HTTPS, got ${tokenUrlParsed.protocol}`);
|
|
1548
|
+
}
|
|
1533
1549
|
const response = await fetchImpl(args.tokenUrl, {
|
|
1534
1550
|
method: "POST",
|
|
1535
1551
|
headers,
|
|
@@ -2476,6 +2492,30 @@ function getVariableAutocomplete(text, cursorPosition, scope) {
|
|
|
2476
2492
|
);
|
|
2477
2493
|
}
|
|
2478
2494
|
|
|
2495
|
+
// src/request/resolveInheritedAuth.ts
|
|
2496
|
+
var NONE = { type: "none" };
|
|
2497
|
+
function resolveInheritedAuth({
|
|
2498
|
+
requestAuth,
|
|
2499
|
+
folderId,
|
|
2500
|
+
folders
|
|
2501
|
+
}) {
|
|
2502
|
+
if (requestAuth.type !== "inherit") return requestAuth;
|
|
2503
|
+
let cursor = folderId;
|
|
2504
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2505
|
+
while (cursor !== null) {
|
|
2506
|
+
if (visited.has(cursor)) {
|
|
2507
|
+
break;
|
|
2508
|
+
}
|
|
2509
|
+
visited.add(cursor);
|
|
2510
|
+
const folder = folders[cursor];
|
|
2511
|
+
if (!folder) break;
|
|
2512
|
+
const auth = folder.auth;
|
|
2513
|
+
if (auth && auth.type !== "inherit" && auth.type !== "none") return auth;
|
|
2514
|
+
cursor = folder.parentId;
|
|
2515
|
+
}
|
|
2516
|
+
return NONE;
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2479
2519
|
// src/request/preSendValidation.ts
|
|
2480
2520
|
var TYPED_BODY_CT = {
|
|
2481
2521
|
json: ["application/json", "application/ld+json", "application/vnd.api+json"],
|
|
@@ -2488,7 +2528,8 @@ function collectMissing(value, scope) {
|
|
|
2488
2528
|
}
|
|
2489
2529
|
function preSendValidation({
|
|
2490
2530
|
request,
|
|
2491
|
-
scope
|
|
2531
|
+
scope,
|
|
2532
|
+
folders
|
|
2492
2533
|
}) {
|
|
2493
2534
|
const warnings = [];
|
|
2494
2535
|
const blockers = [];
|
|
@@ -2563,29 +2604,43 @@ function preSendValidation({
|
|
|
2563
2604
|
});
|
|
2564
2605
|
}
|
|
2565
2606
|
}
|
|
2566
|
-
|
|
2607
|
+
let effectiveAuth = request.auth;
|
|
2608
|
+
let resolvedFromInherit = false;
|
|
2609
|
+
if (folders && effectiveAuth?.type === "inherit") {
|
|
2610
|
+
effectiveAuth = resolveInheritedAuth({
|
|
2611
|
+
requestAuth: { type: "inherit" },
|
|
2612
|
+
folderId: request.folderId,
|
|
2613
|
+
folders
|
|
2614
|
+
});
|
|
2615
|
+
resolvedFromInherit = true;
|
|
2616
|
+
}
|
|
2617
|
+
const auth = effectiveAuth;
|
|
2618
|
+
const inheritedNote = resolvedFromInherit ? " (resolved from folder-level auth)" : "";
|
|
2567
2619
|
if (auth) {
|
|
2568
2620
|
if (auth.type === "bearer" && !auth.token?.trim()) {
|
|
2569
|
-
blockers.push({
|
|
2621
|
+
blockers.push({
|
|
2622
|
+
kind: "auth-fields-missing",
|
|
2623
|
+
message: `Bearer token is empty.${inheritedNote}`
|
|
2624
|
+
});
|
|
2570
2625
|
} else if (auth.type === "basic") {
|
|
2571
2626
|
if (!auth.username?.trim() || !auth.password?.trim()) {
|
|
2572
2627
|
blockers.push({
|
|
2573
2628
|
kind: "auth-fields-missing",
|
|
2574
|
-
message:
|
|
2629
|
+
message: `Basic auth requires both username and password.${inheritedNote}`
|
|
2575
2630
|
});
|
|
2576
2631
|
}
|
|
2577
2632
|
} else if (auth.type === "api-key") {
|
|
2578
2633
|
if (!auth.key?.trim() || !auth.value?.trim()) {
|
|
2579
2634
|
blockers.push({
|
|
2580
2635
|
kind: "auth-fields-missing",
|
|
2581
|
-
message:
|
|
2636
|
+
message: `API key auth requires both name and value.${inheritedNote}`
|
|
2582
2637
|
});
|
|
2583
2638
|
}
|
|
2584
2639
|
} else if (auth.type === "custom-header") {
|
|
2585
2640
|
if (!auth.key?.trim()) {
|
|
2586
2641
|
blockers.push({
|
|
2587
2642
|
kind: "auth-fields-missing",
|
|
2588
|
-
message:
|
|
2643
|
+
message: `Custom header auth requires a header name.${inheritedNote}`
|
|
2589
2644
|
});
|
|
2590
2645
|
}
|
|
2591
2646
|
}
|
|
@@ -2972,6 +3027,15 @@ async function executeRequest(req, opts = {}) {
|
|
|
2972
3027
|
);
|
|
2973
3028
|
const redirectMode = isBrowserRuntime() ? "follow" : "manual";
|
|
2974
3029
|
let currentUrl = builtRequest.url;
|
|
3030
|
+
try {
|
|
3031
|
+
const parsedUrl = new URL(currentUrl);
|
|
3032
|
+
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
|
3033
|
+
throw new Error(`Unsupported URL scheme: ${parsedUrl.protocol}`);
|
|
3034
|
+
}
|
|
3035
|
+
} catch (e) {
|
|
3036
|
+
if (e instanceof Error && e.message.startsWith("Unsupported URL scheme")) throw e;
|
|
3037
|
+
throw new Error(`Invalid request URL: ${currentUrl}`);
|
|
3038
|
+
}
|
|
2975
3039
|
let currentHeaders = { ...builtRequest.headers };
|
|
2976
3040
|
let currentMethod = builtRequest.method;
|
|
2977
3041
|
let currentBody = builtRequest.body;
|
|
@@ -3270,30 +3334,6 @@ function base64UrlEncode2(bytes) {
|
|
|
3270
3334
|
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
3271
3335
|
}
|
|
3272
3336
|
|
|
3273
|
-
// src/request/resolveInheritedAuth.ts
|
|
3274
|
-
var NONE = { type: "none" };
|
|
3275
|
-
function resolveInheritedAuth({
|
|
3276
|
-
requestAuth,
|
|
3277
|
-
folderId,
|
|
3278
|
-
folders
|
|
3279
|
-
}) {
|
|
3280
|
-
if (requestAuth.type !== "inherit") return requestAuth;
|
|
3281
|
-
let cursor = folderId;
|
|
3282
|
-
const visited = /* @__PURE__ */ new Set();
|
|
3283
|
-
while (cursor !== null) {
|
|
3284
|
-
if (visited.has(cursor)) {
|
|
3285
|
-
break;
|
|
3286
|
-
}
|
|
3287
|
-
visited.add(cursor);
|
|
3288
|
-
const folder = folders[cursor];
|
|
3289
|
-
if (!folder) break;
|
|
3290
|
-
const auth = folder.auth;
|
|
3291
|
-
if (auth && auth.type !== "inherit" && auth.type !== "none") return auth;
|
|
3292
|
-
cursor = folder.parentId;
|
|
3293
|
-
}
|
|
3294
|
-
return NONE;
|
|
3295
|
-
}
|
|
3296
|
-
|
|
3297
3337
|
// src/request/parseCurl.ts
|
|
3298
3338
|
var HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
3299
3339
|
function tokenizeCurl(input) {
|
|
@@ -3595,10 +3635,12 @@ function parsePostmanCollection(input) {
|
|
|
3595
3635
|
items.forEach((item, idx) => {
|
|
3596
3636
|
const pathIds = parentPathIds ? [...parentPathIds, idx] : [idx];
|
|
3597
3637
|
if (Array.isArray(item.item)) {
|
|
3638
|
+
const folderAuth = item.auth ? parseAuth(item.auth, warnings, item.name) : void 0;
|
|
3598
3639
|
folders.push({
|
|
3599
3640
|
name: (item.name ?? "Untitled folder").trim() || "Untitled folder",
|
|
3600
3641
|
pathIds,
|
|
3601
|
-
parentPathIds
|
|
3642
|
+
parentPathIds,
|
|
3643
|
+
...folderAuth && folderAuth.type !== "none" ? { auth: folderAuth } : {}
|
|
3602
3644
|
});
|
|
3603
3645
|
walk(item.item, pathIds);
|
|
3604
3646
|
return;
|
|
@@ -3839,10 +3881,12 @@ function parseInsomniaCollection(input) {
|
|
|
3839
3881
|
const parentPath = r.parentId ? folderIndexById.get(r.parentId) ?? null : null;
|
|
3840
3882
|
const ourPath = parentPath ? [...parentPath, folderCounter++] : [folderCounter++];
|
|
3841
3883
|
folderIndexById.set(r._id ?? "", ourPath);
|
|
3884
|
+
const folderAuth = r.authentication ? parseAuth2(r.authentication, warnings, r.name) : void 0;
|
|
3842
3885
|
folders.push({
|
|
3843
3886
|
name: (r.name ?? "Untitled folder").trim() || "Untitled folder",
|
|
3844
3887
|
pathIds: ourPath,
|
|
3845
|
-
parentPathIds: parentPath
|
|
3888
|
+
parentPathIds: parentPath,
|
|
3889
|
+
...folderAuth && folderAuth.type !== "none" ? { auth: folderAuth } : {}
|
|
3846
3890
|
});
|
|
3847
3891
|
}
|
|
3848
3892
|
const requests = [];
|
|
@@ -4243,7 +4287,7 @@ function serializeFolderExport(envelope) {
|
|
|
4243
4287
|
return JSON.stringify(envelope, null, 2);
|
|
4244
4288
|
}
|
|
4245
4289
|
function suggestFolderExportFilename(envelope) {
|
|
4246
|
-
const slug = envelope.folder.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(
|
|
4290
|
+
const slug = envelope.folder.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-/, "").replace(/-$/, "");
|
|
4247
4291
|
const base = slug || "folder";
|
|
4248
4292
|
return `${base}.apicircle.json`;
|
|
4249
4293
|
}
|
|
@@ -4869,6 +4913,140 @@ function extractContext(result, extractions) {
|
|
|
4869
4913
|
return { extracted, warnings };
|
|
4870
4914
|
}
|
|
4871
4915
|
|
|
4916
|
+
// src/environment/resolveRequest.ts
|
|
4917
|
+
import { envPriorityKey } from "@apicircle/shared";
|
|
4918
|
+
function resolveRequestForExecution(args) {
|
|
4919
|
+
const refs = args.envPriorityOverride && args.envPriorityOverride.length > 0 ? args.envPriorityOverride : args.synced.environments.priorityOrder;
|
|
4920
|
+
const flatEnvs = {};
|
|
4921
|
+
for (const [name, vars] of Object.entries(args.localEnvs)) {
|
|
4922
|
+
flatEnvs[envPriorityKey({ kind: "local", name })] = vars;
|
|
4923
|
+
}
|
|
4924
|
+
for (const [linkId, byEnv] of Object.entries(args.linkedEnvs ?? {})) {
|
|
4925
|
+
for (const [envName, vars] of Object.entries(byEnv)) {
|
|
4926
|
+
flatEnvs[envPriorityKey({ kind: "linked", linkedWorkspaceId: linkId, envName })] = vars;
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
const ctxMap = { ...args.globalContext ?? {} };
|
|
4930
|
+
for (const v of args.planVariables ?? []) {
|
|
4931
|
+
if (v.key) ctxMap[v.key] = v.value;
|
|
4932
|
+
}
|
|
4933
|
+
for (const v of args.request.contextVars) {
|
|
4934
|
+
if (v.key) ctxMap[v.key] = v.value;
|
|
4935
|
+
}
|
|
4936
|
+
const contextVars = Object.entries(ctxMap).map(([key, value]) => ({ key, value }));
|
|
4937
|
+
const scope = buildScope({
|
|
4938
|
+
contextVars,
|
|
4939
|
+
environments: flatEnvs,
|
|
4940
|
+
activeEnvName: null,
|
|
4941
|
+
// priorityOrder is the sole list the resolver consults.
|
|
4942
|
+
priorityOrder: refs.map(envPriorityKey),
|
|
4943
|
+
secrets: args.secrets ?? {}
|
|
4944
|
+
});
|
|
4945
|
+
const missing = /* @__PURE__ */ new Set();
|
|
4946
|
+
const interp = (s) => {
|
|
4947
|
+
const r = resolveString(s, scope);
|
|
4948
|
+
for (const m of r.missing) missing.add(m);
|
|
4949
|
+
return r.value;
|
|
4950
|
+
};
|
|
4951
|
+
const url = interp(args.request.url);
|
|
4952
|
+
const headers = args.request.headers.map((h) => ({
|
|
4953
|
+
...h,
|
|
4954
|
+
key: interp(h.key),
|
|
4955
|
+
value: interp(h.value)
|
|
4956
|
+
}));
|
|
4957
|
+
const query = args.request.query.map((q) => ({
|
|
4958
|
+
...q,
|
|
4959
|
+
key: interp(q.key),
|
|
4960
|
+
value: interp(q.value)
|
|
4961
|
+
}));
|
|
4962
|
+
let body = args.request.body;
|
|
4963
|
+
if (body.type === "json" || body.type === "text" || body.type === "xml" || body.type === "graphql" || body.type === "urlencoded") {
|
|
4964
|
+
body = { ...body, content: interp(body.content) };
|
|
4965
|
+
} else if (body.type === "form-data" && body.formRows) {
|
|
4966
|
+
body = {
|
|
4967
|
+
...body,
|
|
4968
|
+
formRows: body.formRows.map(
|
|
4969
|
+
(row) => row.kind === "text" ? { ...row, key: interp(row.key), value: interp(row.value) } : { ...row, key: interp(row.key) }
|
|
4970
|
+
)
|
|
4971
|
+
};
|
|
4972
|
+
}
|
|
4973
|
+
const inheritedAuth = resolveInheritedAuth({
|
|
4974
|
+
requestAuth: args.request.auth ?? { type: "none" },
|
|
4975
|
+
folderId: args.request.folderId,
|
|
4976
|
+
folders: args.synced.collections.folders
|
|
4977
|
+
});
|
|
4978
|
+
const auth = interpolateAuthVariables(inheritedAuth, interp);
|
|
4979
|
+
return {
|
|
4980
|
+
request: { ...args.request, url, headers, query, body, auth },
|
|
4981
|
+
scope,
|
|
4982
|
+
missing: [...missing]
|
|
4983
|
+
};
|
|
4984
|
+
}
|
|
4985
|
+
function interpolateAuthVariables(auth, interp) {
|
|
4986
|
+
const resolved = {};
|
|
4987
|
+
for (const [key, value] of Object.entries(auth)) {
|
|
4988
|
+
resolved[key] = key !== "type" && typeof value === "string" ? interp(value) : value;
|
|
4989
|
+
}
|
|
4990
|
+
return resolved;
|
|
4991
|
+
}
|
|
4992
|
+
function applyLinkedEnvironmentOverrides(source, linkedWorkspaceId, synced) {
|
|
4993
|
+
const overrides = Object.values(synced.linkedOverrides.environmentVars).filter(
|
|
4994
|
+
(o) => o.linkedWorkspaceId === linkedWorkspaceId
|
|
4995
|
+
);
|
|
4996
|
+
if (overrides.length === 0) return source;
|
|
4997
|
+
const items = {};
|
|
4998
|
+
for (const [envName, env] of Object.entries(source.items)) {
|
|
4999
|
+
const envOverrides = overrides.filter((o) => o.envName === envName);
|
|
5000
|
+
if (envOverrides.length === 0) {
|
|
5001
|
+
items[envName] = env;
|
|
5002
|
+
continue;
|
|
5003
|
+
}
|
|
5004
|
+
const removed = new Set(envOverrides.filter((o) => o.removed).map((o) => o.varKey));
|
|
5005
|
+
const replaceMap = new Map(envOverrides.filter((o) => !o.removed).map((o) => [o.varKey, o]));
|
|
5006
|
+
const variables = [];
|
|
5007
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
5008
|
+
for (const v of env.variables) {
|
|
5009
|
+
if (removed.has(v.key)) continue;
|
|
5010
|
+
const ov = replaceMap.get(v.key);
|
|
5011
|
+
if (ov) {
|
|
5012
|
+
variables.push({
|
|
5013
|
+
key: v.key,
|
|
5014
|
+
value: ov.value ?? v.value,
|
|
5015
|
+
encrypted: ov.encrypted ?? v.encrypted,
|
|
5016
|
+
...ov.secretKeyId !== void 0 ? { secretKeyId: ov.secretKeyId } : v.secretKeyId !== void 0 ? { secretKeyId: v.secretKeyId } : {}
|
|
5017
|
+
});
|
|
5018
|
+
} else {
|
|
5019
|
+
variables.push(v);
|
|
5020
|
+
}
|
|
5021
|
+
seenKeys.add(v.key);
|
|
5022
|
+
}
|
|
5023
|
+
for (const ov of envOverrides) {
|
|
5024
|
+
if (ov.removed) continue;
|
|
5025
|
+
if (seenKeys.has(ov.varKey)) continue;
|
|
5026
|
+
variables.push({
|
|
5027
|
+
key: ov.varKey,
|
|
5028
|
+
value: ov.value ?? "",
|
|
5029
|
+
encrypted: ov.encrypted ?? false,
|
|
5030
|
+
...ov.secretKeyId !== void 0 ? { secretKeyId: ov.secretKeyId } : {}
|
|
5031
|
+
});
|
|
5032
|
+
}
|
|
5033
|
+
items[envName] = { ...env, variables };
|
|
5034
|
+
}
|
|
5035
|
+
return { ...source, items };
|
|
5036
|
+
}
|
|
5037
|
+
function plaintextEnvMap(source) {
|
|
5038
|
+
const out = {};
|
|
5039
|
+
for (const [name, env] of Object.entries(source.items)) {
|
|
5040
|
+
const vars = {};
|
|
5041
|
+
for (const v of env.variables) {
|
|
5042
|
+
if (v.encrypted) continue;
|
|
5043
|
+
vars[v.key] = v.value;
|
|
5044
|
+
}
|
|
5045
|
+
out[name] = vars;
|
|
5046
|
+
}
|
|
5047
|
+
return out;
|
|
5048
|
+
}
|
|
5049
|
+
|
|
4872
5050
|
// src/secrets/crypto.ts
|
|
4873
5051
|
var IV_BYTES = 12;
|
|
4874
5052
|
var SALT_BYTES = 16;
|
|
@@ -4953,6 +5131,95 @@ function base64ToBytes(b64) {
|
|
|
4953
5131
|
return out;
|
|
4954
5132
|
}
|
|
4955
5133
|
|
|
5134
|
+
// src/secrets/passphraseKey.ts
|
|
5135
|
+
var PBKDF2_HASH = "SHA-256";
|
|
5136
|
+
var PBKDF2_ITERATIONS2 = 12e5;
|
|
5137
|
+
var SALT_BYTES2 = 16;
|
|
5138
|
+
var VERIFIER_SENTINEL = "apicircle/passphrase-verifier/v1";
|
|
5139
|
+
function base64Encode2(bytes) {
|
|
5140
|
+
let binary = "";
|
|
5141
|
+
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
5142
|
+
return btoa(binary);
|
|
5143
|
+
}
|
|
5144
|
+
function base64Decode2(b64) {
|
|
5145
|
+
const binary = atob(b64);
|
|
5146
|
+
const bytes = new Uint8Array(binary.length);
|
|
5147
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
5148
|
+
return bytes;
|
|
5149
|
+
}
|
|
5150
|
+
function utf8Bytes(s) {
|
|
5151
|
+
return new TextEncoder().encode(s);
|
|
5152
|
+
}
|
|
5153
|
+
async function deriveKey(passphrase, salt, iterations) {
|
|
5154
|
+
const baseKey = await crypto.subtle.importKey(
|
|
5155
|
+
"raw",
|
|
5156
|
+
utf8Bytes(passphrase),
|
|
5157
|
+
{ name: "PBKDF2" },
|
|
5158
|
+
false,
|
|
5159
|
+
["deriveKey"]
|
|
5160
|
+
);
|
|
5161
|
+
return crypto.subtle.deriveKey(
|
|
5162
|
+
{
|
|
5163
|
+
name: "PBKDF2",
|
|
5164
|
+
salt,
|
|
5165
|
+
iterations,
|
|
5166
|
+
hash: PBKDF2_HASH
|
|
5167
|
+
},
|
|
5168
|
+
baseKey,
|
|
5169
|
+
{ name: "AES-GCM", length: 256 },
|
|
5170
|
+
/* extractable */
|
|
5171
|
+
false,
|
|
5172
|
+
["encrypt", "decrypt"]
|
|
5173
|
+
);
|
|
5174
|
+
}
|
|
5175
|
+
async function computeVerifier(key) {
|
|
5176
|
+
const iv = new Uint8Array(12);
|
|
5177
|
+
const ct = await crypto.subtle.encrypt(
|
|
5178
|
+
{ name: "AES-GCM", iv },
|
|
5179
|
+
key,
|
|
5180
|
+
utf8Bytes(VERIFIER_SENTINEL)
|
|
5181
|
+
);
|
|
5182
|
+
return base64Encode2(new Uint8Array(ct));
|
|
5183
|
+
}
|
|
5184
|
+
async function initSecretCrypto(passphrase, iterations = PBKDF2_ITERATIONS2) {
|
|
5185
|
+
if (passphrase.length === 0) throw new Error("Passphrase cannot be empty");
|
|
5186
|
+
if (iterations < 1) throw new Error("iterations must be >= 1");
|
|
5187
|
+
const salt = crypto.getRandomValues(new Uint8Array(SALT_BYTES2));
|
|
5188
|
+
const key = await deriveKey(passphrase, salt, iterations);
|
|
5189
|
+
const verifier = await computeVerifier(key);
|
|
5190
|
+
return {
|
|
5191
|
+
crypto: {
|
|
5192
|
+
kdf: "pbkdf2-sha256-v1",
|
|
5193
|
+
salt: base64Encode2(salt),
|
|
5194
|
+
iterations,
|
|
5195
|
+
verifier
|
|
5196
|
+
},
|
|
5197
|
+
key
|
|
5198
|
+
};
|
|
5199
|
+
}
|
|
5200
|
+
async function unlockSecretCrypto(passphrase, blob) {
|
|
5201
|
+
if (blob.kdf !== "pbkdf2-sha256-v1") {
|
|
5202
|
+
return { ok: false, reason: `Unsupported KDF: ${String(blob.kdf)}` };
|
|
5203
|
+
}
|
|
5204
|
+
let salt;
|
|
5205
|
+
try {
|
|
5206
|
+
salt = base64Decode2(blob.salt);
|
|
5207
|
+
} catch {
|
|
5208
|
+
return { ok: false, reason: "Workspace secret salt is corrupt." };
|
|
5209
|
+
}
|
|
5210
|
+
let key;
|
|
5211
|
+
try {
|
|
5212
|
+
key = await deriveKey(passphrase, salt, blob.iterations);
|
|
5213
|
+
} catch (err) {
|
|
5214
|
+
return { ok: false, reason: err instanceof Error ? err.message : "Key derivation failed" };
|
|
5215
|
+
}
|
|
5216
|
+
const verifier = await computeVerifier(key);
|
|
5217
|
+
if (verifier !== blob.verifier) {
|
|
5218
|
+
return { ok: false, reason: "Wrong passphrase." };
|
|
5219
|
+
}
|
|
5220
|
+
return { ok: true, key };
|
|
5221
|
+
}
|
|
5222
|
+
|
|
4956
5223
|
// src/git/branchNames.ts
|
|
4957
5224
|
var SLUG_FALLBACK = "workspace";
|
|
4958
5225
|
var SUFFIX_LEN = 6;
|
|
@@ -5010,6 +5277,36 @@ function sortedReplacer(_key, value) {
|
|
|
5010
5277
|
return out;
|
|
5011
5278
|
}
|
|
5012
5279
|
|
|
5280
|
+
// src/git/repoPaths.ts
|
|
5281
|
+
var WORKSPACE_DIR = ".apicircle";
|
|
5282
|
+
var REGISTRY_JSON_PATH = `${WORKSPACE_DIR}/registry.json`;
|
|
5283
|
+
function workspaceJsonPath(workspaceId) {
|
|
5284
|
+
return `${WORKSPACE_DIR}/workspace-${workspaceId}/workspace.json`;
|
|
5285
|
+
}
|
|
5286
|
+
function attachmentsDir(workspaceId) {
|
|
5287
|
+
return `${WORKSPACE_DIR}/workspace-${workspaceId}/attachments`;
|
|
5288
|
+
}
|
|
5289
|
+
function attachmentPath(workspaceId, slotId) {
|
|
5290
|
+
return `${attachmentsDir(workspaceId)}/${slotId}`;
|
|
5291
|
+
}
|
|
5292
|
+
function parseRegistryActiveId(registryJsonContent) {
|
|
5293
|
+
try {
|
|
5294
|
+
const parsed = JSON.parse(registryJsonContent);
|
|
5295
|
+
return parsed.activeWorkspaceId ?? parsed.workspaces?.[0]?.id ?? null;
|
|
5296
|
+
} catch {
|
|
5297
|
+
return null;
|
|
5298
|
+
}
|
|
5299
|
+
}
|
|
5300
|
+
async function fetchRemoteWorkspaceJson(fetchFile) {
|
|
5301
|
+
const registryContent = await fetchFile(REGISTRY_JSON_PATH);
|
|
5302
|
+
if (registryContent === null) return { error: "No .apicircle/registry.json found in repo" };
|
|
5303
|
+
const wsId = parseRegistryActiveId(registryContent);
|
|
5304
|
+
if (!wsId) return { error: "Registry is empty \u2014 no workspaces found" };
|
|
5305
|
+
const wsContent = await fetchFile(workspaceJsonPath(wsId));
|
|
5306
|
+
if (wsContent === null) return { error: `No workspace.json at .apicircle/workspace-${wsId}/` };
|
|
5307
|
+
return { workspaceId: wsId, content: wsContent };
|
|
5308
|
+
}
|
|
5309
|
+
|
|
5013
5310
|
// src/git/parseWorkspaceJson.ts
|
|
5014
5311
|
var MAX_WORKSPACE_JSON_BYTES = 16 * 1024 * 1024;
|
|
5015
5312
|
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
@@ -5266,18 +5563,14 @@ function sortVersionsDesc(versions) {
|
|
|
5266
5563
|
}
|
|
5267
5564
|
|
|
5268
5565
|
// src/release/publishRelease.ts
|
|
5269
|
-
async function
|
|
5566
|
+
async function buildReleaseEntry(synced, args) {
|
|
5270
5567
|
const version = args.version.trim();
|
|
5271
5568
|
if (!isValidSemver(version)) {
|
|
5272
5569
|
throw new Error(`Invalid semver: ${args.version}`);
|
|
5273
5570
|
}
|
|
5274
|
-
const ledger = synced.releases.self ?? emptyLedger();
|
|
5275
|
-
if (ledger.versions.some((v) => v.version === version)) {
|
|
5276
|
-
throw new Error(`Version ${version} already exists in this workspace's release ledger`);
|
|
5277
|
-
}
|
|
5278
5571
|
const snapshotSource = serializeWorkspaceForGit(synced);
|
|
5279
5572
|
const workspaceSnapshot = await sha256Hex2(snapshotSource);
|
|
5280
|
-
|
|
5573
|
+
return {
|
|
5281
5574
|
version,
|
|
5282
5575
|
publishedAt: args.publishedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
5283
5576
|
notes: args.notes,
|
|
@@ -5287,23 +5580,36 @@ async function publishRelease(synced, args) {
|
|
|
5287
5580
|
...args.sha ? { sha: args.sha } : {},
|
|
5288
5581
|
...args.tagName ? { tagName: args.tagName } : {}
|
|
5289
5582
|
};
|
|
5583
|
+
}
|
|
5584
|
+
function appendReleaseEntry(synced, entry, now = entry.publishedAt) {
|
|
5585
|
+
if (!isValidSemver(entry.version)) {
|
|
5586
|
+
throw new Error(`Invalid semver: ${entry.version}`);
|
|
5587
|
+
}
|
|
5588
|
+
const ledger = synced.releases.self ?? emptyLedger();
|
|
5589
|
+
if (ledger.versions.some((v) => v.version === entry.version)) {
|
|
5590
|
+
throw new Error(`Version ${entry.version} already exists in this workspace's release ledger`);
|
|
5591
|
+
}
|
|
5290
5592
|
const next = {
|
|
5291
5593
|
versions: [...ledger.versions, entry],
|
|
5292
|
-
currentVersion: version
|
|
5594
|
+
currentVersion: entry.version
|
|
5293
5595
|
};
|
|
5294
5596
|
return {
|
|
5295
5597
|
...synced,
|
|
5296
5598
|
releases: { ...synced.releases, self: next },
|
|
5297
|
-
meta: { ...synced.meta, updatedAt:
|
|
5599
|
+
meta: { ...synced.meta, updatedAt: now }
|
|
5298
5600
|
};
|
|
5299
5601
|
}
|
|
5300
|
-
function
|
|
5301
|
-
|
|
5602
|
+
async function publishRelease(synced, args) {
|
|
5603
|
+
const entry = await buildReleaseEntry(synced, args);
|
|
5604
|
+
return appendReleaseEntry(synced, entry);
|
|
5605
|
+
}
|
|
5606
|
+
function deprecateRelease(synced, version, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
5607
|
+
return mapReleaseVersion(synced, version, (v) => ({ ...v, deprecated: true }), now);
|
|
5302
5608
|
}
|
|
5303
|
-
function yankRelease(synced, version) {
|
|
5304
|
-
return mapReleaseVersion(synced, version, (v) => ({ ...v, yanked: true }));
|
|
5609
|
+
function yankRelease(synced, version, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
5610
|
+
return mapReleaseVersion(synced, version, (v) => ({ ...v, yanked: true }), now);
|
|
5305
5611
|
}
|
|
5306
|
-
function mapReleaseVersion(synced, version, fn) {
|
|
5612
|
+
function mapReleaseVersion(synced, version, fn, now) {
|
|
5307
5613
|
const ledger = synced.releases.self;
|
|
5308
5614
|
if (!ledger) throw new Error("No releases to modify");
|
|
5309
5615
|
const idx = ledger.versions.findIndex((v) => v.version === version);
|
|
@@ -5313,7 +5619,7 @@ function mapReleaseVersion(synced, version, fn) {
|
|
|
5313
5619
|
return {
|
|
5314
5620
|
...synced,
|
|
5315
5621
|
releases: { ...synced.releases, self: { ...ledger, versions } },
|
|
5316
|
-
meta: { ...synced.meta, updatedAt:
|
|
5622
|
+
meta: { ...synced.meta, updatedAt: now }
|
|
5317
5623
|
};
|
|
5318
5624
|
}
|
|
5319
5625
|
function emptyLedger() {
|
|
@@ -5325,6 +5631,99 @@ async function sha256Hex2(text) {
|
|
|
5325
5631
|
return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
5326
5632
|
}
|
|
5327
5633
|
|
|
5634
|
+
// src/linked/linkedSnapshot.ts
|
|
5635
|
+
var LINKED_FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
5636
|
+
var MAX_LINKED_JSON_BYTES = 16 * 1024 * 1024;
|
|
5637
|
+
function parseLinkedWorkspaceJson(text) {
|
|
5638
|
+
if (text.length > MAX_LINKED_JSON_BYTES) {
|
|
5639
|
+
throw new Error("Remote workspace.json exceeds 16 MiB");
|
|
5640
|
+
}
|
|
5641
|
+
let raw;
|
|
5642
|
+
try {
|
|
5643
|
+
raw = JSON.parse(
|
|
5644
|
+
text,
|
|
5645
|
+
(key, value) => LINKED_FORBIDDEN_KEYS.has(key) ? void 0 : value
|
|
5646
|
+
);
|
|
5647
|
+
} catch {
|
|
5648
|
+
throw new Error("Remote workspace.json is not valid JSON");
|
|
5649
|
+
}
|
|
5650
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
5651
|
+
throw new Error("Remote workspace.json is not an object");
|
|
5652
|
+
}
|
|
5653
|
+
const obj = raw;
|
|
5654
|
+
const asObject = (v) => typeof v === "object" && v !== null ? v : void 0;
|
|
5655
|
+
return {
|
|
5656
|
+
workspaceId: typeof obj.workspaceId === "string" ? obj.workspaceId : void 0,
|
|
5657
|
+
releases: asObject(obj.releases),
|
|
5658
|
+
collections: asObject(obj.collections),
|
|
5659
|
+
environments: asObject(obj.environments),
|
|
5660
|
+
secretKeys: asObject(obj.secretKeys),
|
|
5661
|
+
globalAssets: asObject(obj.globalAssets)
|
|
5662
|
+
};
|
|
5663
|
+
}
|
|
5664
|
+
function ledgerFromProbe(parsed) {
|
|
5665
|
+
return parsed.releases?.self ?? { versions: [], currentVersion: null };
|
|
5666
|
+
}
|
|
5667
|
+
function buildLinkedSnapshot(parsed, link) {
|
|
5668
|
+
if (!parsed.collections && !parsed.environments) return null;
|
|
5669
|
+
return {
|
|
5670
|
+
pulledAt: link.linkedAt,
|
|
5671
|
+
ref: link.pinnedVersion ? `v${link.pinnedVersion}` : `HEAD@${link.source.branch}`,
|
|
5672
|
+
collections: parsed.collections ?? {
|
|
5673
|
+
tree: { id: "remote-root", type: "root", children: [] },
|
|
5674
|
+
requests: {},
|
|
5675
|
+
folders: {}
|
|
5676
|
+
},
|
|
5677
|
+
environments: parsed.environments ?? {
|
|
5678
|
+
items: {},
|
|
5679
|
+
activeName: null,
|
|
5680
|
+
priorityOrder: []
|
|
5681
|
+
},
|
|
5682
|
+
...parsed.secretKeys ? { secretKeys: parsed.secretKeys } : {},
|
|
5683
|
+
...parsed.globalAssets ? { globalAssets: parsed.globalAssets } : {}
|
|
5684
|
+
};
|
|
5685
|
+
}
|
|
5686
|
+
|
|
5687
|
+
// src/linked/requestOverride.ts
|
|
5688
|
+
var OVERRIDABLE_FIELDS = [
|
|
5689
|
+
"name",
|
|
5690
|
+
"method",
|
|
5691
|
+
"url",
|
|
5692
|
+
"headers",
|
|
5693
|
+
"query",
|
|
5694
|
+
"pathParams",
|
|
5695
|
+
"cookies",
|
|
5696
|
+
"body",
|
|
5697
|
+
"auth",
|
|
5698
|
+
"contextVars",
|
|
5699
|
+
"extractions",
|
|
5700
|
+
"assertions"
|
|
5701
|
+
];
|
|
5702
|
+
function mergeRequestOverride(base, patch) {
|
|
5703
|
+
const merged = { ...base };
|
|
5704
|
+
const p = patch;
|
|
5705
|
+
const target = merged;
|
|
5706
|
+
for (const field of OVERRIDABLE_FIELDS) {
|
|
5707
|
+
if (p[field] !== void 0) target[field] = p[field];
|
|
5708
|
+
}
|
|
5709
|
+
return merged;
|
|
5710
|
+
}
|
|
5711
|
+
function computeRequestOverridePatch(base, effective) {
|
|
5712
|
+
const baseRec = { ...base };
|
|
5713
|
+
const effRec = { ...effective };
|
|
5714
|
+
const patch = {};
|
|
5715
|
+
const patchRec = patch;
|
|
5716
|
+
for (const field of OVERRIDABLE_FIELDS) {
|
|
5717
|
+
if (JSON.stringify(baseRec[field]) !== JSON.stringify(effRec[field])) {
|
|
5718
|
+
patchRec[field] = effRec[field];
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5721
|
+
return patch;
|
|
5722
|
+
}
|
|
5723
|
+
function isEmptyOverridePatch(patch) {
|
|
5724
|
+
return Object.keys(patch).filter((k) => OVERRIDABLE_FIELDS.includes(k)).length === 0;
|
|
5725
|
+
}
|
|
5726
|
+
|
|
5328
5727
|
// src/editors/contentTypeLanguageMap.ts
|
|
5329
5728
|
var CONTENT_TYPE_LANGUAGE_MAP = {
|
|
5330
5729
|
"application/json": "json",
|
|
@@ -6257,7 +6656,8 @@ function previewLinkedUpdate(args) {
|
|
|
6257
6656
|
status,
|
|
6258
6657
|
base,
|
|
6259
6658
|
target,
|
|
6260
|
-
override
|
|
6659
|
+
override,
|
|
6660
|
+
...status === "both-changed" && base && target && override ? { autoMergeable: requestOverrideIsDisjoint(base, target, override) } : {}
|
|
6261
6661
|
});
|
|
6262
6662
|
}
|
|
6263
6663
|
const baseFolders = args.base?.collections.folders ?? {};
|
|
@@ -6337,6 +6737,34 @@ function classifyRequest(base, target, override) {
|
|
|
6337
6737
|
if (sourceChanged && !hasOverride) return "source-only";
|
|
6338
6738
|
return "both-changed";
|
|
6339
6739
|
}
|
|
6740
|
+
var OVERRIDABLE_REQUEST_FIELDS = [
|
|
6741
|
+
"name",
|
|
6742
|
+
"method",
|
|
6743
|
+
"url",
|
|
6744
|
+
"headers",
|
|
6745
|
+
"query",
|
|
6746
|
+
"pathParams",
|
|
6747
|
+
"cookies",
|
|
6748
|
+
"body",
|
|
6749
|
+
"auth",
|
|
6750
|
+
"contextVars",
|
|
6751
|
+
"extractions",
|
|
6752
|
+
"assertions"
|
|
6753
|
+
];
|
|
6754
|
+
function requestOverrideIsDisjoint(base, target, override) {
|
|
6755
|
+
const baseRec = { ...base };
|
|
6756
|
+
const targetRec = { ...target };
|
|
6757
|
+
const overriddenFields = Object.keys(override.patch);
|
|
6758
|
+
for (const f of overriddenFields) {
|
|
6759
|
+
if (!OVERRIDABLE_REQUEST_FIELDS.includes(f)) {
|
|
6760
|
+
continue;
|
|
6761
|
+
}
|
|
6762
|
+
if (!structurallyEqual2(baseRec[f], targetRec[f])) {
|
|
6763
|
+
return false;
|
|
6764
|
+
}
|
|
6765
|
+
}
|
|
6766
|
+
return true;
|
|
6767
|
+
}
|
|
6340
6768
|
function classifyFolder(base, target) {
|
|
6341
6769
|
if (!base && target) return "new-in-source";
|
|
6342
6770
|
if (base && !target) return "removed-in-source";
|
|
@@ -6386,7 +6814,7 @@ function applyLinkedUpdate(args) {
|
|
|
6386
6814
|
continue;
|
|
6387
6815
|
}
|
|
6388
6816
|
if (entry.status === "both-changed") {
|
|
6389
|
-
const choice = args.resolutions[id];
|
|
6817
|
+
const choice = args.resolutions[id] ?? (entry.autoMergeable ? "mine" : void 0);
|
|
6390
6818
|
if (!choice) {
|
|
6391
6819
|
throw new Error(
|
|
6392
6820
|
`applyLinkedUpdate: unresolved both-changed entry "${entry.label}" (${id})`
|
|
@@ -6397,7 +6825,8 @@ function applyLinkedUpdate(args) {
|
|
|
6397
6825
|
else if (entry.bucket === "environment-var") envVarOverridesByKey.delete(entry.key);
|
|
6398
6826
|
log.push({ entryKey: id, bucket: entry.bucket, action: "accept-source" });
|
|
6399
6827
|
} else {
|
|
6400
|
-
|
|
6828
|
+
const auto = entry.autoMergeable === true && !args.resolutions[id];
|
|
6829
|
+
log.push({ entryKey: id, bucket: entry.bucket, action: auto ? "auto-merge" : "keep-mine" });
|
|
6401
6830
|
}
|
|
6402
6831
|
}
|
|
6403
6832
|
}
|
|
@@ -6413,7 +6842,7 @@ function structurallyEqual2(a, b) {
|
|
|
6413
6842
|
}
|
|
6414
6843
|
|
|
6415
6844
|
// src/workspace/applyMutation.ts
|
|
6416
|
-
import { envPriorityKey, generateId as generateId2 } from "@apicircle/shared";
|
|
6845
|
+
import { envPriorityKey as envPriorityKey2, generateId as generateId2 } from "@apicircle/shared";
|
|
6417
6846
|
|
|
6418
6847
|
// src/workspace/apicircleFolderImport.ts
|
|
6419
6848
|
function importApicircleFolderInto(synced, parsed, parentFolderId) {
|
|
@@ -6654,6 +7083,8 @@ function applyMutation(state, patch, options = {}) {
|
|
|
6654
7083
|
return applyFolderDelete(state, patch.id, now);
|
|
6655
7084
|
case "folder.move":
|
|
6656
7085
|
return applyFolderMove(state, patch.id, patch.newParentId, now);
|
|
7086
|
+
case "folder.update":
|
|
7087
|
+
return applyFolderUpdate(state, patch.id, patch.patch, now);
|
|
6657
7088
|
case "folder.import_apicircle":
|
|
6658
7089
|
return applyFolderImportApicircle(state, patch.parsed, patch.parentFolderId, now);
|
|
6659
7090
|
case "environment.upsert":
|
|
@@ -6666,6 +7097,10 @@ function applyMutation(state, patch, options = {}) {
|
|
|
6666
7097
|
return applyEnvSetPriority(state, patch.order, now);
|
|
6667
7098
|
case "secretKey.upsert":
|
|
6668
7099
|
return applySecretKeyUpsert(state, patch.meta, now);
|
|
7100
|
+
case "secret.crypto.set":
|
|
7101
|
+
return applySecretCryptoSet(state, patch.crypto, now);
|
|
7102
|
+
case "secret.crypto.clear":
|
|
7103
|
+
return applySecretCryptoClear(state, now);
|
|
6669
7104
|
case "assertion.upsert":
|
|
6670
7105
|
return applyAssertionUpsert(state, patch.requestId, patch.assertion, now);
|
|
6671
7106
|
case "assertion.delete":
|
|
@@ -6674,6 +7109,46 @@ function applyMutation(state, patch, options = {}) {
|
|
|
6674
7109
|
return applyMockUpsert(state, patch.mock, now);
|
|
6675
7110
|
case "mock.delete":
|
|
6676
7111
|
return applyMockDelete(state, patch.id, now);
|
|
7112
|
+
case "release.publish":
|
|
7113
|
+
return applyReleasePublish(state, patch.entry, now);
|
|
7114
|
+
case "release.deprecate":
|
|
7115
|
+
return applyReleaseDeprecate(state, patch.version, now);
|
|
7116
|
+
case "release.yank":
|
|
7117
|
+
return applyReleaseYank(state, patch.version, now);
|
|
7118
|
+
case "linkedWorkspace.upsert":
|
|
7119
|
+
return applyLinkedWorkspaceUpsert(state, patch.link, patch.ledger, patch.snapshot, now);
|
|
7120
|
+
case "linkedWorkspace.remove":
|
|
7121
|
+
return applyLinkedWorkspaceRemove(state, patch.id, now);
|
|
7122
|
+
case "linkedWorkspace.applyUpdate":
|
|
7123
|
+
return applyLinkedWorkspaceApplyUpdate(state, patch, now);
|
|
7124
|
+
case "linkedOverride.setRequest":
|
|
7125
|
+
return applyLinkedOverrideSetRequest(state, patch.override, now);
|
|
7126
|
+
case "linkedOverride.removeRequest":
|
|
7127
|
+
return applyLinkedOverrideRemoveRequest(state, patch.linkedWorkspaceId, patch.itemId, now);
|
|
7128
|
+
case "linkedOverride.setEnvVar":
|
|
7129
|
+
return applyLinkedOverrideSetEnvVar(state, patch.override, now);
|
|
7130
|
+
case "linkedOverride.removeEnvVar":
|
|
7131
|
+
return applyLinkedOverrideRemoveEnvVar(
|
|
7132
|
+
state,
|
|
7133
|
+
patch.linkedWorkspaceId,
|
|
7134
|
+
patch.envName,
|
|
7135
|
+
patch.varKey,
|
|
7136
|
+
now
|
|
7137
|
+
);
|
|
7138
|
+
case "linkedOverride.clearForLink":
|
|
7139
|
+
return applyLinkedOverrideClearForLink(state, patch.linkedWorkspaceId, now);
|
|
7140
|
+
case "globalAsset.upsertFile":
|
|
7141
|
+
return applyGlobalAssetUpsertFile(state, patch.file, now);
|
|
7142
|
+
case "globalAsset.removeFile":
|
|
7143
|
+
return applyGlobalAssetRemoveFile(state, patch.id, now);
|
|
7144
|
+
case "globalAsset.markPushed":
|
|
7145
|
+
return applyGlobalAssetMarkPushed(state, patch.id, patch.ref, now);
|
|
7146
|
+
case "globalAsset.markMerged":
|
|
7147
|
+
return applyGlobalAssetMarkMerged(state, patch.id, patch.ref, now);
|
|
7148
|
+
case "globalAsset.cleanupWorkingRef":
|
|
7149
|
+
return applyGlobalAssetCleanupWorkingRef(state, patch.id, now);
|
|
7150
|
+
case "globalAsset.invalidateRef":
|
|
7151
|
+
return applyGlobalAssetInvalidateRef(state, patch.id, patch.which, now);
|
|
6677
7152
|
case "plan.upsert":
|
|
6678
7153
|
return applyPlanUpsert(state, patch.plan, now);
|
|
6679
7154
|
case "plan.delete":
|
|
@@ -6698,12 +7173,13 @@ function applyRequestCreate(state, request, now) {
|
|
|
6698
7173
|
if (state.synced.collections.requests[request.id]) {
|
|
6699
7174
|
return { next: state, changedIds: [] };
|
|
6700
7175
|
}
|
|
7176
|
+
const tree = request.folderId ? state.synced.collections.tree : pushTreeChild(state.synced.collections.tree, { kind: "request", id: request.id });
|
|
6701
7177
|
const synced = {
|
|
6702
7178
|
...state.synced,
|
|
6703
7179
|
collections: {
|
|
6704
7180
|
...state.synced.collections,
|
|
6705
7181
|
requests: { ...state.synced.collections.requests, [request.id]: request },
|
|
6706
|
-
tree
|
|
7182
|
+
tree
|
|
6707
7183
|
},
|
|
6708
7184
|
meta: { ...state.synced.meta, updatedAt: now }
|
|
6709
7185
|
};
|
|
@@ -6752,12 +7228,13 @@ function applyFolderCreate(state, folder, now) {
|
|
|
6752
7228
|
if (state.synced.collections.folders[folder.id]) {
|
|
6753
7229
|
return { next: state, changedIds: [] };
|
|
6754
7230
|
}
|
|
7231
|
+
const tree = folder.parentId ? state.synced.collections.tree : pushTreeChild(state.synced.collections.tree, { kind: "folder", id: folder.id });
|
|
6755
7232
|
const synced = {
|
|
6756
7233
|
...state.synced,
|
|
6757
7234
|
collections: {
|
|
6758
7235
|
...state.synced.collections,
|
|
6759
7236
|
folders: { ...state.synced.collections.folders, [folder.id]: folder },
|
|
6760
|
-
tree
|
|
7237
|
+
tree
|
|
6761
7238
|
},
|
|
6762
7239
|
meta: { ...state.synced.meta, updatedAt: now }
|
|
6763
7240
|
};
|
|
@@ -6826,6 +7303,57 @@ function applyFolderMove(state, id, newParentId, now) {
|
|
|
6826
7303
|
};
|
|
6827
7304
|
return { next: { ...state, synced }, changedIds: [id] };
|
|
6828
7305
|
}
|
|
7306
|
+
function applyFolderUpdate(state, id, patch, now) {
|
|
7307
|
+
const folder = state.synced.collections.folders[id];
|
|
7308
|
+
if (!folder) {
|
|
7309
|
+
return { next: state, changedIds: [] };
|
|
7310
|
+
}
|
|
7311
|
+
const nameChanging = "name" in patch && patch.name !== void 0;
|
|
7312
|
+
const authChanging = "auth" in patch;
|
|
7313
|
+
if (!nameChanging && !authChanging) {
|
|
7314
|
+
return { next: state, changedIds: [] };
|
|
7315
|
+
}
|
|
7316
|
+
let nextName = folder.name;
|
|
7317
|
+
if (nameChanging) {
|
|
7318
|
+
const trimmed = patch.name.trim();
|
|
7319
|
+
if (!trimmed) {
|
|
7320
|
+
} else if (trimmed === folder.name) {
|
|
7321
|
+
} else if (!isFolderNameUnique(state, folder.parentId, trimmed, id)) {
|
|
7322
|
+
return { next: state, changedIds: [] };
|
|
7323
|
+
} else {
|
|
7324
|
+
nextName = trimmed;
|
|
7325
|
+
}
|
|
7326
|
+
}
|
|
7327
|
+
const nextFolder = { ...folder, name: nextName };
|
|
7328
|
+
if (authChanging) {
|
|
7329
|
+
if (patch.auth === void 0) {
|
|
7330
|
+
delete nextFolder.auth;
|
|
7331
|
+
} else {
|
|
7332
|
+
nextFolder.auth = patch.auth;
|
|
7333
|
+
}
|
|
7334
|
+
}
|
|
7335
|
+
if (nextFolder.name === folder.name && nextFolder.auth === folder.auth) {
|
|
7336
|
+
return { next: state, changedIds: [] };
|
|
7337
|
+
}
|
|
7338
|
+
const synced = {
|
|
7339
|
+
...state.synced,
|
|
7340
|
+
collections: {
|
|
7341
|
+
...state.synced.collections,
|
|
7342
|
+
folders: { ...state.synced.collections.folders, [id]: nextFolder }
|
|
7343
|
+
},
|
|
7344
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7345
|
+
};
|
|
7346
|
+
return { next: { ...state, synced }, changedIds: [id] };
|
|
7347
|
+
}
|
|
7348
|
+
function isFolderNameUnique(state, parentId, trimmedCandidate, ignoreId) {
|
|
7349
|
+
const target = trimmedCandidate.toLowerCase();
|
|
7350
|
+
for (const f of Object.values(state.synced.collections.folders)) {
|
|
7351
|
+
if (f.id === ignoreId) continue;
|
|
7352
|
+
if (f.parentId !== parentId) continue;
|
|
7353
|
+
if (f.name.trim().toLowerCase() === target) return false;
|
|
7354
|
+
}
|
|
7355
|
+
return true;
|
|
7356
|
+
}
|
|
6829
7357
|
function applyFolderImportApicircle(state, parsed, parentFolderId, now) {
|
|
6830
7358
|
const result = importApicircleFolderInto(
|
|
6831
7359
|
state.synced,
|
|
@@ -6905,7 +7433,7 @@ function applyEnvSetPriority(state, order, now) {
|
|
|
6905
7433
|
const knownLocal = new Set(Object.keys(state.synced.environments.items));
|
|
6906
7434
|
const seen = /* @__PURE__ */ new Set();
|
|
6907
7435
|
const filtered = order.filter((ref) => {
|
|
6908
|
-
const key =
|
|
7436
|
+
const key = envPriorityKey2(ref);
|
|
6909
7437
|
if (seen.has(key)) return false;
|
|
6910
7438
|
if (ref.kind === "local" && !knownLocal.has(ref.name)) return false;
|
|
6911
7439
|
seen.add(key);
|
|
@@ -6916,7 +7444,7 @@ function applyEnvSetPriority(state, order, now) {
|
|
|
6916
7444
|
environments: { ...state.synced.environments, priorityOrder: filtered },
|
|
6917
7445
|
meta: { ...state.synced.meta, updatedAt: now }
|
|
6918
7446
|
};
|
|
6919
|
-
return { next: { ...state, synced }, changedIds: filtered.map(
|
|
7447
|
+
return { next: { ...state, synced }, changedIds: filtered.map(envPriorityKey2) };
|
|
6920
7448
|
}
|
|
6921
7449
|
function applySecretKeyUpsert(state, meta, now) {
|
|
6922
7450
|
if (!meta.id || !meta.label.trim() || !meta.salt) {
|
|
@@ -6932,6 +7460,28 @@ function applySecretKeyUpsert(state, meta, now) {
|
|
|
6932
7460
|
};
|
|
6933
7461
|
return { next: { ...state, synced }, changedIds: [meta.id] };
|
|
6934
7462
|
}
|
|
7463
|
+
function applySecretCryptoSet(state, crypto2, now) {
|
|
7464
|
+
if (!crypto2 || crypto2.kdf !== "pbkdf2-sha256-v1" || !crypto2.salt || !crypto2.verifier || !(crypto2.iterations >= 1)) {
|
|
7465
|
+
return { next: state, changedIds: [] };
|
|
7466
|
+
}
|
|
7467
|
+
const synced = {
|
|
7468
|
+
...state.synced,
|
|
7469
|
+
secretCrypto: { ...crypto2 },
|
|
7470
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7471
|
+
};
|
|
7472
|
+
return { next: { ...state, synced }, changedIds: ["secret.crypto"] };
|
|
7473
|
+
}
|
|
7474
|
+
function applySecretCryptoClear(state, now) {
|
|
7475
|
+
if (!state.synced.secretCrypto) {
|
|
7476
|
+
return { next: state, changedIds: [] };
|
|
7477
|
+
}
|
|
7478
|
+
const synced = {
|
|
7479
|
+
...state.synced,
|
|
7480
|
+
secretCrypto: null,
|
|
7481
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7482
|
+
};
|
|
7483
|
+
return { next: { ...state, synced }, changedIds: ["secret.crypto"] };
|
|
7484
|
+
}
|
|
6935
7485
|
function applyAssertionUpsert(state, requestId, assertion, now) {
|
|
6936
7486
|
const request = state.synced.collections.requests[requestId];
|
|
6937
7487
|
if (!request) {
|
|
@@ -6989,6 +7539,340 @@ function applyMockDelete(state, id, now) {
|
|
|
6989
7539
|
} : state.local;
|
|
6990
7540
|
return { next: { synced, local }, changedIds: [id] };
|
|
6991
7541
|
}
|
|
7542
|
+
function applyReleasePublish(state, entry, now) {
|
|
7543
|
+
const synced = appendReleaseEntry(state.synced, entry, now);
|
|
7544
|
+
return { next: { ...state, synced }, changedIds: [entry.version] };
|
|
7545
|
+
}
|
|
7546
|
+
function applyReleaseDeprecate(state, version, now) {
|
|
7547
|
+
const synced = deprecateRelease(state.synced, version, now);
|
|
7548
|
+
return { next: { ...state, synced }, changedIds: [version] };
|
|
7549
|
+
}
|
|
7550
|
+
function applyReleaseYank(state, version, now) {
|
|
7551
|
+
const synced = yankRelease(state.synced, version, now);
|
|
7552
|
+
return { next: { ...state, synced }, changedIds: [version] };
|
|
7553
|
+
}
|
|
7554
|
+
function applyLinkedWorkspaceUpsert(state, link, ledger, snapshot2, now) {
|
|
7555
|
+
const synced = {
|
|
7556
|
+
...state.synced,
|
|
7557
|
+
linkedWorkspaces: { ...state.synced.linkedWorkspaces, [link.id]: link },
|
|
7558
|
+
releases: ledger ? {
|
|
7559
|
+
...state.synced.releases,
|
|
7560
|
+
perLink: { ...state.synced.releases.perLink, [link.id]: ledger }
|
|
7561
|
+
} : state.synced.releases,
|
|
7562
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7563
|
+
};
|
|
7564
|
+
const local = snapshot2 ? {
|
|
7565
|
+
...state.local,
|
|
7566
|
+
linkedCollections: { ...state.local.linkedCollections, [link.id]: snapshot2 }
|
|
7567
|
+
} : state.local;
|
|
7568
|
+
return { next: { synced, local }, changedIds: [link.id] };
|
|
7569
|
+
}
|
|
7570
|
+
function applyLinkedWorkspaceRemove(state, id, now) {
|
|
7571
|
+
if (!state.synced.linkedWorkspaces[id]) {
|
|
7572
|
+
return { next: state, changedIds: [] };
|
|
7573
|
+
}
|
|
7574
|
+
const linkedWorkspaces = { ...state.synced.linkedWorkspaces };
|
|
7575
|
+
delete linkedWorkspaces[id];
|
|
7576
|
+
const perLink = { ...state.synced.releases.perLink };
|
|
7577
|
+
delete perLink[id];
|
|
7578
|
+
const prefix = `${id}:`;
|
|
7579
|
+
const dropPrefixed = (map) => Object.fromEntries(Object.entries(map).filter(([k]) => !k.startsWith(prefix)));
|
|
7580
|
+
const synced = {
|
|
7581
|
+
...state.synced,
|
|
7582
|
+
linkedWorkspaces,
|
|
7583
|
+
releases: { ...state.synced.releases, perLink },
|
|
7584
|
+
linkedOverrides: {
|
|
7585
|
+
requests: dropPrefixed(state.synced.linkedOverrides.requests),
|
|
7586
|
+
environmentVars: dropPrefixed(state.synced.linkedOverrides.environmentVars)
|
|
7587
|
+
},
|
|
7588
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7589
|
+
};
|
|
7590
|
+
const linkedCollections = { ...state.local.linkedCollections };
|
|
7591
|
+
delete linkedCollections[id];
|
|
7592
|
+
const githubLinks = { ...state.local.sessions.github.links };
|
|
7593
|
+
delete githubLinks[id];
|
|
7594
|
+
const local = {
|
|
7595
|
+
...state.local,
|
|
7596
|
+
linkedCollections,
|
|
7597
|
+
sessions: {
|
|
7598
|
+
...state.local.sessions,
|
|
7599
|
+
github: { ...state.local.sessions.github, links: githubLinks }
|
|
7600
|
+
}
|
|
7601
|
+
};
|
|
7602
|
+
return { next: { synced, local }, changedIds: [id] };
|
|
7603
|
+
}
|
|
7604
|
+
function applyLinkedWorkspaceApplyUpdate(state, patch, now) {
|
|
7605
|
+
const link = state.synced.linkedWorkspaces[patch.id];
|
|
7606
|
+
if (!link) {
|
|
7607
|
+
return { next: state, changedIds: [] };
|
|
7608
|
+
}
|
|
7609
|
+
const prefix = `${patch.id}:`;
|
|
7610
|
+
const otherRequests = Object.fromEntries(
|
|
7611
|
+
Object.entries(state.synced.linkedOverrides.requests).filter(([k]) => !k.startsWith(prefix))
|
|
7612
|
+
);
|
|
7613
|
+
for (const o of patch.requestOverrides) {
|
|
7614
|
+
otherRequests[`${o.linkedWorkspaceId}:${o.itemId}`] = o;
|
|
7615
|
+
}
|
|
7616
|
+
const otherEnvVars = Object.fromEntries(
|
|
7617
|
+
Object.entries(state.synced.linkedOverrides.environmentVars).filter(
|
|
7618
|
+
([k]) => !k.startsWith(prefix)
|
|
7619
|
+
)
|
|
7620
|
+
);
|
|
7621
|
+
for (const o of patch.envVarOverrides) {
|
|
7622
|
+
otherEnvVars[`${o.linkedWorkspaceId}:${o.envName}:${o.varKey}`] = o;
|
|
7623
|
+
}
|
|
7624
|
+
const synced = {
|
|
7625
|
+
...state.synced,
|
|
7626
|
+
linkedWorkspaces: {
|
|
7627
|
+
...state.synced.linkedWorkspaces,
|
|
7628
|
+
[patch.id]: { ...link, pinnedVersion: patch.pinnedVersion }
|
|
7629
|
+
},
|
|
7630
|
+
releases: {
|
|
7631
|
+
...state.synced.releases,
|
|
7632
|
+
perLink: { ...state.synced.releases.perLink, [patch.id]: patch.ledger }
|
|
7633
|
+
},
|
|
7634
|
+
linkedOverrides: { requests: otherRequests, environmentVars: otherEnvVars },
|
|
7635
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7636
|
+
};
|
|
7637
|
+
const local = {
|
|
7638
|
+
...state.local,
|
|
7639
|
+
linkedCollections: { ...state.local.linkedCollections, [patch.id]: patch.snapshot }
|
|
7640
|
+
};
|
|
7641
|
+
return { next: { synced, local }, changedIds: [patch.id] };
|
|
7642
|
+
}
|
|
7643
|
+
function applyLinkedOverrideSetRequest(state, override, now) {
|
|
7644
|
+
const key = `${override.linkedWorkspaceId}:${override.itemId}`;
|
|
7645
|
+
const synced = {
|
|
7646
|
+
...state.synced,
|
|
7647
|
+
linkedOverrides: {
|
|
7648
|
+
...state.synced.linkedOverrides,
|
|
7649
|
+
requests: {
|
|
7650
|
+
...state.synced.linkedOverrides.requests,
|
|
7651
|
+
[key]: { ...override, updatedAt: now }
|
|
7652
|
+
}
|
|
7653
|
+
},
|
|
7654
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7655
|
+
};
|
|
7656
|
+
return { next: { ...state, synced }, changedIds: [key] };
|
|
7657
|
+
}
|
|
7658
|
+
function applyLinkedOverrideRemoveRequest(state, linkedWorkspaceId, itemId, now) {
|
|
7659
|
+
const key = `${linkedWorkspaceId}:${itemId}`;
|
|
7660
|
+
if (!state.synced.linkedOverrides.requests[key]) {
|
|
7661
|
+
return { next: state, changedIds: [] };
|
|
7662
|
+
}
|
|
7663
|
+
const requests = { ...state.synced.linkedOverrides.requests };
|
|
7664
|
+
delete requests[key];
|
|
7665
|
+
const synced = {
|
|
7666
|
+
...state.synced,
|
|
7667
|
+
linkedOverrides: { ...state.synced.linkedOverrides, requests },
|
|
7668
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7669
|
+
};
|
|
7670
|
+
return { next: { ...state, synced }, changedIds: [key] };
|
|
7671
|
+
}
|
|
7672
|
+
function applyLinkedOverrideSetEnvVar(state, override, now) {
|
|
7673
|
+
const key = `${override.linkedWorkspaceId}:${override.envName}:${override.varKey}`;
|
|
7674
|
+
const synced = {
|
|
7675
|
+
...state.synced,
|
|
7676
|
+
linkedOverrides: {
|
|
7677
|
+
...state.synced.linkedOverrides,
|
|
7678
|
+
environmentVars: {
|
|
7679
|
+
...state.synced.linkedOverrides.environmentVars,
|
|
7680
|
+
[key]: { ...override, updatedAt: now }
|
|
7681
|
+
}
|
|
7682
|
+
},
|
|
7683
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7684
|
+
};
|
|
7685
|
+
return { next: { ...state, synced }, changedIds: [key] };
|
|
7686
|
+
}
|
|
7687
|
+
function applyLinkedOverrideRemoveEnvVar(state, linkedWorkspaceId, envName, varKey, now) {
|
|
7688
|
+
const key = `${linkedWorkspaceId}:${envName}:${varKey}`;
|
|
7689
|
+
if (!state.synced.linkedOverrides.environmentVars[key]) {
|
|
7690
|
+
return { next: state, changedIds: [] };
|
|
7691
|
+
}
|
|
7692
|
+
const environmentVars = { ...state.synced.linkedOverrides.environmentVars };
|
|
7693
|
+
delete environmentVars[key];
|
|
7694
|
+
const synced = {
|
|
7695
|
+
...state.synced,
|
|
7696
|
+
linkedOverrides: { ...state.synced.linkedOverrides, environmentVars },
|
|
7697
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7698
|
+
};
|
|
7699
|
+
return { next: { ...state, synced }, changedIds: [key] };
|
|
7700
|
+
}
|
|
7701
|
+
function applyLinkedOverrideClearForLink(state, linkedWorkspaceId, now) {
|
|
7702
|
+
const prefix = `${linkedWorkspaceId}:`;
|
|
7703
|
+
const requestKeys = Object.keys(state.synced.linkedOverrides.requests).filter(
|
|
7704
|
+
(k) => k.startsWith(prefix)
|
|
7705
|
+
);
|
|
7706
|
+
const envKeys = Object.keys(state.synced.linkedOverrides.environmentVars).filter(
|
|
7707
|
+
(k) => k.startsWith(prefix)
|
|
7708
|
+
);
|
|
7709
|
+
if (requestKeys.length === 0 && envKeys.length === 0) {
|
|
7710
|
+
return { next: state, changedIds: [] };
|
|
7711
|
+
}
|
|
7712
|
+
const requests = Object.fromEntries(
|
|
7713
|
+
Object.entries(state.synced.linkedOverrides.requests).filter(([k]) => !k.startsWith(prefix))
|
|
7714
|
+
);
|
|
7715
|
+
const environmentVars = Object.fromEntries(
|
|
7716
|
+
Object.entries(state.synced.linkedOverrides.environmentVars).filter(
|
|
7717
|
+
([k]) => !k.startsWith(prefix)
|
|
7718
|
+
)
|
|
7719
|
+
);
|
|
7720
|
+
const synced = {
|
|
7721
|
+
...state.synced,
|
|
7722
|
+
linkedOverrides: { requests, environmentVars },
|
|
7723
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7724
|
+
};
|
|
7725
|
+
return { next: { ...state, synced }, changedIds: [...requestKeys, ...envKeys] };
|
|
7726
|
+
}
|
|
7727
|
+
function applyGlobalAssetUpsertFile(state, file, now) {
|
|
7728
|
+
const files = state.synced.globalAssets.files ?? {};
|
|
7729
|
+
const existing = files[file.id];
|
|
7730
|
+
const next = {
|
|
7731
|
+
...file,
|
|
7732
|
+
workingBranchRef: file.workingBranchRef !== void 0 ? file.workingBranchRef : existing?.workingBranchRef,
|
|
7733
|
+
baseBranchRef: file.baseBranchRef !== void 0 ? file.baseBranchRef : existing?.baseBranchRef,
|
|
7734
|
+
updatedAt: now
|
|
7735
|
+
};
|
|
7736
|
+
const synced = {
|
|
7737
|
+
...state.synced,
|
|
7738
|
+
globalAssets: {
|
|
7739
|
+
...state.synced.globalAssets,
|
|
7740
|
+
files: { ...files, [file.id]: next }
|
|
7741
|
+
},
|
|
7742
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7743
|
+
};
|
|
7744
|
+
return { next: { ...state, synced }, changedIds: [file.id] };
|
|
7745
|
+
}
|
|
7746
|
+
function applyGlobalAssetRemoveFile(state, id, now) {
|
|
7747
|
+
const files = state.synced.globalAssets.files ?? {};
|
|
7748
|
+
if (!files[id]) {
|
|
7749
|
+
return { next: state, changedIds: [] };
|
|
7750
|
+
}
|
|
7751
|
+
const { [id]: _drop, ...rest } = files;
|
|
7752
|
+
void _drop;
|
|
7753
|
+
const requests = { ...state.synced.collections.requests };
|
|
7754
|
+
for (const [reqId, req] of Object.entries(requests)) {
|
|
7755
|
+
const body = clearAssetFromRequestBody(req.body, id);
|
|
7756
|
+
if (body !== req.body) requests[reqId] = { ...req, body, updatedAt: now };
|
|
7757
|
+
}
|
|
7758
|
+
const mockServers = { ...state.synced.mockServers };
|
|
7759
|
+
for (const [serverId, server] of Object.entries(mockServers)) {
|
|
7760
|
+
let touchedServer = false;
|
|
7761
|
+
const endpoints = server.endpoints.map((endpoint) => {
|
|
7762
|
+
let touched = false;
|
|
7763
|
+
const defaultResponse = clearAssetFromMockResponse(endpoint.defaultResponse, id);
|
|
7764
|
+
if (defaultResponse !== endpoint.defaultResponse) touched = true;
|
|
7765
|
+
const requestValidation = endpoint.requestValidation.map((rule) => {
|
|
7766
|
+
const failResponse = clearAssetFromMockResponse(rule.failResponse, id);
|
|
7767
|
+
if (failResponse === rule.failResponse) return rule;
|
|
7768
|
+
touched = true;
|
|
7769
|
+
return { ...rule, failResponse };
|
|
7770
|
+
});
|
|
7771
|
+
const responseRules = endpoint.responseRules.map((rule) => {
|
|
7772
|
+
const response = clearAssetFromMockResponse(rule.response, id);
|
|
7773
|
+
if (response === rule.response) return rule;
|
|
7774
|
+
touched = true;
|
|
7775
|
+
return { ...rule, response };
|
|
7776
|
+
});
|
|
7777
|
+
if (!touched) return endpoint;
|
|
7778
|
+
touchedServer = true;
|
|
7779
|
+
return { ...endpoint, defaultResponse, requestValidation, responseRules };
|
|
7780
|
+
});
|
|
7781
|
+
if (touchedServer) {
|
|
7782
|
+
const source = server.source.kind === "manual" ? { kind: "manual", endpoints } : server.source;
|
|
7783
|
+
mockServers[serverId] = { ...server, source, endpoints, updatedAt: now };
|
|
7784
|
+
}
|
|
7785
|
+
}
|
|
7786
|
+
let local = state.local;
|
|
7787
|
+
if (local.pendingFileUploads && local.pendingFileUploads[id]) {
|
|
7788
|
+
const nextPending = { ...local.pendingFileUploads };
|
|
7789
|
+
delete nextPending[id];
|
|
7790
|
+
local = { ...local, pendingFileUploads: nextPending };
|
|
7791
|
+
}
|
|
7792
|
+
if (local.assetUsageIndex && local.assetUsageIndex[id]) {
|
|
7793
|
+
const nextUsage = { ...local.assetUsageIndex };
|
|
7794
|
+
delete nextUsage[id];
|
|
7795
|
+
local = { ...local, assetUsageIndex: nextUsage };
|
|
7796
|
+
}
|
|
7797
|
+
const existing = files[id];
|
|
7798
|
+
const hadRemoteRef = Boolean(existing.workingBranchRef || existing.baseBranchRef);
|
|
7799
|
+
if (hadRemoteRef && !(local.pendingAttachmentDeletes ?? []).includes(existing.slotId)) {
|
|
7800
|
+
local = {
|
|
7801
|
+
...local,
|
|
7802
|
+
pendingAttachmentDeletes: [...local.pendingAttachmentDeletes ?? [], existing.slotId]
|
|
7803
|
+
};
|
|
7804
|
+
}
|
|
7805
|
+
const synced = {
|
|
7806
|
+
...state.synced,
|
|
7807
|
+
collections: { ...state.synced.collections, requests },
|
|
7808
|
+
mockServers,
|
|
7809
|
+
globalAssets: { ...state.synced.globalAssets, files: rest },
|
|
7810
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7811
|
+
};
|
|
7812
|
+
return { next: { synced, local }, changedIds: [id] };
|
|
7813
|
+
}
|
|
7814
|
+
function applyGlobalAssetMarkPushed(state, id, ref, now) {
|
|
7815
|
+
return mutateAssetRef(state, id, now, (asset) => ({ ...asset, workingBranchRef: ref }));
|
|
7816
|
+
}
|
|
7817
|
+
function applyGlobalAssetMarkMerged(state, id, ref, now) {
|
|
7818
|
+
return mutateAssetRef(state, id, now, (asset) => ({ ...asset, baseBranchRef: ref }));
|
|
7819
|
+
}
|
|
7820
|
+
function applyGlobalAssetCleanupWorkingRef(state, id, now) {
|
|
7821
|
+
return mutateAssetRef(state, id, now, (asset) => {
|
|
7822
|
+
if (!asset.workingBranchRef) return asset;
|
|
7823
|
+
return { ...asset, workingBranchRef: null };
|
|
7824
|
+
});
|
|
7825
|
+
}
|
|
7826
|
+
function applyGlobalAssetInvalidateRef(state, id, which, now) {
|
|
7827
|
+
return mutateAssetRef(state, id, now, (asset) => {
|
|
7828
|
+
if (which === "working") {
|
|
7829
|
+
if (!asset.workingBranchRef) return asset;
|
|
7830
|
+
return { ...asset, workingBranchRef: null };
|
|
7831
|
+
}
|
|
7832
|
+
if (!asset.baseBranchRef) return asset;
|
|
7833
|
+
return { ...asset, baseBranchRef: null };
|
|
7834
|
+
});
|
|
7835
|
+
}
|
|
7836
|
+
function mutateAssetRef(state, id, now, transform) {
|
|
7837
|
+
const files = state.synced.globalAssets.files ?? {};
|
|
7838
|
+
const existing = files[id];
|
|
7839
|
+
if (!existing) return { next: state, changedIds: [] };
|
|
7840
|
+
const updated = transform(existing);
|
|
7841
|
+
if (updated === existing) return { next: state, changedIds: [] };
|
|
7842
|
+
const next = { ...updated, updatedAt: now };
|
|
7843
|
+
const synced = {
|
|
7844
|
+
...state.synced,
|
|
7845
|
+
globalAssets: {
|
|
7846
|
+
...state.synced.globalAssets,
|
|
7847
|
+
files: { ...files, [id]: next }
|
|
7848
|
+
},
|
|
7849
|
+
meta: { ...state.synced.meta, updatedAt: now }
|
|
7850
|
+
};
|
|
7851
|
+
return { next: { ...state, synced }, changedIds: [id] };
|
|
7852
|
+
}
|
|
7853
|
+
function clearAssetFromRequestBody(body, id) {
|
|
7854
|
+
if (body.type === "binary" && body.attachment?.globalFileAssetId === id) {
|
|
7855
|
+
return { type: "binary", content: "" };
|
|
7856
|
+
}
|
|
7857
|
+
if (body.type !== "form-data" || !body.formRows) return body;
|
|
7858
|
+
let touched = false;
|
|
7859
|
+
const formRows = body.formRows.map((row) => {
|
|
7860
|
+
if (row.kind !== "file" || row.globalFileAssetId !== id) return row;
|
|
7861
|
+
touched = true;
|
|
7862
|
+
return { kind: "file", key: row.key, enabled: row.enabled, slotId: null };
|
|
7863
|
+
});
|
|
7864
|
+
return touched ? { ...body, formRows } : body;
|
|
7865
|
+
}
|
|
7866
|
+
function clearAssetFromMockResponse(response, id) {
|
|
7867
|
+
const body = clearAssetFromMockBody(response.body, id);
|
|
7868
|
+
return body === response.body ? response : { ...response, body };
|
|
7869
|
+
}
|
|
7870
|
+
function clearAssetFromMockBody(body, id) {
|
|
7871
|
+
if (body.type === "binary" && body.attachment?.globalFileAssetId === id) {
|
|
7872
|
+
return { type: "binary", content: "" };
|
|
7873
|
+
}
|
|
7874
|
+
return body;
|
|
7875
|
+
}
|
|
6992
7876
|
function applyPlanUpsert(state, plan, now) {
|
|
6993
7877
|
const existing = state.local.executionPlans[plan.id];
|
|
6994
7878
|
const merged = existing ? { ...existing, ...plan, id: existing.id, createdAt: existing.createdAt, updatedAt: now } : { ...plan, updatedAt: now };
|
|
@@ -7163,7 +8047,7 @@ function applyHistoryPurge(state, olderThanMs) {
|
|
|
7163
8047
|
}
|
|
7164
8048
|
|
|
7165
8049
|
// src/workspace/runPlan.ts
|
|
7166
|
-
import { envPriorityKey as
|
|
8050
|
+
import { envPriorityKey as envPriorityKey3, generateId as generateId3, RUN_BODY_PREVIEW_LIMIT } from "@apicircle/shared";
|
|
7167
8051
|
var MAX_REQUEST_RUNS = 500;
|
|
7168
8052
|
var MAX_PLAN_RUNS = 200;
|
|
7169
8053
|
var ANONYMOUS_ACTOR = { kind: "unknown", name: "unknown" };
|
|
@@ -7229,22 +8113,6 @@ function lookupPlanStepRequest(step, synced, local) {
|
|
|
7229
8113
|
linkedGlobalAssets: snapshot2.globalAssets
|
|
7230
8114
|
};
|
|
7231
8115
|
}
|
|
7232
|
-
function mergeRequestOverride(base, patch) {
|
|
7233
|
-
const merged = { ...base };
|
|
7234
|
-
if (patch.name !== void 0) merged.name = patch.name;
|
|
7235
|
-
if (patch.method !== void 0) merged.method = patch.method;
|
|
7236
|
-
if (patch.url !== void 0) merged.url = patch.url;
|
|
7237
|
-
if (patch.headers !== void 0) merged.headers = patch.headers;
|
|
7238
|
-
if (patch.query !== void 0) merged.query = patch.query;
|
|
7239
|
-
if (patch.pathParams !== void 0) merged.pathParams = patch.pathParams;
|
|
7240
|
-
if (patch.cookies !== void 0) merged.cookies = patch.cookies;
|
|
7241
|
-
if (patch.body !== void 0) merged.body = patch.body;
|
|
7242
|
-
if (patch.auth !== void 0) merged.auth = patch.auth;
|
|
7243
|
-
if (patch.contextVars !== void 0) merged.contextVars = patch.contextVars;
|
|
7244
|
-
if (patch.extractions !== void 0) merged.extractions = patch.extractions;
|
|
7245
|
-
if (patch.assertions !== void 0) merged.assertions = patch.assertions;
|
|
7246
|
-
return merged;
|
|
7247
|
-
}
|
|
7248
8116
|
function applyEnvironmentOverrides(source, linkedWorkspaceId, synced) {
|
|
7249
8117
|
const overrides = Object.values(synced.linkedOverrides.environmentVars).filter(
|
|
7250
8118
|
(override) => override.linkedWorkspaceId === linkedWorkspaceId
|
|
@@ -7461,7 +8329,7 @@ function buildEnvMaps(synced, secretsById, local) {
|
|
|
7461
8329
|
vars[v.key] = v.value;
|
|
7462
8330
|
}
|
|
7463
8331
|
}
|
|
7464
|
-
flat[
|
|
8332
|
+
flat[envPriorityKey3({ kind: "local", name })] = vars;
|
|
7465
8333
|
}
|
|
7466
8334
|
if (local) {
|
|
7467
8335
|
for (const [linkId, snapshot2] of Object.entries(local.linkedCollections)) {
|
|
@@ -7478,7 +8346,7 @@ function buildEnvMaps(synced, secretsById, local) {
|
|
|
7478
8346
|
vars[variable.key] = variable.value;
|
|
7479
8347
|
}
|
|
7480
8348
|
}
|
|
7481
|
-
flat[
|
|
8349
|
+
flat[envPriorityKey3({ kind: "linked", linkedWorkspaceId: linkId, envName })] = vars;
|
|
7482
8350
|
}
|
|
7483
8351
|
}
|
|
7484
8352
|
}
|
|
@@ -7505,7 +8373,7 @@ function resolveRequest(request, synced, plan, envRefs, globalContext, flatEnvs,
|
|
|
7505
8373
|
contextVars: Object.entries(ctxMap).map(([key, value]) => ({ key, value })),
|
|
7506
8374
|
environments: flatEnvs,
|
|
7507
8375
|
activeEnvName: null,
|
|
7508
|
-
priorityOrder: envRefs.map(
|
|
8376
|
+
priorityOrder: envRefs.map(envPriorityKey3),
|
|
7509
8377
|
secrets: secretsByLabel
|
|
7510
8378
|
});
|
|
7511
8379
|
const missing = /* @__PURE__ */ new Set();
|
|
@@ -7887,22 +8755,30 @@ export {
|
|
|
7887
8755
|
HTTP_HEADERS_MAP,
|
|
7888
8756
|
OAuth2TokenError,
|
|
7889
8757
|
PlanRunDeniedError,
|
|
8758
|
+
REGISTRY_JSON_PATH,
|
|
7890
8759
|
RemoteWorkspaceParseError,
|
|
7891
8760
|
TRANSFORM_FORMAT_LABELS,
|
|
8761
|
+
WORKSPACE_DIR,
|
|
8762
|
+
appendReleaseEntry,
|
|
7892
8763
|
applyAuth,
|
|
7893
8764
|
applyAwsSigV4,
|
|
7894
8765
|
applyContentTypeForBodyType,
|
|
8766
|
+
applyLinkedEnvironmentOverrides,
|
|
7895
8767
|
applyLinkedUpdate,
|
|
7896
8768
|
applyMerge,
|
|
7897
8769
|
applyMutation,
|
|
7898
8770
|
applyPathParams,
|
|
7899
8771
|
assertNoPlaintextCredentials,
|
|
8772
|
+
attachmentPath,
|
|
8773
|
+
attachmentsDir,
|
|
7900
8774
|
buildAuthorizeUrl,
|
|
7901
8775
|
buildAutoHeaders,
|
|
7902
8776
|
buildDigestAuthHeader,
|
|
7903
8777
|
buildHawkAuthHeader,
|
|
8778
|
+
buildLinkedSnapshot,
|
|
7904
8779
|
buildNtlmType1Negotiate,
|
|
7905
8780
|
buildNtlmType3Authenticate,
|
|
8781
|
+
buildReleaseEntry,
|
|
7906
8782
|
buildRequest,
|
|
7907
8783
|
buildScope,
|
|
7908
8784
|
collectAttachmentSlots,
|
|
@@ -7916,6 +8792,7 @@ export {
|
|
|
7916
8792
|
composeUrl,
|
|
7917
8793
|
composeUrlWithQuery,
|
|
7918
8794
|
computeCodeChallenge,
|
|
8795
|
+
computeRequestOverridePatch,
|
|
7919
8796
|
computeThreeWayDiff,
|
|
7920
8797
|
computeTransformSavings,
|
|
7921
8798
|
decryptString,
|
|
@@ -7928,6 +8805,7 @@ export {
|
|
|
7928
8805
|
exportKey,
|
|
7929
8806
|
extractContext,
|
|
7930
8807
|
fetchOAuth2Token,
|
|
8808
|
+
fetchRemoteWorkspaceJson,
|
|
7931
8809
|
findPathPlaceholders,
|
|
7932
8810
|
generateAesKey,
|
|
7933
8811
|
generateCodeVerifier,
|
|
@@ -7945,14 +8823,18 @@ export {
|
|
|
7945
8823
|
hasUnpushedChanges,
|
|
7946
8824
|
importApicircleFolderInto,
|
|
7947
8825
|
importKey,
|
|
8826
|
+
initSecretCrypto,
|
|
7948
8827
|
isApicircleEnvironment,
|
|
7949
8828
|
isApicircleFolderExport,
|
|
7950
8829
|
isDesktop,
|
|
8830
|
+
isEmptyOverridePatch,
|
|
7951
8831
|
isInsomniaExport,
|
|
7952
8832
|
isPostmanEnvironment,
|
|
7953
8833
|
isPostmanV2Collection,
|
|
7954
8834
|
isValidSemver,
|
|
8835
|
+
ledgerFromProbe,
|
|
7955
8836
|
lookup,
|
|
8837
|
+
mergeRequestOverride,
|
|
7956
8838
|
mergeWithAutoHeaders,
|
|
7957
8839
|
normalizeContentType,
|
|
7958
8840
|
parseApicircleEnvironment,
|
|
@@ -7963,12 +8845,15 @@ export {
|
|
|
7963
8845
|
parseDigestChallenge,
|
|
7964
8846
|
parseGraphqlSchema,
|
|
7965
8847
|
parseInsomniaCollection,
|
|
8848
|
+
parseLinkedWorkspaceJson,
|
|
7966
8849
|
parseNtlmType2Challenge,
|
|
7967
8850
|
parsePostmanCollection,
|
|
7968
8851
|
parsePostmanEnvironment,
|
|
8852
|
+
parseRegistryActiveId,
|
|
7969
8853
|
parseSemver,
|
|
7970
8854
|
parseUrlQuery,
|
|
7971
8855
|
parseWorkspaceJson,
|
|
8856
|
+
plaintextEnvMap,
|
|
7972
8857
|
pollDeviceFlow,
|
|
7973
8858
|
preSendValidation,
|
|
7974
8859
|
previewLinkedUpdate,
|
|
@@ -7981,6 +8866,7 @@ export {
|
|
|
7981
8866
|
requestRunToExecutionResult,
|
|
7982
8867
|
resolveInheritedAuth,
|
|
7983
8868
|
resolvePlanRef,
|
|
8869
|
+
resolveRequestForExecution,
|
|
7984
8870
|
resolveString,
|
|
7985
8871
|
resolveStringMap,
|
|
7986
8872
|
runAssertions,
|
|
@@ -8002,7 +8888,9 @@ export {
|
|
|
8002
8888
|
toYaml,
|
|
8003
8889
|
tokenizeCurl,
|
|
8004
8890
|
tryParsePayload,
|
|
8891
|
+
unlockSecretCrypto,
|
|
8005
8892
|
validateBranchName,
|
|
8893
|
+
workspaceJsonPath,
|
|
8006
8894
|
yankRelease
|
|
8007
8895
|
};
|
|
8008
8896
|
//# sourceMappingURL=index.js.map
|