@volter/world-core 3.0.163 → 3.0.165
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/generated/pack-facts.json +44 -3660
- package/dist/pack-facts.cjs +4 -4
- package/dist/src/browser-session.d.ts +7 -1
- package/dist/src/browser-session.js +21 -10
- package/dist/src/browser.d.ts +4 -0
- package/dist/src/browser.js +10 -0
- package/dist/src/derived-core.d.ts +46 -6
- package/dist/src/derived-core.js +60 -13
- package/dist/src/derived-real.js +159 -5
- package/dist/src/derived.d.ts +1 -1
- package/dist/src/events.d.ts +6 -3
- package/dist/src/events.js +52 -7
- package/dist/src/executor.d.ts +2 -2
- package/dist/src/executor.js +13 -0
- package/dist/src/file-response.d.ts +6 -2
- package/dist/src/file-response.js +29 -5
- package/dist/src/git/index.d.ts +1 -0
- package/dist/src/git/index.js +1 -0
- package/dist/src/git/smart-http-client.d.ts +28 -0
- package/dist/src/git/smart-http-client.js +95 -0
- package/dist/src/git/smart-http.d.ts +3 -0
- package/dist/src/git/smart-http.js +9 -0
- package/dist/src/head.d.ts +16 -1
- package/dist/src/head.js +52 -7
- package/dist/src/html.d.ts +29 -0
- package/dist/src/html.js +64 -0
- package/dist/src/index.d.ts +5 -2
- package/dist/src/index.js +5 -1
- package/dist/src/managed-database.d.ts +4 -2
- package/dist/src/managed-database.js +1 -0
- package/dist/src/media-header.d.ts +34 -0
- package/dist/src/media-header.js +129 -0
- package/dist/src/pack-fetch.d.ts +24 -3
- package/dist/src/pack-fetch.js +155 -96
- package/dist/src/pack-paths.d.ts +2 -3
- package/dist/src/pack-paths.js +3 -13
- package/dist/src/packRegistry.d.ts +17 -1
- package/dist/src/request-body.d.ts +2 -3
- package/dist/src/request-body.js +2 -3
- package/dist/src/runtime.d.ts +4 -1
- package/dist/src/runtime.js +5 -1
- package/dist/src/serve-http.d.ts +2 -2
- package/dist/src/serve-http.js +104 -19
- package/dist/src/smtp-server.d.ts +78 -0
- package/dist/src/smtp-server.js +160 -0
- package/dist/src/smtp.d.ts +29 -11
- package/dist/src/smtp.js +221 -52
- package/generated/pack-facts.json +44 -3660
- package/pack-facts.cjs +4 -4
- package/package.json +4 -2
- package/src/browser-session.ts +17 -5
- package/src/browser.ts +10 -0
- package/src/derived-core.ts +77 -19
- package/src/derived-real.ts +131 -4
- package/src/derived.ts +2 -2
- package/src/events.ts +54 -9
- package/src/executor.ts +13 -2
- package/src/file-response.ts +28 -6
- package/src/git/index.ts +1 -0
- package/src/git/smart-http-client.ts +99 -0
- package/src/git/smart-http.ts +7 -0
- package/src/head.ts +37 -6
- package/src/html.ts +72 -0
- package/src/index.ts +6 -2
- package/src/managed-database.ts +4 -3
- package/src/media-header.ts +108 -0
- package/src/pack-fetch.ts +142 -87
- package/src/pack-paths.ts +3 -11
- package/src/packRegistry.ts +9 -1
- package/src/request-body.ts +2 -3
- package/src/runtime.ts +5 -1
- package/src/serve-http.ts +81 -19
- package/src/smtp-server.ts +166 -0
- package/src/smtp.ts +199 -53
package/dist/src/pack-fetch.js
CHANGED
|
@@ -19,7 +19,9 @@ import { isReadOnlyRequest, READ_ONLY_REQUEST_HEADER, runAsVendorMove } from "./
|
|
|
19
19
|
import { parseScenarioDocument, ScenarioEngine, ScenarioError, statefulTwinManifest, twinManifest } from "./scenario.js";
|
|
20
20
|
import { getActiveWorldStore } from "./world-store.js";
|
|
21
21
|
import { runWithCorrelationId } from "./actions.js";
|
|
22
|
-
import { deferPerform, performRequestGroup, RefusedWriteError, VendorWriteError } from "./head.js";
|
|
22
|
+
import { deferPerform, deployableEntries, performRequestGroup, RefusedWriteError, VendorWriteError } from "./head.js";
|
|
23
|
+
import { FLUSH, ZERO_SHA, pktLine, readPktLines } from "./git/smart-http.js";
|
|
24
|
+
import { concat } from "./git/objects.js";
|
|
23
25
|
import { withCors } from "./cors.js";
|
|
24
26
|
import { contentAddressOf } from "./content-route.js";
|
|
25
27
|
import { getPack } from "./packRegistry.js";
|
|
@@ -27,7 +29,7 @@ import { hostRules } from '../host-routing.cjs';
|
|
|
27
29
|
import { expireSocketWaits, socketUpgrade } from "./sockets.js";
|
|
28
30
|
import { liveIngest } from "./live/ingest.js";
|
|
29
31
|
import { readResourceBlob, removeResourceBlob, writeResourceBlob } from "./resource-blob.js";
|
|
30
|
-
import { resourcesOfType } from "./derived-core.js";
|
|
32
|
+
import { gitRepositoryOf, resourcesOfType } from "./derived-core.js";
|
|
31
33
|
import { worldNow } from "./world-clock.js";
|
|
32
34
|
import { MACHINE_POOL, POOL_KINDS, resetFakePool } from "./machines.js";
|
|
33
35
|
import { applyTwinWrite } from "./serve.js";
|
|
@@ -71,7 +73,7 @@ function hostMatcher(pattern) {
|
|
|
71
73
|
};
|
|
72
74
|
}
|
|
73
75
|
/** The host a request is for: the vendor host the World's injector names, else its own. */
|
|
74
|
-
const hostOf = (request) => readHostPort(request.headers.get('x-volter-twin-original-host') ?? request.headers.get('host') ?? new URL(request.url).host)?.host ?? '';
|
|
76
|
+
export const hostOf = (request) => readHostPort(request.headers.get('x-volter-twin-original-host') ?? request.headers.get('host') ?? new URL(request.url).host)?.host ?? '';
|
|
75
77
|
/** Where a workspace asked `?show=<type>:<id>` opens the record (architecture, "Workspace"): the built workspace page
|
|
76
78
|
* that shows that type, its parameter the id; else the one that lists the type; else the workspace's home (its root
|
|
77
79
|
* page). Every other parameter a page's path names is the asking page's, and a page that cannot be filled is passed
|
|
@@ -152,12 +154,26 @@ async function encoded(m, request, res) {
|
|
|
152
154
|
const buffered = new ReadableStream({ start(controller) { controller.enqueue(body); controller.close(); } });
|
|
153
155
|
return new Response(buffered, { status: res.status, statusText: res.statusText, headers });
|
|
154
156
|
}
|
|
155
|
-
/** A pack's HTTP surface, as the kernel serves it from the pack's parts. */
|
|
156
157
|
/** A door's path as it is served and listed: under `/_twin/`, whether the manifest writes the prefix or not. */
|
|
157
158
|
const doorPath = (path) => (path.startsWith('/_twin/') ? path : `/_twin${path.startsWith('/') ? '' : '/'}${path}`);
|
|
159
|
+
/** A declared World door, matched by the same normalized path for serving and response ownership. */
|
|
160
|
+
export function worldDoorMatcher(doors) {
|
|
161
|
+
const routes = doors.map((door) => ({ door, method: door.method.toUpperCase(), matches: pathMatcher(doorPath(door.path), false) }));
|
|
162
|
+
return (method, path) => {
|
|
163
|
+
const at = path.replace(/\/+$/, '') || '/';
|
|
164
|
+
for (const route of routes) {
|
|
165
|
+
if (route.method !== method.toUpperCase())
|
|
166
|
+
continue;
|
|
167
|
+
const hit = route.matches(at);
|
|
168
|
+
if (hit)
|
|
169
|
+
return { door: route.door, params: hit.params };
|
|
170
|
+
}
|
|
171
|
+
return undefined;
|
|
172
|
+
};
|
|
173
|
+
}
|
|
158
174
|
/** A request with its path's repeated slashes merged, as a front that merges them routes it (the manifest's
|
|
159
175
|
* `mergeSlashes`); one with none is itself. */
|
|
160
|
-
function slashesMerged(request) {
|
|
176
|
+
export function slashesMerged(request) {
|
|
161
177
|
const url = new URL(request.url);
|
|
162
178
|
const merged = url.pathname.replace(/\/{2,}/g, '/');
|
|
163
179
|
if (merged === url.pathname)
|
|
@@ -165,6 +181,63 @@ function slashesMerged(request) {
|
|
|
165
181
|
url.pathname = merged;
|
|
166
182
|
return new Request(url, request);
|
|
167
183
|
}
|
|
184
|
+
/** The selected unit's request transformation, shared by serving and response judgment. */
|
|
185
|
+
export function unitRequestRouter(m, surface, routes) {
|
|
186
|
+
const prefix = m.pathPrefix ? new RegExp(`^(?:${m.pathPrefix})(?=/)`) : undefined;
|
|
187
|
+
const base = surface.basePath ?? '';
|
|
188
|
+
const baseAlias = m.basePathAlias && base ? new RegExp(`^(?:${m.basePathAlias})(?=/)`) : undefined;
|
|
189
|
+
const hostPath = m.pathFromHost ? new RegExp(m.pathFromHost, 'i') : undefined;
|
|
190
|
+
const hostParams = m.hostParams ? new RegExp(m.hostParams, 'i') : undefined;
|
|
191
|
+
// each operation's spanning parameters, keyed by the operation and the parameter as the route compiles them (a whole
|
|
192
|
+
// segment's label whose name the surface or the manifest declares spanning), so one operation's spanning name never
|
|
193
|
+
// keeps another's slashes, nor a label that only part of a segment holds
|
|
194
|
+
const spanningNames = new Set(surface.spanning ?? []);
|
|
195
|
+
const spanningOf = new Set(surface.operations.flatMap((op) => ((op.basePath ?? base) + op.path).split('/').flatMap((seg) => {
|
|
196
|
+
const label = /^\{([^}]+)\}$/.exec(seg)?.[1];
|
|
197
|
+
return label !== undefined && spanningNames.has(label) ? [`${op.id}\u0000${label}`] : [];
|
|
198
|
+
})));
|
|
199
|
+
/** A path as the vendor routes it: the manifest's path prefix taken off, the spec's base in an alias's place, a host's
|
|
200
|
+
* path label (pathFromHost) put in front. */
|
|
201
|
+
const rewritten = (pathname, host) => {
|
|
202
|
+
let path = pathname;
|
|
203
|
+
if (prefix)
|
|
204
|
+
path = path.replace(prefix, '') || '/';
|
|
205
|
+
// a base the vendor serves as its own: the spec's base in its place (a path already under the spec's base stays)
|
|
206
|
+
if (baseAlias && !(path === base || path.startsWith(`${base}/`)))
|
|
207
|
+
path = path.replace(baseAlias, base);
|
|
208
|
+
const label = hostPath?.exec(host)?.[1];
|
|
209
|
+
if (label)
|
|
210
|
+
path = `/${label}${path === '/' ? '' : path}`;
|
|
211
|
+
return path;
|
|
212
|
+
};
|
|
213
|
+
/** The request as its vendor routes it: duplicate slashes collapsed, unless the rewritten path matches an operation
|
|
214
|
+
* through one of that operation's spanning parameters (the surface's and the manifest's), whose value keeps its
|
|
215
|
+
* slashes; then rewritten. */
|
|
216
|
+
return (request) => {
|
|
217
|
+
const url = new URL(request.url);
|
|
218
|
+
const host = hostOf(request);
|
|
219
|
+
const kept = rewritten(url.pathname, host);
|
|
220
|
+
const matched = matchOperation(routes, request.method, kept);
|
|
221
|
+
const spanning = matched !== undefined && Object.keys(matched.params).some((name) => spanningOf.has(`${matched.operation.id}\u0000${name}`));
|
|
222
|
+
const source = spanning ? url.pathname : url.pathname.replace(/\/{2,}/g, '/');
|
|
223
|
+
// a prefix's named groups (Jira's `/ex/jira/(?<cloudId>[^/]+)`) and the host's (E2B's envd at
|
|
224
|
+
// `49983-(?<sandboxID>…).e2b.app`) join the operation's parameters
|
|
225
|
+
const groups = { ...(prefix?.exec(source)?.groups ?? {}), ...(hostParams?.exec(host)?.groups ?? {}), ...Object.fromEntries(Object.entries(m.headerParams ?? {}).flatMap(([param, header]) => { const value = request.headers.get(header); return value === null ? [] : [[param, value]]; })) };
|
|
226
|
+
const path = spanning ? kept : rewritten(source, host);
|
|
227
|
+
if (path === url.pathname && !Object.keys(groups).length)
|
|
228
|
+
return request;
|
|
229
|
+
// the path the client sent, which a signature over the request (SigV4's canonical URI) covers
|
|
230
|
+
const headers = new Headers(request.headers);
|
|
231
|
+
// the path as first sent: a front before this (an attach bridge) may have named the one its client signed
|
|
232
|
+
if (!headers.has(ORIGINAL_PATH_HEADER))
|
|
233
|
+
headers.set(ORIGINAL_PATH_HEADER, url.pathname);
|
|
234
|
+
if (Object.keys(groups).length)
|
|
235
|
+
headers.set(PREFIX_PARAMS_HEADER, JSON.stringify(groups));
|
|
236
|
+
url.pathname = path;
|
|
237
|
+
return new Request(url, { method: request.method, headers, body: request.body, signal: request.signal, ...(request.body ? { duplex: 'half' } : {}) });
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/** A pack's HTTP surface, as the kernel serves it from the pack's parts. */
|
|
168
241
|
export function createPackFetch(parts, options = {}) {
|
|
169
242
|
const m = parts.manifest;
|
|
170
243
|
const scope = {
|
|
@@ -267,12 +340,10 @@ export function createPackFetch(parts, options = {}) {
|
|
|
267
340
|
return Response.json(decision.result.body, { status: decision.result.status, headers: decision.result.headers ?? {} });
|
|
268
341
|
return api(withHeaders(request, new Headers([...request.headers, [SCENARIO_DECISION_HEADER, scenarioDecisionHeader(decision)]])));
|
|
269
342
|
};
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
if (!answer)
|
|
343
|
+
for (const d of m.doors ?? [])
|
|
344
|
+
if (!parts.doors?.[d.id])
|
|
273
345
|
throw new Error(`${m.vendor}: the manifest declares the door ${d.id}, and semantics/doors.ts exports no ${d.id}`);
|
|
274
|
-
|
|
275
|
-
});
|
|
346
|
+
const door = worldDoorMatcher(m.doors ?? []);
|
|
276
347
|
const screens = (m.screens ?? []).filter((s) => s.status === 'done' && parts.screens?.[s.id]).map((s) => {
|
|
277
348
|
const hosts = [s.host, ...(s.hosts ?? [])].map(hostMatcher);
|
|
278
349
|
return { decl: s, host: (h) => hosts.map((match) => match(h)).find(Boolean), path: pathMatcher(s.path, true), answer: parts.screens[s.id] };
|
|
@@ -304,59 +375,7 @@ export function createPackFetch(parts, options = {}) {
|
|
|
304
375
|
await runAsVendorMove(async () => parts.clock(await open(new Request(request.url, m.tenant ? { headers: request.headers } : {}), 'clock', 'POST', '/_twin/clock', {})));
|
|
305
376
|
};
|
|
306
377
|
const refusedReadOnly = () => vendorError(m, m.readOnly);
|
|
307
|
-
const
|
|
308
|
-
const base = surface.basePath ?? '';
|
|
309
|
-
const baseAlias = m.basePathAlias && base ? new RegExp(`^(?:${m.basePathAlias})(?=/)`) : undefined;
|
|
310
|
-
const hostPath = m.pathFromHost ? new RegExp(m.pathFromHost, 'i') : undefined;
|
|
311
|
-
const hostParams = m.hostParams ? new RegExp(m.hostParams, 'i') : undefined;
|
|
312
|
-
// each operation's spanning parameters, keyed by the operation and the parameter as the route compiles them (a whole
|
|
313
|
-
// segment's label whose name the surface or the manifest declares spanning), so one operation's spanning name never
|
|
314
|
-
// keeps another's slashes, nor a label that only part of a segment holds
|
|
315
|
-
const spanningNames = new Set(surface.spanning ?? []);
|
|
316
|
-
const spanningOf = new Set(surface.operations.flatMap((op) => ((op.basePath ?? base) + op.path).split('/').flatMap((seg) => {
|
|
317
|
-
const label = /^\{([^}]+)\}$/.exec(seg)?.[1];
|
|
318
|
-
return label !== undefined && spanningNames.has(label) ? [`${op.id}\u0000${label}`] : [];
|
|
319
|
-
})));
|
|
320
|
-
/** A path as the vendor routes it: the manifest's path prefix taken off, the spec's base in an alias's place, a host's
|
|
321
|
-
* path label (pathFromHost) put in front. */
|
|
322
|
-
const rewritten = (pathname, host) => {
|
|
323
|
-
let path = pathname;
|
|
324
|
-
if (prefix)
|
|
325
|
-
path = path.replace(prefix, '') || '/';
|
|
326
|
-
// a base the vendor serves as its own: the spec's base in its place (a path already under the spec's base stays)
|
|
327
|
-
if (baseAlias && !(path === base || path.startsWith(`${base}/`)))
|
|
328
|
-
path = path.replace(baseAlias, base);
|
|
329
|
-
const label = hostPath?.exec(host)?.[1];
|
|
330
|
-
if (label)
|
|
331
|
-
path = `/${label}${path === '/' ? '' : path}`;
|
|
332
|
-
return path;
|
|
333
|
-
};
|
|
334
|
-
/** The request as its vendor routes it: duplicate slashes collapsed, unless the rewritten path matches an operation
|
|
335
|
-
* through one of that operation's spanning parameters (the surface's and the manifest's), whose value keeps its
|
|
336
|
-
* slashes; then rewritten. */
|
|
337
|
-
const routed = (request) => {
|
|
338
|
-
const url = new URL(request.url);
|
|
339
|
-
const host = hostOf(request);
|
|
340
|
-
const kept = rewritten(url.pathname, host);
|
|
341
|
-
const matched = matchOperation(routes, request.method, kept);
|
|
342
|
-
const spanning = matched !== undefined && Object.keys(matched.params).some((name) => spanningOf.has(`${matched.operation.id}\u0000${name}`));
|
|
343
|
-
const source = spanning ? url.pathname : url.pathname.replace(/\/{2,}/g, '/');
|
|
344
|
-
// a prefix's named groups (Jira's `/ex/jira/(?<cloudId>[^/]+)`) and the host's (E2B's envd at
|
|
345
|
-
// `49983-(?<sandboxID>…).e2b.app`) join the operation's parameters
|
|
346
|
-
const groups = { ...(prefix?.exec(source)?.groups ?? {}), ...(hostParams?.exec(host)?.groups ?? {}), ...Object.fromEntries(Object.entries(m.headerParams ?? {}).flatMap(([param, header]) => { const value = request.headers.get(header); return value === null ? [] : [[param, value]]; })) };
|
|
347
|
-
const path = spanning ? kept : rewritten(source, host);
|
|
348
|
-
if (path === url.pathname && !Object.keys(groups).length)
|
|
349
|
-
return request;
|
|
350
|
-
// the path the client sent, which a signature over the request (SigV4's canonical URI) covers
|
|
351
|
-
const headers = new Headers(request.headers);
|
|
352
|
-
// the path as first sent: a front before this (an attach bridge) may have named the one its client signed
|
|
353
|
-
if (!headers.has(ORIGINAL_PATH_HEADER))
|
|
354
|
-
headers.set(ORIGINAL_PATH_HEADER, url.pathname);
|
|
355
|
-
if (Object.keys(groups).length)
|
|
356
|
-
headers.set(PREFIX_PARAMS_HEADER, JSON.stringify(groups));
|
|
357
|
-
url.pathname = path;
|
|
358
|
-
return new Request(url, { method: request.method, headers, body: request.body, signal: request.signal, ...(request.body ? { duplex: 'half' } : {}) });
|
|
359
|
-
};
|
|
378
|
+
const routed = unitRequestRouter(m, surface, routes);
|
|
360
379
|
const rootServes = (method, path) => Boolean(matchOperation(routes, method, path));
|
|
361
380
|
const lane = m.lanes ? laneRouter(m.vendor, m.lanes, parts.lanes ?? {}, rootServes) : undefined;
|
|
362
381
|
// Inspect lane-owned World doors directly, without routing them back through the vendor gateway.
|
|
@@ -455,7 +474,7 @@ export function createPackFetch(parts, options = {}) {
|
|
|
455
474
|
if (own)
|
|
456
475
|
return own;
|
|
457
476
|
// Root World doors belong to the pack at every routed host, including a lane's host.
|
|
458
|
-
const rootDoor =
|
|
477
|
+
const rootDoor = door(arrived.method, asked);
|
|
459
478
|
const toLane = discovery || rootDoor ? undefined : lane?.(arrived);
|
|
460
479
|
if (toLane)
|
|
461
480
|
return toLane(arrived);
|
|
@@ -531,15 +550,12 @@ export function createPackFetch(parts, options = {}) {
|
|
|
531
550
|
resetFakePool(m.service, options.root);
|
|
532
551
|
return Response.json({ kind }, { status: 201 });
|
|
533
552
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
continue;
|
|
537
|
-
const hit = d.path(path);
|
|
538
|
-
if (!hit)
|
|
539
|
-
continue;
|
|
553
|
+
const hit = door(request.method, path);
|
|
554
|
+
if (hit) {
|
|
540
555
|
if (readOnly && request.method !== 'GET')
|
|
541
556
|
return refusedReadOnly();
|
|
542
|
-
|
|
557
|
+
const d = hit.door;
|
|
558
|
+
return parts.doors[d.id](await open(request, d.id, d.method, d.path, hit.params));
|
|
543
559
|
}
|
|
544
560
|
return gapAnswer(m, request);
|
|
545
561
|
}
|
|
@@ -621,7 +637,44 @@ export function createPackFetch(parts, options = {}) {
|
|
|
621
637
|
const { value: answer, appended } = await runWithCorrelationId(id, () => deferPerform(id, () => fetch(request)));
|
|
622
638
|
if (!appended)
|
|
623
639
|
return answer;
|
|
640
|
+
// a receive-pack's pushes (derived-core.ts pushedInput): what the World's refs held before and after each
|
|
641
|
+
const pushes = m.git ? deployableEntries(m.service, options.root).filter((e) => e.correlationId === id && e.input?.git).map((e) => ({ actionId: e.id, ...e.input.git })) : [];
|
|
624
642
|
const performed = await runWithCorrelationId(id, () => performRequestGroup(m.service, id, options.root));
|
|
643
|
+
// under `auto`, what the root did is what the World keeps (ADR 0031): a ref it refused goes back to its value before
|
|
644
|
+
// the push, by compare-and-set under the ref lock (a later push built on it keeps its value), its push and effects
|
|
645
|
+
// are reverted (head.ts), and the client hears git's own report, `ok` or `ng` for each ref
|
|
646
|
+
if (pushes.length) {
|
|
647
|
+
const whole = performed instanceof RefusedWriteError ? performed.reason : performed instanceof VendorWriteError ? performed.message : undefined;
|
|
648
|
+
const refused = new Map();
|
|
649
|
+
for (const p of pushes) {
|
|
650
|
+
const reason = whole ?? performed.refused?.find((r) => r.actionId === p.actionId)?.reason;
|
|
651
|
+
if (reason !== undefined)
|
|
652
|
+
refused.set(p.actionId, reason);
|
|
653
|
+
}
|
|
654
|
+
if (refused.size) {
|
|
655
|
+
for (const p of [...pushes].reverse()) {
|
|
656
|
+
if (!refused.has(p.actionId))
|
|
657
|
+
continue;
|
|
658
|
+
const { refs } = gitRepositoryOf(m, p.name, options.root);
|
|
659
|
+
refs.withLock(() => {
|
|
660
|
+
if ((refs.get(p.ref) ?? ZERO_SHA) !== p.after)
|
|
661
|
+
return;
|
|
662
|
+
if (p.before === ZERO_SHA)
|
|
663
|
+
refs.delete(p.ref);
|
|
664
|
+
else
|
|
665
|
+
refs.set(p.ref, p.before);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
const ng = new Map(pushes.filter((p) => refused.has(p.actionId)).map((p) => [p.ref, refused.get(p.actionId).replace(/\s+/g, ' ')]));
|
|
669
|
+
const report = concat(...readPktLines(new Uint8Array(await answer.arrayBuffer())).lines.map((line) => {
|
|
670
|
+
const ref = line.startsWith('ok ') ? line.slice(3).trim() : undefined;
|
|
671
|
+
return pktLine(ref !== undefined && ng.has(ref) ? `ng ${ref} ${ng.get(ref)}\n` : line);
|
|
672
|
+
}), FLUSH);
|
|
673
|
+
const headers = new Headers(answer.headers);
|
|
674
|
+
headers.delete('content-length');
|
|
675
|
+
return new Response(report, { status: answer.status, headers });
|
|
676
|
+
}
|
|
677
|
+
}
|
|
625
678
|
// the vendor's refusal, as its status and words (perform names them `<status>: <message>`), in the vendor's error body
|
|
626
679
|
if (performed instanceof RefusedWriteError) {
|
|
627
680
|
const said = /^(\d{3}): ([\s\S]*)$/.exec(performed.reason);
|
|
@@ -720,31 +773,16 @@ export function createPackFetch(parts, options = {}) {
|
|
|
720
773
|
const owners = () => flatOwners(ownersByUnit());
|
|
721
774
|
return derivedRequestScopes(m, Object.assign(served, { owners, ownersByUnit, serves, advanceClock: async (request) => runWithStorageTenant(m.service, await selectedAccount(request), () => advanceClock(request)), workspaces, boards: Boolean(parts.board), maxRequestBodySize: m.descriptor?.maxRequestBodySize, draws: drawsUrl, contentHosts, ...(upgrade ? { upgrade } : {}), ...(ingest ? { ingest } : {}) }), options.readOnly);
|
|
722
775
|
}
|
|
723
|
-
/** The
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
const served = (name) => {
|
|
727
|
-
const part = lanes[name];
|
|
728
|
-
if (!part)
|
|
729
|
-
throw new Error(`${vendor}: the manifest routes to the lane ${name}, and the vendor's fetch is given no ${name}`);
|
|
730
|
-
// the lane is named on the request, so a write it makes records which lane's API made it (LANE_HEADER)
|
|
731
|
-
const named = (request) => {
|
|
732
|
-
const headers = new Headers(request.headers);
|
|
733
|
-
headers.set(LANE_HEADER, name);
|
|
734
|
-
return part.fetch(withHeaders(request, headers));
|
|
735
|
-
};
|
|
736
|
-
return decl.cors ? withCors(decl.cors, named) : named;
|
|
737
|
-
};
|
|
738
|
-
const doors = Object.entries(lanes).flatMap(([name, part]) => (part.manifest.doors ?? []).map((d) => ({
|
|
739
|
-
name, method: d.method.toUpperCase(), matches: pathMatcher(d.path.startsWith('/_twin/') ? d.path : `/_twin/${d.path.replace(/^\//, '')}`, false),
|
|
740
|
-
})));
|
|
776
|
+
/** The lane selected by the vendor gateway, shared by serving and response judgment. */
|
|
777
|
+
export function laneSelector(decl, lanes, rootServes = () => false) {
|
|
778
|
+
const door = worldDoorMatcher(Object.entries(lanes).flatMap(([name, part]) => (part.manifest.doors ?? []).map((d) => ({ ...d, name }))));
|
|
741
779
|
const laneRoutes = decl.routes.map((r) => {
|
|
742
780
|
const at = r.path?.replace(/\/+$/, '');
|
|
743
781
|
const host = r.host !== undefined ? new RegExp(r.host, 'i') : undefined;
|
|
744
782
|
const except = (r.exceptHosts ?? []).map((h) => h.toLowerCase());
|
|
745
783
|
const own = lanes[r.lane]?.fetch.serves;
|
|
746
784
|
return {
|
|
747
|
-
lane:
|
|
785
|
+
lane: r.lane,
|
|
748
786
|
holds: (request, path, name) => (at === undefined || path === at || path.startsWith(`${at}/`))
|
|
749
787
|
&& (host === undefined || host.test(name))
|
|
750
788
|
&& (r.header === undefined || (request.headers.get(r.header.name) ?? '').startsWith(r.header.prefix) !== (r.header.absent === true))
|
|
@@ -752,7 +790,6 @@ function laneRouter(vendor, decl, lanes, rootServes = () => false) {
|
|
|
752
790
|
&& !(r.unlessOnlyRoot && rootServes(request.method, path) && !(own?.(request.method, path) ?? false)),
|
|
753
791
|
};
|
|
754
792
|
});
|
|
755
|
-
const fallback = decl.default !== undefined ? served(decl.default) : undefined;
|
|
756
793
|
return (request) => {
|
|
757
794
|
// `<base>/@<host>/<path>` at a World's place, for a host a lane's built content screen names, goes to that lane, before
|
|
758
795
|
// any lane is picked by the request's host or path: its own fetch answers it by its content route, under the vendor's
|
|
@@ -760,15 +797,37 @@ function laneRouter(vendor, decl, lanes, rootServes = () => false) {
|
|
|
760
797
|
const address = request.headers.has(TWIN_PREFIX_HEADER) ? contentAddressOf(new URL(request.url).pathname) : undefined;
|
|
761
798
|
const content = address && Object.keys(lanes).find((l) => lanes[l].fetch.contentHosts?.includes(address.host));
|
|
762
799
|
if (content)
|
|
763
|
-
return
|
|
800
|
+
return content;
|
|
764
801
|
const path = new URL(request.url).pathname.replace(/\/{2,}/g, '/');
|
|
765
802
|
if (path.startsWith('/_twin/')) {
|
|
766
|
-
const
|
|
767
|
-
if (
|
|
768
|
-
return
|
|
803
|
+
const hit = door(request.method, path);
|
|
804
|
+
if (hit)
|
|
805
|
+
return hit.door.name;
|
|
769
806
|
}
|
|
770
807
|
const name = hostOf(request);
|
|
771
|
-
return laneRoutes.find((r) => r.holds(request, path, name))?.lane ??
|
|
808
|
+
return laneRoutes.find((r) => r.holds(request, path, name))?.lane ?? decl.default;
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
/** The vendor's gateway over its lanes: the lane a request goes to (a door to the lane that declares it, then the first
|
|
812
|
+
* route whose conditions all hold, then the default), behind the gateway's CORS; undefined when none takes it. */
|
|
813
|
+
function laneRouter(vendor, decl, lanes, rootServes = () => false) {
|
|
814
|
+
const served = (name) => {
|
|
815
|
+
const part = lanes[name];
|
|
816
|
+
if (!part)
|
|
817
|
+
throw new Error(`${vendor}: the manifest routes to the lane ${name}, and the vendor's fetch is given no ${name}`);
|
|
818
|
+
// the lane is named on the request, so a write it makes records which lane's API made it (LANE_HEADER)
|
|
819
|
+
const named = (request) => {
|
|
820
|
+
const headers = new Headers(request.headers);
|
|
821
|
+
headers.set(LANE_HEADER, name);
|
|
822
|
+
return part.fetch(withHeaders(request, headers));
|
|
823
|
+
};
|
|
824
|
+
return decl.cors ? withCors(decl.cors, named) : named;
|
|
825
|
+
};
|
|
826
|
+
const selected = laneSelector(decl, lanes, rootServes);
|
|
827
|
+
const handlers = new Map([...new Set([...decl.routes.map((route) => route.lane), ...(decl.default === undefined ? [] : [decl.default]), ...Object.keys(lanes)])].map((name) => [name, served(name)]));
|
|
828
|
+
return (request) => {
|
|
829
|
+
const name = selected(request);
|
|
830
|
+
return name === undefined ? undefined : handlers.get(name);
|
|
772
831
|
};
|
|
773
832
|
}
|
|
774
833
|
/** A vendor whose every API is a lane (Cloudflare's API v4 and R2, AWS's services), served from its manifest: the
|
package/dist/src/pack-paths.d.ts
CHANGED
|
@@ -8,9 +8,8 @@ export declare function installedVendorPacks(from: string, options?: {
|
|
|
8
8
|
}): Map<string, string>;
|
|
9
9
|
/** Node's nearest installed package, including linked packages and hoisted workspace dependencies. */
|
|
10
10
|
export declare function installedPackageDir(from: string, name: string): string | undefined;
|
|
11
|
-
/**
|
|
12
|
-
*
|
|
13
|
-
* sibling checkout merely because one happens to be beside them. */
|
|
11
|
+
/** The pack repository a World is rooted in, when it is one (ADR 0034: a platform checkout finds no pack repository
|
|
12
|
+
* beside it; a pack reaches any other World as an installed or linked package). */
|
|
14
13
|
export declare function packRepository(from: string): string | undefined;
|
|
15
14
|
/** Resolve one vendor's package directory for host-side reads (assets, infrastructure launchers). */
|
|
16
15
|
export declare function vendorPackageDir(from: string, vendor: string): string | undefined;
|
package/dist/src/pack-paths.js
CHANGED
|
@@ -121,23 +121,13 @@ export function installedPackageDir(from, name) {
|
|
|
121
121
|
dir = up;
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
-
/**
|
|
125
|
-
*
|
|
126
|
-
* sibling checkout merely because one happens to be beside them. */
|
|
124
|
+
/** The pack repository a World is rooted in, when it is one (ADR 0034: a platform checkout finds no pack repository
|
|
125
|
+
* beside it; a pack reaches any other World as an installed or linked package). */
|
|
127
126
|
export function packRepository(from) {
|
|
128
127
|
let dir = resolve(from);
|
|
129
128
|
for (;;) {
|
|
130
129
|
if (packageName(dir) === 'twin-packs-p3')
|
|
131
130
|
return dir;
|
|
132
|
-
if (packageName(join(dir, 'packages', 'world-core')) === '@volter/world-core') {
|
|
133
|
-
const env = globalThis.process?.env?.VOLTER_PACKS;
|
|
134
|
-
const named = env ? resolve(env) : undefined;
|
|
135
|
-
if (named && packageName(named) === 'twin-packs-p3')
|
|
136
|
-
return named;
|
|
137
|
-
const sibling = join(dirname(dir), 'twin-packs-p3');
|
|
138
|
-
if (packageName(sibling) === 'twin-packs-p3')
|
|
139
|
-
return sibling;
|
|
140
|
-
}
|
|
141
131
|
const up = dirname(dir);
|
|
142
132
|
if (up === dir)
|
|
143
133
|
return undefined;
|
|
@@ -148,7 +138,7 @@ export function packRepository(from) {
|
|
|
148
138
|
export function vendorPackageDir(from, vendor) {
|
|
149
139
|
if (!/^[a-z][a-z0-9-]*$/.test(vendor))
|
|
150
140
|
return undefined;
|
|
151
|
-
const installed = installedVendorPacks(from).get(vendor);
|
|
141
|
+
const installed = installedVendorPacks(from, { vendors: [vendor] }).get(vendor);
|
|
152
142
|
const repo = packRepository(from);
|
|
153
143
|
const checkout = repo === undefined ? undefined : join(repo, vendor);
|
|
154
144
|
if (!checkout || packageName(checkout) !== `@volter/twin-${vendor}` || !isDerivedPack(checkout))
|
|
@@ -118,7 +118,9 @@ export declare function pullOnSchedule<T>(vendor: PullVendor, pull: () => Promis
|
|
|
118
118
|
* matches and no exclude does. Validated by scripts/pack-facts.ts: keys are `[a-z0-9-]+`, a
|
|
119
119
|
* foreign key is never another pack's vendor id, belongs to one pack, and must appear in the
|
|
120
120
|
* pack's `adoption.worldIds` (the world wires `<KEY>_TWIN_URL` from there); a pack must claim its
|
|
121
|
-
* own vendor id too; no key is exclude-only; `exclude` is `true` or absent.
|
|
121
|
+
* own vendor id too; no key is exclude-only; `exclude` is `true` or absent. `basic` is a vendor fact for the executor: on
|
|
122
|
+
* a URL the rule names, the host takes the sealed token as HTTP Basic's password under this user (a vendor's git over
|
|
123
|
+
* HTTP whose API takes the same token as a bearer; ADR 0031).
|
|
122
124
|
*/
|
|
123
125
|
export type HostRule = ({
|
|
124
126
|
host: string;
|
|
@@ -131,6 +133,7 @@ export type HostRule = ({
|
|
|
131
133
|
demandPathPattern?: string | false;
|
|
132
134
|
key?: string;
|
|
133
135
|
exclude?: true;
|
|
136
|
+
basic?: string;
|
|
134
137
|
};
|
|
135
138
|
export type RoundTripWrite = {
|
|
136
139
|
method: string;
|
|
@@ -255,6 +258,19 @@ export type TwinPack = {
|
|
|
255
258
|
scheme?: string;
|
|
256
259
|
note: string;
|
|
257
260
|
}>;
|
|
261
|
+
/** The vendor's own requests a World's generated seed makes for what its world.json declares (ADR 0033 §3), each a URL
|
|
262
|
+
* at the vendor's own host, defined by the twin's own screen form for that act: its sign-up, and its domain screen's
|
|
263
|
+
* add and verify with `record`, the one `/_twin/` stand-in, for a record at the domain's DNS host; `settles` is the
|
|
264
|
+
* World-clock time the vendor takes to settle a verified domain (`10m`). */
|
|
265
|
+
seed?: {
|
|
266
|
+
signUp?: string;
|
|
267
|
+
domain?: {
|
|
268
|
+
add: string;
|
|
269
|
+
record: string;
|
|
270
|
+
verify: string;
|
|
271
|
+
settles: string;
|
|
272
|
+
};
|
|
273
|
+
};
|
|
258
274
|
stateSystem?: StateSystemAdapters;
|
|
259
275
|
/** The pack has no vendor-backed half, and why (copied from the manifest by packOf). */
|
|
260
276
|
vendorBacked?: {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The default is Bun's existing largest plain body (128 MiB), so both runtimes decode the same.
|
|
1
|
+
/** The shared HTTP transport and compressed-request output retain Bun's 128 MiB plain-body default.
|
|
3
2
|
* Source: bun-types/serve.d.ts, ServeOptions.maxRequestBodySize (@default 1024 * 1024 * 128),
|
|
4
3
|
* https://github.com/oven-sh/bun/blob/main/packages/bun-types/serve.d.ts .
|
|
5
|
-
* A pack's descriptor.maxRequestBodySize overrides
|
|
4
|
+
* A pack's descriptor.maxRequestBodySize overrides both the transport and decoded-output limits. */
|
|
6
5
|
export declare const DEFAULT_MAX_DECODED_REQUEST_BODY_SIZE: number;
|
|
7
6
|
export declare class RequestBodyTooLargeError extends Error {
|
|
8
7
|
constructor();
|
package/dist/src/request-body.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The default is Bun's existing largest plain body (128 MiB), so both runtimes decode the same.
|
|
1
|
+
/** The shared HTTP transport and compressed-request output retain Bun's 128 MiB plain-body default.
|
|
3
2
|
* Source: bun-types/serve.d.ts, ServeOptions.maxRequestBodySize (@default 1024 * 1024 * 128),
|
|
4
3
|
* https://github.com/oven-sh/bun/blob/main/packages/bun-types/serve.d.ts .
|
|
5
|
-
* A pack's descriptor.maxRequestBodySize overrides
|
|
4
|
+
* A pack's descriptor.maxRequestBodySize overrides both the transport and decoded-output limits. */
|
|
6
5
|
export const DEFAULT_MAX_DECODED_REQUEST_BODY_SIZE = 128 * 1024 * 1024;
|
|
7
6
|
export class RequestBodyTooLargeError extends Error {
|
|
8
7
|
constructor() { super('Payload Too Large'); this.name = 'RequestBodyTooLargeError'; }
|
package/dist/src/runtime.d.ts
CHANGED
|
@@ -7,11 +7,12 @@ export { ORIGINAL_PATH_HEADER } from './sigv4.js';
|
|
|
7
7
|
export { createTwinFetchFromHandler, placedCookies, TWIN_PREFIX_HEADER, TWIN_REQUEST_SCOPES, TWIN_SITES_HEADER, TWIN_SITE_HEADER, siteLabel, siteUrlOf, twinPublicBase, twinSiteUrl, twinVendorUrl, withRequestScopes } from './twin-fetch.js';
|
|
8
8
|
export { isReadOnlyRequest, READ_ONLY_REQUEST_HEADER, ReadOnlyRequestError, runAsReadOnlyRequest, runAsVendorMove, writesRefused } from './request-scope.js';
|
|
9
9
|
export { compileSurface, createDerivedFetch, matchOperation, observeDerivedDispatch } from './derived.js';
|
|
10
|
+
export { hostOf, laneSelector, slashesMerged, unitRequestRouter, worldDoorMatcher } from './pack-fetch.js';
|
|
10
11
|
export { currentTraceparent, deliveryTraceHeaders, newTraceparent, parseTraceparent, runWithRequestTrace, runWithTraceparent, TRACEPARENT_HEADER, traceparentForDelivery, validTraceparent } from './trace-context.js';
|
|
11
12
|
export type { Traceparent } from './trace-context.js';
|
|
12
13
|
export type { DerivedCall, DerivedCoreOutcome, DerivedDispatch, DerivedFetch, DerivedFetchOptions, DerivedHandler, DerivedOperation, DerivedOwner, DerivedSurface, BoardFrame } from './derived.js';
|
|
13
14
|
export { authRefusal, bindSemantics, coreFor, crossCutting, flagOf, derivedRequestScopes, observeTransitions, resourcesOfType as twinResourcesOfType, semanticsContext, stateOf, transitionFor, parseBracketForm, readParams, render as renderDerived, serveCore, sse, vendorError } from './derived-core.js';
|
|
14
|
-
export { deliverEvents, fillEnvelope, setDeliveryObserver, signEvent, signEventAll, verifyEvent } from './events.js';
|
|
15
|
+
export { deliverEvents, fillEnvelope, setDeliveryObserver, settleEventDeliveries, signEvent, signEventAll, verifyEvent } from './events.js';
|
|
15
16
|
export { equalSecrets, hmac, jwks, jwtDecode, jwtSign, jwtSignClaims, jwtVerify, jwtVerifySignature, keyId, lettersFrom, md5, sha256, SIGNING_ALGORITHMS, uuidFrom } from './signing.js';
|
|
16
17
|
export type { HandlerCrypto, Jwk, Jwks, JwtDecodeOptions, JwtSignatureOptions, JwtVerdict, JwtVerificationKey, SigningKey } from './signing.js';
|
|
17
18
|
export type { EventDelivery, EventRender, EventScheme, EventsDecl, EventTransport, EventWrite } from './events.js';
|
|
@@ -54,6 +55,7 @@ export { installedPackageDir, installedVendorPacks, packVendor, packRepository,
|
|
|
54
55
|
export * as git from './git/index.js';
|
|
55
56
|
export * as s3 from './s3/wire.js';
|
|
56
57
|
export * as xml from './xml.js';
|
|
58
|
+
export * as html from './html.js';
|
|
57
59
|
export { PLACEHOLDER_REMOTE, beginPlaceholderPull, endPlaceholderPull, placeholderPullActive, placeholderPullMarkerPath, withPlaceholderPull, placeholderEventsFor } from './placeholder-remote.js';
|
|
58
60
|
export { aliasesFrom, ownFields, subjectHistory, rebaseBranch, cutCheckpoint, appendParentEntry, wholeLog, branchEntries, branchLogPath, branchMetaPath, CHECKPOINT_EVERY, dropCheckpoint, foldEntries, landedCopy, landedIds, parentEntries, parentLogPath, position, readBranchMeta, readTree, toEntry, toEvent, unpushedEntries, writeBranchMeta } from './log.js';
|
|
59
61
|
export type { BranchMeta, Entry, EntryOp, Receipt } from './log.js';
|
|
@@ -78,6 +80,7 @@ export { brandTokensResponse } from './brand-tokens.js';
|
|
|
78
80
|
export { multipartBoundary, multipartParts, multipartEnvelope, inferMultipartBoundary, multipartDelimiterPattern, type MultipartPart, type MultipartFramedPart, type MultipartEnvelope } from './multipart.js';
|
|
79
81
|
export { corsHeaders, withCors, type CorsDecl } from './cors.js';
|
|
80
82
|
export { setInProcessTwins, vendorFetch, VendorUnreachableError, type InProcessTwin } from './vendor-call.js';
|
|
83
|
+
export { setInProcessMailServers } from './smtp.js';
|
|
81
84
|
export { closeManagedDatabases, databaseSession, EngineDatabaseError, EngineUnavailableError, managedDatabase, quoteIdent, useDatabaseDriver, type DatabaseDriver, type DatabaseSession } from './managed-database.js';
|
|
82
85
|
export type { Batch as EngineBatch, ManagedDatabase, Statement as EngineStatement, StatementResult as EngineStatementResult } from './managed-database.js';
|
|
83
86
|
export { readResourceBlob, readResourceBlobRange, removeResourceBlob, resourceBlobFile, resourceBlobKey, resourceBlobSize, resourceChain, writeResourceBlob, writeResourceBlobStream } from './resource-blob.js';
|
package/dist/src/runtime.js
CHANGED
|
@@ -15,9 +15,10 @@ export { ORIGINAL_PATH_HEADER } from "./sigv4.js";
|
|
|
15
15
|
export { createTwinFetchFromHandler, placedCookies, TWIN_PREFIX_HEADER, TWIN_REQUEST_SCOPES, TWIN_SITES_HEADER, TWIN_SITE_HEADER, siteLabel, siteUrlOf, twinPublicBase, twinSiteUrl, twinVendorUrl, withRequestScopes } from "./twin-fetch.js";
|
|
16
16
|
export { isReadOnlyRequest, READ_ONLY_REQUEST_HEADER, ReadOnlyRequestError, runAsReadOnlyRequest, runAsVendorMove, writesRefused } from "./request-scope.js";
|
|
17
17
|
export { compileSurface, createDerivedFetch, matchOperation, observeDerivedDispatch } from "./derived.js";
|
|
18
|
+
export { hostOf, laneSelector, slashesMerged, unitRequestRouter, worldDoorMatcher } from "./pack-fetch.js";
|
|
18
19
|
export { currentTraceparent, deliveryTraceHeaders, newTraceparent, parseTraceparent, runWithRequestTrace, runWithTraceparent, TRACEPARENT_HEADER, traceparentForDelivery, validTraceparent } from "./trace-context.js";
|
|
19
20
|
export { authRefusal, bindSemantics, coreFor, crossCutting, flagOf, derivedRequestScopes, observeTransitions, resourcesOfType as twinResourcesOfType, semanticsContext, stateOf, transitionFor, parseBracketForm, readParams, render as renderDerived, serveCore, sse, vendorError } from "./derived-core.js";
|
|
20
|
-
export { deliverEvents, fillEnvelope, setDeliveryObserver, signEvent, signEventAll, verifyEvent } from "./events.js";
|
|
21
|
+
export { deliverEvents, fillEnvelope, setDeliveryObserver, settleEventDeliveries, signEvent, signEventAll, verifyEvent } from "./events.js";
|
|
21
22
|
export { equalSecrets, hmac, jwks, jwtDecode, jwtSign, jwtSignClaims, jwtVerify, jwtVerifySignature, keyId, lettersFrom, md5, sha256, SIGNING_ALGORITHMS, uuidFrom } from "./signing.js";
|
|
22
23
|
export { emitTwinEvent, eventSubscriptionMatches, listEmittable, runEmitCli } from "./emit.js";
|
|
23
24
|
// The vendor-agnostic CLIENT-SIDE RATE BUDGET — the fail-closed backstop a pack's guarded client
|
|
@@ -77,6 +78,8 @@ export * as git from "./git/index.js";
|
|
|
77
78
|
export * as s3 from "./s3/wire.js";
|
|
78
79
|
// Generic strict XML grammar; vendor interpretation remains in its consumer.
|
|
79
80
|
export * as xml from "./xml.js";
|
|
81
|
+
// Generic WHATWG HTML grammar; what a pack keeps of received HTML remains the pack's policy.
|
|
82
|
+
export * as html from "./html.js";
|
|
80
83
|
// The push arm: the kernel's transaction over a pack's one `perform<Name>Action` (contract section of that name).
|
|
81
84
|
// The placeholder remote (contract section of that name): the seed is a pull, never a pending write.
|
|
82
85
|
export { PLACEHOLDER_REMOTE, beginPlaceholderPull, endPlaceholderPull, placeholderPullActive, placeholderPullMarkerPath, withPlaceholderPull, placeholderEventsFor } from "./placeholder-remote.js";
|
|
@@ -101,6 +104,7 @@ export { brandTokensResponse } from "./brand-tokens.js";
|
|
|
101
104
|
export { multipartBoundary, multipartParts, multipartEnvelope, inferMultipartBoundary, multipartDelimiterPattern } from "./multipart.js";
|
|
102
105
|
export { corsHeaders, withCors } from "./cors.js";
|
|
103
106
|
export { setInProcessTwins, vendorFetch, VendorUnreachableError } from "./vendor-call.js";
|
|
107
|
+
export { setInProcessMailServers } from "./smtp.js";
|
|
104
108
|
export { closeManagedDatabases, databaseSession, EngineDatabaseError, EngineUnavailableError, managedDatabase, quoteIdent, useDatabaseDriver } from "./managed-database.js";
|
|
105
109
|
export { readResourceBlob, readResourceBlobRange, removeResourceBlob, resourceBlobFile, resourceBlobKey, resourceBlobSize, resourceChain, writeResourceBlob, writeResourceBlobStream } from "./resource-blob.js";
|
|
106
110
|
export { assertStateRemovable, checkParent, stateGeneration, withAncestryLock, withHistoryLock, withHistoryLocks, withStateLock, withStateRemoval } from "./ancestry.js";
|
package/dist/src/serve-http.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export type ServeHttpOptions = {
|
|
|
29
29
|
fetch: HttpHandler;
|
|
30
30
|
/** seconds a connection may idle (Bun honors it; Node's keep-alive timeout is set from it) */
|
|
31
31
|
idleTimeout?: number;
|
|
32
|
-
/** Explicit transport limit in bytes; absent,
|
|
32
|
+
/** Explicit transport limit in bytes; absent, 128 MiB on both runtimes. */
|
|
33
33
|
maxRequestBodySize?: number;
|
|
34
34
|
/** what a thrown handler answers with; absent, a 500 carrying the error (as Bun.serve) */
|
|
35
35
|
error?: (error: Error) => Response | Promise<Response>;
|
|
@@ -67,7 +67,7 @@ export declare function serveStream(options: {
|
|
|
67
67
|
maxConnections?: number;
|
|
68
68
|
}): Promise<{
|
|
69
69
|
port: number;
|
|
70
|
-
stop: () => void
|
|
70
|
+
stop: () => Promise<void>;
|
|
71
71
|
}>;
|
|
72
72
|
export type WebSocketRelay = {
|
|
73
73
|
url: string;
|