@volter/world-core 3.0.2 → 3.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/src/pack-fetch.js +4 -3
- package/dist/src/serve.js +2 -1
- package/dist/src/with-headers.d.ts +5 -0
- package/dist/src/with-headers.js +8 -0
- package/package.json +1 -1
- package/src/pack-fetch.ts +4 -3
- package/src/serve.ts +2 -1
- package/src/with-headers.ts +8 -0
package/dist/src/pack-fetch.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { ORIGINAL_PATH_HEADER } from "./sigv4.js";
|
|
9
9
|
import { TWIN_PREFIX_HEADER } from "./twin-fetch.js";
|
|
10
10
|
import { PREFIX_PARAMS_HEADER } from "./derived.js";
|
|
11
|
+
import { withHeaders } from "./with-headers.js";
|
|
11
12
|
import { authRefusal, bindSemantics, contextFor, LANE_HEADER, SCENARIO_DECISION_HEADER, coreFor, crossCutting, derivedRequestScopes, vendorError } from "./derived-core.js";
|
|
12
13
|
import { compileSurface, createDerivedFetch, matchOperation } from "./derived.js";
|
|
13
14
|
import { isReadOnlyRequest, READ_ONLY_REQUEST_HEADER, runAsVendorMove } from "./request-scope.js";
|
|
@@ -195,7 +196,7 @@ export function createPackFetch(parts, options = {}) {
|
|
|
195
196
|
}
|
|
196
197
|
if (decision.kind === 'fault')
|
|
197
198
|
return Response.json(decision.result.body, { status: decision.result.status, headers: decision.result.headers ?? {} });
|
|
198
|
-
return api(
|
|
199
|
+
return api(withHeaders(request, new Headers([...request.headers, [SCENARIO_DECISION_HEADER, JSON.stringify(decision)]])));
|
|
199
200
|
};
|
|
200
201
|
const doors = (m.doors ?? []).map((d) => {
|
|
201
202
|
const answer = parts.doors?.[d.id];
|
|
@@ -257,7 +258,7 @@ export function createPackFetch(parts, options = {}) {
|
|
|
257
258
|
return vendorError(m, m.encodings?.undecodable ?? { status: 400, message: `the body does not decode as its Content-Encoding (${arrived.headers.get('content-encoding')}) says` });
|
|
258
259
|
const incoming = routed(readable);
|
|
259
260
|
// a twin started read-only marks every request so, so every context it opens knows (isReadOnlyRequest)
|
|
260
|
-
const request = options.readOnly && !isReadOnlyRequest(incoming) ?
|
|
261
|
+
const request = options.readOnly && !isReadOnlyRequest(incoming) ? withHeaders(incoming, new Headers([...incoming.headers, [READ_ONLY_REQUEST_HEADER, '1']])) : incoming;
|
|
261
262
|
const url = new URL(request.url);
|
|
262
263
|
const path = url.pathname.replace(/\/+$/, '') || '/';
|
|
263
264
|
const readOnly = (options.readOnly ?? false) || isReadOnlyRequest(request);
|
|
@@ -400,7 +401,7 @@ function laneRouter(vendor, decl, lanes, rootServes = () => false) {
|
|
|
400
401
|
const named = (request) => {
|
|
401
402
|
const headers = new Headers(request.headers);
|
|
402
403
|
headers.set(LANE_HEADER, name);
|
|
403
|
-
return part.fetch(
|
|
404
|
+
return part.fetch(withHeaders(request, headers));
|
|
404
405
|
};
|
|
405
406
|
return decl.cors ? withCors(decl.cors, named) : named;
|
|
406
407
|
};
|
package/dist/src/serve.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
13
13
|
import { serveHttp, setServeDecorator, WORLD_BOOT_PATH } from "./serve-http.js";
|
|
14
14
|
import { performAtHead } from "./head.js";
|
|
15
|
+
import { withHeaders } from "./with-headers.js";
|
|
15
16
|
import { createHash } from 'node:crypto';
|
|
16
17
|
import { dirname, join } from 'node:path';
|
|
17
18
|
import { hashFieldValue } from "./hash.js";
|
|
@@ -350,7 +351,7 @@ function journalingFetch(config, callSiteVendor) {
|
|
|
350
351
|
if (caller !== undefined) {
|
|
351
352
|
const headers = new Headers(request.headers);
|
|
352
353
|
headers.delete(CALLER_HEADER);
|
|
353
|
-
request =
|
|
354
|
+
request = withHeaders(request, headers);
|
|
354
355
|
}
|
|
355
356
|
// The World's own boot probe (serve-http.ts, WORLD_BOOT_PATH) is not the app's traffic: never journaled.
|
|
356
357
|
if (!twinRequestJournalEnabled() || new URL(request.url).pathname === WORLD_BOOT_PATH)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** `request` with `headers` in place of its own, its body passed through as the same stream. Bun's
|
|
2
|
+
* `new Request(request, { headers })` drops a streamed body (one a node:http server gives as `Readable.toWeb`, duplex
|
|
3
|
+
* half): reading the copy waits forever, and so does the twin that reads it. Measured on Bun 1.3.10; rebuilding from
|
|
4
|
+
* the URL with `body: request.body` keeps it readable. */
|
|
5
|
+
export declare function withHeaders(request: Request, headers: Headers): Request;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** `request` with `headers` in place of its own, its body passed through as the same stream. Bun's
|
|
2
|
+
* `new Request(request, { headers })` drops a streamed body (one a node:http server gives as `Readable.toWeb`, duplex
|
|
3
|
+
* half): reading the copy waits forever, and so does the twin that reads it. Measured on Bun 1.3.10; rebuilding from
|
|
4
|
+
* the URL with `body: request.body` keeps it readable. */
|
|
5
|
+
export function withHeaders(request, headers) {
|
|
6
|
+
const body = request.method === 'GET' || request.method === 'HEAD' ? null : request.body;
|
|
7
|
+
return new Request(request.url, { method: request.method, headers, body, signal: request.signal, redirect: request.redirect, ...(body ? { duplex: 'half' } : {}) });
|
|
8
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/world-core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
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",
|
package/src/pack-fetch.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { ORIGINAL_PATH_HEADER } from './sigv4.ts';
|
|
9
9
|
import { TWIN_PREFIX_HEADER } from './twin-fetch.ts';
|
|
10
10
|
import { PREFIX_PARAMS_HEADER } from './derived.ts';
|
|
11
|
+
import { withHeaders } from './with-headers.ts';
|
|
11
12
|
import { authRefusal, bindSemantics, contextFor, LANE_HEADER, SCENARIO_DECISION_HEADER, coreFor, crossCutting, derivedRequestScopes, vendorError, type CoreScope, type DerivedManifest, type ErrorSpec, type Handler, type HandlerContext, type LanesDecl, type VendorManifest } from './derived-core.ts';
|
|
12
13
|
import { compileSurface, createDerivedFetch, matchOperation, type DerivedFetch, type DerivedOperation, type DerivedOwner, type DerivedSurface } from './derived.ts';
|
|
13
14
|
import { isReadOnlyRequest, READ_ONLY_REQUEST_HEADER, runAsVendorMove } from './request-scope.ts';
|
|
@@ -230,7 +231,7 @@ export function createPackFetch(parts: PackParts, options: PackFetchOptions = {}
|
|
|
230
231
|
return vendorError(m, { status: 500, code: 'scenario_error', message: `the World's scenario: ${error.message}` });
|
|
231
232
|
}
|
|
232
233
|
if (decision.kind === 'fault') return Response.json(decision.result.body, { status: decision.result.status, headers: decision.result.headers ?? {} });
|
|
233
|
-
return api(
|
|
234
|
+
return api(withHeaders(request, new Headers([...request.headers, [SCENARIO_DECISION_HEADER, JSON.stringify(decision)]])));
|
|
234
235
|
};
|
|
235
236
|
const doors = (m.doors ?? []).map((d) => {
|
|
236
237
|
const answer = parts.doors?.[d.id];
|
|
@@ -287,7 +288,7 @@ export function createPackFetch(parts: PackParts, options: PackFetchOptions = {}
|
|
|
287
288
|
if (!readable) return vendorError(m, m.encodings?.undecodable ?? { status: 400, message: `the body does not decode as its Content-Encoding (${arrived.headers.get('content-encoding')}) says` });
|
|
288
289
|
const incoming = routed(readable);
|
|
289
290
|
// a twin started read-only marks every request so, so every context it opens knows (isReadOnlyRequest)
|
|
290
|
-
const request = options.readOnly && !isReadOnlyRequest(incoming) ?
|
|
291
|
+
const request = options.readOnly && !isReadOnlyRequest(incoming) ? withHeaders(incoming, new Headers([...incoming.headers, [READ_ONLY_REQUEST_HEADER, '1']])) : incoming;
|
|
291
292
|
const url = new URL(request.url);
|
|
292
293
|
const path = url.pathname.replace(/\/+$/, '') || '/';
|
|
293
294
|
const readOnly = (options.readOnly ?? false) || isReadOnlyRequest(request);
|
|
@@ -407,7 +408,7 @@ function laneRouter(vendor: string, decl: LanesDecl, lanes: Record<string, LaneP
|
|
|
407
408
|
const named = (request: Request): Promise<Response> => {
|
|
408
409
|
const headers = new Headers(request.headers);
|
|
409
410
|
headers.set(LANE_HEADER, name);
|
|
410
|
-
return part.fetch(
|
|
411
|
+
return part.fetch(withHeaders(request, headers));
|
|
411
412
|
};
|
|
412
413
|
return decl.cors ? withCors(decl.cors, named) : named;
|
|
413
414
|
};
|
package/src/serve.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
13
13
|
import { serveHttp, setServeDecorator, WORLD_BOOT_PATH, type HttpHandler } from './serve-http.ts';
|
|
14
14
|
import { performAtHead } from './head.ts';
|
|
15
|
+
import { withHeaders } from './with-headers.ts';
|
|
15
16
|
import { createHash } from 'node:crypto';
|
|
16
17
|
import { dirname, join } from 'node:path';
|
|
17
18
|
import type { SubjectFields } from './hash.ts';
|
|
@@ -405,7 +406,7 @@ function journalingFetch(config: Record<string | symbol, unknown> & { fetch: (..
|
|
|
405
406
|
const journaling = async function (this: unknown, request: Request, server: unknown) {
|
|
406
407
|
// who made it is read here and goes no further: no twin's handler (a tunnel's, a proxy's) hands it on
|
|
407
408
|
const sent = request.headers.get(CALLER_HEADER); const caller = sent === null ? undefined : callerFromHeader(sent);
|
|
408
|
-
if (caller !== undefined) { const headers = new Headers(request.headers); headers.delete(CALLER_HEADER); request =
|
|
409
|
+
if (caller !== undefined) { const headers = new Headers(request.headers); headers.delete(CALLER_HEADER); request = withHeaders(request, headers); }
|
|
409
410
|
// The World's own boot probe (serve-http.ts, WORLD_BOOT_PATH) is not the app's traffic: never journaled.
|
|
410
411
|
if (!twinRequestJournalEnabled() || new URL(request.url).pathname === WORLD_BOOT_PATH) return inner.call(this, request, server);
|
|
411
412
|
const slot: { identity?: TwinJournalIdentity } = {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** `request` with `headers` in place of its own, its body passed through as the same stream. Bun's
|
|
2
|
+
* `new Request(request, { headers })` drops a streamed body (one a node:http server gives as `Readable.toWeb`, duplex
|
|
3
|
+
* half): reading the copy waits forever, and so does the twin that reads it. Measured on Bun 1.3.10; rebuilding from
|
|
4
|
+
* the URL with `body: request.body` keeps it readable. */
|
|
5
|
+
export function withHeaders(request: Request, headers: Headers): Request {
|
|
6
|
+
const body = request.method === 'GET' || request.method === 'HEAD' ? null : request.body;
|
|
7
|
+
return new Request(request.url, { method: request.method, headers, body, signal: request.signal, redirect: request.redirect, ...(body ? { duplex: 'half' } : {}) } as RequestInit);
|
|
8
|
+
}
|