@volter/world-core 3.0.123 → 3.0.125

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.
@@ -417,7 +417,7 @@ function performOf(units) {
417
417
  adopted.push({ actionId: entry.id, externalId, data });
418
418
  }
419
419
  const primary = adopted.shift();
420
- return primary ? { ...primary, ...(adopted.length ? { also: adopted } : {}) } : { externalId: '', actionId: group.at(-1).id };
420
+ return primary ? { ...primary, response: answer, ...(adopted.length ? { also: adopted } : {}) } : { externalId: '', actionId: group.at(-1).id, response: answer };
421
421
  }
422
422
  // an XML answer is read through its document element (S3's `InitiateMultipartUploadResult`), as a JSON one under its key
423
423
  const documentElement = () => {
@@ -434,7 +434,7 @@ function performOf(units) {
434
434
  // CreateSecretResponse, S3's CreateMultipartUploadOutput) is the group's subject's, its id read by that resource
435
435
  const subjectDecl = () => Object.entries(m.resources).find(([r]) => storedType(m, r) === group.at(-1).subject.type)?.[1];
436
436
  const decl = resource ? m.resources[resource] : subjectDecl();
437
- const idOf = (o, d) => { const v = o?.[d?.idAs ?? 'id']; return typeof v === 'string' ? v : undefined; };
437
+ const idOf = (o, d) => o ? storedIdOf(d ?? { idPrefix: '' }, o) : undefined;
438
438
  // the group's entry the answer is: of the answered resource's stored type (a cascade writes children first), else
439
439
  // the last; a delete answers its subject's own vendor id
440
440
  const answered = resource ? [...group].reverse().find((e) => e.subject.type === storedType(m, resource)) : undefined;
@@ -443,7 +443,7 @@ function performOf(units) {
443
443
  // the membership and its channel_join message) gives no entry its id or its fields: each settles deployed with no
444
444
  // vendor id, and refresh adopts it ("Perform", step 2)
445
445
  if (resource && !answered)
446
- return { externalId: '', actionId: primary.id };
446
+ return { externalId: '', actionId: primary.id, response: answer };
447
447
  const deleted = primary.fields?.deleted === true;
448
448
  // a keyed subject's id is its key filled from the answer (its envelope's fields, Slack's `channel` and `ts`, among them)
449
449
  const keyed = decl?.key ? decl : Object.entries(m.resources).find(([r]) => storedType(m, r) === primary.subject.type)?.[1];
@@ -453,10 +453,10 @@ function performOf(units) {
453
453
  for (const [companion, where] of Object.entries(decl?.companions ?? keyed?.companions ?? {})) {
454
454
  const id = at(parsed, where) ?? at(obj, where);
455
455
  const entry = group.find((e) => e !== primary && e.subject.type === storedType(m, companion));
456
- if (entry && typeof id === 'string')
457
- also.push({ actionId: entry.id, externalId: id });
456
+ if (entry && (typeof id === 'string' || typeof id === 'number'))
457
+ also.push({ actionId: entry.id, externalId: String(id) });
458
458
  }
459
- return { externalId, actionId: primary.id, data: obj, ...(also.length ? { also } : {}) };
459
+ return { externalId, actionId: primary.id, data: obj, response: answer, ...(also.length ? { also } : {}) };
460
460
  };
461
461
  return async (execute, group, ctx) => {
462
462
  await adoptAccount(units, execute, ctx);
@@ -620,7 +620,7 @@ function refreshOf(units, service) {
620
620
  if (answer.status === 404)
621
621
  continue;
622
622
  if (answer.status >= 400 || refusedIn(m, parse(answer)))
623
- throw new Error(`${answer.status}: ${refusalWords(m, parse(answer))}`);
623
+ throw new Error(`${m.vendor}: ${resource} refresh ${op.id} (${op.method.toUpperCase()} ${op.path}): ${answer.status}: ${refusalWords(m, parse(answer))}`);
624
624
  const full = object(op.answers?.key ? at(parse(answer), op.answers.key) : parse(answer)) ?? {};
625
625
  const local = m.worldFields?.[resource] ?? {};
626
626
  const fields = Object.fromEntries(Object.entries(full).filter(([k]) => !(k in local)));
@@ -703,7 +703,7 @@ function refreshOf(units, service) {
703
703
  break;
704
704
  }
705
705
  if (answer.status >= 400 || refusedIn(m, parse(answer)))
706
- throw new Error(`${answer.status}: ${refusalWords(m, parse(answer))}`);
706
+ throw new Error(`${m.vendor}: ${resource} refresh ${op.id} (${op.method.toUpperCase()} ${op.path}): ${answer.status}: ${refusalWords(m, parse(answer))}`);
707
707
  answered += 1;
708
708
  const parsed = parse(answer);
709
709
  const shape = 'list' in scope ? scope.shape : undefined;
@@ -753,7 +753,7 @@ function refreshOf(units, service) {
753
753
  const detailSent = detailRpc && Object.keys(withValues).length ? encode(detail, withValues) : undefined;
754
754
  const answer = await execute({ method: detail.method.toUpperCase(), path: sentTo(m, marked(detail, Object.keys(detailQuery).length ? `${path}?${new URLSearchParams(detailQuery)}` : path), { ...item, ...under?.params }), ...onLane, headers: { ...fixedHeaders(detail), ...(detailSent?.type ? { 'content-type': detailSent.type } : {}) }, ...(detailSent?.body !== undefined ? { body: detailSent.body } : {}) });
755
755
  if (answer.status >= 400 || refusedIn(m, parse(answer)))
756
- throw new Error(`${answer.status}: ${refusalWords(m, parse(answer)) || 'the vendor refused the refresh detail; nothing folded'}`);
756
+ throw new Error(`${m.vendor}: ${resource} refresh detail ${detail.id} (${detail.method.toUpperCase()} ${detail.path}): ${answer.status}: ${refusalWords(m, parse(answer)) || 'the vendor refused the refresh detail; nothing folded'}`);
757
757
  const detailBody = parse(answer);
758
758
  const detailKey = scope.detailMerge ?? detail.answers?.key;
759
759
  const fragment = object(detailKey ? at(detailBody, detailKey) : detailBody);
@@ -857,7 +857,7 @@ function refreshOf(units, service) {
857
857
  // parent is never the account's whole list there
858
858
  if (whole && !decl.parent && !opts.scope)
859
859
  complete.push(type);
860
- else if (whole && parents.length)
860
+ else if (whole && parents.length && decl.parent)
861
861
  complete.push({ type, field: decl.parent.field, within: parents.map((p) => p.value) });
862
862
  }
863
863
  }
@@ -1,7 +1,7 @@
1
1
  import { type TwinAction } from './actions.js';
2
2
  import { type CredentialPayload, type Sealer } from './credential.js';
3
3
  import { type CredentialCustody } from './executor.js';
4
- import type { RemoteExecute } from './remote-execute.js';
4
+ import type { RemoteExecute, RemoteExecuteResponse } from './remote-execute.js';
5
5
  import { type Check, type DeployPolicy, type RootConfig, type StateSystemAdapters } from './state-system.js';
6
6
  /** What a perform answers: the id the vendor minted (the local id when it kept ours), its URL, and
7
7
  * the vendor's answer as fields for the landed copy. */
@@ -9,6 +9,8 @@ export type PushOutcome = {
9
9
  externalId: string;
10
10
  url?: string;
11
11
  data?: Record<string, unknown>;
12
+ /** the successful vendor response for the caller, distinct from the resource fields stored in data */
13
+ response?: RemoteExecuteResponse;
12
14
  /** the group's entry the id is for, when it is not the group's first (a cascade writes children first) */
13
15
  actionId?: string;
14
16
  /** the group's other entries the answer named an id for (a resource's `companions`) */
@@ -123,6 +125,7 @@ export type DeployReport = {
123
125
  externalId?: string;
124
126
  at: string; /** what the vendor answered, as the adapter returned it */
125
127
  data?: unknown;
128
+ response?: RemoteExecuteResponse;
126
129
  }>;
127
130
  /** the first refusal; nothing after it was attempted */
128
131
  refused?: {
@@ -187,6 +190,7 @@ export declare function deferPerform<T>(request: string, fn: () => Promise<T>):
187
190
  * reverts one. Nothing when the head is not real and auto, or the request wrote nothing the vendor is offered. */
188
191
  export declare function performRequestGroup(service: string, request: string, root?: string): Promise<{
189
192
  ids: Map<string, string>;
193
+ response?: RemoteExecuteResponse;
190
194
  } | RefusedWriteError | VendorWriteError>;
191
195
  /** Wrap a pack's fetch: a `RefusedWriteError` or `VendorWriteError` the head raised becomes the
192
196
  * vendor's own error body (the pack's `shape`), so an app under live use sees what the vendor
package/dist/src/head.js CHANGED
@@ -412,7 +412,7 @@ async function performNow(opts) {
412
412
  settle(action, at, { status: 'deployed', ...(action === primary && outcome.url ? { url: outcome.url } : {}) }, { ...action.fields, ...(action === primary ? answered : also.get(action.id)?.data ?? {}) }, externalId || undefined);
413
413
  const data = action === primary ? outcome.data : also.get(action.id)?.data;
414
414
  report.pushed += 1;
415
- report.deployed.push({ actionId: action.id, at, ...(externalId ? { externalId } : {}), ...(data !== undefined ? { data } : {}) });
415
+ report.deployed.push({ actionId: action.id, at, ...(externalId ? { externalId } : {}), ...(data !== undefined ? { data } : {}), ...(action === primary && outcome.response ? { response: outcome.response } : {}) });
416
416
  }
417
417
  }
418
418
  catch (error) {
@@ -514,7 +514,8 @@ export async function performRequestGroup(service, request, root) {
514
514
  if (e && d.externalId && d.externalId !== e.subject.id)
515
515
  ids.set(e.subject.id, d.externalId);
516
516
  }
517
- return { ids };
517
+ const response = report.deployed.find((entry) => entry.response)?.response;
518
+ return { ids, ...(response ? { response } : {}) };
518
519
  }
519
520
  /** Wrap a pack's fetch: a `RefusedWriteError` or `VendorWriteError` the head raised becomes the
520
521
  * vendor's own error body (the pack's `shape`), so an app under live use sees what the vendor
@@ -557,6 +557,13 @@ export function createPackFetch(parts, options = {}) {
557
557
  }
558
558
  if (performed instanceof VendorWriteError)
559
559
  return vendorError(m, { status: 502, message: performed.message });
560
+ if (performed.response) {
561
+ const { status, headers: received, body, bodyBytes } = performed.response;
562
+ const headers = new Headers(received);
563
+ for (const name of ['content-length', 'content-encoding', 'transfer-encoding', 'connection', 'keep-alive'])
564
+ headers.delete(name);
565
+ return new Response([204, 205, 304].includes(status) ? null : bodyBytes ? new Uint8Array(bodyBytes).buffer : body, { status, headers });
566
+ }
560
567
  if (!performed.ids.size || !(answer.headers.get('content-type') ?? '').includes('json'))
561
568
  return answer;
562
569
  let text = await answer.text();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/world-core",
3
- "version": "3.0.123",
3
+ "version": "3.0.125",
4
4
  "description": "The kernel of Volter World: one log per twin, branches as pointers, checkpoints, the fold that keeps a twin current, the head that performs a write against the vendor, references, and the git plane. A twin package builds on it; the runtime serves it.",
5
5
  "keywords": [
6
6
  "twin",
@@ -348,7 +348,7 @@ function performOf(units: Unit[]): PerformAction {
348
348
  adopted.push({ actionId: entry.id, externalId, data });
349
349
  }
350
350
  const primary = adopted.shift();
351
- return primary ? { ...primary, ...(adopted.length ? { also: adopted } : {}) } : { externalId: '', actionId: group.at(-1)!.id };
351
+ return primary ? { ...primary, response: answer, ...(adopted.length ? { also: adopted } : {}) } : { externalId: '', actionId: group.at(-1)!.id, response: answer };
352
352
  }
353
353
  // an XML answer is read through its document element (S3's `InitiateMultipartUploadResult`), as a JSON one under its key
354
354
  const documentElement = (): Row | undefined => {
@@ -365,7 +365,7 @@ function performOf(units: Unit[]): PerformAction {
365
365
  // CreateSecretResponse, S3's CreateMultipartUploadOutput) is the group's subject's, its id read by that resource
366
366
  const subjectDecl = (): ResourceDecl | undefined => Object.entries(m.resources).find(([r]) => storedType(m, r) === group.at(-1)!.subject.type)?.[1];
367
367
  const decl: ResourceDecl | undefined = resource ? m.resources[resource] : subjectDecl();
368
- const idOf = (o: Row | undefined, d: ResourceDecl | undefined): string | undefined => { const v = o?.[d?.idAs ?? 'id']; return typeof v === 'string' ? v : undefined; };
368
+ const idOf = (o: Row | undefined, d: ResourceDecl | undefined): string | undefined => o ? storedIdOf(d ?? { idPrefix: '' }, o) : undefined;
369
369
  // the group's entry the answer is: of the answered resource's stored type (a cascade writes children first), else
370
370
  // the last; a delete answers its subject's own vendor id
371
371
  const answered = resource ? [...group].reverse().find((e) => e.subject.type === storedType(m, resource)) : undefined;
@@ -373,7 +373,7 @@ function performOf(units: Unit[]): PerformAction {
373
373
  // an answer about a subject no entry of the group is (Slack's conversations.join answers the channel; the group is
374
374
  // the membership and its channel_join message) gives no entry its id or its fields: each settles deployed with no
375
375
  // vendor id, and refresh adopts it ("Perform", step 2)
376
- if (resource && !answered) return { externalId: '', actionId: primary.id };
376
+ if (resource && !answered) return { externalId: '', actionId: primary.id, response: answer };
377
377
  const deleted = primary.fields?.deleted === true;
378
378
  // a keyed subject's id is its key filled from the answer (its envelope's fields, Slack's `channel` and `ts`, among them)
379
379
  const keyed = decl?.key ? decl : Object.entries(m.resources).find(([r]) => storedType(m, r) === primary.subject.type)?.[1];
@@ -383,9 +383,9 @@ function performOf(units: Unit[]): PerformAction {
383
383
  for (const [companion, where] of Object.entries(decl?.companions ?? keyed?.companions ?? {})) {
384
384
  const id = at(parsed, where) ?? at(obj, where);
385
385
  const entry = group.find((e) => e !== primary && e.subject.type === storedType(m, companion));
386
- if (entry && typeof id === 'string') also.push({ actionId: entry.id, externalId: id });
386
+ if (entry && (typeof id === 'string' || typeof id === 'number')) also.push({ actionId: entry.id, externalId: String(id) });
387
387
  }
388
- return { externalId, actionId: primary.id, data: obj, ...(also.length ? { also } : {}) };
388
+ return { externalId, actionId: primary.id, data: obj, response: answer, ...(also.length ? { also } : {}) };
389
389
  };
390
390
  return async (execute: RemoteExecute, group: TwinAction[], ctx: PerformContext): Promise<PushOutcome> => {
391
391
  await adoptAccount(units, execute, ctx);
@@ -529,7 +529,7 @@ function refreshOf(units: Unit[], service: string): NonNullable<StateSystemAdapt
529
529
  read.push(String(subject.id));
530
530
  answered += 1;
531
531
  if (answer.status === 404) continue;
532
- if (answer.status >= 400 || refusedIn(m, parse(answer))) throw new Error(`${answer.status}: ${refusalWords(m, parse(answer))}`);
532
+ if (answer.status >= 400 || refusedIn(m, parse(answer))) throw new Error(`${m.vendor}: ${resource} refresh ${op.id} (${op.method.toUpperCase()} ${op.path}): ${answer.status}: ${refusalWords(m, parse(answer))}`);
533
533
  const full = object(op.answers?.key ? at(parse(answer), op.answers.key) : parse(answer)) ?? {};
534
534
  const local = (m as { worldFields?: Record<string, Record<string, unknown>> }).worldFields?.[resource] ?? {};
535
535
  const fields = Object.fromEntries(Object.entries(full).filter(([k]) => !(k in local)));
@@ -596,7 +596,7 @@ function refreshOf(units: Unit[], service: string): NonNullable<StateSystemAdapt
596
596
  // one subject read by its parent and not there (an account with no workers.dev subdomain): nothing to observe. A
597
597
  // path missing a parameter (an empty segment) is the kernel's mistake, never the vendor's absence
598
598
  if ('get' in scope && answer.status === 404 && !unfilled) { answered += 1; break; }
599
- if (answer.status >= 400 || refusedIn(m, parse(answer))) throw new Error(`${answer.status}: ${refusalWords(m, parse(answer))}`);
599
+ if (answer.status >= 400 || refusedIn(m, parse(answer))) throw new Error(`${m.vendor}: ${resource} refresh ${op.id} (${op.method.toUpperCase()} ${op.path}): ${answer.status}: ${refusalWords(m, parse(answer))}`);
600
600
  answered += 1;
601
601
  const parsed = parse(answer);
602
602
  const shape = 'list' in scope ? scope.shape : undefined;
@@ -643,7 +643,7 @@ function refreshOf(units: Unit[], service: string): NonNullable<StateSystemAdapt
643
643
  const detailQuery = { ...(scope.query ?? {}), ...(detailRpc ? {} : withValues) };
644
644
  const detailSent = detailRpc && Object.keys(withValues).length ? encode(detail, withValues) : undefined;
645
645
  const answer = await execute({ method: detail.method.toUpperCase(), path: sentTo(m, marked(detail, Object.keys(detailQuery).length ? `${path}?${new URLSearchParams(detailQuery)}` : path), { ...item, ...under?.params }), ...onLane, headers: { ...fixedHeaders(detail), ...(detailSent?.type ? { 'content-type': detailSent.type } : {}) }, ...(detailSent?.body !== undefined ? { body: detailSent.body } : {}) });
646
- if (answer.status >= 400 || refusedIn(m, parse(answer))) throw new Error(`${answer.status}: ${refusalWords(m, parse(answer)) || 'the vendor refused the refresh detail; nothing folded'}`);
646
+ if (answer.status >= 400 || refusedIn(m, parse(answer))) throw new Error(`${m.vendor}: ${resource} refresh detail ${detail.id} (${detail.method.toUpperCase()} ${detail.path}): ${answer.status}: ${refusalWords(m, parse(answer)) || 'the vendor refused the refresh detail; nothing folded'}`);
647
647
  const detailBody = parse(answer);
648
648
  const detailKey = scope.detailMerge ?? detail.answers?.key;
649
649
  const fragment = object(detailKey ? at(detailBody, detailKey) : detailBody);
@@ -718,7 +718,7 @@ function refreshOf(units: Unit[], service: string): NonNullable<StateSystemAdapt
718
718
  // scoped to one resource (`RootConfig.scope`, GitHub's `repos/o/r`) lists only what is under it, so a type with no
719
719
  // parent is never the account's whole list there
720
720
  if (whole && !decl.parent && !opts.scope) complete.push(type);
721
- else if (whole && parents.length) complete.push({ type, field: decl.parent!.field, within: parents.map((p) => p!.value) });
721
+ else if (whole && parents.length && decl.parent) complete.push({ type, field: decl.parent.field, within: parents.map((p) => p!.value) });
722
722
  }
723
723
  }
724
724
  // every read refused is a credential the vendor refuses, never an account that holds nothing
package/src/head.ts CHANGED
@@ -14,7 +14,7 @@ import { confirmAction, currentCorrelationId, isTwinBookkeeping, resolveSubjectI
14
14
  import { keySealer, type CredentialPayload, type SealedCredential, type Sealer } from './credential.ts';
15
15
  import { buildRemoteExecute, validateRemoteOrigin, type CredentialCustody } from './executor.ts';
16
16
  import { getPack } from './packRegistry.ts';
17
- import type { RemoteExecute } from './remote-execute.ts';
17
+ import type { RemoteExecute, RemoteExecuteResponse } from './remote-execute.ts';
18
18
  import { aliasesFrom, branchEntries, parentEntries, readTree, type Entry, type Receipt } from './log.ts';
19
19
  import { resolveReferences } from './references.ts';
20
20
  import { authStrategyFor, NO_SECRETS_CHECK, readRoot, runChecks, stateSystemFor, type Check, type DeployPolicy, type RootConfig, type StateSystemAdapters } from './state-system.ts';
@@ -28,6 +28,8 @@ import { getActiveWorldStore } from './world-store.ts';
28
28
  * the vendor's answer as fields for the landed copy. */
29
29
  export type PushOutcome = {
30
30
  externalId: string; url?: string; data?: Record<string, unknown>;
31
+ /** the successful vendor response for the caller, distinct from the resource fields stored in data */
32
+ response?: RemoteExecuteResponse;
31
33
  /** the group's entry the id is for, when it is not the group's first (a cascade writes children first) */
32
34
  actionId?: string;
33
35
  /** the group's other entries the answer named an id for (a resource's `companions`) */
@@ -253,7 +255,7 @@ export class VendorWriteError extends Error {
253
255
  export type DeployReport = {
254
256
  /** entries performed by this call */
255
257
  pushed: number;
256
- deployed: Array<{ actionId: string; externalId?: string; at: string; /** what the vendor answered, as the adapter returned it */ data?: unknown }>;
258
+ deployed: Array<{ actionId: string; externalId?: string; at: string; /** what the vendor answered, as the adapter returned it */ data?: unknown; response?: RemoteExecuteResponse }>;
257
259
  /** the first refusal; nothing after it was attempted */
258
260
  refused?: { actionId: string; check: string; reason: string };
259
261
  /** the first failure, with the vendor's words; nothing after it was attempted */
@@ -417,7 +419,7 @@ async function performNow(opts: Parameters<typeof performEntries>[0]): Promise<D
417
419
  settle(action, at, { status: 'deployed', ...(action === primary && outcome.url ? { url: outcome.url } : {}) }, { ...action.fields, ...(action === primary ? answered : also.get(action.id)?.data ?? {}) }, externalId || undefined);
418
420
  const data = action === primary ? outcome.data : also.get(action.id)?.data;
419
421
  report.pushed += 1;
420
- report.deployed.push({ actionId: action.id, at, ...(externalId ? { externalId } : {}), ...(data !== undefined ? { data } : {}) });
422
+ report.deployed.push({ actionId: action.id, at, ...(externalId ? { externalId } : {}), ...(data !== undefined ? { data } : {}), ...(action === primary && outcome.response ? { response: outcome.response } : {}) });
421
423
  }
422
424
  } catch (error) {
423
425
  // a write the vendor refuses (a 4xx: it can never be sent as it is) settles the group refused, as a check's
@@ -475,7 +477,7 @@ export async function deferPerform<T>(request: string, fn: () => Promise<T>): Pr
475
477
  /** Under `auto`, perform the entries one request made as one call (the real-system adapters, "Perform"): the ids the
476
478
  * vendor gave them by local id, or the vendor's refusal or failure, the request's entries reverted as performAtHead
477
479
  * reverts one. Nothing when the head is not real and auto, or the request wrote nothing the vendor is offered. */
478
- export async function performRequestGroup(service: string, request: string, root?: string): Promise<{ ids: Map<string, string> } | RefusedWriteError | VendorWriteError> {
480
+ export async function performRequestGroup(service: string, request: string, root?: string): Promise<{ ids: Map<string, string>; response?: RemoteExecuteResponse } | RefusedWriteError | VendorWriteError> {
479
481
  const head = headOf(service, root);
480
482
  const ids = new Map<string, string>();
481
483
  if (head.kind !== 'real' || head.deploy !== 'auto') return { ids };
@@ -492,7 +494,8 @@ export async function performRequestGroup(service: string, request: string, root
492
494
  const e = group.find((g) => g.id === d.actionId);
493
495
  if (e && d.externalId && d.externalId !== e.subject.id) ids.set(e.subject.id, d.externalId);
494
496
  }
495
- return { ids };
497
+ const response = report.deployed.find((entry) => entry.response)?.response;
498
+ return { ids, ...(response ? { response } : {}) };
496
499
  }
497
500
 
498
501
  /** Wrap a pack's fetch: a `RefusedWriteError` or `VendorWriteError` the head raised becomes the
package/src/pack-fetch.ts CHANGED
@@ -563,6 +563,12 @@ export function createPackFetch(parts: PackParts, options: PackFetchOptions = {}
563
563
  // the vendor's refusal, as its status and words (perform names them `<status>: <message>`), in the vendor's error body
564
564
  if (performed instanceof RefusedWriteError) { const said = /^(\d{3}): ([\s\S]*)$/.exec(performed.reason); return vendorError(m, { status: said ? Number(said[1]) : 400, message: said ? said[2]! : performed.reason }); }
565
565
  if (performed instanceof VendorWriteError) return vendorError(m, { status: 502, message: performed.message });
566
+ if (performed.response) {
567
+ const { status, headers: received, body, bodyBytes } = performed.response;
568
+ const headers = new Headers(received);
569
+ for (const name of ['content-length', 'content-encoding', 'transfer-encoding', 'connection', 'keep-alive']) headers.delete(name);
570
+ return new Response([204, 205, 304].includes(status) ? null : bodyBytes ? new Uint8Array(bodyBytes).buffer : body, { status, headers });
571
+ }
566
572
  if (!performed.ids.size || !(answer.headers.get('content-type') ?? '').includes('json')) return answer;
567
573
  let text = await answer.text();
568
574
  for (const [local, vendor] of performed.ids) text = text.split(JSON.stringify(local)).join(JSON.stringify(vendor));