@apicircle/core 1.0.3 → 1.0.5
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-PUXJFN2Z.js} +6 -3
- package/dist/chunk-PUXJFN2Z.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 +2 -2
- package/dist/chunk-SGI6KGQ7.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -2310,6 +2310,20 @@ function mergeWithAutoHeaders(userHeaders, overrides = {}) {
|
|
|
2310
2310
|
}
|
|
2311
2311
|
|
|
2312
2312
|
// src/request/buildRequest.ts
|
|
2313
|
+
function missingAttachmentMessage(slotId, filename) {
|
|
2314
|
+
const label = filename ? `${filename} (${slotId})` : slotId;
|
|
2315
|
+
return `Attachment ${label} is required for this request but is not downloaded locally. Download the missing attachments before sending or running the plan.`;
|
|
2316
|
+
}
|
|
2317
|
+
async function requireAttachment(slotId, resolveAttachment, filename) {
|
|
2318
|
+
if (!resolveAttachment) {
|
|
2319
|
+
throw new Error(missingAttachmentMessage(slotId, filename));
|
|
2320
|
+
}
|
|
2321
|
+
const file = await resolveAttachment(slotId);
|
|
2322
|
+
if (!file) {
|
|
2323
|
+
throw new Error(missingAttachmentMessage(slotId, filename));
|
|
2324
|
+
}
|
|
2325
|
+
return file;
|
|
2326
|
+
}
|
|
2313
2327
|
var PATH_PLACEHOLDER = /(?::([A-Za-z_][\w-]*)|(?<!\{)\{([A-Za-z_][\w-]*)\}(?!\}))/g;
|
|
2314
2328
|
function splitOnQuery(rawUrl) {
|
|
2315
2329
|
const q = rawUrl.indexOf("?");
|
|
@@ -2460,17 +2474,21 @@ async function composeBody(body, resolveAttachment) {
|
|
|
2460
2474
|
if (!row.enabled || !row.key.trim()) continue;
|
|
2461
2475
|
if (row.kind === "text") {
|
|
2462
2476
|
fd.append(row.key, row.value);
|
|
2463
|
-
} else if (row.slotId
|
|
2464
|
-
const file = await
|
|
2465
|
-
|
|
2477
|
+
} else if (row.slotId) {
|
|
2478
|
+
const file = await requireAttachment(row.slotId, resolveAttachment, row.filename);
|
|
2479
|
+
fd.append(row.key, file.blob, file.filename);
|
|
2466
2480
|
}
|
|
2467
2481
|
}
|
|
2468
2482
|
return fd;
|
|
2469
2483
|
}
|
|
2470
2484
|
if (body.type === "binary") {
|
|
2471
|
-
if (body.attachment?.slotId
|
|
2472
|
-
const file = await
|
|
2473
|
-
|
|
2485
|
+
if (body.attachment?.slotId) {
|
|
2486
|
+
const file = await requireAttachment(
|
|
2487
|
+
body.attachment.slotId,
|
|
2488
|
+
resolveAttachment,
|
|
2489
|
+
body.attachment.filename
|
|
2490
|
+
);
|
|
2491
|
+
return file.blob;
|
|
2474
2492
|
}
|
|
2475
2493
|
return null;
|
|
2476
2494
|
}
|
|
@@ -3062,30 +3080,33 @@ function resolveLocation(from, location) {
|
|
|
3062
3080
|
}
|
|
3063
3081
|
}
|
|
3064
3082
|
async function executeRequest(req, opts = {}) {
|
|
3065
|
-
const built = await buildRequest(req, {
|
|
3066
|
-
resolveAttachment: opts.resolveAttachment,
|
|
3067
|
-
authOptions: opts.authOptions,
|
|
3068
|
-
autoHeaderOverrides: opts.autoHeaderOverrides
|
|
3069
|
-
});
|
|
3070
3083
|
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
3071
3084
|
const timeoutMs = opts.timeoutMs === void 0 ? DEFAULT_TIMEOUT_MS : opts.timeoutMs;
|
|
3072
3085
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3073
3086
|
const t0 = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
3087
|
+
let built = null;
|
|
3074
3088
|
const controller = new AbortController();
|
|
3075
3089
|
const externalAbort = () => controller.abort(opts.signal.reason);
|
|
3076
3090
|
if (opts.signal) {
|
|
3077
3091
|
if (opts.signal.aborted) controller.abort(opts.signal.reason);
|
|
3078
3092
|
else opts.signal.addEventListener("abort", externalAbort, { once: true });
|
|
3079
3093
|
}
|
|
3080
|
-
|
|
3081
|
-
() => controller.abort(new Error(`Request timed out after ${timeoutMs}ms`)),
|
|
3082
|
-
timeoutMs
|
|
3083
|
-
);
|
|
3094
|
+
let timeoutHandle = null;
|
|
3084
3095
|
try {
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3096
|
+
built = await buildRequest(req, {
|
|
3097
|
+
resolveAttachment: opts.resolveAttachment,
|
|
3098
|
+
authOptions: opts.authOptions,
|
|
3099
|
+
autoHeaderOverrides: opts.autoHeaderOverrides
|
|
3100
|
+
});
|
|
3101
|
+
const builtRequest = built;
|
|
3102
|
+
timeoutHandle = timeoutMs === null ? null : setTimeout(
|
|
3103
|
+
() => controller.abort(new Error(`Request timed out after ${timeoutMs}ms`)),
|
|
3104
|
+
timeoutMs
|
|
3105
|
+
);
|
|
3106
|
+
let currentUrl = builtRequest.url;
|
|
3107
|
+
let currentHeaders = { ...builtRequest.headers };
|
|
3108
|
+
let currentMethod = builtRequest.method;
|
|
3109
|
+
let currentBody = builtRequest.body;
|
|
3089
3110
|
let response = await fetchImpl(currentUrl, {
|
|
3090
3111
|
method: currentMethod,
|
|
3091
3112
|
headers: currentHeaders,
|
|
@@ -3174,7 +3195,7 @@ async function executeRequest(req, opts = {}) {
|
|
|
3174
3195
|
bodyKind,
|
|
3175
3196
|
url: currentUrl,
|
|
3176
3197
|
method: currentMethod,
|
|
3177
|
-
authWarnings:
|
|
3198
|
+
authWarnings: builtRequest.authWarnings,
|
|
3178
3199
|
...truncated ? { responseTruncated: true } : {}
|
|
3179
3200
|
};
|
|
3180
3201
|
} catch (err) {
|
|
@@ -3191,9 +3212,9 @@ async function executeRequest(req, opts = {}) {
|
|
|
3191
3212
|
error: err instanceof Error ? err.message : String(err),
|
|
3192
3213
|
// We may have already followed redirects before throwing — report the
|
|
3193
3214
|
// last URL we tried so the user sees where the error originated.
|
|
3194
|
-
url: built.url,
|
|
3195
|
-
method: built.method,
|
|
3196
|
-
authWarnings: built
|
|
3215
|
+
url: built?.url ?? req.url,
|
|
3216
|
+
method: built?.method ?? req.method,
|
|
3217
|
+
authWarnings: built?.authWarnings ?? []
|
|
3197
3218
|
};
|
|
3198
3219
|
} finally {
|
|
3199
3220
|
if (timeoutHandle !== null) clearTimeout(timeoutHandle);
|
|
@@ -4588,8 +4609,45 @@ function collectAttachmentSlots(synced) {
|
|
|
4588
4609
|
}
|
|
4589
4610
|
}
|
|
4590
4611
|
}
|
|
4612
|
+
for (const server of Object.values(synced.mockServers ?? {})) {
|
|
4613
|
+
for (const endpoint of server.endpoints) {
|
|
4614
|
+
collectMockResponseAttachment(endpoint.defaultResponse, seen);
|
|
4615
|
+
for (const rule of endpoint.requestValidation ?? []) {
|
|
4616
|
+
collectMockResponseAttachment(rule.failResponse, seen);
|
|
4617
|
+
}
|
|
4618
|
+
for (const rule of endpoint.responseRules ?? []) {
|
|
4619
|
+
collectMockResponseAttachment(rule.response, seen);
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
for (const file of Object.values(synced.globalAssets.files ?? {})) {
|
|
4624
|
+
if (!seen.has(file.slotId)) {
|
|
4625
|
+
seen.set(file.slotId, {
|
|
4626
|
+
slotId: file.slotId,
|
|
4627
|
+
sha256: file.sha256,
|
|
4628
|
+
filename: file.filename,
|
|
4629
|
+
mimeType: file.mimeType,
|
|
4630
|
+
size: file.size
|
|
4631
|
+
});
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
4591
4634
|
return [...seen.values()];
|
|
4592
4635
|
}
|
|
4636
|
+
function collectMockResponseAttachment(response, seen) {
|
|
4637
|
+
collectMockResponseBodyAttachment(response?.body, seen);
|
|
4638
|
+
}
|
|
4639
|
+
function collectMockResponseBodyAttachment(body, seen) {
|
|
4640
|
+
if (body?.type !== "binary") return;
|
|
4641
|
+
const ref = body.attachment;
|
|
4642
|
+
if (!ref?.slotId || seen.has(ref.slotId)) return;
|
|
4643
|
+
seen.set(ref.slotId, {
|
|
4644
|
+
slotId: ref.slotId,
|
|
4645
|
+
sha256: ref.sha256,
|
|
4646
|
+
filename: ref.filename,
|
|
4647
|
+
mimeType: ref.mimeType,
|
|
4648
|
+
size: ref.size
|
|
4649
|
+
});
|
|
4650
|
+
}
|
|
4593
4651
|
|
|
4594
4652
|
// src/release/semver.ts
|
|
4595
4653
|
var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+([0-9A-Za-z.-]+))?$/;
|
|
@@ -4945,6 +5003,16 @@ var dictBuckets = [
|
|
|
4945
5003
|
return v?.name ?? key;
|
|
4946
5004
|
}
|
|
4947
5005
|
},
|
|
5006
|
+
{
|
|
5007
|
+
// Reusable file assets registered at workspace scope. The metadata
|
|
5008
|
+
// travels in workspace.json; bytes travel as Git blobs by slotId.
|
|
5009
|
+
bucket: "globalFile",
|
|
5010
|
+
extract: (s) => s.globalAssets.files ?? {},
|
|
5011
|
+
label: (key, value) => {
|
|
5012
|
+
const v = value;
|
|
5013
|
+
return v?.name ?? v?.filename ?? key;
|
|
5014
|
+
}
|
|
5015
|
+
},
|
|
4948
5016
|
{
|
|
4949
5017
|
// Consumer-side patches to a linked workspace's requests. Keyed
|
|
4950
5018
|
// `${linkedWorkspaceId}:${requestId}`; the label leans on the key
|
|
@@ -5044,6 +5112,7 @@ function computeThreeWayDiff(base, local, remote) {
|
|
|
5044
5112
|
remote: r
|
|
5045
5113
|
});
|
|
5046
5114
|
}
|
|
5115
|
+
resolveAutoMergeableTreeConflict(entries, base, local, remote);
|
|
5047
5116
|
const conflicts = entries.filter((e) => e.status === "conflict");
|
|
5048
5117
|
return { entries, conflicts };
|
|
5049
5118
|
}
|
|
@@ -5065,6 +5134,85 @@ function classify(hasBase, base, local, remote) {
|
|
|
5065
5134
|
if (structurallyEqual(local, remote)) return "both-equal";
|
|
5066
5135
|
return "conflict";
|
|
5067
5136
|
}
|
|
5137
|
+
function resolveAutoMergeableTreeConflict(entries, base, local, remote) {
|
|
5138
|
+
if (!base) return;
|
|
5139
|
+
const treeEntry = entries.find((entry) => entry.bucket === "tree" && entry.status === "conflict");
|
|
5140
|
+
if (!treeEntry) return;
|
|
5141
|
+
const merged = mergeRootTreeMembershipIfSafe(base, local, remote, entries);
|
|
5142
|
+
if (!merged) return;
|
|
5143
|
+
treeEntry.status = "remote-only";
|
|
5144
|
+
treeEntry.remote = merged;
|
|
5145
|
+
}
|
|
5146
|
+
function mergeRootTreeMembershipIfSafe(base, local, remote, entries) {
|
|
5147
|
+
const baseChildren = base.collections.tree.children;
|
|
5148
|
+
const localChildren = local.collections.tree.children;
|
|
5149
|
+
const remoteChildren = remote.collections.tree.children;
|
|
5150
|
+
const baseKeys = treeKeySet(baseChildren);
|
|
5151
|
+
const localKeys = treeKeySet(localChildren);
|
|
5152
|
+
const remoteKeys = treeKeySet(remoteChildren);
|
|
5153
|
+
if (!baseKeys || !localKeys || !remoteKeys) return null;
|
|
5154
|
+
const changed = /* @__PURE__ */ new Set();
|
|
5155
|
+
for (const key of /* @__PURE__ */ new Set([...baseKeys, ...localKeys, ...remoteKeys])) {
|
|
5156
|
+
const localChanged = baseKeys.has(key) !== localKeys.has(key);
|
|
5157
|
+
const remoteChanged = baseKeys.has(key) !== remoteKeys.has(key);
|
|
5158
|
+
if (!localChanged && !remoteChanged) continue;
|
|
5159
|
+
if (localChanged && remoteChanged) return null;
|
|
5160
|
+
const entry = bucketEntryForTreeChild(entries, key);
|
|
5161
|
+
if (!entry || entry.status === "conflict") return null;
|
|
5162
|
+
if (localChanged && entry.status !== "local-only") return null;
|
|
5163
|
+
if (remoteChanged && entry.status !== "remote-only") return null;
|
|
5164
|
+
changed.add(key);
|
|
5165
|
+
}
|
|
5166
|
+
if (changed.size === 0) return null;
|
|
5167
|
+
const stableBaseOrder = baseChildren.map(treeChildKey).filter((key) => !changed.has(key));
|
|
5168
|
+
if (!sameOrder(
|
|
5169
|
+
localChildren.map(treeChildKey).filter((key) => !changed.has(key)),
|
|
5170
|
+
stableBaseOrder
|
|
5171
|
+
)) {
|
|
5172
|
+
return null;
|
|
5173
|
+
}
|
|
5174
|
+
if (!sameOrder(
|
|
5175
|
+
remoteChildren.map(treeChildKey).filter((key) => !changed.has(key)),
|
|
5176
|
+
stableBaseOrder
|
|
5177
|
+
)) {
|
|
5178
|
+
return null;
|
|
5179
|
+
}
|
|
5180
|
+
let children = [...localChildren];
|
|
5181
|
+
for (const child of remoteChildren) {
|
|
5182
|
+
const key = treeChildKey(child);
|
|
5183
|
+
if (!changed.has(key) || !remoteKeys.has(key)) continue;
|
|
5184
|
+
if (!children.some((existing) => treeChildKey(existing) === key)) children.push(child);
|
|
5185
|
+
}
|
|
5186
|
+
for (const key of changed) {
|
|
5187
|
+
if (remoteKeys.has(key)) continue;
|
|
5188
|
+
const entry = bucketEntryForTreeChild(entries, key);
|
|
5189
|
+
if (entry?.status === "remote-only") {
|
|
5190
|
+
children = children.filter((child) => treeChildKey(child) !== key);
|
|
5191
|
+
}
|
|
5192
|
+
}
|
|
5193
|
+
return { ...local.collections.tree, children };
|
|
5194
|
+
}
|
|
5195
|
+
function bucketEntryForTreeChild(entries, key) {
|
|
5196
|
+
const [kind, id] = key.split(":", 2);
|
|
5197
|
+
if (kind !== "request" && kind !== "folder") return void 0;
|
|
5198
|
+
return entries.find((entry) => entry.bucket === kind && entry.key === id);
|
|
5199
|
+
}
|
|
5200
|
+
function treeKeySet(children) {
|
|
5201
|
+
const keys = /* @__PURE__ */ new Set();
|
|
5202
|
+
for (const child of children) {
|
|
5203
|
+
const key = treeChildKey(child);
|
|
5204
|
+
if (keys.has(key)) return null;
|
|
5205
|
+
keys.add(key);
|
|
5206
|
+
}
|
|
5207
|
+
return keys;
|
|
5208
|
+
}
|
|
5209
|
+
function treeChildKey(child) {
|
|
5210
|
+
return `${child.kind}:${child.id}`;
|
|
5211
|
+
}
|
|
5212
|
+
function sameOrder(a, b) {
|
|
5213
|
+
if (a.length !== b.length) return false;
|
|
5214
|
+
return a.every((value, index) => value === b[index]);
|
|
5215
|
+
}
|
|
5068
5216
|
function applyMerge(local, remote, diff, resolutions) {
|
|
5069
5217
|
let merged = local;
|
|
5070
5218
|
for (const entry of diff.entries) {
|
|
@@ -5103,7 +5251,14 @@ function applyEntry(local, remote, entry, chosen) {
|
|
|
5103
5251
|
const requests = { ...local.collections.requests };
|
|
5104
5252
|
const treeOp = value === void 0 ? { kind: "remove" } : { kind: "upsert", parent: value.folderId ?? null };
|
|
5105
5253
|
if (value === void 0) delete requests[entry.key];
|
|
5106
|
-
else
|
|
5254
|
+
else {
|
|
5255
|
+
const remoteRequest = value;
|
|
5256
|
+
const localRequest = local.collections.requests[entry.key];
|
|
5257
|
+
requests[entry.key] = localRequest && remoteRequest.auth ? {
|
|
5258
|
+
...remoteRequest,
|
|
5259
|
+
auth: preserveLocalCredentialPlaceholders(localRequest.auth, remoteRequest.auth)
|
|
5260
|
+
} : remoteRequest;
|
|
5261
|
+
}
|
|
5107
5262
|
const tree = reconcileTreeForEntry(local.collections.tree, "request", entry.key, treeOp);
|
|
5108
5263
|
return { ...local, collections: { ...local.collections, requests, tree } };
|
|
5109
5264
|
}
|
|
@@ -5111,7 +5266,14 @@ function applyEntry(local, remote, entry, chosen) {
|
|
|
5111
5266
|
const folders = { ...local.collections.folders };
|
|
5112
5267
|
const treeOp = value === void 0 ? { kind: "remove" } : { kind: "upsert", parent: value.parentId ?? null };
|
|
5113
5268
|
if (value === void 0) delete folders[entry.key];
|
|
5114
|
-
else
|
|
5269
|
+
else {
|
|
5270
|
+
const remoteFolder = value;
|
|
5271
|
+
const localFolder = local.collections.folders[entry.key];
|
|
5272
|
+
folders[entry.key] = localFolder?.auth && remoteFolder.auth ? {
|
|
5273
|
+
...remoteFolder,
|
|
5274
|
+
auth: preserveLocalCredentialPlaceholders(localFolder.auth, remoteFolder.auth)
|
|
5275
|
+
} : remoteFolder;
|
|
5276
|
+
}
|
|
5115
5277
|
const tree = reconcileTreeForEntry(local.collections.tree, "folder", entry.key, treeOp);
|
|
5116
5278
|
return { ...local, collections: { ...local.collections, folders, tree } };
|
|
5117
5279
|
}
|
|
@@ -5158,6 +5320,13 @@ function applyEntry(local, remote, entry, chosen) {
|
|
|
5158
5320
|
else graphql[entry.key] = value;
|
|
5159
5321
|
return { ...local, globalAssets: { ...local.globalAssets, graphql } };
|
|
5160
5322
|
}
|
|
5323
|
+
case "globalFile": {
|
|
5324
|
+
const files = { ...local.globalAssets.files ?? {} };
|
|
5325
|
+
if (value === void 0) delete files[entry.key];
|
|
5326
|
+
else
|
|
5327
|
+
files[entry.key] = value;
|
|
5328
|
+
return { ...local, globalAssets: { ...local.globalAssets, files } };
|
|
5329
|
+
}
|
|
5161
5330
|
case "linkedRequestOverride": {
|
|
5162
5331
|
const requests = { ...local.linkedOverrides.requests };
|
|
5163
5332
|
if (value === void 0) delete requests[entry.key];
|
|
@@ -5177,7 +5346,13 @@ function applyEntry(local, remote, entry, chosen) {
|
|
|
5177
5346
|
return { ...local, releases: { ...local.releases, perLink } };
|
|
5178
5347
|
}
|
|
5179
5348
|
case "tree":
|
|
5180
|
-
return {
|
|
5349
|
+
return {
|
|
5350
|
+
...local,
|
|
5351
|
+
collections: {
|
|
5352
|
+
...local.collections,
|
|
5353
|
+
tree: value ?? remote.collections.tree
|
|
5354
|
+
}
|
|
5355
|
+
};
|
|
5181
5356
|
case "environmentsActive":
|
|
5182
5357
|
return {
|
|
5183
5358
|
...local,
|
|
@@ -5197,6 +5372,63 @@ function applyEntry(local, remote, entry, chosen) {
|
|
|
5197
5372
|
return { ...local, secretCrypto: remote.secretCrypto ?? null };
|
|
5198
5373
|
}
|
|
5199
5374
|
}
|
|
5375
|
+
function preserveLocalCredentialPlaceholders(localAuth, remoteAuth) {
|
|
5376
|
+
if (localAuth.type !== remoteAuth.type) return remoteAuth;
|
|
5377
|
+
switch (remoteAuth.type) {
|
|
5378
|
+
case "basic":
|
|
5379
|
+
case "digest":
|
|
5380
|
+
case "ntlm":
|
|
5381
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["password"]);
|
|
5382
|
+
case "bearer":
|
|
5383
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["token"]);
|
|
5384
|
+
case "api-key":
|
|
5385
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["value"]);
|
|
5386
|
+
case "hawk":
|
|
5387
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["hawkKey"]);
|
|
5388
|
+
case "jwt-bearer":
|
|
5389
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["secretOrKey", "token"]);
|
|
5390
|
+
case "aws-sigv4":
|
|
5391
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["secretAccessKey", "sessionToken"]);
|
|
5392
|
+
case "oauth2-client-credentials":
|
|
5393
|
+
case "oauth2-auth-code":
|
|
5394
|
+
case "oauth2-pkce":
|
|
5395
|
+
return preserveBlankStringFields(localAuth, remoteAuth, [
|
|
5396
|
+
"clientSecret",
|
|
5397
|
+
"accessToken",
|
|
5398
|
+
"refreshToken"
|
|
5399
|
+
]);
|
|
5400
|
+
case "oauth2-password":
|
|
5401
|
+
return preserveBlankStringFields(localAuth, remoteAuth, [
|
|
5402
|
+
"clientSecret",
|
|
5403
|
+
"password",
|
|
5404
|
+
"accessToken",
|
|
5405
|
+
"refreshToken"
|
|
5406
|
+
]);
|
|
5407
|
+
case "oauth2-implicit":
|
|
5408
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["accessToken"]);
|
|
5409
|
+
case "oauth2-device":
|
|
5410
|
+
return preserveBlankStringFields(localAuth, remoteAuth, ["accessToken", "refreshToken"]);
|
|
5411
|
+
case "none":
|
|
5412
|
+
case "inherit":
|
|
5413
|
+
case "custom-header":
|
|
5414
|
+
return remoteAuth;
|
|
5415
|
+
default: {
|
|
5416
|
+
const _exhaustive = remoteAuth;
|
|
5417
|
+
void _exhaustive;
|
|
5418
|
+
return remoteAuth;
|
|
5419
|
+
}
|
|
5420
|
+
}
|
|
5421
|
+
}
|
|
5422
|
+
function preserveBlankStringFields(localAuth, remoteAuth, fields) {
|
|
5423
|
+
const next = { ...remoteAuth };
|
|
5424
|
+
const local = localAuth;
|
|
5425
|
+
for (const field of fields) {
|
|
5426
|
+
if (next[field] === "" && typeof local[field] === "string" && local[field] !== "") {
|
|
5427
|
+
next[field] = local[field];
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
return next;
|
|
5431
|
+
}
|
|
5200
5432
|
|
|
5201
5433
|
// src/git/summarizeUnpushedChanges.ts
|
|
5202
5434
|
var BUCKET_ORDER = [
|
|
@@ -5213,6 +5445,7 @@ var BUCKET_ORDER = [
|
|
|
5213
5445
|
"executionPlan",
|
|
5214
5446
|
"globalSchema",
|
|
5215
5447
|
"globalGraphql",
|
|
5448
|
+
"globalFile",
|
|
5216
5449
|
"secretKey",
|
|
5217
5450
|
"secretCrypto",
|
|
5218
5451
|
"releaseSelf",
|
|
@@ -5228,10 +5461,12 @@ var EMPTY_SUMMARY = {
|
|
|
5228
5461
|
};
|
|
5229
5462
|
function summarizeUnpushedChanges(base, current, options = {}) {
|
|
5230
5463
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
5464
|
+
const gitCurrent = redactForGit(current);
|
|
5231
5465
|
if (!base) {
|
|
5232
|
-
return summarizeAllAsAdded(
|
|
5466
|
+
return summarizeAllAsAdded(gitCurrent, now().toISOString());
|
|
5233
5467
|
}
|
|
5234
|
-
const
|
|
5468
|
+
const gitBase = redactForGit(base);
|
|
5469
|
+
const diff = computeThreeWayDiff(gitBase, gitCurrent, gitBase);
|
|
5235
5470
|
const changes = [];
|
|
5236
5471
|
for (const entry of diff.entries) {
|
|
5237
5472
|
if (entry.status !== "local-only") continue;
|
|
@@ -5348,6 +5583,16 @@ function summarizeAllAsAdded(synced, computedAt) {
|
|
|
5348
5583
|
local: gql
|
|
5349
5584
|
});
|
|
5350
5585
|
}
|
|
5586
|
+
for (const [id, file] of Object.entries(synced.globalAssets.files ?? {})) {
|
|
5587
|
+
changes.push({
|
|
5588
|
+
bucket: "globalFile",
|
|
5589
|
+
key: id,
|
|
5590
|
+
label: file.name || file.filename || id,
|
|
5591
|
+
kind: "added",
|
|
5592
|
+
base: void 0,
|
|
5593
|
+
local: file
|
|
5594
|
+
});
|
|
5595
|
+
}
|
|
5351
5596
|
for (const [key, override] of Object.entries(synced.linkedOverrides.requests)) {
|
|
5352
5597
|
changes.push({
|
|
5353
5598
|
bucket: "linkedRequestOverride",
|
|
@@ -6128,6 +6373,106 @@ function resolvePlanRef(synced, ref) {
|
|
|
6128
6373
|
}
|
|
6129
6374
|
return { ok: false, error: `No plan named "${ref}" in this workspace.`, available };
|
|
6130
6375
|
}
|
|
6376
|
+
function lookupPlanStepRequest(step, synced, local) {
|
|
6377
|
+
if (!step.linkedWorkspaceId) {
|
|
6378
|
+
const request2 = synced.collections.requests[step.requestId];
|
|
6379
|
+
return request2 ? { request: request2 } : { request: null, error: "Request no longer exists in workspace." };
|
|
6380
|
+
}
|
|
6381
|
+
const link = synced.linkedWorkspaces[step.linkedWorkspaceId];
|
|
6382
|
+
if (!link) return { request: null, error: "Linked workspace was unlinked." };
|
|
6383
|
+
const snapshot2 = local.linkedCollections[step.linkedWorkspaceId];
|
|
6384
|
+
if (!snapshot2) {
|
|
6385
|
+
return {
|
|
6386
|
+
request: null,
|
|
6387
|
+
error: `No cached snapshot for "${link.name}". Refresh the link before running this plan.`
|
|
6388
|
+
};
|
|
6389
|
+
}
|
|
6390
|
+
const baseRequest = snapshot2.collections.requests[step.requestId];
|
|
6391
|
+
if (!baseRequest) {
|
|
6392
|
+
return {
|
|
6393
|
+
request: null,
|
|
6394
|
+
error: `Request not present in the cached snapshot of "${link.name}".`
|
|
6395
|
+
};
|
|
6396
|
+
}
|
|
6397
|
+
const overrideKey = `${step.linkedWorkspaceId}:${step.requestId}`;
|
|
6398
|
+
const override = synced.linkedOverrides.requests[overrideKey];
|
|
6399
|
+
const request = override ? mergeRequestOverride(baseRequest, override.patch) : baseRequest;
|
|
6400
|
+
return {
|
|
6401
|
+
request,
|
|
6402
|
+
linkedEnvironments: applyEnvironmentOverrides(
|
|
6403
|
+
snapshot2.environments,
|
|
6404
|
+
step.linkedWorkspaceId,
|
|
6405
|
+
synced
|
|
6406
|
+
),
|
|
6407
|
+
linkedFolders: snapshot2.collections.folders,
|
|
6408
|
+
linkedGlobalAssets: snapshot2.globalAssets
|
|
6409
|
+
};
|
|
6410
|
+
}
|
|
6411
|
+
function mergeRequestOverride(base, patch) {
|
|
6412
|
+
const merged = { ...base };
|
|
6413
|
+
if (patch.name !== void 0) merged.name = patch.name;
|
|
6414
|
+
if (patch.method !== void 0) merged.method = patch.method;
|
|
6415
|
+
if (patch.url !== void 0) merged.url = patch.url;
|
|
6416
|
+
if (patch.headers !== void 0) merged.headers = patch.headers;
|
|
6417
|
+
if (patch.query !== void 0) merged.query = patch.query;
|
|
6418
|
+
if (patch.pathParams !== void 0) merged.pathParams = patch.pathParams;
|
|
6419
|
+
if (patch.cookies !== void 0) merged.cookies = patch.cookies;
|
|
6420
|
+
if (patch.body !== void 0) merged.body = patch.body;
|
|
6421
|
+
if (patch.auth !== void 0) merged.auth = patch.auth;
|
|
6422
|
+
if (patch.contextVars !== void 0) merged.contextVars = patch.contextVars;
|
|
6423
|
+
if (patch.extractions !== void 0) merged.extractions = patch.extractions;
|
|
6424
|
+
if (patch.assertions !== void 0) merged.assertions = patch.assertions;
|
|
6425
|
+
return merged;
|
|
6426
|
+
}
|
|
6427
|
+
function applyEnvironmentOverrides(source, linkedWorkspaceId, synced) {
|
|
6428
|
+
const overrides = Object.values(synced.linkedOverrides.environmentVars).filter(
|
|
6429
|
+
(override) => override.linkedWorkspaceId === linkedWorkspaceId
|
|
6430
|
+
);
|
|
6431
|
+
if (overrides.length === 0) return source;
|
|
6432
|
+
const items = {};
|
|
6433
|
+
for (const [envName, env] of Object.entries(source.items)) {
|
|
6434
|
+
const envOverrides = overrides.filter((override) => override.envName === envName);
|
|
6435
|
+
if (envOverrides.length === 0) {
|
|
6436
|
+
items[envName] = env;
|
|
6437
|
+
continue;
|
|
6438
|
+
}
|
|
6439
|
+
const removed = new Set(
|
|
6440
|
+
envOverrides.filter((override) => override.removed).map((override) => override.varKey)
|
|
6441
|
+
);
|
|
6442
|
+
const replaceMap = /* @__PURE__ */ new Map();
|
|
6443
|
+
for (const override of envOverrides) {
|
|
6444
|
+
if (!override.removed) replaceMap.set(override.varKey, override);
|
|
6445
|
+
}
|
|
6446
|
+
const variables = [];
|
|
6447
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
6448
|
+
for (const variable of env.variables) {
|
|
6449
|
+
if (removed.has(variable.key)) continue;
|
|
6450
|
+
const override = replaceMap.get(variable.key);
|
|
6451
|
+
if (override) {
|
|
6452
|
+
variables.push({
|
|
6453
|
+
key: variable.key,
|
|
6454
|
+
value: override.value ?? variable.value,
|
|
6455
|
+
encrypted: override.encrypted ?? variable.encrypted,
|
|
6456
|
+
...override.secretKeyId !== void 0 ? { secretKeyId: override.secretKeyId } : variable.secretKeyId !== void 0 ? { secretKeyId: variable.secretKeyId } : {}
|
|
6457
|
+
});
|
|
6458
|
+
} else {
|
|
6459
|
+
variables.push(variable);
|
|
6460
|
+
}
|
|
6461
|
+
seenKeys.add(variable.key);
|
|
6462
|
+
}
|
|
6463
|
+
for (const override of envOverrides) {
|
|
6464
|
+
if (override.removed || seenKeys.has(override.varKey)) continue;
|
|
6465
|
+
variables.push({
|
|
6466
|
+
key: override.varKey,
|
|
6467
|
+
value: override.value ?? "",
|
|
6468
|
+
encrypted: override.encrypted ?? false,
|
|
6469
|
+
...override.secretKeyId !== void 0 ? { secretKeyId: override.secretKeyId } : {}
|
|
6470
|
+
});
|
|
6471
|
+
}
|
|
6472
|
+
items[envName] = { ...env, variables };
|
|
6473
|
+
}
|
|
6474
|
+
return { ...source, items };
|
|
6475
|
+
}
|
|
6131
6476
|
async function runPlan(state, planId, opts = {}) {
|
|
6132
6477
|
const plan = state.synced.executionPlans?.[planId];
|
|
6133
6478
|
if (!plan) throw new Error(`Plan "${planId}" not found in workspace`);
|
|
@@ -6139,7 +6484,7 @@ async function runPlan(state, planId, opts = {}) {
|
|
|
6139
6484
|
const bail = opts.bail ?? false;
|
|
6140
6485
|
const stopOnAssertion = withAssertions && (plan.stopOnAssertionFailure ?? false);
|
|
6141
6486
|
const secretsById = opts.secretsById ?? {};
|
|
6142
|
-
const flatEnvs = buildEnvMaps(state.synced, secretsById);
|
|
6487
|
+
const flatEnvs = buildEnvMaps(state.synced, secretsById, state.local);
|
|
6143
6488
|
const secretsByLabel = buildSecretsByLabel(state.synced, secretsById);
|
|
6144
6489
|
const baseRefs = plan.envPriorityOrder.length > 0 ? plan.envPriorityOrder : state.synced.environments.priorityOrder;
|
|
6145
6490
|
const envRefs = opts.env ? [{ kind: "local", name: opts.env }, ...baseRefs] : baseRefs;
|
|
@@ -6174,36 +6519,17 @@ async function runPlan(state, planId, opts = {}) {
|
|
|
6174
6519
|
continue;
|
|
6175
6520
|
}
|
|
6176
6521
|
if (opts.signal?.aborted) break;
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
const error = "Linked-workspace plan steps are not supported by the headless runner. Run this plan from the desktop app.";
|
|
6180
|
-
newRequestRuns.push(orphanRun(runId, step.requestId, error));
|
|
6181
|
-
stepRecords.push({ requestRunId: runId, passed: false });
|
|
6182
|
-
record({
|
|
6183
|
-
stepIndex: i,
|
|
6184
|
-
requestId: step.requestId,
|
|
6185
|
-
requestName: "(linked request)",
|
|
6186
|
-
requestMethod: "\u2014",
|
|
6187
|
-
skipped: false,
|
|
6188
|
-
result: null,
|
|
6189
|
-
assertionResults: [],
|
|
6190
|
-
missingVariables: [],
|
|
6191
|
-
passed: false,
|
|
6192
|
-
error
|
|
6193
|
-
});
|
|
6194
|
-
if (bail) break;
|
|
6195
|
-
continue;
|
|
6196
|
-
}
|
|
6197
|
-
const baseRequest = requests[step.requestId];
|
|
6522
|
+
const lookup2 = lookupPlanStepRequest(step, state.synced, state.local);
|
|
6523
|
+
const baseRequest = lookup2.request;
|
|
6198
6524
|
if (!baseRequest) {
|
|
6199
6525
|
const runId = (0, import_shared2.generateId)();
|
|
6200
|
-
const error = "Request no longer exists in workspace.";
|
|
6526
|
+
const error = lookup2.error ?? "Request no longer exists in workspace.";
|
|
6201
6527
|
newRequestRuns.push(orphanRun(runId, step.requestId, error));
|
|
6202
6528
|
stepRecords.push({ requestRunId: runId, passed: false });
|
|
6203
6529
|
record({
|
|
6204
6530
|
stepIndex: i,
|
|
6205
6531
|
requestId: step.requestId,
|
|
6206
|
-
requestName: "(missing request)",
|
|
6532
|
+
requestName: step.linkedWorkspaceId ? "(linked request)" : "(missing request)",
|
|
6207
6533
|
requestMethod: "\u2014",
|
|
6208
6534
|
skipped: false,
|
|
6209
6535
|
result: null,
|
|
@@ -6215,22 +6541,33 @@ async function runPlan(state, planId, opts = {}) {
|
|
|
6215
6541
|
if (bail) break;
|
|
6216
6542
|
continue;
|
|
6217
6543
|
}
|
|
6544
|
+
const resolveSynced = step.linkedWorkspaceId && lookup2.linkedEnvironments ? {
|
|
6545
|
+
...state.synced,
|
|
6546
|
+
environments: lookup2.linkedEnvironments,
|
|
6547
|
+
globalAssets: lookup2.linkedGlobalAssets ?? state.synced.globalAssets,
|
|
6548
|
+
collections: {
|
|
6549
|
+
...state.synced.collections,
|
|
6550
|
+
folders: lookup2.linkedFolders ?? {}
|
|
6551
|
+
}
|
|
6552
|
+
} : state.synced;
|
|
6553
|
+
const stepEnvRefs = step.linkedWorkspaceId && plan.envPriorityOrder.length === 0 ? lookup2.linkedEnvironments?.priorityOrder ?? envRefs : envRefs;
|
|
6218
6554
|
const { request: resolved, missing } = resolveRequest(
|
|
6219
6555
|
baseRequest,
|
|
6220
|
-
|
|
6556
|
+
resolveSynced,
|
|
6221
6557
|
plan,
|
|
6222
|
-
|
|
6558
|
+
stepEnvRefs,
|
|
6223
6559
|
globalContext,
|
|
6224
|
-
flatEnvs,
|
|
6560
|
+
step.linkedWorkspaceId ? buildEnvMaps(resolveSynced, secretsById, state.local) : flatEnvs,
|
|
6225
6561
|
secretsByLabel
|
|
6226
6562
|
);
|
|
6227
6563
|
const result = await executeRequest(resolved, {
|
|
6228
6564
|
fetchImpl: opts.fetchImpl,
|
|
6229
6565
|
signal: opts.signal,
|
|
6230
6566
|
timeoutMs: opts.timeoutMs,
|
|
6567
|
+
resolveAttachment: opts.resolveAttachment,
|
|
6231
6568
|
authOptions: {
|
|
6232
6569
|
onTokenRefreshed: (refreshedAuth) => {
|
|
6233
|
-
tokenRefreshes.set(baseRequest.id, refreshedAuth);
|
|
6570
|
+
if (!step.linkedWorkspaceId) tokenRefreshes.set(baseRequest.id, refreshedAuth);
|
|
6234
6571
|
}
|
|
6235
6572
|
}
|
|
6236
6573
|
});
|
|
@@ -6254,7 +6591,7 @@ async function runPlan(state, planId, opts = {}) {
|
|
|
6254
6591
|
const { extracted } = extractContext(result, baseRequest.extractions);
|
|
6255
6592
|
globalContext = { ...globalContext, ...extracted };
|
|
6256
6593
|
}
|
|
6257
|
-
const refreshed = tokenRefreshes.get(baseRequest.id);
|
|
6594
|
+
const refreshed = step.linkedWorkspaceId ? void 0 : tokenRefreshes.get(baseRequest.id);
|
|
6258
6595
|
if (refreshed) {
|
|
6259
6596
|
requests = {
|
|
6260
6597
|
...requests,
|
|
@@ -6289,7 +6626,7 @@ async function runPlan(state, planId, opts = {}) {
|
|
|
6289
6626
|
const passed = executed.every((s) => s.passed);
|
|
6290
6627
|
return { planRun, steps: stepResults, nextState, passed };
|
|
6291
6628
|
}
|
|
6292
|
-
function buildEnvMaps(synced, secretsById) {
|
|
6629
|
+
function buildEnvMaps(synced, secretsById, local) {
|
|
6293
6630
|
const flat = {};
|
|
6294
6631
|
for (const [name, env] of Object.entries(synced.environments.items)) {
|
|
6295
6632
|
const vars = {};
|
|
@@ -6305,6 +6642,25 @@ function buildEnvMaps(synced, secretsById) {
|
|
|
6305
6642
|
}
|
|
6306
6643
|
flat[(0, import_shared2.envPriorityKey)({ kind: "local", name })] = vars;
|
|
6307
6644
|
}
|
|
6645
|
+
if (local) {
|
|
6646
|
+
for (const [linkId, snapshot2] of Object.entries(local.linkedCollections)) {
|
|
6647
|
+
const overridden = applyEnvironmentOverrides(snapshot2.environments, linkId, synced);
|
|
6648
|
+
for (const [envName, env] of Object.entries(overridden.items)) {
|
|
6649
|
+
const vars = {};
|
|
6650
|
+
for (const variable of env.variables) {
|
|
6651
|
+
if (!variable.key) continue;
|
|
6652
|
+
if (variable.encrypted) {
|
|
6653
|
+
const supplied = variable.secretKeyId ? secretsById[variable.secretKeyId] : void 0;
|
|
6654
|
+
if (supplied === void 0) continue;
|
|
6655
|
+
vars[variable.key] = supplied;
|
|
6656
|
+
} else {
|
|
6657
|
+
vars[variable.key] = variable.value;
|
|
6658
|
+
}
|
|
6659
|
+
}
|
|
6660
|
+
flat[(0, import_shared2.envPriorityKey)({ kind: "linked", linkedWorkspaceId: linkId, envName })] = vars;
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
}
|
|
6308
6664
|
return flat;
|
|
6309
6665
|
}
|
|
6310
6666
|
function buildSecretsByLabel(synced, secretsById) {
|