@apicircle/core 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-SGI6KGQ7.js → chunk-L5DQT7V6.js} +6 -3
- package/dist/chunk-L5DQT7V6.js.map +1 -0
- package/dist/index.cjs +414 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +414 -58
- package/dist/index.js.map +1 -1
- package/dist/workspace/file-backed.cjs +5 -2
- package/dist/workspace/file-backed.cjs.map +1 -1
- package/dist/workspace/file-backed.js +1 -1
- package/dist/workspace/registry.cjs +4 -2
- package/dist/workspace/registry.cjs.map +1 -1
- package/dist/workspace/registry.js +1 -1
- package/package.json +52 -62
- package/dist/chunk-SGI6KGQ7.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -2174,6 +2174,20 @@ function mergeWithAutoHeaders(userHeaders, overrides = {}) {
|
|
|
2174
2174
|
}
|
|
2175
2175
|
|
|
2176
2176
|
// src/request/buildRequest.ts
|
|
2177
|
+
function missingAttachmentMessage(slotId, filename) {
|
|
2178
|
+
const label = filename ? `${filename} (${slotId})` : slotId;
|
|
2179
|
+
return `Attachment ${label} is required for this request but is not downloaded locally. Download the missing attachments before sending or running the plan.`;
|
|
2180
|
+
}
|
|
2181
|
+
async function requireAttachment(slotId, resolveAttachment, filename) {
|
|
2182
|
+
if (!resolveAttachment) {
|
|
2183
|
+
throw new Error(missingAttachmentMessage(slotId, filename));
|
|
2184
|
+
}
|
|
2185
|
+
const file = await resolveAttachment(slotId);
|
|
2186
|
+
if (!file) {
|
|
2187
|
+
throw new Error(missingAttachmentMessage(slotId, filename));
|
|
2188
|
+
}
|
|
2189
|
+
return file;
|
|
2190
|
+
}
|
|
2177
2191
|
var PATH_PLACEHOLDER = /(?::([A-Za-z_][\w-]*)|(?<!\{)\{([A-Za-z_][\w-]*)\}(?!\}))/g;
|
|
2178
2192
|
function splitOnQuery(rawUrl) {
|
|
2179
2193
|
const q = rawUrl.indexOf("?");
|
|
@@ -2324,17 +2338,21 @@ async function composeBody(body, resolveAttachment) {
|
|
|
2324
2338
|
if (!row.enabled || !row.key.trim()) continue;
|
|
2325
2339
|
if (row.kind === "text") {
|
|
2326
2340
|
fd.append(row.key, row.value);
|
|
2327
|
-
} else if (row.slotId
|
|
2328
|
-
const file = await
|
|
2329
|
-
|
|
2341
|
+
} else if (row.slotId) {
|
|
2342
|
+
const file = await requireAttachment(row.slotId, resolveAttachment, row.filename);
|
|
2343
|
+
fd.append(row.key, file.blob, file.filename);
|
|
2330
2344
|
}
|
|
2331
2345
|
}
|
|
2332
2346
|
return fd;
|
|
2333
2347
|
}
|
|
2334
2348
|
if (body.type === "binary") {
|
|
2335
|
-
if (body.attachment?.slotId
|
|
2336
|
-
const file = await
|
|
2337
|
-
|
|
2349
|
+
if (body.attachment?.slotId) {
|
|
2350
|
+
const file = await requireAttachment(
|
|
2351
|
+
body.attachment.slotId,
|
|
2352
|
+
resolveAttachment,
|
|
2353
|
+
body.attachment.filename
|
|
2354
|
+
);
|
|
2355
|
+
return file.blob;
|
|
2338
2356
|
}
|
|
2339
2357
|
return null;
|
|
2340
2358
|
}
|
|
@@ -2926,30 +2944,33 @@ function resolveLocation(from, location) {
|
|
|
2926
2944
|
}
|
|
2927
2945
|
}
|
|
2928
2946
|
async function executeRequest(req, opts = {}) {
|
|
2929
|
-
const built = await buildRequest(req, {
|
|
2930
|
-
resolveAttachment: opts.resolveAttachment,
|
|
2931
|
-
authOptions: opts.authOptions,
|
|
2932
|
-
autoHeaderOverrides: opts.autoHeaderOverrides
|
|
2933
|
-
});
|
|
2934
2947
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
2935
2948
|
const timeoutMs = opts.timeoutMs === void 0 ? DEFAULT_TIMEOUT_MS : opts.timeoutMs;
|
|
2936
2949
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2937
2950
|
const t0 = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
2951
|
+
let built = null;
|
|
2938
2952
|
const controller = new AbortController();
|
|
2939
2953
|
const externalAbort = () => controller.abort(opts.signal.reason);
|
|
2940
2954
|
if (opts.signal) {
|
|
2941
2955
|
if (opts.signal.aborted) controller.abort(opts.signal.reason);
|
|
2942
2956
|
else opts.signal.addEventListener("abort", externalAbort, { once: true });
|
|
2943
2957
|
}
|
|
2944
|
-
|
|
2945
|
-
() => controller.abort(new Error(`Request timed out after ${timeoutMs}ms`)),
|
|
2946
|
-
timeoutMs
|
|
2947
|
-
);
|
|
2958
|
+
let timeoutHandle = null;
|
|
2948
2959
|
try {
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2960
|
+
built = await buildRequest(req, {
|
|
2961
|
+
resolveAttachment: opts.resolveAttachment,
|
|
2962
|
+
authOptions: opts.authOptions,
|
|
2963
|
+
autoHeaderOverrides: opts.autoHeaderOverrides
|
|
2964
|
+
});
|
|
2965
|
+
const builtRequest = built;
|
|
2966
|
+
timeoutHandle = timeoutMs === null ? null : setTimeout(
|
|
2967
|
+
() => controller.abort(new Error(`Request timed out after ${timeoutMs}ms`)),
|
|
2968
|
+
timeoutMs
|
|
2969
|
+
);
|
|
2970
|
+
let currentUrl = builtRequest.url;
|
|
2971
|
+
let currentHeaders = { ...builtRequest.headers };
|
|
2972
|
+
let currentMethod = builtRequest.method;
|
|
2973
|
+
let currentBody = builtRequest.body;
|
|
2953
2974
|
let response = await fetchImpl(currentUrl, {
|
|
2954
2975
|
method: currentMethod,
|
|
2955
2976
|
headers: currentHeaders,
|
|
@@ -3038,7 +3059,7 @@ async function executeRequest(req, opts = {}) {
|
|
|
3038
3059
|
bodyKind,
|
|
3039
3060
|
url: currentUrl,
|
|
3040
3061
|
method: currentMethod,
|
|
3041
|
-
authWarnings:
|
|
3062
|
+
authWarnings: builtRequest.authWarnings,
|
|
3042
3063
|
...truncated ? { responseTruncated: true } : {}
|
|
3043
3064
|
};
|
|
3044
3065
|
} catch (err) {
|
|
@@ -3055,9 +3076,9 @@ async function executeRequest(req, opts = {}) {
|
|
|
3055
3076
|
error: err instanceof Error ? err.message : String(err),
|
|
3056
3077
|
// We may have already followed redirects before throwing — report the
|
|
3057
3078
|
// last URL we tried so the user sees where the error originated.
|
|
3058
|
-
url: built.url,
|
|
3059
|
-
method: built.method,
|
|
3060
|
-
authWarnings: built
|
|
3079
|
+
url: built?.url ?? req.url,
|
|
3080
|
+
method: built?.method ?? req.method,
|
|
3081
|
+
authWarnings: built?.authWarnings ?? []
|
|
3061
3082
|
};
|
|
3062
3083
|
} finally {
|
|
3063
3084
|
if (timeoutHandle !== null) clearTimeout(timeoutHandle);
|
|
@@ -4452,8 +4473,45 @@ function collectAttachmentSlots(synced) {
|
|
|
4452
4473
|
}
|
|
4453
4474
|
}
|
|
4454
4475
|
}
|
|
4476
|
+
for (const server of Object.values(synced.mockServers ?? {})) {
|
|
4477
|
+
for (const endpoint of server.endpoints) {
|
|
4478
|
+
collectMockResponseAttachment(endpoint.defaultResponse, seen);
|
|
4479
|
+
for (const rule of endpoint.requestValidation) {
|
|
4480
|
+
collectMockResponseAttachment(rule.failResponse, seen);
|
|
4481
|
+
}
|
|
4482
|
+
for (const rule of endpoint.responseRules) {
|
|
4483
|
+
collectMockResponseAttachment(rule.response, seen);
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4487
|
+
for (const file of Object.values(synced.globalAssets.files ?? {})) {
|
|
4488
|
+
if (!seen.has(file.slotId)) {
|
|
4489
|
+
seen.set(file.slotId, {
|
|
4490
|
+
slotId: file.slotId,
|
|
4491
|
+
sha256: file.sha256,
|
|
4492
|
+
filename: file.filename,
|
|
4493
|
+
mimeType: file.mimeType,
|
|
4494
|
+
size: file.size
|
|
4495
|
+
});
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4455
4498
|
return [...seen.values()];
|
|
4456
4499
|
}
|
|
4500
|
+
function collectMockResponseAttachment(response, seen) {
|
|
4501
|
+
collectMockResponseBodyAttachment(response.body, seen);
|
|
4502
|
+
}
|
|
4503
|
+
function collectMockResponseBodyAttachment(body, seen) {
|
|
4504
|
+
if (body.type !== "binary") return;
|
|
4505
|
+
const ref = body.attachment;
|
|
4506
|
+
if (!ref?.slotId || seen.has(ref.slotId)) return;
|
|
4507
|
+
seen.set(ref.slotId, {
|
|
4508
|
+
slotId: ref.slotId,
|
|
4509
|
+
sha256: ref.sha256,
|
|
4510
|
+
filename: ref.filename,
|
|
4511
|
+
mimeType: ref.mimeType,
|
|
4512
|
+
size: ref.size
|
|
4513
|
+
});
|
|
4514
|
+
}
|
|
4457
4515
|
|
|
4458
4516
|
// src/release/semver.ts
|
|
4459
4517
|
var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+([0-9A-Za-z.-]+))?$/;
|
|
@@ -4809,6 +4867,16 @@ var dictBuckets = [
|
|
|
4809
4867
|
return v?.name ?? key;
|
|
4810
4868
|
}
|
|
4811
4869
|
},
|
|
4870
|
+
{
|
|
4871
|
+
// Reusable file assets registered at workspace scope. The metadata
|
|
4872
|
+
// travels in workspace.json; bytes travel as Git blobs by slotId.
|
|
4873
|
+
bucket: "globalFile",
|
|
4874
|
+
extract: (s) => s.globalAssets.files ?? {},
|
|
4875
|
+
label: (key, value) => {
|
|
4876
|
+
const v = value;
|
|
4877
|
+
return v?.name ?? v?.filename ?? key;
|
|
4878
|
+
}
|
|
4879
|
+
},
|
|
4812
4880
|
{
|
|
4813
4881
|
// Consumer-side patches to a linked workspace's requests. Keyed
|
|
4814
4882
|
// `${linkedWorkspaceId}:${requestId}`; the label leans on the key
|
|
@@ -4908,6 +4976,7 @@ function computeThreeWayDiff(base, local, remote) {
|
|
|
4908
4976
|
remote: r
|
|
4909
4977
|
});
|
|
4910
4978
|
}
|
|
4979
|
+
resolveAutoMergeableTreeConflict(entries, base, local, remote);
|
|
4911
4980
|
const conflicts = entries.filter((e) => e.status === "conflict");
|
|
4912
4981
|
return { entries, conflicts };
|
|
4913
4982
|
}
|
|
@@ -4929,6 +4998,85 @@ function classify(hasBase, base, local, remote) {
|
|
|
4929
4998
|
if (structurallyEqual(local, remote)) return "both-equal";
|
|
4930
4999
|
return "conflict";
|
|
4931
5000
|
}
|
|
5001
|
+
function resolveAutoMergeableTreeConflict(entries, base, local, remote) {
|
|
5002
|
+
if (!base) return;
|
|
5003
|
+
const treeEntry = entries.find((entry) => entry.bucket === "tree" && entry.status === "conflict");
|
|
5004
|
+
if (!treeEntry) return;
|
|
5005
|
+
const merged = mergeRootTreeMembershipIfSafe(base, local, remote, entries);
|
|
5006
|
+
if (!merged) return;
|
|
5007
|
+
treeEntry.status = "remote-only";
|
|
5008
|
+
treeEntry.remote = merged;
|
|
5009
|
+
}
|
|
5010
|
+
function mergeRootTreeMembershipIfSafe(base, local, remote, entries) {
|
|
5011
|
+
const baseChildren = base.collections.tree.children;
|
|
5012
|
+
const localChildren = local.collections.tree.children;
|
|
5013
|
+
const remoteChildren = remote.collections.tree.children;
|
|
5014
|
+
const baseKeys = treeKeySet(baseChildren);
|
|
5015
|
+
const localKeys = treeKeySet(localChildren);
|
|
5016
|
+
const remoteKeys = treeKeySet(remoteChildren);
|
|
5017
|
+
if (!baseKeys || !localKeys || !remoteKeys) return null;
|
|
5018
|
+
const changed = /* @__PURE__ */ new Set();
|
|
5019
|
+
for (const key of /* @__PURE__ */ new Set([...baseKeys, ...localKeys, ...remoteKeys])) {
|
|
5020
|
+
const localChanged = baseKeys.has(key) !== localKeys.has(key);
|
|
5021
|
+
const remoteChanged = baseKeys.has(key) !== remoteKeys.has(key);
|
|
5022
|
+
if (!localChanged && !remoteChanged) continue;
|
|
5023
|
+
if (localChanged && remoteChanged) return null;
|
|
5024
|
+
const entry = bucketEntryForTreeChild(entries, key);
|
|
5025
|
+
if (!entry || entry.status === "conflict") return null;
|
|
5026
|
+
if (localChanged && entry.status !== "local-only") return null;
|
|
5027
|
+
if (remoteChanged && entry.status !== "remote-only") return null;
|
|
5028
|
+
changed.add(key);
|
|
5029
|
+
}
|
|
5030
|
+
if (changed.size === 0) return null;
|
|
5031
|
+
const stableBaseOrder = baseChildren.map(treeChildKey).filter((key) => !changed.has(key));
|
|
5032
|
+
if (!sameOrder(
|
|
5033
|
+
localChildren.map(treeChildKey).filter((key) => !changed.has(key)),
|
|
5034
|
+
stableBaseOrder
|
|
5035
|
+
)) {
|
|
5036
|
+
return null;
|
|
5037
|
+
}
|
|
5038
|
+
if (!sameOrder(
|
|
5039
|
+
remoteChildren.map(treeChildKey).filter((key) => !changed.has(key)),
|
|
5040
|
+
stableBaseOrder
|
|
5041
|
+
)) {
|
|
5042
|
+
return null;
|
|
5043
|
+
}
|
|
5044
|
+
let children = [...localChildren];
|
|
5045
|
+
for (const child of remoteChildren) {
|
|
5046
|
+
const key = treeChildKey(child);
|
|
5047
|
+
if (!changed.has(key) || !remoteKeys.has(key)) continue;
|
|
5048
|
+
if (!children.some((existing) => treeChildKey(existing) === key)) children.push(child);
|
|
5049
|
+
}
|
|
5050
|
+
for (const key of changed) {
|
|
5051
|
+
if (remoteKeys.has(key)) continue;
|
|
5052
|
+
const entry = bucketEntryForTreeChild(entries, key);
|
|
5053
|
+
if (entry?.status === "remote-only") {
|
|
5054
|
+
children = children.filter((child) => treeChildKey(child) !== key);
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
return { ...local.collections.tree, children };
|
|
5058
|
+
}
|
|
5059
|
+
function bucketEntryForTreeChild(entries, key) {
|
|
5060
|
+
const [kind, id] = key.split(":", 2);
|
|
5061
|
+
if (kind !== "request" && kind !== "folder") return void 0;
|
|
5062
|
+
return entries.find((entry) => entry.bucket === kind && entry.key === id);
|
|
5063
|
+
}
|
|
5064
|
+
function treeKeySet(children) {
|
|
5065
|
+
const keys = /* @__PURE__ */ new Set();
|
|
5066
|
+
for (const child of children) {
|
|
5067
|
+
const key = treeChildKey(child);
|
|
5068
|
+
if (keys.has(key)) return null;
|
|
5069
|
+
keys.add(key);
|
|
5070
|
+
}
|
|
5071
|
+
return keys;
|
|
5072
|
+
}
|
|
5073
|
+
function treeChildKey(child) {
|
|
5074
|
+
return `${child.kind}:${child.id}`;
|
|
5075
|
+
}
|
|
5076
|
+
function sameOrder(a, b) {
|
|
5077
|
+
if (a.length !== b.length) return false;
|
|
5078
|
+
return a.every((value, index) => value === b[index]);
|
|
5079
|
+
}
|
|
4932
5080
|
function applyMerge(local, remote, diff, resolutions) {
|
|
4933
5081
|
let merged = local;
|
|
4934
5082
|
for (const entry of diff.entries) {
|
|
@@ -4967,7 +5115,14 @@ function applyEntry(local, remote, entry, chosen) {
|
|
|
4967
5115
|
const requests = { ...local.collections.requests };
|
|
4968
5116
|
const treeOp = value === void 0 ? { kind: "remove" } : { kind: "upsert", parent: value.folderId ?? null };
|
|
4969
5117
|
if (value === void 0) delete requests[entry.key];
|
|
4970
|
-
else
|
|
5118
|
+
else {
|
|
5119
|
+
const remoteRequest = value;
|
|
5120
|
+
const localRequest = local.collections.requests[entry.key];
|
|
5121
|
+
requests[entry.key] = localRequest && remoteRequest.auth ? {
|
|
5122
|
+
...remoteRequest,
|
|
5123
|
+
auth: preserveLocalCredentialPlaceholders(localRequest.auth, remoteRequest.auth)
|
|
5124
|
+
} : remoteRequest;
|
|
5125
|
+
}
|
|
4971
5126
|
const tree = reconcileTreeForEntry(local.collections.tree, "request", entry.key, treeOp);
|
|
4972
5127
|
return { ...local, collections: { ...local.collections, requests, tree } };
|
|
4973
5128
|
}
|
|
@@ -4975,7 +5130,14 @@ function applyEntry(local, remote, entry, chosen) {
|
|
|
4975
5130
|
const folders = { ...local.collections.folders };
|
|
4976
5131
|
const treeOp = value === void 0 ? { kind: "remove" } : { kind: "upsert", parent: value.parentId ?? null };
|
|
4977
5132
|
if (value === void 0) delete folders[entry.key];
|
|
4978
|
-
else
|
|
5133
|
+
else {
|
|
5134
|
+
const remoteFolder = value;
|
|
5135
|
+
const localFolder = local.collections.folders[entry.key];
|
|
5136
|
+
folders[entry.key] = localFolder?.auth && remoteFolder.auth ? {
|
|
5137
|
+
...remoteFolder,
|
|
5138
|
+
auth: preserveLocalCredentialPlaceholders(localFolder.auth, remoteFolder.auth)
|
|
5139
|
+
} : remoteFolder;
|
|
5140
|
+
}
|
|
4979
5141
|
const tree = reconcileTreeForEntry(local.collections.tree, "folder", entry.key, treeOp);
|
|
4980
5142
|
return { ...local, collections: { ...local.collections, folders, tree } };
|
|
4981
5143
|
}
|
|
@@ -5022,6 +5184,13 @@ function applyEntry(local, remote, entry, chosen) {
|
|
|
5022
5184
|
else graphql[entry.key] = value;
|
|
5023
5185
|
return { ...local, globalAssets: { ...local.globalAssets, graphql } };
|
|
5024
5186
|
}
|
|
5187
|
+
case "globalFile": {
|
|
5188
|
+
const files = { ...local.globalAssets.files ?? {} };
|
|
5189
|
+
if (value === void 0) delete files[entry.key];
|
|
5190
|
+
else
|
|
5191
|
+
files[entry.key] = value;
|
|
5192
|
+
return { ...local, globalAssets: { ...local.globalAssets, files } };
|
|
5193
|
+
}
|
|
5025
5194
|
case "linkedRequestOverride": {
|
|
5026
5195
|
const requests = { ...local.linkedOverrides.requests };
|
|
5027
5196
|
if (value === void 0) delete requests[entry.key];
|
|
@@ -5041,7 +5210,13 @@ function applyEntry(local, remote, entry, chosen) {
|
|
|
5041
5210
|
return { ...local, releases: { ...local.releases, perLink } };
|
|
5042
5211
|
}
|
|
5043
5212
|
case "tree":
|
|
5044
|
-
return {
|
|
5213
|
+
return {
|
|
5214
|
+
...local,
|
|
5215
|
+
collections: {
|
|
5216
|
+
...local.collections,
|
|
5217
|
+
tree: value ?? remote.collections.tree
|
|
5218
|
+
}
|
|
5219
|
+
};
|
|
5045
5220
|
case "environmentsActive":
|
|
5046
5221
|
return {
|
|
5047
5222
|
...local,
|
|
@@ -5061,6 +5236,63 @@ function applyEntry(local, remote, entry, chosen) {
|
|
|
5061
5236
|
return { ...local, secretCrypto: remote.secretCrypto ?? null };
|
|
5062
5237
|
}
|
|
5063
5238
|
}
|
|
5239
|
+
function preserveLocalCredentialPlaceholders(localAuth, remoteAuth) {
|
|
5240
|
+
if (localAuth.type !== remoteAuth.type) return remoteAuth;
|
|
5241
|
+
switch (remoteAuth.type) {
|
|
5242
|
+
case "basic":
|
|
5243
|
+
case "digest":
|
|
5244
|
+
case "ntlm":
|
|
5245
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["password"]);
|
|
5246
|
+
case "bearer":
|
|
5247
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["token"]);
|
|
5248
|
+
case "api-key":
|
|
5249
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["value"]);
|
|
5250
|
+
case "hawk":
|
|
5251
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["hawkKey"]);
|
|
5252
|
+
case "jwt-bearer":
|
|
5253
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["secretOrKey", "token"]);
|
|
5254
|
+
case "aws-sigv4":
|
|
5255
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["secretAccessKey", "sessionToken"]);
|
|
5256
|
+
case "oauth2-client-credentials":
|
|
5257
|
+
case "oauth2-auth-code":
|
|
5258
|
+
case "oauth2-pkce":
|
|
5259
|
+
return preserveBlankStringFields(localAuth, remoteAuth, [
|
|
5260
|
+
"clientSecret",
|
|
5261
|
+
"accessToken",
|
|
5262
|
+
"refreshToken"
|
|
5263
|
+
]);
|
|
5264
|
+
case "oauth2-password":
|
|
5265
|
+
return preserveBlankStringFields(localAuth, remoteAuth, [
|
|
5266
|
+
"clientSecret",
|
|
5267
|
+
"password",
|
|
5268
|
+
"accessToken",
|
|
5269
|
+
"refreshToken"
|
|
5270
|
+
]);
|
|
5271
|
+
case "oauth2-implicit":
|
|
5272
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["accessToken"]);
|
|
5273
|
+
case "oauth2-device":
|
|
5274
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["accessToken", "refreshToken"]);
|
|
5275
|
+
case "none":
|
|
5276
|
+
case "inherit":
|
|
5277
|
+
case "custom-header":
|
|
5278
|
+
return remoteAuth;
|
|
5279
|
+
default: {
|
|
5280
|
+
const _exhaustive = remoteAuth;
|
|
5281
|
+
void _exhaustive;
|
|
5282
|
+
return remoteAuth;
|
|
5283
|
+
}
|
|
5284
|
+
}
|
|
5285
|
+
}
|
|
5286
|
+
function preserveBlankStringFields(localAuth, remoteAuth, fields) {
|
|
5287
|
+
const next = { ...remoteAuth };
|
|
5288
|
+
const local = localAuth;
|
|
5289
|
+
for (const field of fields) {
|
|
5290
|
+
if (next[field] === "" && typeof local[field] === "string" && local[field] !== "") {
|
|
5291
|
+
next[field] = local[field];
|
|
5292
|
+
}
|
|
5293
|
+
}
|
|
5294
|
+
return next;
|
|
5295
|
+
}
|
|
5064
5296
|
|
|
5065
5297
|
// src/git/summarizeUnpushedChanges.ts
|
|
5066
5298
|
var BUCKET_ORDER = [
|
|
@@ -5077,6 +5309,7 @@ var BUCKET_ORDER = [
|
|
|
5077
5309
|
"executionPlan",
|
|
5078
5310
|
"globalSchema",
|
|
5079
5311
|
"globalGraphql",
|
|
5312
|
+
"globalFile",
|
|
5080
5313
|
"secretKey",
|
|
5081
5314
|
"secretCrypto",
|
|
5082
5315
|
"releaseSelf",
|
|
@@ -5092,10 +5325,12 @@ var EMPTY_SUMMARY = {
|
|
|
5092
5325
|
};
|
|
5093
5326
|
function summarizeUnpushedChanges(base, current, options = {}) {
|
|
5094
5327
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
5328
|
+
const gitCurrent = redactForGit(current);
|
|
5095
5329
|
if (!base) {
|
|
5096
|
-
return summarizeAllAsAdded(
|
|
5330
|
+
return summarizeAllAsAdded(gitCurrent, now().toISOString());
|
|
5097
5331
|
}
|
|
5098
|
-
const
|
|
5332
|
+
const gitBase = redactForGit(base);
|
|
5333
|
+
const diff = computeThreeWayDiff(gitBase, gitCurrent, gitBase);
|
|
5099
5334
|
const changes = [];
|
|
5100
5335
|
for (const entry of diff.entries) {
|
|
5101
5336
|
if (entry.status !== "local-only") continue;
|
|
@@ -5212,6 +5447,16 @@ function summarizeAllAsAdded(synced, computedAt) {
|
|
|
5212
5447
|
local: gql
|
|
5213
5448
|
});
|
|
5214
5449
|
}
|
|
5450
|
+
for (const [id, file] of Object.entries(synced.globalAssets.files ?? {})) {
|
|
5451
|
+
changes.push({
|
|
5452
|
+
bucket: "globalFile",
|
|
5453
|
+
key: id,
|
|
5454
|
+
label: file.name || file.filename || id,
|
|
5455
|
+
kind: "added",
|
|
5456
|
+
base: void 0,
|
|
5457
|
+
local: file
|
|
5458
|
+
});
|
|
5459
|
+
}
|
|
5215
5460
|
for (const [key, override] of Object.entries(synced.linkedOverrides.requests)) {
|
|
5216
5461
|
changes.push({
|
|
5217
5462
|
bucket: "linkedRequestOverride",
|
|
@@ -5992,6 +6237,106 @@ function resolvePlanRef(synced, ref) {
|
|
|
5992
6237
|
}
|
|
5993
6238
|
return { ok: false, error: `No plan named "${ref}" in this workspace.`, available };
|
|
5994
6239
|
}
|
|
6240
|
+
function lookupPlanStepRequest(step, synced, local) {
|
|
6241
|
+
if (!step.linkedWorkspaceId) {
|
|
6242
|
+
const request2 = synced.collections.requests[step.requestId];
|
|
6243
|
+
return request2 ? { request: request2 } : { request: null, error: "Request no longer exists in workspace." };
|
|
6244
|
+
}
|
|
6245
|
+
const link = synced.linkedWorkspaces[step.linkedWorkspaceId];
|
|
6246
|
+
if (!link) return { request: null, error: "Linked workspace was unlinked." };
|
|
6247
|
+
const snapshot2 = local.linkedCollections[step.linkedWorkspaceId];
|
|
6248
|
+
if (!snapshot2) {
|
|
6249
|
+
return {
|
|
6250
|
+
request: null,
|
|
6251
|
+
error: `No cached snapshot for "${link.name}". Refresh the link before running this plan.`
|
|
6252
|
+
};
|
|
6253
|
+
}
|
|
6254
|
+
const baseRequest = snapshot2.collections.requests[step.requestId];
|
|
6255
|
+
if (!baseRequest) {
|
|
6256
|
+
return {
|
|
6257
|
+
request: null,
|
|
6258
|
+
error: `Request not present in the cached snapshot of "${link.name}".`
|
|
6259
|
+
};
|
|
6260
|
+
}
|
|
6261
|
+
const overrideKey = `${step.linkedWorkspaceId}:${step.requestId}`;
|
|
6262
|
+
const override = synced.linkedOverrides.requests[overrideKey];
|
|
6263
|
+
const request = override ? mergeRequestOverride(baseRequest, override.patch) : baseRequest;
|
|
6264
|
+
return {
|
|
6265
|
+
request,
|
|
6266
|
+
linkedEnvironments: applyEnvironmentOverrides(
|
|
6267
|
+
snapshot2.environments,
|
|
6268
|
+
step.linkedWorkspaceId,
|
|
6269
|
+
synced
|
|
6270
|
+
),
|
|
6271
|
+
linkedFolders: snapshot2.collections.folders,
|
|
6272
|
+
linkedGlobalAssets: snapshot2.globalAssets
|
|
6273
|
+
};
|
|
6274
|
+
}
|
|
6275
|
+
function mergeRequestOverride(base, patch) {
|
|
6276
|
+
const merged = { ...base };
|
|
6277
|
+
if (patch.name !== void 0) merged.name = patch.name;
|
|
6278
|
+
if (patch.method !== void 0) merged.method = patch.method;
|
|
6279
|
+
if (patch.url !== void 0) merged.url = patch.url;
|
|
6280
|
+
if (patch.headers !== void 0) merged.headers = patch.headers;
|
|
6281
|
+
if (patch.query !== void 0) merged.query = patch.query;
|
|
6282
|
+
if (patch.pathParams !== void 0) merged.pathParams = patch.pathParams;
|
|
6283
|
+
if (patch.cookies !== void 0) merged.cookies = patch.cookies;
|
|
6284
|
+
if (patch.body !== void 0) merged.body = patch.body;
|
|
6285
|
+
if (patch.auth !== void 0) merged.auth = patch.auth;
|
|
6286
|
+
if (patch.contextVars !== void 0) merged.contextVars = patch.contextVars;
|
|
6287
|
+
if (patch.extractions !== void 0) merged.extractions = patch.extractions;
|
|
6288
|
+
if (patch.assertions !== void 0) merged.assertions = patch.assertions;
|
|
6289
|
+
return merged;
|
|
6290
|
+
}
|
|
6291
|
+
function applyEnvironmentOverrides(source, linkedWorkspaceId, synced) {
|
|
6292
|
+
const overrides = Object.values(synced.linkedOverrides.environmentVars).filter(
|
|
6293
|
+
(override) => override.linkedWorkspaceId === linkedWorkspaceId
|
|
6294
|
+
);
|
|
6295
|
+
if (overrides.length === 0) return source;
|
|
6296
|
+
const items = {};
|
|
6297
|
+
for (const [envName, env] of Object.entries(source.items)) {
|
|
6298
|
+
const envOverrides = overrides.filter((override) => override.envName === envName);
|
|
6299
|
+
if (envOverrides.length === 0) {
|
|
6300
|
+
items[envName] = env;
|
|
6301
|
+
continue;
|
|
6302
|
+
}
|
|
6303
|
+
const removed = new Set(
|
|
6304
|
+
envOverrides.filter((override) => override.removed).map((override) => override.varKey)
|
|
6305
|
+
);
|
|
6306
|
+
const replaceMap = /* @__PURE__ */ new Map();
|
|
6307
|
+
for (const override of envOverrides) {
|
|
6308
|
+
if (!override.removed) replaceMap.set(override.varKey, override);
|
|
6309
|
+
}
|
|
6310
|
+
const variables = [];
|
|
6311
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
6312
|
+
for (const variable of env.variables) {
|
|
6313
|
+
if (removed.has(variable.key)) continue;
|
|
6314
|
+
const override = replaceMap.get(variable.key);
|
|
6315
|
+
if (override) {
|
|
6316
|
+
variables.push({
|
|
6317
|
+
key: variable.key,
|
|
6318
|
+
value: override.value ?? variable.value,
|
|
6319
|
+
encrypted: override.encrypted ?? variable.encrypted,
|
|
6320
|
+
...override.secretKeyId !== void 0 ? { secretKeyId: override.secretKeyId } : variable.secretKeyId !== void 0 ? { secretKeyId: variable.secretKeyId } : {}
|
|
6321
|
+
});
|
|
6322
|
+
} else {
|
|
6323
|
+
variables.push(variable);
|
|
6324
|
+
}
|
|
6325
|
+
seenKeys.add(variable.key);
|
|
6326
|
+
}
|
|
6327
|
+
for (const override of envOverrides) {
|
|
6328
|
+
if (override.removed || seenKeys.has(override.varKey)) continue;
|
|
6329
|
+
variables.push({
|
|
6330
|
+
key: override.varKey,
|
|
6331
|
+
value: override.value ?? "",
|
|
6332
|
+
encrypted: override.encrypted ?? false,
|
|
6333
|
+
...override.secretKeyId !== void 0 ? { secretKeyId: override.secretKeyId } : {}
|
|
6334
|
+
});
|
|
6335
|
+
}
|
|
6336
|
+
items[envName] = { ...env, variables };
|
|
6337
|
+
}
|
|
6338
|
+
return { ...source, items };
|
|
6339
|
+
}
|
|
5995
6340
|
async function runPlan(state, planId, opts = {}) {
|
|
5996
6341
|
const plan = state.synced.executionPlans?.[planId];
|
|
5997
6342
|
if (!plan) throw new Error(`Plan "${planId}" not found in workspace`);
|
|
@@ -6003,7 +6348,7 @@ async function runPlan(state, planId, opts = {}) {
|
|
|
6003
6348
|
const bail = opts.bail ?? false;
|
|
6004
6349
|
const stopOnAssertion = withAssertions && (plan.stopOnAssertionFailure ?? false);
|
|
6005
6350
|
const secretsById = opts.secretsById ?? {};
|
|
6006
|
-
const flatEnvs = buildEnvMaps(state.synced, secretsById);
|
|
6351
|
+
const flatEnvs = buildEnvMaps(state.synced, secretsById, state.local);
|
|
6007
6352
|
const secretsByLabel = buildSecretsByLabel(state.synced, secretsById);
|
|
6008
6353
|
const baseRefs = plan.envPriorityOrder.length > 0 ? plan.envPriorityOrder : state.synced.environments.priorityOrder;
|
|
6009
6354
|
const envRefs = opts.env ? [{ kind: "local", name: opts.env }, ...baseRefs] : baseRefs;
|
|
@@ -6038,36 +6383,17 @@ async function runPlan(state, planId, opts = {}) {
|
|
|
6038
6383
|
continue;
|
|
6039
6384
|
}
|
|
6040
6385
|
if (opts.signal?.aborted) break;
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
const error = "Linked-workspace plan steps are not supported by the headless runner. Run this plan from the desktop app.";
|
|
6044
|
-
newRequestRuns.push(orphanRun(runId, step.requestId, error));
|
|
6045
|
-
stepRecords.push({ requestRunId: runId, passed: false });
|
|
6046
|
-
record({
|
|
6047
|
-
stepIndex: i,
|
|
6048
|
-
requestId: step.requestId,
|
|
6049
|
-
requestName: "(linked request)",
|
|
6050
|
-
requestMethod: "\u2014",
|
|
6051
|
-
skipped: false,
|
|
6052
|
-
result: null,
|
|
6053
|
-
assertionResults: [],
|
|
6054
|
-
missingVariables: [],
|
|
6055
|
-
passed: false,
|
|
6056
|
-
error
|
|
6057
|
-
});
|
|
6058
|
-
if (bail) break;
|
|
6059
|
-
continue;
|
|
6060
|
-
}
|
|
6061
|
-
const baseRequest = requests[step.requestId];
|
|
6386
|
+
const lookup2 = lookupPlanStepRequest(step, state.synced, state.local);
|
|
6387
|
+
const baseRequest = lookup2.request;
|
|
6062
6388
|
if (!baseRequest) {
|
|
6063
6389
|
const runId = generateId2();
|
|
6064
|
-
const error = "Request no longer exists in workspace.";
|
|
6390
|
+
const error = lookup2.error ?? "Request no longer exists in workspace.";
|
|
6065
6391
|
newRequestRuns.push(orphanRun(runId, step.requestId, error));
|
|
6066
6392
|
stepRecords.push({ requestRunId: runId, passed: false });
|
|
6067
6393
|
record({
|
|
6068
6394
|
stepIndex: i,
|
|
6069
6395
|
requestId: step.requestId,
|
|
6070
|
-
requestName: "(missing request)",
|
|
6396
|
+
requestName: step.linkedWorkspaceId ? "(linked request)" : "(missing request)",
|
|
6071
6397
|
requestMethod: "\u2014",
|
|
6072
6398
|
skipped: false,
|
|
6073
6399
|
result: null,
|
|
@@ -6079,22 +6405,33 @@ async function runPlan(state, planId, opts = {}) {
|
|
|
6079
6405
|
if (bail) break;
|
|
6080
6406
|
continue;
|
|
6081
6407
|
}
|
|
6408
|
+
const resolveSynced = step.linkedWorkspaceId && lookup2.linkedEnvironments ? {
|
|
6409
|
+
...state.synced,
|
|
6410
|
+
environments: lookup2.linkedEnvironments,
|
|
6411
|
+
globalAssets: lookup2.linkedGlobalAssets ?? state.synced.globalAssets,
|
|
6412
|
+
collections: {
|
|
6413
|
+
...state.synced.collections,
|
|
6414
|
+
folders: lookup2.linkedFolders ?? {}
|
|
6415
|
+
}
|
|
6416
|
+
} : state.synced;
|
|
6417
|
+
const stepEnvRefs = step.linkedWorkspaceId && plan.envPriorityOrder.length === 0 ? lookup2.linkedEnvironments?.priorityOrder ?? envRefs : envRefs;
|
|
6082
6418
|
const { request: resolved, missing } = resolveRequest(
|
|
6083
6419
|
baseRequest,
|
|
6084
|
-
|
|
6420
|
+
resolveSynced,
|
|
6085
6421
|
plan,
|
|
6086
|
-
|
|
6422
|
+
stepEnvRefs,
|
|
6087
6423
|
globalContext,
|
|
6088
|
-
flatEnvs,
|
|
6424
|
+
step.linkedWorkspaceId ? buildEnvMaps(resolveSynced, secretsById, state.local) : flatEnvs,
|
|
6089
6425
|
secretsByLabel
|
|
6090
6426
|
);
|
|
6091
6427
|
const result = await executeRequest(resolved, {
|
|
6092
6428
|
fetchImpl: opts.fetchImpl,
|
|
6093
6429
|
signal: opts.signal,
|
|
6094
6430
|
timeoutMs: opts.timeoutMs,
|
|
6431
|
+
resolveAttachment: opts.resolveAttachment,
|
|
6095
6432
|
authOptions: {
|
|
6096
6433
|
onTokenRefreshed: (refreshedAuth) => {
|
|
6097
|
-
tokenRefreshes.set(baseRequest.id, refreshedAuth);
|
|
6434
|
+
if (!step.linkedWorkspaceId) tokenRefreshes.set(baseRequest.id, refreshedAuth);
|
|
6098
6435
|
}
|
|
6099
6436
|
}
|
|
6100
6437
|
});
|
|
@@ -6118,7 +6455,7 @@ async function runPlan(state, planId, opts = {}) {
|
|
|
6118
6455
|
const { extracted } = extractContext(result, baseRequest.extractions);
|
|
6119
6456
|
globalContext = { ...globalContext, ...extracted };
|
|
6120
6457
|
}
|
|
6121
|
-
const refreshed = tokenRefreshes.get(baseRequest.id);
|
|
6458
|
+
const refreshed = step.linkedWorkspaceId ? void 0 : tokenRefreshes.get(baseRequest.id);
|
|
6122
6459
|
if (refreshed) {
|
|
6123
6460
|
requests = {
|
|
6124
6461
|
...requests,
|
|
@@ -6153,7 +6490,7 @@ async function runPlan(state, planId, opts = {}) {
|
|
|
6153
6490
|
const passed = executed.every((s) => s.passed);
|
|
6154
6491
|
return { planRun, steps: stepResults, nextState, passed };
|
|
6155
6492
|
}
|
|
6156
|
-
function buildEnvMaps(synced, secretsById) {
|
|
6493
|
+
function buildEnvMaps(synced, secretsById, local) {
|
|
6157
6494
|
const flat = {};
|
|
6158
6495
|
for (const [name, env] of Object.entries(synced.environments.items)) {
|
|
6159
6496
|
const vars = {};
|
|
@@ -6169,6 +6506,25 @@ function buildEnvMaps(synced, secretsById) {
|
|
|
6169
6506
|
}
|
|
6170
6507
|
flat[envPriorityKey2({ kind: "local", name })] = vars;
|
|
6171
6508
|
}
|
|
6509
|
+
if (local) {
|
|
6510
|
+
for (const [linkId, snapshot2] of Object.entries(local.linkedCollections)) {
|
|
6511
|
+
const overridden = applyEnvironmentOverrides(snapshot2.environments, linkId, synced);
|
|
6512
|
+
for (const [envName, env] of Object.entries(overridden.items)) {
|
|
6513
|
+
const vars = {};
|
|
6514
|
+
for (const variable of env.variables) {
|
|
6515
|
+
if (!variable.key) continue;
|
|
6516
|
+
if (variable.encrypted) {
|
|
6517
|
+
const supplied = variable.secretKeyId ? secretsById[variable.secretKeyId] : void 0;
|
|
6518
|
+
if (supplied === void 0) continue;
|
|
6519
|
+
vars[variable.key] = supplied;
|
|
6520
|
+
} else {
|
|
6521
|
+
vars[variable.key] = variable.value;
|
|
6522
|
+
}
|
|
6523
|
+
}
|
|
6524
|
+
flat[envPriorityKey2({ kind: "linked", linkedWorkspaceId: linkId, envName })] = vars;
|
|
6525
|
+
}
|
|
6526
|
+
}
|
|
6527
|
+
}
|
|
6172
6528
|
return flat;
|
|
6173
6529
|
}
|
|
6174
6530
|
function buildSecretsByLabel(synced, secretsById) {
|