@volter/world-core 3.0.156 → 3.0.158
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/attach.cjs +2 -0
- package/capability-decision.cjs +6 -4
- package/dist/attach.cjs +2 -0
- package/dist/capability-decision.cjs +6 -4
- package/dist/generated/pack-facts.json +77 -8
- package/dist/network-policy.cjs +11 -1
- package/dist/network-policy.d.cts +1 -0
- package/dist/pack-facts.d.cts +3 -0
- package/dist/required-credential-transport.cjs +29 -0
- package/dist/required-credential-transport.d.cts +3 -0
- package/dist/src/blob-store.d.ts +16 -0
- package/dist/src/blob-store.js +76 -1
- package/dist/src/derived-core.d.ts +72 -6
- package/dist/src/derived-core.js +192 -51
- package/dist/src/derived-real.js +97 -15
- package/dist/src/derived.d.ts +5 -0
- package/dist/src/executor.d.ts +7 -0
- package/dist/src/executor.js +15 -1
- package/dist/src/git/lfs.d.ts +13 -3
- package/dist/src/git/lfs.js +41 -9
- package/dist/src/head.d.ts +2 -0
- package/dist/src/head.js +6 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/live/ingest.js +9 -13
- package/dist/src/live/rtmp.js +7 -5
- package/dist/src/live/segmenter.js +7 -1
- package/dist/src/machines.js +5 -1
- package/dist/src/managed-go-programs.d.ts +55 -0
- package/dist/src/managed-go-programs.js +133 -0
- package/dist/src/mongodb/stream.d.ts +23 -0
- package/dist/src/pack-fetch.d.ts +6 -1
- package/dist/src/pack-fetch.js +53 -7
- package/dist/src/pack-paths.d.ts +2 -1
- package/dist/src/pack-paths.js +6 -1
- package/dist/src/resource-blob.d.ts +4 -0
- package/dist/src/resource-blob.js +8 -1
- package/dist/src/runtime.d.ts +3 -3
- package/dist/src/runtime.js +2 -2
- package/dist/src/serve-http.js +53 -3
- package/dist/src/sockets.d.ts +4 -1
- package/dist/src/sockets.js +59 -6
- package/dist/src/vendor-call.js +58 -0
- package/dist/src/world-store.d.ts +15 -1
- package/dist/src/world-store.js +78 -2
- package/generated/pack-facts.json +77 -8
- package/network-policy.cjs +11 -1
- package/network-policy.d.cts +1 -0
- package/pack-facts.d.cts +3 -0
- package/package.json +8 -1
- package/required-credential-transport.cjs +29 -0
- package/required-credential-transport.d.cts +3 -0
- package/src/blob-store.ts +59 -1
- package/src/derived-core.ts +215 -51
- package/src/derived-real.ts +80 -14
- package/src/derived.ts +6 -1
- package/src/executor.ts +17 -3
- package/src/git/lfs.ts +40 -9
- package/src/head.ts +6 -0
- package/src/index.ts +1 -0
- package/src/live/ingest.ts +7 -3
- package/src/live/rtmp.ts +7 -5
- package/src/live/segmenter.ts +7 -1
- package/src/machines.ts +5 -1
- package/src/managed-go-programs.ts +146 -0
- package/src/mongodb/stream.ts +10 -0
- package/src/pack-fetch.ts +50 -9
- package/src/pack-paths.ts +5 -1
- package/src/resource-blob.ts +9 -1
- package/src/runtime.ts +4 -2
- package/src/serve-http.ts +43 -3
- package/src/sockets.ts +39 -8
- package/src/vendor-call.ts +54 -0
- package/src/world-store.ts +66 -2
package/dist/src/sockets.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// A vendor's persistent wire (architecture, "Other wires: sockets"): a WebSocket its clients hold open, on which the
|
|
2
|
+
// vendor sends what happens as it happens (Discord's Gateway: a bot identifies, is told the guilds it is in, and is sent
|
|
3
|
+
// each message, member and interaction as it lands). The manifest declares the socket (`sockets`); the pack's engine
|
|
4
|
+
// (`semantics/sockets.ts`, one export per socket id) speaks its protocol: what a session is sent when it opens, what it
|
|
5
|
+
// answers each frame, and which frames a write of the World sends it. The kernel holds the open sessions, serves the
|
|
6
|
+
// upgrade through the serve seam, and offers every write to every open session of the vendor, after the write's
|
|
7
|
+
// webhooks. A session's own counters (a sequence number, a heartbeat) are the connection's, never the World's state.
|
|
8
|
+
// Nothing here runs at module scope beyond the empty registry.
|
|
9
|
+
import { worldNow } from "./world-clock.js";
|
|
1
10
|
const sessions = new Map();
|
|
2
11
|
/** A World's next session number, per World (its service and root): a session's id is the World's, never the
|
|
3
12
|
* process's, so a life replayed on a fresh World numbers its sessions as it did the first time. */
|
|
@@ -7,6 +16,21 @@ const keyOf = (service, root) => `${service}\0${root ?? ''}`;
|
|
|
7
16
|
export function openSessions(service, root, socket) {
|
|
8
17
|
return [...(sessions.get(keyOf(service, root)) ?? [])].filter((o) => o.session.socket === socket).map((o) => o.session);
|
|
9
18
|
}
|
|
19
|
+
/** What a session awaits that the World's clock has run out (a life's `wait` past a relay's timeout), rejected as the
|
|
20
|
+
* machine's clock would reject it; called as the vendor serves a request ("A request answered by a session"). */
|
|
21
|
+
export function expireSocketWaits(service, root) {
|
|
22
|
+
const open = sessions.get(keyOf(service, root));
|
|
23
|
+
if (!open?.size)
|
|
24
|
+
return;
|
|
25
|
+
const now = Date.parse(worldNow());
|
|
26
|
+
for (const o of open)
|
|
27
|
+
for (const [k, p] of o.pending)
|
|
28
|
+
if (p.due <= now) {
|
|
29
|
+
clearTimeout(p.timer);
|
|
30
|
+
o.pending.delete(k);
|
|
31
|
+
p.reject(new Error('no reply in time'));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
10
34
|
/** Offer a write to every open session of the vendor, each in its turn after what it is already doing. Queued, never
|
|
11
35
|
* awaited: the write may be one a session's own frame made, which that session finishes before it is told. */
|
|
12
36
|
export function socketWrite(service, root, write) {
|
|
@@ -20,12 +44,41 @@ export function socketWrite(service, root, write) {
|
|
|
20
44
|
o.queue = o.queue.then(async () => engine.write(o.session, write, await o.context())).catch((error) => o.session.close(1011, String(error.message ?? error).slice(0, 120)));
|
|
21
45
|
}
|
|
22
46
|
}
|
|
47
|
+
/** A declared socket path's parameters for a request's path, or undefined when it does not match: a `{name}` segment
|
|
48
|
+
* matches one segment, as an operation's path parameter does (architecture, sockets). */
|
|
49
|
+
function socketParams(declared, path) {
|
|
50
|
+
const names = [];
|
|
51
|
+
const pattern = (declared.replace(/\/+$/, '') || '/').split(/(\{[^}/]+\})/).map((part) => {
|
|
52
|
+
const name = /^\{([^}/]+)\}$/.exec(part)?.[1];
|
|
53
|
+
if (name === undefined)
|
|
54
|
+
return part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
55
|
+
names.push(name);
|
|
56
|
+
return '([^/]+)';
|
|
57
|
+
}).join('');
|
|
58
|
+
const hit = new RegExp(`^${pattern}$`).exec(path);
|
|
59
|
+
if (!hit)
|
|
60
|
+
return undefined;
|
|
61
|
+
try {
|
|
62
|
+
return Object.fromEntries(names.map((name, i) => [name, decodeURIComponent(hit[i + 1])]));
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
23
68
|
/** The upgrade the serve seam takes: a request to a declared socket's path (and host) opens a session of its engine. */
|
|
24
69
|
export function socketUpgrade(service, root, decls, engines, context, hostOf, pathOf = (request) => new URL(request.url).pathname, decide = async () => undefined) {
|
|
25
|
-
const
|
|
70
|
+
const matchFor = (request) => {
|
|
26
71
|
const path = pathOf(request).replace(/\/+$/, '') || '/';
|
|
27
|
-
|
|
28
|
-
|
|
72
|
+
for (const d of decls) {
|
|
73
|
+
if ((d.host && d.host.toLowerCase() !== hostOf(request)) || engines[d.id] === undefined)
|
|
74
|
+
continue;
|
|
75
|
+
const params = socketParams(d.path, path);
|
|
76
|
+
if (params)
|
|
77
|
+
return { decl: d, params };
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
};
|
|
81
|
+
const declFor = (request) => matchFor(request)?.decl;
|
|
29
82
|
const byPeer = new WeakMap();
|
|
30
83
|
const key = keyOf(service, root);
|
|
31
84
|
return {
|
|
@@ -38,7 +91,7 @@ export function socketUpgrade(service, root, decls, engines, context, hostOf, pa
|
|
|
38
91
|
return declared ? offered.find((p) => declared.includes(p)) : offered[0];
|
|
39
92
|
},
|
|
40
93
|
open: (peer, request) => {
|
|
41
|
-
const decl =
|
|
94
|
+
const { decl, params } = matchFor(request);
|
|
42
95
|
const engine = engines[decl.id];
|
|
43
96
|
const pending = new Map();
|
|
44
97
|
const session = {
|
|
@@ -47,7 +100,7 @@ export function socketUpgrade(service, root, decls, engines, context, hostOf, pa
|
|
|
47
100
|
close: (code, reason) => { peer.close(code, reason); },
|
|
48
101
|
expect: (key, ms) => new Promise((resolve, reject) => {
|
|
49
102
|
const timer = setTimeout(() => { pending.delete(key); reject(new Error('no reply in time')); }, ms);
|
|
50
|
-
pending.set(key, { resolve, reject, timer });
|
|
103
|
+
pending.set(key, { resolve, reject, timer, due: Date.parse(worldNow()) + ms });
|
|
51
104
|
}),
|
|
52
105
|
fulfil: (key, value) => {
|
|
53
106
|
const p = pending.get(key);
|
|
@@ -60,7 +113,7 @@ export function socketUpgrade(service, root, decls, engines, context, hostOf, pa
|
|
|
60
113
|
},
|
|
61
114
|
decide,
|
|
62
115
|
};
|
|
63
|
-
const o = { session, engine, context: () => context(request.clone(), decl), queue: Promise.resolve(), pending };
|
|
116
|
+
const o = { session, engine, context: () => context(request.clone(), decl, params), queue: Promise.resolve(), pending };
|
|
64
117
|
byPeer.set(peer, o);
|
|
65
118
|
(sessions.get(key) ?? sessions.set(key, new Set()).get(key)).add(o);
|
|
66
119
|
if (engine.open)
|
package/dist/src/vendor-call.js
CHANGED
|
@@ -34,6 +34,52 @@ async function worldTwins() {
|
|
|
34
34
|
}
|
|
35
35
|
return { ...twinOrigins(env), ...twinOrigins(instanceEnv) };
|
|
36
36
|
}
|
|
37
|
+
/** The hosts each claiming twin last listed at its `hostsClaimed` door, by the door's address, and when; read again
|
|
38
|
+
* after CLAIMS_TTL_MS or for a host it lacks, as the injector polls the doors every five seconds (inject.cjs, "HOSTS A TWIN CLAIMS WHILE IT
|
|
39
|
+
* RUNS"). */
|
|
40
|
+
const claimsRead = new Map();
|
|
41
|
+
const CLAIMS_TTL_MS = 5_000;
|
|
42
|
+
/** The hosts a twin claims at its door: in process (a journey walk's companion) or at its address, within the injector's
|
|
43
|
+
* first-read deadline (inject.cjs CLAIMS_WAIT_MS, 1500 ms, measured beside it); a door that does not answer claims
|
|
44
|
+
* nothing. */
|
|
45
|
+
async function claimedHosts(at, ask, host) {
|
|
46
|
+
const held = claimsRead.get(at);
|
|
47
|
+
// a host the held list lacks is asked for again once, as the injector refreshes before it refuses: a domain claimed a
|
|
48
|
+
// moment ago is found
|
|
49
|
+
if (held && Date.now() - held.at < CLAIMS_TTL_MS && held.hosts.has(host))
|
|
50
|
+
return held.hosts;
|
|
51
|
+
let hosts = new Set();
|
|
52
|
+
try {
|
|
53
|
+
const r = await ask();
|
|
54
|
+
const body = r.ok ? await r.json() : null;
|
|
55
|
+
hosts = new Set((body?.hosts ?? []).map((h) => String(h).toLowerCase()));
|
|
56
|
+
}
|
|
57
|
+
catch { /* an unanswered door claims nothing */ }
|
|
58
|
+
claimsRead.set(at, { hosts, at: Date.now() });
|
|
59
|
+
return hosts;
|
|
60
|
+
}
|
|
61
|
+
/** The twin that claims a host at its `hostsClaimed` door (an R2 bucket's custom domain, a Worker's), as the injector
|
|
62
|
+
* routes it: a twin this process answers first, the World's twins after. */
|
|
63
|
+
async function claimingTwin(target, twins) {
|
|
64
|
+
const { packFacts } = await import('../pack-facts.cjs');
|
|
65
|
+
const host = target.hostname.toLowerCase();
|
|
66
|
+
const key = process.env.VOLTER_TWINS_KEY;
|
|
67
|
+
const doors = Object.entries(packFacts().packs).flatMap(([vendor, facts]) => (facts.hostsClaimed?.door ? [[vendor, facts.hostsClaimed.door]] : []));
|
|
68
|
+
for (const [vendor, door] of doors) {
|
|
69
|
+
const twin = inProcessTwins[vendor];
|
|
70
|
+
if (twin && (await claimedHosts(`in-process:${vendor}${door}`, async () => twin(new Request(`http://${vendor}.twin${door}`)), host)).has(host))
|
|
71
|
+
return { vendor, local: true };
|
|
72
|
+
}
|
|
73
|
+
for (const [vendor, door] of doors) {
|
|
74
|
+
const origin = twins[vendor];
|
|
75
|
+
if (origin === undefined)
|
|
76
|
+
continue;
|
|
77
|
+
const at = `${origin.replace(/\/$/, '')}${door}`;
|
|
78
|
+
if ((await claimedHosts(at, () => fetch(at, { headers: key ? { 'x-twins-key': key } : {}, signal: AbortSignal.timeout(1500) }), host)).has(host))
|
|
79
|
+
return { vendor, local: false };
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
37
83
|
/** One URL answered only by a World-held application or vendor twin: the fetch's application stand-in first (its answer
|
|
38
84
|
* is the application's; `'unreachable'` leaves no application to reach, the World's twins still reachable). */
|
|
39
85
|
async function worldResponse(target, init, standIn) {
|
|
@@ -61,9 +107,21 @@ async function worldResponse(target, init, standIn) {
|
|
|
61
107
|
headers.set('x-twins-key', key);
|
|
62
108
|
return fetch(`${twins[vendor].replace(/\/$/, '')}${target.pathname}${target.search}`, { ...init, headers, redirect: 'manual' });
|
|
63
109
|
}
|
|
110
|
+
// the application's own hosts before any twin's claimed ones: a call to the application asks no door
|
|
64
111
|
const { appDestination, appFetch } = await import('../app-route.cjs');
|
|
65
112
|
if (scripted !== 'unreachable' && appDestination(target))
|
|
66
113
|
return appFetch(target, { ...init, redirect: 'manual' });
|
|
114
|
+
const claimer = await claimingTwin(target, twins);
|
|
115
|
+
if (claimer !== undefined) {
|
|
116
|
+
const headers = new Headers(init.headers);
|
|
117
|
+
headers.set('x-volter-twin-original-host', target.host);
|
|
118
|
+
if (claimer.local)
|
|
119
|
+
return inProcessTwins[claimer.vendor](new Request(target, { ...init, headers, redirect: 'manual' }));
|
|
120
|
+
const key = process.env.VOLTER_TWINS_KEY;
|
|
121
|
+
if (key && !headers.has('x-twins-key'))
|
|
122
|
+
headers.set('x-twins-key', key);
|
|
123
|
+
return fetch(`${twins[claimer.vendor].replace(/\/$/, '')}${target.pathname}${target.search}`, { ...init, headers, redirect: 'manual' });
|
|
124
|
+
}
|
|
67
125
|
throw new VendorUnreachableError(`${target.host}${target.pathname}: this World holds no application or vendor twin at this URL`);
|
|
68
126
|
}
|
|
69
127
|
/** A vendor URL (or the World's application), including redirects, always answered inside the World; `standIn` is the
|
|
@@ -13,7 +13,21 @@ export type WorldLockOptions = {
|
|
|
13
13
|
operation?: string;
|
|
14
14
|
caller?: string;
|
|
15
15
|
};
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Capture at the outermost lock helper, then carry the same caller through nested helpers.
|
|
18
|
+
* LIMIT THE CAPTURE TO THE FRAMES READ (browser-substrate S2 R44, AC-LC3): V8's third frame supplies the caller,
|
|
19
|
+
* while new Error().stack normally collects up to Error.stackTraceLimit frames (ten by default).
|
|
20
|
+
* Retained browser-substrate LibreChat World-realm warm-up profiles 73/74 from runs 36c6d279 and 991f63e7e
|
|
21
|
+
* attributed about 10-57 ms self/inclusive to storageLockScope over each full 13.3-13.4 second profile window.
|
|
22
|
+
* Samples are about 5 ms apart. Those totals include this helper's capture, string and regex work; they are
|
|
23
|
+
* neither isolated capture time nor the credential doors' share. The co-located host serves doors on one loop.
|
|
24
|
+
* Original profiles are under /Users/aaronclaw/heavy-lead/librechat/logs-kept/:
|
|
25
|
+
* librechat-sn-36c6d279-e9e2632a-004051/cpuprofile/ (SHA256 prefixes 04dfc4fe, 77df6e8e) and
|
|
26
|
+
* librechat-sn-991f63e7e/cpuprofile/ (be6c6a93, 2fe80e7c), realms 73 and 74 respectively.
|
|
27
|
+
* The limit is lowered to three only for Error's own writable numeric data property above three, and restored
|
|
28
|
+
* only when lowered. Absent, accessor, non-writable and lower limits receive no writes. Existing constructor
|
|
29
|
+
* and user stack-formatter failure paths remain; this diagnostic optimization changes no lock ownership rule.
|
|
30
|
+
*/
|
|
17
31
|
export declare function storageLockScope(options?: WorldLockOptions): WorldLockOptions & {
|
|
18
32
|
operation: string;
|
|
19
33
|
caller: string;
|
package/dist/src/world-store.js
CHANGED
|
@@ -28,9 +28,77 @@ import { hostname } from 'node:os';
|
|
|
28
28
|
import { randomUUID } from 'node:crypto';
|
|
29
29
|
import { refuseReadOnlyWrite } from "./request-scope.js";
|
|
30
30
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
31
|
-
|
|
31
|
+
// WHAT A LOCK HOLD ASKS (architecture, "What a hold asks is counted"): the file
|
|
32
|
+
// store's questions counted inside an outermost lock hold and outside one, said at each outermost release where
|
|
33
|
+
// VOLTER_TWIN_LOG=1 (storage.ts twinLog's gate and line), and not counted otherwise. Each counter counts the calls of
|
|
34
|
+
// its own method: a readKept that misses its kept text, or meets a link, also counts the read it makes. A hold's
|
|
35
|
+
// `heldMs` is the time of the hold's body, run once the lock is taken and before it is released, as the lock's release
|
|
36
|
+
// line times its hold; the wait before the take is the wait line's `waitedMs`.
|
|
37
|
+
const QUESTION_KINDS = ['generation', 'read', 'readKept', 'stat', 'exists', 'list', 'append'];
|
|
38
|
+
// Read once, where a process is: a realm with none counts nothing.
|
|
39
|
+
const questionsCounted = typeof process !== 'undefined' && process.env?.VOLTER_TWIN_LOG === '1';
|
|
40
|
+
const noQuestions = () => ({ generation: 0, read: 0, readKept: 0, stat: 0, exists: 0, list: 0, append: 0 });
|
|
41
|
+
let lockHoldDepth = 0;
|
|
42
|
+
let questionsHeld = noQuestions();
|
|
43
|
+
let questionsUnheld = noQuestions();
|
|
44
|
+
function asked(kind) {
|
|
45
|
+
if (questionsCounted)
|
|
46
|
+
(lockHoldDepth > 0 ? questionsHeld : questionsUnheld)[kind] += 1;
|
|
47
|
+
}
|
|
48
|
+
function countedHold(operation, fn) {
|
|
49
|
+
if (!questionsCounted)
|
|
50
|
+
return fn;
|
|
51
|
+
return () => {
|
|
52
|
+
const began = Date.now();
|
|
53
|
+
lockHoldDepth += 1;
|
|
54
|
+
try {
|
|
55
|
+
return fn();
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
lockHoldDepth -= 1;
|
|
59
|
+
if (lockHoldDepth === 0) {
|
|
60
|
+
const held = questionsHeld, before = questionsUnheld;
|
|
61
|
+
questionsHeld = noQuestions();
|
|
62
|
+
questionsUnheld = noQuestions();
|
|
63
|
+
try {
|
|
64
|
+
console.error('[twin:store-lock]', JSON.stringify({ operation, heldMs: Date.now() - began, held, before }));
|
|
65
|
+
}
|
|
66
|
+
catch { /* an instrument */ }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Capture at the outermost lock helper, then carry the same caller through nested helpers.
|
|
73
|
+
* LIMIT THE CAPTURE TO THE FRAMES READ (browser-substrate S2 R44, AC-LC3): V8's third frame supplies the caller,
|
|
74
|
+
* while new Error().stack normally collects up to Error.stackTraceLimit frames (ten by default).
|
|
75
|
+
* Retained browser-substrate LibreChat World-realm warm-up profiles 73/74 from runs 36c6d279 and 991f63e7e
|
|
76
|
+
* attributed about 10-57 ms self/inclusive to storageLockScope over each full 13.3-13.4 second profile window.
|
|
77
|
+
* Samples are about 5 ms apart. Those totals include this helper's capture, string and regex work; they are
|
|
78
|
+
* neither isolated capture time nor the credential doors' share. The co-located host serves doors on one loop.
|
|
79
|
+
* Original profiles are under /Users/aaronclaw/heavy-lead/librechat/logs-kept/:
|
|
80
|
+
* librechat-sn-36c6d279-e9e2632a-004051/cpuprofile/ (SHA256 prefixes 04dfc4fe, 77df6e8e) and
|
|
81
|
+
* librechat-sn-991f63e7e/cpuprofile/ (be6c6a93, 2fe80e7c), realms 73 and 74 respectively.
|
|
82
|
+
* The limit is lowered to three only for Error's own writable numeric data property above three, and restored
|
|
83
|
+
* only when lowered. Absent, accessor, non-writable and lower limits receive no writes. Existing constructor
|
|
84
|
+
* and user stack-formatter failure paths remain; this diagnostic optimization changes no lock ownership rule.
|
|
85
|
+
*/
|
|
32
86
|
export function storageLockScope(options = {}) {
|
|
33
|
-
|
|
87
|
+
let frame;
|
|
88
|
+
if (!options.caller) {
|
|
89
|
+
const held = Object.getOwnPropertyDescriptor(Error, 'stackTraceLimit');
|
|
90
|
+
const lowered = held !== undefined && 'value' in held && held.writable === true
|
|
91
|
+
&& typeof held.value === 'number' && held.value > 3;
|
|
92
|
+
if (lowered)
|
|
93
|
+
Error.stackTraceLimit = 3;
|
|
94
|
+
try {
|
|
95
|
+
frame = new Error().stack?.split('\n')[3];
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
if (lowered)
|
|
99
|
+
Error.stackTraceLimit = held.value;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
34
102
|
const location = frame?.slice(frame.lastIndexOf('(') + 1).trim().replace(/^at\s+/, '');
|
|
35
103
|
const site = location?.match(/^(.*):(\d+):\d+\)?$/);
|
|
36
104
|
const caller = options.caller ?? (site ? `${site[1].replace(/^file:\/\//, '')}:${site[2]}` : 'unknown:0');
|
|
@@ -564,6 +632,7 @@ export class FsWorldStore {
|
|
|
564
632
|
// pays a kernel exchange for each, and a tree's stamp is a handful of these on every operation: Twenty's Redis twin
|
|
565
633
|
// spent 54 s of 136 in `existsSync` and `statSync` under `treeStamp`, and its server's requests waited on it.
|
|
566
634
|
read(path) {
|
|
635
|
+
asked('read');
|
|
567
636
|
try {
|
|
568
637
|
return readFileSync(path, 'utf8');
|
|
569
638
|
}
|
|
@@ -586,6 +655,7 @@ export class FsWorldStore {
|
|
|
586
655
|
*/
|
|
587
656
|
kept = new Map();
|
|
588
657
|
readKept(path) {
|
|
658
|
+
asked('readKept');
|
|
589
659
|
// A missing file is asked for without an error being made of it: a World with no parent has no branch.json, and
|
|
590
660
|
// every stamp asks.
|
|
591
661
|
let own;
|
|
@@ -639,6 +709,7 @@ export class FsWorldStore {
|
|
|
639
709
|
* World with a parent: its view and its ancestors' logs) is never kept under a generation (log.ts).
|
|
640
710
|
*/
|
|
641
711
|
generation(dirPath) {
|
|
712
|
+
asked('generation');
|
|
642
713
|
let own;
|
|
643
714
|
try {
|
|
644
715
|
own = lstatSync(join(dirPath, TREE_GENERATION), { throwIfNoEntry: false });
|
|
@@ -721,6 +792,7 @@ export class FsWorldStore {
|
|
|
721
792
|
}
|
|
722
793
|
}
|
|
723
794
|
append(path, data) {
|
|
795
|
+
asked('append');
|
|
724
796
|
refuseReadOnlyWrite('the World'); // a read-only request writes nothing, whatever path it takes to the store
|
|
725
797
|
this.kept.delete(path);
|
|
726
798
|
// A tree's file in a tab: the generation's byte, the append and the byte again as one request of the kernel
|
|
@@ -774,6 +846,7 @@ export class FsWorldStore {
|
|
|
774
846
|
});
|
|
775
847
|
}
|
|
776
848
|
exists(path) {
|
|
849
|
+
asked('exists');
|
|
777
850
|
return existsSync(path);
|
|
778
851
|
}
|
|
779
852
|
remove(path) {
|
|
@@ -787,6 +860,7 @@ export class FsWorldStore {
|
|
|
787
860
|
mkdirSync(dirPath, { recursive: true });
|
|
788
861
|
}
|
|
789
862
|
list(dirPath) {
|
|
863
|
+
asked('list');
|
|
790
864
|
try {
|
|
791
865
|
return readdirSync(dirPath);
|
|
792
866
|
}
|
|
@@ -818,6 +892,7 @@ export class FsWorldStore {
|
|
|
818
892
|
}
|
|
819
893
|
}
|
|
820
894
|
stat(path) {
|
|
895
|
+
asked('stat');
|
|
821
896
|
// The path itself first: where it is no link, that is the whole answer. A link is followed by a second call,
|
|
822
897
|
// and one that leads nowhere is absent, as it was.
|
|
823
898
|
// Asked without an error being made of a missing path (a tree's parent log is absent in a World with no parent).
|
|
@@ -852,6 +927,7 @@ export class FsWorldStore {
|
|
|
852
927
|
withLock(lockPath, fn, options = {}) {
|
|
853
928
|
const started = Date.now();
|
|
854
929
|
const scope = storageLockScope(options);
|
|
930
|
+
fn = countedHold(scope.operation, fn);
|
|
855
931
|
let fd = null;
|
|
856
932
|
// A kernel's file step takes the lock with its record in one request and keeps no descriptor (fileSteps).
|
|
857
933
|
const steps = fileSteps();
|
|
@@ -221,6 +221,50 @@
|
|
|
221
221
|
},
|
|
222
222
|
"serveExport": "createAxiomTwinServer"
|
|
223
223
|
},
|
|
224
|
+
"bitly": {
|
|
225
|
+
"adoption": {
|
|
226
|
+
"sdks": [],
|
|
227
|
+
"envStems": [
|
|
228
|
+
"BITLY",
|
|
229
|
+
"BUFFER_BITLY"
|
|
230
|
+
]
|
|
231
|
+
},
|
|
232
|
+
"hosts": [
|
|
233
|
+
{
|
|
234
|
+
"host": "api-ssl.bitly.com"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"host": "bitly.com",
|
|
238
|
+
"pathPattern": "^/oauth/authorize/?$"
|
|
239
|
+
}
|
|
240
|
+
],
|
|
241
|
+
"archetype": "crud",
|
|
242
|
+
"protocol": {
|
|
243
|
+
"declared": "3",
|
|
244
|
+
"major": 3,
|
|
245
|
+
"standing": "current"
|
|
246
|
+
},
|
|
247
|
+
"transport": "rest",
|
|
248
|
+
"resources": [
|
|
249
|
+
"group",
|
|
250
|
+
"bitlink"
|
|
251
|
+
],
|
|
252
|
+
"endpointEnvNone": "Dub uses literal vendor URLs and native fetch; the World routes them by host.",
|
|
253
|
+
"credentialDoor": {
|
|
254
|
+
"path": "/_twin/signup",
|
|
255
|
+
"body": {
|
|
256
|
+
"email": "publisher@example.test",
|
|
257
|
+
"password": "throwaway-bitly-password",
|
|
258
|
+
"login": "publisher"
|
|
259
|
+
},
|
|
260
|
+
"fill": {
|
|
261
|
+
"BITLY_CLIENT_ID": "client_id",
|
|
262
|
+
"BITLY_CLIENT_SECRET": "client_secret",
|
|
263
|
+
"BUFFER_BITLY_API_KEY": "access_token"
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
"serveExport": "createBitlyTwinServer"
|
|
267
|
+
},
|
|
224
268
|
"cerebras": {
|
|
225
269
|
"adoption": {
|
|
226
270
|
"sdks": [
|
|
@@ -270,6 +314,9 @@
|
|
|
270
314
|
{
|
|
271
315
|
"host": "api.clerk.com"
|
|
272
316
|
},
|
|
317
|
+
{
|
|
318
|
+
"host": "dashboard.clerk.com"
|
|
319
|
+
},
|
|
273
320
|
{
|
|
274
321
|
"suffix": ".clerk.accounts.dev"
|
|
275
322
|
},
|
|
@@ -594,7 +641,7 @@
|
|
|
594
641
|
},
|
|
595
642
|
"transport": "rest",
|
|
596
643
|
"resources": [],
|
|
597
|
-
"endpointEnvNone": "
|
|
644
|
+
"endpointEnvNone": "No scanned application calls Deepgram; the World claims api.deepgram.com",
|
|
598
645
|
"serveExport": "createDeepgramTwinServer"
|
|
599
646
|
},
|
|
600
647
|
"discord": {
|
|
@@ -842,7 +889,7 @@
|
|
|
842
889
|
},
|
|
843
890
|
"transport": "rest",
|
|
844
891
|
"resources": [],
|
|
845
|
-
"endpointEnvNone": "
|
|
892
|
+
"endpointEnvNone": "the customer life calls https://fal.run/fal-ai/<model>; the World claims the hosts",
|
|
846
893
|
"credentialDoor": {
|
|
847
894
|
"path": "/_twin/app-credentials",
|
|
848
895
|
"body": {},
|
|
@@ -1012,7 +1059,7 @@
|
|
|
1012
1059
|
},
|
|
1013
1060
|
{
|
|
1014
1061
|
"host": "github.com",
|
|
1015
|
-
"pathPattern": "^/login|^/session|^/settings/|^/organizations/|^/apps/|^/[^/]+/[^/]+/(?:info/refs|git-upload-pack|git-receive-pack)$|^/[^/]+/[^/]+\\.git
|
|
1062
|
+
"pathPattern": "^/login|^/session|^/settings/|^/organizations/|^/apps/|^/[^/]+/[^/]+/(?:info/refs|git-upload-pack|git-receive-pack)$|^/[^/]+/[^/]+\\.git/|^/[^/]+/[^/]+/?$|^/[^/]+/[^/]+/(?:issues|pulls?|blob|raw|tree|commits?|compare)(?:/|$)"
|
|
1016
1063
|
}
|
|
1017
1064
|
],
|
|
1018
1065
|
"archetype": "crud",
|
|
@@ -1088,7 +1135,7 @@
|
|
|
1088
1135
|
},
|
|
1089
1136
|
{
|
|
1090
1137
|
"host": "fonts.googleapis.com",
|
|
1091
|
-
"pathPattern": "^/
|
|
1138
|
+
"pathPattern": "^/css2?$"
|
|
1092
1139
|
},
|
|
1093
1140
|
{
|
|
1094
1141
|
"host": "fonts.gstatic.com",
|
|
@@ -1417,8 +1464,7 @@
|
|
|
1417
1464
|
"host": "api.linear.app"
|
|
1418
1465
|
},
|
|
1419
1466
|
{
|
|
1420
|
-
"host": "linear.app"
|
|
1421
|
-
"pathPattern": "^/oauth/"
|
|
1467
|
+
"host": "linear.app"
|
|
1422
1468
|
}
|
|
1423
1469
|
],
|
|
1424
1470
|
"archetype": "crud",
|
|
@@ -1438,7 +1484,7 @@
|
|
|
1438
1484
|
"comment",
|
|
1439
1485
|
"issue_label"
|
|
1440
1486
|
],
|
|
1441
|
-
"endpointEnvNone": "Linear uses api.linear.app/graphql and linear.app
|
|
1487
|
+
"endpointEnvNone": "Linear uses api.linear.app/graphql and linear.app for its workspace and OAuth authorize page; the World claims those hosts",
|
|
1442
1488
|
"credentialDoor": {
|
|
1443
1489
|
"path": "/_twin/local-credentials",
|
|
1444
1490
|
"body": {},
|
|
@@ -2048,6 +2094,10 @@
|
|
|
2048
2094
|
"host": "reddit.com",
|
|
2049
2095
|
"demandPathPattern": "^/api/"
|
|
2050
2096
|
},
|
|
2097
|
+
{
|
|
2098
|
+
"host": "old.reddit.com",
|
|
2099
|
+
"demandPathPattern": "^/api/"
|
|
2100
|
+
},
|
|
2051
2101
|
{
|
|
2052
2102
|
"host": "reddit-uploaded-media.s3-accelerate.amazonaws.com"
|
|
2053
2103
|
},
|
|
@@ -2163,6 +2213,10 @@
|
|
|
2163
2213
|
},
|
|
2164
2214
|
{
|
|
2165
2215
|
"host": "inbound-cdn.resend.com"
|
|
2216
|
+
},
|
|
2217
|
+
{
|
|
2218
|
+
"host": "resend.com",
|
|
2219
|
+
"pathPattern": "^/emails(?:/|$)"
|
|
2166
2220
|
}
|
|
2167
2221
|
],
|
|
2168
2222
|
"archetype": "crud",
|
|
@@ -2319,6 +2373,9 @@
|
|
|
2319
2373
|
]
|
|
2320
2374
|
},
|
|
2321
2375
|
"hosts": [
|
|
2376
|
+
{
|
|
2377
|
+
"host": "app.slack.com"
|
|
2378
|
+
},
|
|
2322
2379
|
{
|
|
2323
2380
|
"host": "slack.com"
|
|
2324
2381
|
},
|
|
@@ -2697,7 +2754,7 @@
|
|
|
2697
2754
|
"publish",
|
|
2698
2755
|
"video"
|
|
2699
2756
|
],
|
|
2700
|
-
"endpointEnvNone": "
|
|
2757
|
+
"endpointEnvNone": "an app calls open.tiktokapis.com and sends people to www.tiktok.com; the World claims the hosts",
|
|
2701
2758
|
"credentialDoor": {
|
|
2702
2759
|
"path": "/_twin/app-credentials",
|
|
2703
2760
|
"body": {},
|
|
@@ -3280,6 +3337,18 @@
|
|
|
3280
3337
|
"_code_challenge",
|
|
3281
3338
|
"upload"
|
|
3282
3339
|
],
|
|
3340
|
+
"credentialDoor": {
|
|
3341
|
+
"path": "/_twin/app-credentials",
|
|
3342
|
+
"body": {},
|
|
3343
|
+
"fill": {
|
|
3344
|
+
"TWITTER_API_KEY": "consumer_key",
|
|
3345
|
+
"TWITTER_API_SECRET": "consumer_secret",
|
|
3346
|
+
"TWITTER_ACCESS_TOKEN": "access_token",
|
|
3347
|
+
"TWITTER_ACCESS_SECRET": "access_token_secret",
|
|
3348
|
+
"TWITTER_CLIENT_ID": "client_id",
|
|
3349
|
+
"TWITTER_CLIENT_SECRET": "client_secret"
|
|
3350
|
+
}
|
|
3351
|
+
},
|
|
3283
3352
|
"serveExport": "createXTwinServer"
|
|
3284
3353
|
},
|
|
3285
3354
|
"xai": {
|
package/network-policy.cjs
CHANGED
|
@@ -140,6 +140,16 @@ function allowsWorldNetworkEgress(policy, value, method = 'GET') {
|
|
|
140
140
|
return granted(policy.egress, to) || (policy.phase === 'build' && policy.publicReads === true && PUBLIC_BUILD_READS.includes(to.url.origin));
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/** Whether the config grants this request by name (`egress` for a read, `writes` for any method): the grant that takes a
|
|
144
|
+
* build World's request past a twinned host's twin to its real origin. The unlisted public build reads are not one:
|
|
145
|
+
* on a twinned host the twin answers them (architecture, "Reads are granted generously, writes narrowly"). */
|
|
146
|
+
function explicitlyGrantsWorldNetworkEgress(policy, value, method = 'GET') {
|
|
147
|
+
const to = destination(value, true);
|
|
148
|
+
if (!to) return false;
|
|
149
|
+
if (granted(policy.writes ?? [], to)) return true;
|
|
150
|
+
return READ_METHODS.includes(String(method).toUpperCase()) && granted(policy.egress, to);
|
|
151
|
+
}
|
|
152
|
+
|
|
143
153
|
function worldNetworkUsesPassthrough(policy, value) {
|
|
144
154
|
const to = destination(value, true);
|
|
145
155
|
if (!to || policy.phase !== 'build') return false;
|
|
@@ -222,4 +232,4 @@ function worldEgressRefusal(value, env = process.env, method = 'GET') {
|
|
|
222
232
|
return answer.outcome === 'refuse' ? `${answer.reason} (${refusalSide(answer)})` : null;
|
|
223
233
|
}
|
|
224
234
|
|
|
225
|
-
module.exports = { worldAttachmentNetworkEnv, worldStrictEgress, worldGrantFromEnv, PUBLIC_BUILD_READS, READ_METHODS, intersectWorldNetwork, worldNetworkNeedsPathInspection, worldNetworkUsesPassthrough, isWorldInternalHost, worldEgressRefusal, WORLD_NETWORK_POLICY_ENV, normalizeWorldNetwork, createWorldNetworkPolicy, parseWorldNetworkPolicy, worldNetworkPolicyFromEnv, allowsWorldNetworkEgress };
|
|
235
|
+
module.exports = { worldAttachmentNetworkEnv, worldStrictEgress, worldGrantFromEnv, PUBLIC_BUILD_READS, READ_METHODS, intersectWorldNetwork, worldNetworkNeedsPathInspection, worldNetworkUsesPassthrough, isWorldInternalHost, worldEgressRefusal, WORLD_NETWORK_POLICY_ENV, normalizeWorldNetwork, createWorldNetworkPolicy, parseWorldNetworkPolicy, worldNetworkPolicyFromEnv, allowsWorldNetworkEgress, explicitlyGrantsWorldNetworkEgress };
|
package/network-policy.d.cts
CHANGED
|
@@ -10,6 +10,7 @@ export function worldNetworkPolicyFromEnv(env: Readonly<Record<string, string |
|
|
|
10
10
|
export const READ_METHODS: readonly string[];
|
|
11
11
|
export const PUBLIC_BUILD_READS: readonly string[];
|
|
12
12
|
export function allowsWorldNetworkEgress(policy: WorldNetworkPolicy, value: string | URL, method?: string): boolean;
|
|
13
|
+
export function explicitlyGrantsWorldNetworkEgress(policy: WorldNetworkPolicy, value: string | URL, method?: string): boolean;
|
|
13
14
|
export function isWorldInternalHost(hostname: string): boolean;
|
|
14
15
|
export function worldEgressRefusal(value: string | URL, env?: Readonly<Record<string, string | undefined>>, method?: string): string | null;
|
|
15
16
|
export function worldNetworkNeedsPathInspection(policy: WorldNetworkPolicy, value: string | URL): boolean;
|
package/pack-facts.d.cts
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/world-core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.158",
|
|
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",
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
"attach.cjs",
|
|
26
26
|
"stream-bridge.cjs",
|
|
27
27
|
"http-bridge.cjs",
|
|
28
|
+
"required-credential-transport.cjs",
|
|
29
|
+
"required-credential-transport.d.cts",
|
|
28
30
|
"network-policy.cjs",
|
|
29
31
|
"network-policy.d.cts",
|
|
30
32
|
"capability-decision.cjs",
|
|
@@ -34,6 +36,7 @@
|
|
|
34
36
|
"host-routing.cjs",
|
|
35
37
|
"host-routing.d.cts",
|
|
36
38
|
"pack-facts.cjs",
|
|
39
|
+
"pack-facts.d.cts",
|
|
37
40
|
"vendor-hosts.d.cts",
|
|
38
41
|
"world-clock.cjs",
|
|
39
42
|
"world-clock.d.cts",
|
|
@@ -89,6 +92,10 @@
|
|
|
89
92
|
"./attach": "./attach.cjs",
|
|
90
93
|
"./stream-bridge": "./stream-bridge.cjs",
|
|
91
94
|
"./http-bridge": "./http-bridge.cjs",
|
|
95
|
+
"./required-credential-transport": {
|
|
96
|
+
"types": "./required-credential-transport.d.cts",
|
|
97
|
+
"default": "./required-credential-transport.cjs"
|
|
98
|
+
},
|
|
92
99
|
"./network-policy": {
|
|
93
100
|
"types": "./network-policy.d.cts",
|
|
94
101
|
"default": "./network-policy.cjs"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Metadata-only admission for native machine backings. Imports start no process and read no file.
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
exports.refuseRequiredMachineTransport = function refuseRequiredMachineTransport(source, transport) {
|
|
5
|
+
const path = source.VOLTER_WORLD_INSTANCE;
|
|
6
|
+
if (!path) {
|
|
7
|
+
if (source.VOLTER_WORLD) throw new Error('World machine execution has no retained credential declaration');
|
|
8
|
+
return; // No registered World declaration is asserted by the standalone provider.
|
|
9
|
+
}
|
|
10
|
+
const instance = JSON.parse(require('node:fs').readFileSync(path, 'utf8'));
|
|
11
|
+
if (!instance || typeof instance !== 'object' || Array.isArray(instance)
|
|
12
|
+
|| !instance.env || instance.env.VOLTER_WORLD_INSTANCE !== path || instance.env.VOLTER_WORLD !== source.VOLTER_WORLD
|
|
13
|
+
|| !Object.hasOwn(instance, 'requiredCredentials')) throw new Error('World machine boot lacks its own retained credential declaration');
|
|
14
|
+
const record = instance.requiredCredentials;
|
|
15
|
+
if (!record || typeof record !== 'object' || Array.isArray(record)
|
|
16
|
+
|| Object.keys(record).some(key => !['version', 'owners', 'valuesSha256'].includes(key))
|
|
17
|
+
|| !Object.hasOwn(record, 'version') || record.version !== 1 || !Object.hasOwn(record, 'owners')
|
|
18
|
+
|| !record.owners || typeof record.owners !== 'object' || Array.isArray(record.owners))
|
|
19
|
+
throw new Error('World machine credential declaration has an unsupported version or shape');
|
|
20
|
+
if (Object.keys(record.owners).length) {
|
|
21
|
+
const error = new Error(`${transport} does not carry required credential bindings`);
|
|
22
|
+
error.code = 'WORLD_MACHINE_CAPABILITY_GAP';
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
if (Object.hasOwn(record, 'valuesSha256')) {
|
|
26
|
+
const empty = require('node:crypto').createHash('sha256').update('[]').digest('hex');
|
|
27
|
+
if (record.valuesSha256 !== empty) throw new Error('World machine empty credential declaration changed');
|
|
28
|
+
}
|
|
29
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Refuse a registered World's required declarations before a backing with no credential transport acts.
|
|
2
|
+
* Reads only the actual instance named by the provider's own environment; standalone providers assert no World proof. */
|
|
3
|
+
export declare function refuseRequiredMachineTransport(source: Readonly<Record<string, string | undefined>>, transport: string): void;
|