@volter/world-core 2.0.37 → 3.0.1

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.
Files changed (205) hide show
  1. package/README.md +4 -5
  2. package/app-route.cjs +12 -6
  3. package/app-route.d.cts +1 -1
  4. package/dist/app-route.cjs +12 -6
  5. package/dist/app-route.d.cts +1 -1
  6. package/dist/generated/pack-facts.json +1410 -3069
  7. package/dist/inject.cjs +64 -9
  8. package/dist/pack-facts.cjs +44 -0
  9. package/dist/src/actions.d.ts +3 -3
  10. package/dist/src/actions.js +22 -16
  11. package/dist/src/ancestry.d.ts +14 -2
  12. package/dist/src/ancestry.js +92 -2
  13. package/dist/src/anthropic-wire.d.ts +39 -0
  14. package/dist/src/anthropic-wire.js +136 -0
  15. package/dist/src/bytes.d.ts +7 -0
  16. package/dist/src/bytes.js +35 -0
  17. package/dist/src/changeset.d.ts +1 -1
  18. package/dist/src/changeset.js +0 -0
  19. package/dist/src/clickhouse/index.d.ts +3 -0
  20. package/dist/src/clickhouse/index.js +6 -0
  21. package/dist/src/clickhouse/sql.d.ts +233 -0
  22. package/dist/src/clickhouse/sql.js +4329 -0
  23. package/dist/src/clickhouse/types.d.ts +18 -0
  24. package/dist/src/clickhouse/types.js +47 -0
  25. package/dist/src/clickhouse/values.d.ts +146 -0
  26. package/dist/src/clickhouse/values.js +858 -0
  27. package/dist/src/client-bundle.js +2 -3
  28. package/dist/src/cors.d.ts +15 -0
  29. package/dist/src/cors.js +31 -0
  30. package/dist/src/derived-core.d.ts +487 -24
  31. package/dist/src/derived-core.js +788 -144
  32. package/dist/src/derived-real.d.ts +13 -0
  33. package/dist/src/derived-real.js +518 -0
  34. package/dist/src/derived.d.ts +35 -1
  35. package/dist/src/derived.js +61 -9
  36. package/dist/src/emit.js +1 -2
  37. package/dist/src/events.d.ts +206 -0
  38. package/dist/src/events.js +341 -0
  39. package/dist/src/executor.d.ts +3 -0
  40. package/dist/src/executor.js +19 -2
  41. package/dist/src/file-response.d.ts +6 -0
  42. package/dist/src/file-response.js +30 -0
  43. package/dist/src/fork.js +3 -2
  44. package/dist/src/git/history.d.ts +7 -0
  45. package/dist/src/git/history.js +24 -0
  46. package/dist/src/git/index.d.ts +1 -0
  47. package/dist/src/git/index.js +1 -0
  48. package/dist/src/git/lfs.d.ts +28 -0
  49. package/dist/src/git/lfs.js +66 -0
  50. package/dist/src/git/objects.js +3 -8
  51. package/dist/src/git/smart-http.d.ts +3 -1
  52. package/dist/src/git/smart-http.js +67 -6
  53. package/dist/src/graphql-wire.d.ts +29 -0
  54. package/dist/src/graphql-wire.js +101 -0
  55. package/dist/src/grpc-wire.d.ts +67 -0
  56. package/dist/src/grpc-wire.js +170 -0
  57. package/dist/src/h2.d.ts +40 -0
  58. package/dist/src/h2.js +656 -0
  59. package/dist/src/head.d.ts +32 -3
  60. package/dist/src/head.js +161 -40
  61. package/dist/src/history.d.ts +1 -1
  62. package/dist/src/history.js +6 -6
  63. package/dist/src/hpack.json +1 -0
  64. package/dist/src/index.d.ts +64 -75
  65. package/dist/src/index.js +58 -101
  66. package/dist/src/log.js +28 -19
  67. package/dist/src/machines.d.ts +50 -0
  68. package/dist/src/machines.js +151 -0
  69. package/dist/src/managed-database.d.ts +86 -0
  70. package/dist/src/managed-database.js +283 -0
  71. package/dist/src/multipart.d.ts +11 -0
  72. package/dist/src/multipart.js +51 -0
  73. package/dist/src/observe.d.ts +15 -5
  74. package/dist/src/observe.js +23 -9
  75. package/dist/src/openai-wire.d.ts +108 -0
  76. package/dist/src/openai-wire.js +337 -0
  77. package/dist/src/pack-assets.d.ts +3 -4
  78. package/dist/src/pack-assets.js +15 -10
  79. package/dist/src/pack-fetch.d.ts +77 -0
  80. package/dist/src/pack-fetch.js +449 -0
  81. package/dist/src/pack-paths.d.ts +12 -0
  82. package/dist/src/pack-paths.js +86 -0
  83. package/dist/src/packRegistry.d.ts +69 -162
  84. package/dist/src/packRegistry.js +55 -20
  85. package/dist/src/people.d.ts +13 -0
  86. package/dist/src/people.js +18 -0
  87. package/dist/src/placeholder-image.d.ts +5 -0
  88. package/dist/src/placeholder-image.js +114 -0
  89. package/dist/src/protobuf.d.ts +28 -0
  90. package/dist/src/protobuf.js +332 -0
  91. package/dist/src/redis/engine.js +1 -1
  92. package/dist/src/request-scope.d.ts +1 -1
  93. package/dist/src/request-scope.js +6 -4
  94. package/dist/src/resource-blob.d.ts +5 -0
  95. package/dist/src/resource-blob.js +11 -0
  96. package/dist/src/runtime.d.ts +85 -0
  97. package/dist/src/runtime.js +104 -0
  98. package/dist/src/s3/wire.d.ts +60 -0
  99. package/dist/src/s3/wire.js +157 -0
  100. package/dist/src/scenario.d.ts +3 -0
  101. package/dist/src/scenario.js +2 -0
  102. package/dist/src/schema-sample.d.ts +1 -0
  103. package/dist/src/schema-sample.js +21 -0
  104. package/dist/src/sealed-box.d.ts +14 -0
  105. package/dist/src/sealed-box.js +225 -0
  106. package/dist/src/serve-http.d.ts +14 -0
  107. package/dist/src/serve-http.js +27 -3
  108. package/dist/src/serve.d.ts +6 -0
  109. package/dist/src/serve.js +69 -14
  110. package/dist/src/signing.d.ts +135 -0
  111. package/dist/src/signing.js +222 -0
  112. package/dist/src/sigv4.d.ts +48 -0
  113. package/dist/src/sigv4.js +167 -0
  114. package/dist/src/smtp.d.ts +16 -0
  115. package/dist/src/smtp.js +72 -0
  116. package/dist/src/sockets.d.ts +51 -0
  117. package/dist/src/sockets.js +90 -0
  118. package/dist/src/state-system.d.ts +1 -0
  119. package/dist/src/state-system.js +1 -1
  120. package/dist/src/storage.d.ts +1 -1
  121. package/dist/src/storage.js +3 -3
  122. package/dist/src/trace-context.js +1 -1
  123. package/dist/src/twin-fetch.d.ts +0 -7
  124. package/dist/src/twin-fetch.js +0 -14
  125. package/dist/src/vendor-call.d.ts +6 -0
  126. package/dist/src/vendor-call.js +41 -0
  127. package/dist/src/world-store.js +1 -1
  128. package/dist/vendor-hosts.cjs +36 -125
  129. package/dist/vendor-hosts.d.cts +8 -0
  130. package/generated/pack-facts.json +1410 -3069
  131. package/inject.cjs +64 -9
  132. package/pack-facts.cjs +44 -0
  133. package/package.json +17 -3
  134. package/src/actions.ts +23 -16
  135. package/src/ancestry.ts +74 -2
  136. package/src/anthropic-wire.ts +137 -0
  137. package/src/bytes.ts +42 -0
  138. package/src/changeset.ts +5 -5
  139. package/src/clickhouse/index.ts +6 -0
  140. package/src/clickhouse/sql.ts +3059 -0
  141. package/src/clickhouse/types.ts +44 -0
  142. package/src/clickhouse/values.ts +697 -0
  143. package/src/client-bundle.ts +2 -3
  144. package/src/cors.ts +34 -0
  145. package/src/derived-core.ts +1013 -146
  146. package/src/derived-real.ts +434 -0
  147. package/src/derived.ts +73 -3
  148. package/src/emit.ts +1 -2
  149. package/src/events.ts +449 -0
  150. package/src/executor.ts +24 -2
  151. package/src/file-response.ts +27 -0
  152. package/src/fork.ts +3 -2
  153. package/src/git/history.ts +19 -0
  154. package/src/git/index.ts +1 -0
  155. package/src/git/lfs.ts +67 -0
  156. package/src/git/objects.ts +3 -5
  157. package/src/git/smart-http.ts +56 -6
  158. package/src/graphql-wire.ts +106 -0
  159. package/src/grpc-wire.ts +159 -0
  160. package/src/h2.ts +627 -0
  161. package/src/head.ts +132 -41
  162. package/src/history.ts +6 -6
  163. package/src/hpack.json +1 -0
  164. package/src/index.ts +82 -329
  165. package/src/log.ts +27 -18
  166. package/src/machines.ts +151 -0
  167. package/src/managed-database.ts +299 -0
  168. package/src/multipart.ts +51 -0
  169. package/src/observe.ts +31 -15
  170. package/src/openai-wire.ts +371 -0
  171. package/src/pack-assets.ts +15 -11
  172. package/src/pack-fetch.ts +458 -0
  173. package/src/pack-paths.ts +72 -0
  174. package/src/packRegistry.ts +79 -167
  175. package/src/people.ts +31 -0
  176. package/src/placeholder-image.ts +88 -0
  177. package/src/protobuf.ts +251 -0
  178. package/src/redis/engine.ts +1 -1
  179. package/src/request-scope.ts +8 -4
  180. package/src/resource-blob.ts +13 -0
  181. package/src/runtime.ts +344 -0
  182. package/src/s3/wire.ts +172 -0
  183. package/src/scenario.ts +4 -0
  184. package/src/schema-sample.ts +24 -0
  185. package/src/sealed-box.ts +182 -0
  186. package/src/serve-http.ts +31 -3
  187. package/src/serve.ts +58 -14
  188. package/src/signing.ts +231 -0
  189. package/src/sigv4.ts +158 -0
  190. package/src/smtp.ts +76 -0
  191. package/src/sockets.ts +140 -0
  192. package/src/state-system.ts +2 -2
  193. package/src/storage.ts +3 -3
  194. package/src/trace-context.ts +1 -1
  195. package/src/twin-fetch.ts +0 -20
  196. package/src/vendor-call.ts +41 -0
  197. package/src/world-store.ts +1 -1
  198. package/vendor-hosts.cjs +36 -125
  199. package/vendor-hosts.d.cts +8 -0
  200. package/dist/src/mirror-shell.d.ts +0 -2
  201. package/dist/src/mirror-shell.js +0 -13
  202. package/dist/src/v1-removed.d.ts +0 -159
  203. package/dist/src/v1-removed.js +0 -124
  204. package/src/mirror-shell.ts +0 -15
  205. package/src/v1-removed.ts +0 -172
@@ -0,0 +1,449 @@
1
+ // A Protocol 3 pack's whole HTTP surface, served by the kernel from the pack's declarations (docs/contributing/
2
+ // architecture.md, "The pack contract: the kernel's root"): the discovery door (`GET /twin`, the manifest's
3
+ // `discovery`), the vendor's own moves as time passes (`semantics/clock.ts`), the World's doors (the manifest's `doors`,
4
+ // answered by `semantics/doors.ts`), the vendor's screens (the manifest's `screens`, each answered by its
5
+ // `screens/<id>.tsx`), and the API (the derived dispatch over the generated surface, the handlers and the core). Every
6
+ // one of them is answered over the contract's context, which the kernel opens: a pack opens none, reads the tree and
7
+ // writes it only through one.
8
+ import { ORIGINAL_PATH_HEADER } from "./sigv4.js";
9
+ import { TWIN_PREFIX_HEADER } from "./twin-fetch.js";
10
+ import { PREFIX_PARAMS_HEADER } from "./derived.js";
11
+ import { authRefusal, bindSemantics, contextFor, LANE_HEADER, SCENARIO_DECISION_HEADER, coreFor, crossCutting, derivedRequestScopes, vendorError } from "./derived-core.js";
12
+ import { compileSurface, createDerivedFetch, matchOperation } from "./derived.js";
13
+ import { isReadOnlyRequest, READ_ONLY_REQUEST_HEADER, runAsVendorMove } from "./request-scope.js";
14
+ import { parseScenarioDocument, ScenarioEngine, ScenarioError, statefulTwinManifest, twinManifest } from "./scenario.js";
15
+ import { getActiveWorldStore } from "./world-store.js";
16
+ import { runWithCorrelationId } from "./actions.js";
17
+ import { deferPerform, performRequestGroup, RefusedWriteError, VendorWriteError } from "./head.js";
18
+ import { withCors } from "./cors.js";
19
+ import { socketUpgrade } from "./sockets.js";
20
+ import { MACHINE_POOL, POOL_KINDS } from "./machines.js";
21
+ import { applyTwinWrite } from "./serve.js";
22
+ import { graphqlEndpoint } from "./graphql-wire.js";
23
+ import { brotliDecompressSync, gunzipSync, gzipSync, inflateSync, zstdDecompressSync } from 'node:zlib';
24
+ /** A declared path (`/{account}/r2/api-tokens`, `/<room>`, a door's `/_twin/users/{email}`) as a matcher of a request's
25
+ * path: the path itself, or `prefix` a path under it too (a screen answers its page and the forms it posts). */
26
+ function pathMatcher(pattern, prefix) {
27
+ const names = [];
28
+ const body = pattern.replace(/\/+$/, '').split('/').map((seg) => {
29
+ const m = /^(?:\{([^}]+)\}|<([^>]+)>|:(\w+))$/.exec(seg);
30
+ if (!m)
31
+ return seg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
32
+ names.push(m[1] ?? m[2] ?? m[3]);
33
+ return '([^/]+)';
34
+ }).join('/');
35
+ const re = new RegExp(`^${body}${prefix ? '(?:/.*)?' : ''}/?$`);
36
+ return (path) => {
37
+ const hit = re.exec(path);
38
+ return hit ? { params: Object.fromEntries(names.map((n, i) => [n, decodeURIComponent(hit[i + 1])])) } : undefined;
39
+ };
40
+ }
41
+ /** A declared host (`dash.cloudflare.com`, `<domain>.daily.co`) as a matcher of a request's host. */
42
+ function hostMatcher(pattern) {
43
+ const names = [];
44
+ const re = new RegExp(`^${pattern.split('.').map((label) => {
45
+ const m = /^(?:\{([^}]+)\}|<([^>]+)>)$/.exec(label);
46
+ if (!m)
47
+ return label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
48
+ names.push(m[1] ?? m[2]);
49
+ return '([^.]+)';
50
+ }).join('\\.')}$`, 'i');
51
+ return (host) => {
52
+ const hit = re.exec(host);
53
+ return hit ? { params: Object.fromEntries(names.map((n, i) => [n, hit[i + 1]])) } : undefined;
54
+ };
55
+ }
56
+ /** The host a request is for: the vendor host the World's injector names, else its own. */
57
+ const hostOf = (request) => (request.headers.get('x-volter-twin-original-host') ?? request.headers.get('host') ?? new URL(request.url).host).split(':')[0].toLowerCase();
58
+ /** The answer for a path no operation, door or screen serves, and for an operation nothing models: the manifest's
59
+ * `gap`, its `{method}` and `{path}` filled, else the manifest's not-found refusal. */
60
+ function gapAnswer(m, request, otherMethod = false) {
61
+ const nf = m.notFound;
62
+ const gap = (otherMethod ? m.wrongMethod : undefined) ?? m.gap ?? { status: nf.status, message: nf.message, ...(nf.code !== undefined ? { code: nf.code } : {}), ...(nf.kind !== undefined ? { kind: nf.kind } : {}) };
63
+ const path = new URL(request.url).pathname;
64
+ return vendorError(m, { ...gap, message: gap.message.replace(/\{method\}/g, request.method).replace(/\{path\}/g, path) });
65
+ }
66
+ const METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'];
67
+ const DECODE = { gzip: gunzipSync, zstd: zstdDecompressSync, deflate: inflateSync, br: brotliDecompressSync };
68
+ /** A request with its body decoded as its Content-Encoding says, when the manifest's `encodings` reads that coding;
69
+ * undefined when the bytes are not what the header names. */
70
+ async function decoded(m, request) {
71
+ const coding = (request.headers.get('content-encoding') ?? '').trim().toLowerCase();
72
+ if (!m.encodings?.request?.includes(coding) || request.method === 'GET' || request.method === 'HEAD')
73
+ return request;
74
+ let body;
75
+ try {
76
+ body = DECODE[coding](new Uint8Array(await request.arrayBuffer()));
77
+ }
78
+ catch {
79
+ return undefined;
80
+ }
81
+ const headers = new Headers(request.headers);
82
+ headers.delete('content-encoding');
83
+ headers.delete('content-length');
84
+ // a copy over an ArrayBuffer of its own: zlib's result may view a shared buffer, which a request body does not take
85
+ return new Request(request.url, { method: request.method, headers, body: new Uint8Array(body) });
86
+ }
87
+ /** An answer in the coding the request accepts, when the manifest's `encodings` answers in it. */
88
+ async function encoded(m, request, res) {
89
+ if (!m.encodings?.response?.includes('gzip') || res.body === null || res.status === 204 || res.status === 205 || res.headers.has('content-encoding'))
90
+ return res;
91
+ const accepts = (request.headers.get('accept-encoding') ?? '').split(',').some((e) => e.trim().split(';')[0].toLowerCase() === 'gzip');
92
+ if (!accepts)
93
+ return res;
94
+ const headers = new Headers(res.headers);
95
+ headers.set('content-encoding', 'gzip');
96
+ headers.delete('content-length');
97
+ headers.append('vary', 'Accept-Encoding');
98
+ return new Response(gzipSync(new Uint8Array(await res.arrayBuffer())), { status: res.status, statusText: res.statusText, headers });
99
+ }
100
+ /** A pack's HTTP surface, as the kernel serves it from the pack's parts. */
101
+ export function createPackFetch(parts, options = {}) {
102
+ const m = parts.manifest;
103
+ const scope = {
104
+ ...(options.root !== undefined ? { root: options.root } : {}),
105
+ ...(options.clock ? { clock: options.clock } : {}),
106
+ ...(options.database !== undefined ? { database: options.database, readOnly: options.readOnly ?? false } : {}),
107
+ ...(parts.tenant ? { tenant: parts.tenant } : {}),
108
+ };
109
+ // a command table's surface (Redis's, a vendor that carries Redis over HTTP) names no HTTP operation: its wire is the
110
+ // pack's front (its `around`), and the table is the denominator the grade counts
111
+ const surface = { ...parts.surface, operations: parts.surface.operations ?? [], ...(m.spanning?.length ? { spanning: [...(parts.surface.spanning ?? []), ...m.spanning] } : {}) };
112
+ const routes = compileSurface(surface);
113
+ const rest = createDerivedFetch({
114
+ surface,
115
+ handlers: bindSemantics(m, (parts.handlers ?? {}), scope),
116
+ core: coreFor(m, scope),
117
+ around: crossCutting(m, { readOnly: options.readOnly ?? false, ...scope }),
118
+ gap: async (request, operation) => {
119
+ const refused = m.auth?.beforeRouting ? authRefusal(m, request, options.root) : undefined;
120
+ if (refused)
121
+ return refused;
122
+ if (parts.gap) {
123
+ const url = new URL(request.url);
124
+ const matched = operation ? matchOperation(routes, request.method, url.pathname, url.searchParams, request.headers) : undefined;
125
+ return parts.gap(await contextFor(m, { request, operation: operation ?? { id: 'unmatched', method: request.method, path: url.pathname, class: request.method === 'GET' ? 'retrieve' : 'action' }, params: matched?.params ?? {} }, scope));
126
+ }
127
+ const url = new URL(request.url);
128
+ const other = !matchOperation(routes, request.method, url.pathname, url.searchParams, request.headers)
129
+ && METHODS.some((x) => x !== request.method && matchOperation(routes, x, url.pathname, url.searchParams, request.headers));
130
+ return gapAnswer(m, request, other);
131
+ },
132
+ });
133
+ // the GraphQL wire over the same state, at the paths the manifest names (graphql-wire.ts)
134
+ const gql = m.graphql && parts.graphql ? m.graphql : undefined;
135
+ const graphql = gql && parts.graphql ? graphqlEndpoint({
136
+ sdl: parts.graphql.sdl, part: parts.graphql,
137
+ context: async (request) => contextFor(m, { request: request.clone(), operation: { id: 'graphql', method: 'POST', path: gql.paths[0], class: 'action' }, params: {} }, scope),
138
+ unmodeled: ({ type, field }) => Response.json({ errors: [{ ...(gql.unmodeled.type ? { type: gql.unmodeled.type } : {}), message: gql.unmodeled.message.replace('{field}', field).replace('{type}', type) }] }, { status: gql.unmodeled.status ?? 200 }),
139
+ mutationRefused: (request) => ((options.readOnly ?? false) || isReadOnlyRequest(request) ? vendorError(m, m.readOnly) : undefined),
140
+ }) : undefined;
141
+ const api = Object.assign(async (request) => {
142
+ if (graphql && gql && request.method === 'POST' && gql.paths.includes(new URL(request.url).pathname.replace(/\/+$/, '')))
143
+ return graphql(request);
144
+ return rest(request);
145
+ }, { owners: rest.owners });
146
+ // the World's scenario, read from the World's store at every turn, HTTP or socket alike: a file the World rewrites takes
147
+ // effect at the next turn, with no reboot; the engine (its handlers' counts) is kept while the file is unchanged
148
+ let engine;
149
+ let engineRaw;
150
+ const engineOf = () => {
151
+ if (!parts.scenario || options.scenarioPath === undefined)
152
+ return undefined;
153
+ const raw = getActiveWorldStore().read(options.scenarioPath);
154
+ if (raw === null)
155
+ throw new ScenarioError(`${m.vendor} scenario: no file at ${options.scenarioPath}`);
156
+ if (!engine || raw !== engineRaw) {
157
+ engine = new ScenarioEngine(parts.scenario.adapter, parseScenarioDocument(JSON.parse(raw), parts.scenario.adapter));
158
+ engineRaw = raw;
159
+ }
160
+ return engine;
161
+ };
162
+ /** The API, a turn the scenario decides handed its decision (ctx.scenario), or answered as the fault it names. */
163
+ const decided = async (arrived) => {
164
+ if (!parts.scenario)
165
+ return api(arrived);
166
+ // the body is read once and the request made again from its bytes: a served request's body cloned (for the turn's
167
+ // context) and then forwarded tees a stream whose second branch a Bun server never finishes, and every turn hung
168
+ // (measured on merge served with --scenario: no answer in 8 s, matched or not; t_c0279b69)
169
+ const bytes = arrived.method === 'GET' || arrived.method === 'HEAD' ? undefined : await arrived.arrayBuffer();
170
+ const request = new Request(arrived.url, { method: arrived.method, headers: arrived.headers, ...(bytes ? { body: bytes } : {}) });
171
+ const url = new URL(request.url);
172
+ const matched = matchOperation(routes, request.method, url.pathname, url.searchParams, request.headers);
173
+ if (!matched || !parts.scenario.operations.includes(matched.operation.id))
174
+ return api(request);
175
+ // the credential is the vendor's first question: a request it refuses takes no scripted turn
176
+ if (!m.auth?.exempt?.includes(matched.operation.id)) {
177
+ const refused = authRefusal(m, request, options.root);
178
+ if (refused)
179
+ return refused;
180
+ }
181
+ let decision;
182
+ try {
183
+ const turns = engineOf();
184
+ if (!turns)
185
+ return api(request);
186
+ const asked = parts.scenario.request(await contextFor(m, { request: request.clone(), operation: matched.operation, params: matched.params }, scope));
187
+ if (asked === undefined)
188
+ return api(request);
189
+ decision = await turns.serve(asked);
190
+ }
191
+ catch (error) {
192
+ if (!(error instanceof ScenarioError))
193
+ throw error;
194
+ return vendorError(m, { status: 500, code: 'scenario_error', message: `the World's scenario: ${error.message}` });
195
+ }
196
+ if (decision.kind === 'fault')
197
+ return Response.json(decision.result.body, { status: decision.result.status, headers: decision.result.headers ?? {} });
198
+ return api(new Request(request, { headers: new Headers([...request.headers, [SCENARIO_DECISION_HEADER, JSON.stringify(decision)]]) }));
199
+ };
200
+ const doors = (m.doors ?? []).map((d) => {
201
+ const answer = parts.doors?.[d.id];
202
+ if (!answer)
203
+ throw new Error(`${m.vendor}: the manifest declares the door ${d.id}, and semantics/doors.ts exports no ${d.id}`);
204
+ return { decl: d, path: pathMatcher(d.path.startsWith('/_twin/') ? d.path : `/_twin${d.path.startsWith('/') ? '' : '/'}${d.path}`, false), answer };
205
+ });
206
+ const screens = (m.screens ?? []).filter((s) => s.status === 'done' && parts.screens?.[s.id]).map((s) => {
207
+ const hosts = [s.host, ...(s.hosts ?? [])].map(hostMatcher);
208
+ return { decl: s, host: (h) => hosts.map((match) => match(h)).find(Boolean), path: pathMatcher(s.path, true), answer: parts.screens[s.id] };
209
+ });
210
+ // a context the kernel opens for what no surface operation names: a door, a screen, the clock
211
+ const open = (request, id, method, path, params) => contextFor(m, { request, operation: { id, method, path, class: method === 'GET' ? 'retrieve' : 'action' }, params }, scope);
212
+ const refusedReadOnly = () => vendorError(m, m.readOnly);
213
+ const prefix = m.pathPrefix ? new RegExp(`^(?:${m.pathPrefix})(?=/)`) : undefined;
214
+ const hostPath = m.pathFromHost ? new RegExp(m.pathFromHost, 'i') : undefined;
215
+ const hostParams = m.hostParams ? new RegExp(m.hostParams, 'i') : undefined;
216
+ /** The request as its vendor routes it: duplicate slashes collapsed, the manifest's path prefix taken off, a host's
217
+ * path label (pathFromHost) put in front. */
218
+ const routed = (request) => {
219
+ const url = new URL(request.url);
220
+ let path = url.pathname.replace(/\/{2,}/g, '/');
221
+ // a prefix's named groups (Jira's `/ex/jira/(?<cloudId>[^/]+)`) and the host's (E2B's envd at
222
+ // `49983-(?<sandboxID>…).e2b.app`) join the operation's parameters
223
+ const groups = { ...(prefix?.exec(path)?.groups ?? {}), ...(hostParams?.exec(hostOf(request))?.groups ?? {}) };
224
+ if (prefix)
225
+ path = path.replace(prefix, '') || '/';
226
+ const label = hostPath?.exec(hostOf(request))?.[1];
227
+ if (label)
228
+ path = `/${label}${path === '/' ? '' : path}`;
229
+ if (path === url.pathname && !Object.keys(groups).length)
230
+ return request;
231
+ // the path the client sent, which a signature over the request (SigV4's canonical URI) covers
232
+ const headers = new Headers(request.headers);
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, ...(request.body ? { duplex: 'half' } : {}) });
238
+ };
239
+ const rootServes = (method, path) => Boolean(matchOperation(routes, method, path));
240
+ const lane = m.lanes ? laneRouter(m.vendor, m.lanes, parts.lanes ?? {}, rootServes) : undefined;
241
+ const fetch = async (arrived) => {
242
+ // the discovery door is the pack's own, whichever lane would take its path
243
+ const asked = new URL(arrived.url).pathname.replace(/\/+$/, '') || '/';
244
+ const discovery = arrived.method === 'GET' && m.discovery !== undefined && (asked === '/twin' || asked === '/twin/scenario');
245
+ const toLane = discovery ? undefined : lane?.(arrived);
246
+ if (toLane)
247
+ return toLane(arrived);
248
+ // only the kernel names a scenario's decision, or a path prefix's parameters
249
+ if (arrived.headers.has(SCENARIO_DECISION_HEADER) || arrived.headers.has(PREFIX_PARAMS_HEADER)) {
250
+ const headers = new Headers(arrived.headers);
251
+ headers.delete(SCENARIO_DECISION_HEADER);
252
+ headers.delete(PREFIX_PARAMS_HEADER);
253
+ arrived = new Request(arrived, { headers });
254
+ }
255
+ const readable = await decoded(m, arrived);
256
+ if (!readable)
257
+ 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
+ const incoming = routed(readable);
259
+ // a twin started read-only marks every request so, so every context it opens knows (isReadOnlyRequest)
260
+ const request = options.readOnly && !isReadOnlyRequest(incoming) ? new Request(incoming, { headers: new Headers([...incoming.headers, [READ_ONLY_REQUEST_HEADER, '1']]) }) : incoming;
261
+ const url = new URL(request.url);
262
+ const path = url.pathname.replace(/\/+$/, '') || '/';
263
+ const readOnly = (options.readOnly ?? false) || isReadOnlyRequest(request);
264
+ if (request.method === 'GET' && path === '/twin' && m.discovery) {
265
+ const d = m.discovery;
266
+ // the workspaces a person can be shown (Viewing a World): each built workspace screen's id and path
267
+ const workspaces = (m.screens ?? []).filter((s) => s.kind === 'workspace' && s.status === 'done' && parts.screens?.[s.id]).map((s) => ({ id: s.id, path: s.path }));
268
+ const shown = workspaces.length ? { screens: workspaces } : {};
269
+ // a model vendor's: its state, and what its scenario scripts, with the scenario's live counts
270
+ if (parts.scenario && d.behavior) {
271
+ return Response.json({ ...shown, ...twinManifest({
272
+ vendor: m.vendor, twinOf: d.twinOf,
273
+ stateSentence: `Stateful: it stores ${d.stores}. Create state through the vendor's OWN API with the real SDK or plain fetch pointed here.`,
274
+ behaviorSentence: d.behavior, exampleHandler: d.exampleHandler ?? null, ...(engineOf() ? { engine: engineOf() } : {}),
275
+ }) });
276
+ }
277
+ return Response.json({ ...statefulTwinManifest({ vendor: m.vendor, twinOf: d.twinOf, stores: d.stores, ...(d.identity ? { identity: d.identity } : {}), ...(d.notes ? { notes: d.notes } : {}) }), ...shown });
278
+ }
279
+ // the scenario's handlers with their match counts, and the recent misses
280
+ if (request.method === 'GET' && path === '/twin/scenario' && parts.scenario)
281
+ return Response.json(engineOf()?.status() ?? { vendor: m.vendor, handlers: [], misses: 0, recentMisses: [] });
282
+ // the vendor's own moves, not the caller's: its context carries none of the request's headers (a Stripe-Account
283
+ // header would make a payout time makes the caller's), but a tenant's, when the vendor keeps tenants apart
284
+ if (parts.clock)
285
+ await runAsVendorMove(async () => parts.clock(await open(new Request(request.url, m.tenant ? { headers: request.headers } : {}), 'clock', 'POST', '/_twin/clock', {})));
286
+ if (path.startsWith('/_twin/')) {
287
+ // the World's machine pool, enrolled by the kernel for a vendor that runs images (machines.ts)
288
+ if (m.machines && path === '/_twin/machine-pool' && request.method === 'POST') {
289
+ if (readOnly)
290
+ return refusedReadOnly();
291
+ let kind;
292
+ try {
293
+ kind = JSON.parse(await request.clone().text()).kind;
294
+ }
295
+ catch {
296
+ kind = undefined;
297
+ }
298
+ if (!POOL_KINDS.includes(kind))
299
+ return Response.json({ message: `kind must be one of ${POOL_KINDS.join(', ')}` }, { status: 400 });
300
+ await applyTwinWrite(m.service, { operation: 'machine_pool.enroll', subjectType: MACHINE_POOL, subjectId: 'pool', fields: { kind }, occurredAt: (await open(request, 'machine-pool', 'POST', '/_twin/machine-pool', {})).occurredAt, actor: { kind: 'system' } }, options.root);
301
+ return Response.json({ kind }, { status: 201 });
302
+ }
303
+ for (const d of doors) {
304
+ if (d.decl.method.toUpperCase() !== request.method)
305
+ continue;
306
+ const hit = d.path(path);
307
+ if (!hit)
308
+ continue;
309
+ if (readOnly && request.method !== 'GET')
310
+ return refusedReadOnly();
311
+ return d.answer(await open(request, d.decl.id, d.decl.method, d.decl.path, hit.params));
312
+ }
313
+ return gapAnswer(m, request);
314
+ }
315
+ // a screen on a host it names exactly answers there first (a dashboard's /login on dash.cloudflare.com, where the
316
+ // API's /{Bucket} would take it); then a path the vendor's API names is the API's; then a screen on a host it names by
317
+ // pattern (a room's page on <domain>.daily.co, which api.daily.co also matches); then a screen by its path alone, at
318
+ // whichever host the twin is reached (its links carry the twin's own base), never a root screen off its own host
319
+ const host = hostOf(request);
320
+ const exact = (s) => [s.decl.host, ...(s.decl.hosts ?? [])].some((h) => !/[{<]/.test(h) && h.toLowerCase() === host);
321
+ const hits = screens.map((s) => ({ s, hit: s.path(path), onHost: s.host(host) })).filter((x) => x.hit);
322
+ const apiPath = Boolean(matchOperation(routes, request.method, url.pathname, url.searchParams, request.headers));
323
+ // reached at a World's place for the twin (`x-forwarded-prefix`, served-world's wire), the host is the World's: a
324
+ // workspace is shown there by its path alone, its root too (the console frames it so)
325
+ const placed = request.headers.has(TWIN_PREFIX_HEADER);
326
+ const byPath = hits.find((x) => x.onHost && exact(x.s))
327
+ ?? (apiPath ? undefined : hits.find((x) => x.onHost) ?? hits.find((x) => x.s.decl.path.replace(/\/+$/, '') !== '')
328
+ ?? (placed ? hits.find((x) => x.s.decl.kind === 'workspace') : undefined));
329
+ if (byPath) {
330
+ if (readOnly && request.method !== 'GET' && request.method !== 'HEAD')
331
+ return refusedReadOnly();
332
+ return byPath.s.answer(await open(request, byPath.s.decl.id, request.method, byPath.s.decl.path, { ...(byPath.onHost?.params ?? {}), ...byPath.hit.params }));
333
+ }
334
+ if (!parts.around)
335
+ return encoded(m, request, await decided(request));
336
+ const matched = matchOperation(routes, request.method, url.pathname, url.searchParams, request.headers);
337
+ const operation = matched?.operation ?? { id: 'unmatched', method: request.method, path: url.pathname, class: request.method === 'GET' ? 'retrieve' : 'action' };
338
+ const ctx = await contextFor(m, { request: request.clone(), operation, params: matched?.params ?? {} }, scope);
339
+ return encoded(m, request, await parts.around(ctx, (rewritten) => decided(rewritten ?? request)));
340
+ };
341
+ // the request's id is the correlation of every entry it appends (runtime contract D3), minted here, always: it is the
342
+ // group a perform sends as one call (the real-system adapters, "Perform"). Under `auto` the group is performed once
343
+ // the handler has answered, and the answer names the subjects by the ids the vendor gave them.
344
+ const correlated = async (request) => {
345
+ const id = `req_${globalThis.crypto.randomUUID()}`;
346
+ const { value: answer, appended } = await runWithCorrelationId(id, () => deferPerform(id, () => fetch(request)));
347
+ if (!appended)
348
+ return answer;
349
+ const performed = await runWithCorrelationId(id, () => performRequestGroup(m.service, id, options.root));
350
+ // the vendor's refusal, as its status and words (perform names them `<status>: <message>`), in the vendor's error body
351
+ if (performed instanceof RefusedWriteError) {
352
+ const said = /^(\d{3}): ([\s\S]*)$/.exec(performed.reason);
353
+ return vendorError(m, { status: said ? Number(said[1]) : 400, message: said ? said[2] : performed.reason });
354
+ }
355
+ if (performed instanceof VendorWriteError)
356
+ return vendorError(m, { status: 502, message: performed.message });
357
+ if (!performed.ids.size || !(answer.headers.get('content-type') ?? '').includes('json'))
358
+ return answer;
359
+ let text = await answer.text();
360
+ for (const [local, vendor] of performed.ids)
361
+ text = text.split(JSON.stringify(local)).join(JSON.stringify(vendor));
362
+ const headers = new Headers(answer.headers);
363
+ headers.delete('content-length');
364
+ return new Response(text, { status: answer.status, statusText: answer.statusText, headers });
365
+ };
366
+ // the vendor's CORS, as its gateway answers a browser: every answer, a preflight at any path
367
+ // a JSON answer labelled as the vendor labels it
368
+ const labelled = m.jsonContentType === undefined ? correlated : async (request) => {
369
+ const res = await correlated(request);
370
+ const type = res.headers.get('content-type') ?? '';
371
+ if (!type.startsWith('application/json') || type === m.jsonContentType)
372
+ return res;
373
+ const headers = new Headers(res.headers);
374
+ headers.set('content-type', m.jsonContentType);
375
+ return new Response(res.body, { status: res.status, statusText: res.statusText, headers });
376
+ };
377
+ const served = m.cors ? withCors(m.cors, labelled) : labelled;
378
+ // which paths this pack's API serves, for a vendor's lane router that asks (unlessOnlyRoot)
379
+ const serves = (method, path) => Boolean(matchOperation(routes, method, path));
380
+ // the vendor's sockets: a request to a declared socket's path opens a session of its engine, over a context of the
381
+ // request that opened it (operation `socket:<id>`)
382
+ for (const d of m.sockets ?? [])
383
+ if (!parts.sockets?.[d.id])
384
+ throw new Error(`${m.vendor}: the manifest declares the socket ${d.id}, and semantics/sockets.ts exports no ${d.id}`);
385
+ const upgrade = m.sockets?.length ? socketUpgrade(m.service, options.root, m.sockets, parts.sockets, (request, d) => open(request, `socket:${d.id}`, 'GET', d.path, {}), hostOf, (request) => new URL(routed(request).url).pathname,
386
+ // a turn a session speaks (a voice agent's reply) is the World's scenario's, as a turn over HTTP is
387
+ async (asked) => engineOf()?.serve(asked)) : undefined;
388
+ // what serves each operation: the pack's own, and each lane's
389
+ const owners = () => ({ ...api.owners(), ...lanesOwners(parts.lanes ?? {}) });
390
+ return derivedRequestScopes(m, Object.assign(served, { owners, serves, ...(upgrade ? { upgrade } : {}) }));
391
+ }
392
+ /** The vendor's gateway over its lanes: the lane a request goes to (a door to the lane that declares it, then the first
393
+ * route whose conditions all hold, then the default), behind the gateway's CORS; undefined when none takes it. */
394
+ function laneRouter(vendor, decl, lanes, rootServes = () => false) {
395
+ const served = (name) => {
396
+ const part = lanes[name];
397
+ if (!part)
398
+ throw new Error(`${vendor}: the manifest routes to the lane ${name}, and the vendor's fetch is given no ${name}`);
399
+ // the lane is named on the request, so a write it makes records which lane's API made it (LANE_HEADER)
400
+ const named = (request) => {
401
+ const headers = new Headers(request.headers);
402
+ headers.set(LANE_HEADER, name);
403
+ return part.fetch(new Request(request, { headers, ...(request.body ? { duplex: 'half' } : {}) }));
404
+ };
405
+ return decl.cors ? withCors(decl.cors, named) : named;
406
+ };
407
+ const doors = Object.entries(lanes).flatMap(([name, part]) => (part.manifest.doors ?? []).map((d) => ({
408
+ name, method: d.method.toUpperCase(), matches: pathMatcher(d.path.startsWith('/_twin/') ? d.path : `/_twin/${d.path.replace(/^\//, '')}`, false),
409
+ })));
410
+ const laneRoutes = decl.routes.map((r) => {
411
+ const at = r.path?.replace(/\/+$/, '');
412
+ const host = r.host !== undefined ? new RegExp(r.host, 'i') : undefined;
413
+ const except = (r.exceptHosts ?? []).map((h) => h.toLowerCase());
414
+ const own = lanes[r.lane]?.fetch.serves;
415
+ return {
416
+ lane: served(r.lane),
417
+ holds: (request, path, name) => (at === undefined || path === at || path.startsWith(`${at}/`))
418
+ && (host === undefined || host.test(name))
419
+ && (r.header === undefined || (request.headers.get(r.header.name) ?? '').startsWith(r.header.prefix) !== (r.header.absent === true))
420
+ && !except.includes(name)
421
+ && !(r.unlessOnlyRoot && rootServes(request.method, path) && !(own?.(request.method, path) ?? false)),
422
+ };
423
+ });
424
+ const fallback = decl.default !== undefined ? served(decl.default) : undefined;
425
+ return (request) => {
426
+ const path = new URL(request.url).pathname.replace(/\/{2,}/g, '/');
427
+ if (path.startsWith('/_twin/')) {
428
+ const door = doors.find((d) => d.method === request.method && d.matches(path));
429
+ if (door)
430
+ return served(door.name);
431
+ }
432
+ const name = hostOf(request);
433
+ return laneRoutes.find((r) => r.holds(request, path, name))?.lane ?? fallback;
434
+ };
435
+ }
436
+ /** A vendor whose every API is a lane (Cloudflare's API v4 and R2, AWS's services), served from its manifest: the
437
+ * discovery door, then its lanes as its gateway sends them; a request no lane takes is the vendor's not-found. */
438
+ /** What serves each of a vendor's lanes' operations (a handler, the core, or the gap), as one map. */
439
+ const lanesOwners = (lanes) => Object.assign({}, ...Object.values(lanes).map((l) => l.fetch.owners?.() ?? {}));
440
+ export function createVendorFetch(manifest, lanes) {
441
+ const lane = laneRouter(manifest.vendor, manifest.lanes, lanes);
442
+ return Object.assign(async (request) => {
443
+ const path = new URL(request.url).pathname.replace(/\/+$/, '') || '/';
444
+ if (request.method === 'GET' && path === '/twin' && manifest.discovery)
445
+ return Response.json(statefulTwinManifest({ vendor: manifest.vendor, ...manifest.discovery }));
446
+ const to = lane(request);
447
+ return to ? to(request) : Response.json({ error: `${manifest.vendor}: no lane serves ${request.method} ${path}` }, { status: 404 });
448
+ }, { owners: () => lanesOwners(lanes) });
449
+ }
@@ -0,0 +1,12 @@
1
+ /** Node's nearest installed package, including linked packages and hoisted workspace dependencies. */
2
+ export declare function installedPackageDir(from: string, name: string): string | undefined;
3
+ /** A pack repository itself, or the P3 repository beside a platform checkout. Applications do not acquire a
4
+ * sibling checkout merely because one happens to be beside them. */
5
+ export declare function packRepository(from: string): string | undefined;
6
+ /** Resolve one vendor's package directory for host-side reads (assets, infrastructure launchers). */
7
+ export declare function vendorPackageDir(from: string, vendor: string): string | undefined;
8
+ /** Whether a directory holds a derived pack: a generated surface, its own or a lane's (a vendor whose APIs are all
9
+ * lanes). A catalog lists no other package as a twin. This is what it is, not its grade. */
10
+ export declare function isDerivedPack(dir: string): boolean;
11
+ /** Package identity from a selected module, independent of the checkout's folder name and a service's alias. */
12
+ export declare function vendorOfPackModule(modulePath: string): string | undefined;
@@ -0,0 +1,86 @@
1
+ // Host-side package locations. Pure at import: the browser-facing kernel does not discover packages or read files
2
+ // until its host asks. Architecture, "The catalog: where twins come from"; ADR 0005.
3
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
4
+ import { dirname, join, resolve } from 'node:path';
5
+ function packageName(dir) {
6
+ try {
7
+ return JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')).name;
8
+ }
9
+ catch {
10
+ return undefined;
11
+ }
12
+ }
13
+ /** Node's nearest installed package, including linked packages and hoisted workspace dependencies. */
14
+ export function installedPackageDir(from, name) {
15
+ let dir = resolve(from);
16
+ for (;;) {
17
+ const candidate = join(dir, 'node_modules', ...name.split('/'));
18
+ if (existsSync(join(candidate, 'package.json')))
19
+ return candidate;
20
+ const up = dirname(dir);
21
+ if (up === dir)
22
+ return undefined;
23
+ dir = up;
24
+ }
25
+ }
26
+ /** A pack repository itself, or the P3 repository beside a platform checkout. Applications do not acquire a
27
+ * sibling checkout merely because one happens to be beside them. */
28
+ export function packRepository(from) {
29
+ let dir = resolve(from);
30
+ for (;;) {
31
+ if (packageName(dir) === 'twin-packs-p3')
32
+ return dir;
33
+ if (packageName(join(dir, 'packages', 'world-core')) === '@volter/world-core') {
34
+ const sibling = join(dirname(dir), 'twin-packs-p3');
35
+ if (packageName(sibling) === 'twin-packs-p3')
36
+ return sibling;
37
+ }
38
+ const up = dirname(dir);
39
+ if (up === dir)
40
+ return undefined;
41
+ dir = up;
42
+ }
43
+ }
44
+ /** Resolve one vendor's package directory for host-side reads (assets, infrastructure launchers). */
45
+ export function vendorPackageDir(from, vendor) {
46
+ if (!/^[a-z][a-z0-9-]*$/.test(vendor))
47
+ return undefined;
48
+ const candidate = installedPackageDir(from, `@volter/twin-${vendor}`);
49
+ const installed = candidate && isDerivedPack(candidate) ? candidate : undefined;
50
+ const repo = packRepository(from);
51
+ const checkout = repo === undefined ? undefined : join(repo, vendor);
52
+ if (!checkout || packageName(checkout) !== `@volter/twin-${vendor}` || !isDerivedPack(checkout))
53
+ return installed;
54
+ if (!installed)
55
+ return checkout;
56
+ const major = (dir) => {
57
+ try {
58
+ return Number(/^(\d+)/.exec(JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')).version ?? '')?.[1] ?? 0);
59
+ }
60
+ catch {
61
+ return 0;
62
+ }
63
+ };
64
+ return major(installed) > major(checkout) ? installed : checkout;
65
+ }
66
+ /** Whether a directory holds a derived pack: a generated surface, its own or a lane's (a vendor whose APIs are all
67
+ * lanes). A catalog lists no other package as a twin. This is what it is, not its grade. */
68
+ export function isDerivedPack(dir) {
69
+ const surface = (unit) => existsSync(join(unit, 'src', 'generated', 'surface.gen.json')) || existsSync(join(unit, 'src', 'generated', 'proto.gen.json'));
70
+ if (surface(dir))
71
+ return true;
72
+ return existsSync(dir) && readdirSync(dir, { withFileTypes: true }).some((entry) => entry.isDirectory() && surface(join(dir, entry.name)));
73
+ }
74
+ /** Package identity from a selected module, independent of the checkout's folder name and a service's alias. */
75
+ export function vendorOfPackModule(modulePath) {
76
+ let dir = dirname(resolve(modulePath));
77
+ for (;;) {
78
+ const name = packageName(dir);
79
+ if (name !== undefined)
80
+ return name.startsWith('@volter/twin-') ? name.slice('@volter/twin-'.length) : undefined;
81
+ const up = dirname(dir);
82
+ if (up === dir)
83
+ return undefined;
84
+ dir = up;
85
+ }
86
+ }