@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,283 @@
1
+ // THE WORLD'S MANAGED DATABASE, as a pack reaches it (architecture.md, "The engine slot" and managed infrastructure): a
2
+ // vendor whose data plane is a server in front of Postgres (Supabase's PostgREST, Storage and Auth; a Neon or a
3
+ // PlanetScale) serves it over the World's own Postgres, whose URL the runtime hands the pack (the descriptor's
4
+ // `managedDatabase`: `serve --database <url>`, or the colocated host's `database` option). A protocol-3 handler reaches
5
+ // it as `ctx.engine`; a pack's own server in front of it (a PostgREST) through `managedDatabase(url)`. Its terms:
6
+ //
7
+ // - Nothing here reads the environment, and the tree references none of the rows it writes.
8
+ // - Each batch is ONE transaction sent as ONE extended-protocol pipeline ending in ONE Sync: `BEGIN`, the role
9
+ // (`SET LOCAL ROLE`), the transaction-local settings (`set_config(…, true)`), the statements and `COMMIT`, every value a
10
+ // bind parameter. The PGlite host runs a connection's messages up to its Sync under one lock and lets another
11
+ // connection's read into a session idle inside a transaction; a batch that never sits idle inside its transaction lets
12
+ // nobody in, so its role and settings reach no other connection.
13
+ // - The role, which Postgres takes only as SQL text, is quoted as an identifier; which roles a caller may take is the
14
+ // pack's rule (Supabase's three API roles), checked before a batch is made.
15
+ // - A batch that fails leaves its transaction aborted (the backend skips to the Sync); it is ended with `ROLLBACK` before
16
+ // the connection is released, and a connection that cannot be rolled back is destroyed, never reused.
17
+ //
18
+ // The Postgres driver is node-postgres (`pg`), loaded the first time a database is bound and installed by the package that
19
+ // declares a managed database (its package.json): the kernel's dependencies are zod and ws alone (architecture B1), and a
20
+ // browser bundle imports the kernel without a database. A host may register another (`useDatabaseDriver`).
21
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
22
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
23
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
24
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
25
+ });
26
+ }
27
+ return path;
28
+ };
29
+ /** A Postgres error as the backend reported it (SQLSTATE and its fields), what a vendor's server passes through. */
30
+ export class EngineDatabaseError extends Error {
31
+ code;
32
+ detail;
33
+ hint;
34
+ constructor(message, fields) {
35
+ super(message);
36
+ this.name = 'EngineDatabaseError';
37
+ this.code = fields.code ?? 'XX000';
38
+ this.detail = fields.detail ?? null;
39
+ this.hint = fields.hint ?? null;
40
+ }
41
+ }
42
+ /** The World's database cannot be reached: none is bound, or it does not answer. */
43
+ export class EngineUnavailableError extends Error {
44
+ constructor(message) { super(message); this.name = 'EngineUnavailableError'; }
45
+ }
46
+ /** The transaction-local setting every batch a handler makes carries: the World clock at the call (ctx.occurredAt). */
47
+ export const WORLD_CLOCK_SETTING = 'volter.world_clock';
48
+ /** The World clock inside the database, for the schemas a vendor lays (its migrations stamp rows with Postgres's
49
+ * `now()`, which reads the wall clock): `volter.now()`, the World clock the batch set or the wall clock where none is
50
+ * set, and every column default in `schemas` that reads `now()` or `CURRENT_TIMESTAMP` (alone or inside an expression,
51
+ * GoTrue's `timezone('utc', now())`) rewritten to read it. Run after the schemas are laid; running it again changes
52
+ * nothing. A trigger or function that calls `now()` itself is the pack's to redefine. */
53
+ export function worldClockSql(schemas) {
54
+ const list = schemas.map((s) => `'${s.replace(/'/g, "''")}'`).join(', ');
55
+ return `CREATE SCHEMA IF NOT EXISTS volter;
56
+ CREATE OR REPLACE FUNCTION volter.now() RETURNS timestamptz LANGUAGE sql STABLE AS $fn$
57
+ SELECT coalesce(nullif(current_setting('${WORLD_CLOCK_SETTING}', true), '')::timestamptz, pg_catalog.now())
58
+ $fn$;
59
+ GRANT USAGE ON SCHEMA volter TO PUBLIC;
60
+ GRANT EXECUTE ON FUNCTION volter.now() TO PUBLIC;
61
+ DO $do$
62
+ DECLARE d record; rewritten text;
63
+ BEGIN
64
+ FOR d IN
65
+ SELECT n.nspname AS schema, c.relname AS tbl, a.attname AS col, pg_get_expr(ad.adbin, ad.adrelid) AS expr
66
+ FROM pg_attrdef ad
67
+ JOIN pg_class c ON c.oid = ad.adrelid
68
+ JOIN pg_namespace n ON n.oid = c.relnamespace
69
+ JOIN pg_attribute a ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
70
+ WHERE n.nspname IN (${list}) AND c.relkind IN ('r', 'p')
71
+ AND pg_get_expr(ad.adbin, ad.adrelid) ~* '(^|[^.[:alnum:]_])(now\\(\\)|current_timestamp)'
72
+ LOOP
73
+ rewritten := regexp_replace(d.expr, '(^|[^.[:alnum:]_])(now\\(\\)|current_timestamp)', '\\1volter.now()', 'gi');
74
+ EXECUTE format('ALTER TABLE %I.%I ALTER COLUMN %I SET DEFAULT %s', d.schema, d.tbl, d.col, rewritten);
75
+ END LOOP;
76
+ END
77
+ $do$;`;
78
+ }
79
+ /** Quote an identifier the way Postgres's quote_ident does for a name that needs it. */
80
+ export function quoteIdent(name) {
81
+ return `"${name.replace(/"/g, '""')}"`;
82
+ }
83
+ /** pg's Submittable (the extension point pg-cursor and pg-query-stream use): the client hands it the connection once
84
+ * it is its turn, it writes every statement's Parse/Bind/Describe/Execute and ONE Sync, and the client hands it each
85
+ * backend message until ReadyForQuery. */
86
+ class Pipeline {
87
+ statements;
88
+ done;
89
+ results = [];
90
+ current = null;
91
+ error = null;
92
+ settled = false;
93
+ constructor(statements, done) {
94
+ this.statements = statements;
95
+ this.done = done;
96
+ }
97
+ submit(connection) {
98
+ for (const statement of this.statements) {
99
+ connection.parse({ text: statement.text, types: [] });
100
+ connection.bind({ values: statement.values ?? [] });
101
+ connection.describe({ type: 'P' });
102
+ connection.execute({ rows: 0 });
103
+ }
104
+ connection.sync();
105
+ }
106
+ handleRowDescription(message) {
107
+ this.current = { fields: message.fields.map((field) => field.name), rows: [] };
108
+ }
109
+ handleDataRow(message) {
110
+ (this.current ??= { fields: [], rows: [] }).rows.push(message.fields);
111
+ }
112
+ handleCommandComplete(message) {
113
+ this.results.push({ ...(this.current ?? { fields: [], rows: [] }), command: String(message.text ?? '') });
114
+ this.current = null;
115
+ }
116
+ handleEmptyQuery() {
117
+ this.results.push({ fields: [], rows: [], command: '' });
118
+ }
119
+ handlePortalSuspended() { }
120
+ handleCopyInResponse(connection) { connection.sendCopyFail('COPY FROM STDIN is not served by a managed database batch'); }
121
+ handleCopyData() { }
122
+ handleError(error) {
123
+ // An ErrorResponse (the backend's, with a SQLSTATE) or a failed connection. pg ends the submittable's turn here:
124
+ // it drops it as the active query, so the ReadyForQuery the backend sends after skipping to the Sync goes to no
125
+ // one, and the next query this client is given (the ROLLBACK) is sent once that ReadyForQuery arrives.
126
+ this.error ??= error;
127
+ this.finish();
128
+ }
129
+ handleReadyForQuery() { this.finish(); }
130
+ finish() {
131
+ if (this.settled)
132
+ return;
133
+ this.settled = true;
134
+ this.done(this.error, this.results);
135
+ }
136
+ }
137
+ function asDatabaseError(error) {
138
+ const fields = error;
139
+ if (fields.severity === undefined)
140
+ return new EngineUnavailableError(`the World's Postgres failed: ${error.message}`);
141
+ return new EngineDatabaseError(error.message, fields);
142
+ }
143
+ let driver;
144
+ /** A Postgres driver a host registers in place of node-postgres. */
145
+ export function useDatabaseDriver(given) {
146
+ driver = given;
147
+ }
148
+ /** The driver: the registered one, else node-postgres, loaded on first use (a name the bundler does not resolve). */
149
+ async function loadDriver() {
150
+ if (driver !== undefined)
151
+ return driver;
152
+ const name = 'pg';
153
+ try {
154
+ const mod = (await import(__rewriteRelativeImportExtension(name)));
155
+ driver = mod.default ?? mod;
156
+ }
157
+ catch (error) {
158
+ throw new EngineUnavailableError(`no Postgres driver: node-postgres (pg) is not installed beside the pack that declares a managed database (${error.message})`);
159
+ }
160
+ return driver;
161
+ }
162
+ const pools = new Map(); // cache: one pool per bound database URL — the connections this process holds to the World's Postgres, never state
163
+ async function poolFor(url) {
164
+ let pool = pools.get(url);
165
+ if (pool === undefined) {
166
+ const { Pool } = await loadDriver();
167
+ // another request may have made it while the driver loaded
168
+ const made = pools.get(url);
169
+ if (made !== undefined)
170
+ return made;
171
+ // a few connections: the PGlite host serializes them anyway, and a container Postgres needs no more for a twin
172
+ pool = new Pool({ connectionString: url, max: 4, idleTimeoutMillis: 10_000, connectionTimeoutMillis: 10_000 });
173
+ // an idle connection that the database ends (a World's Postgres restarting) is dropped from the pool, not thrown
174
+ pool.on('error', () => { });
175
+ pools.set(url, pool);
176
+ }
177
+ return pool;
178
+ }
179
+ async function checkout(url) {
180
+ const pool = await poolFor(url);
181
+ try {
182
+ return await pool.connect();
183
+ }
184
+ catch (error) {
185
+ throw new EngineUnavailableError(`the World's Postgres is not answering: ${error.message}`);
186
+ }
187
+ }
188
+ function pipeline(client, statements) {
189
+ return new Promise((resolve) => {
190
+ client.query(new Pipeline(statements, (error, results) => resolve({ error, results })));
191
+ });
192
+ }
193
+ /** Run one transaction as one pipeline with one Sync; on failure, ROLLBACK before the connection goes back. */
194
+ async function transaction(url, statements, lead) {
195
+ const client = await checkout(url);
196
+ let broken;
197
+ try {
198
+ const { error, results } = await pipeline(client, statements);
199
+ if (error !== null) {
200
+ if (error.severity !== undefined) {
201
+ try {
202
+ await client.query('ROLLBACK');
203
+ }
204
+ catch (rollback) {
205
+ broken = rollback;
206
+ }
207
+ }
208
+ else
209
+ broken = error;
210
+ throw asDatabaseError(error);
211
+ }
212
+ // the answer is the batch's own statements: not BEGIN, the role, the settings, or its end
213
+ return results.slice(lead, statements.length - 1);
214
+ }
215
+ finally {
216
+ client.release(broken);
217
+ }
218
+ }
219
+ function settingsStatement(settings) {
220
+ const names = Object.keys(settings);
221
+ if (names.length === 0)
222
+ return null;
223
+ const values = [];
224
+ const calls = names.map((name) => {
225
+ values.push(name, settings[name]);
226
+ return `set_config($${values.length - 1}, $${values.length}, true)`;
227
+ });
228
+ return { text: `SELECT ${calls.join(', ')}`, values };
229
+ }
230
+ /** The World's Postgres at `url` (the runtime's binding, never the environment); with `readOnly`, every transaction is
231
+ * READ ONLY whatever the batch asks (a read-only twin or request: Postgres itself refuses a write, 25006); with `clock`,
232
+ * every batch carries the World clock as `volter.world_clock`. */
233
+ export function managedDatabase(url, options = {}) {
234
+ return {
235
+ bound: true,
236
+ async batch(batch) {
237
+ // the World clock at the call, which volter.now() and the defaults worldClockSql rewrote read
238
+ const set = settingsStatement({ ...(options.clock !== undefined ? { [WORLD_CLOCK_SETTING]: options.clock } : {}), ...(batch.settings ?? {}) });
239
+ const head = [
240
+ { text: batch.readOnly || options.readOnly ? 'BEGIN READ ONLY' : 'BEGIN' },
241
+ ...(batch.role === undefined ? [] : [{ text: `SET LOCAL ROLE ${quoteIdent(batch.role)}` }]),
242
+ ...(set === null ? [] : [set]),
243
+ ];
244
+ return transaction(url, [...head, ...batch.statements, { text: batch.rollback ? 'ROLLBACK' : 'COMMIT' }], head.length);
245
+ },
246
+ async script(text) {
247
+ const client = await checkout(url);
248
+ let broken;
249
+ try {
250
+ await client.query(text);
251
+ }
252
+ catch (error) {
253
+ if (error.severity === undefined)
254
+ broken = error;
255
+ else {
256
+ try {
257
+ await client.query('ROLLBACK');
258
+ }
259
+ catch (rollback) {
260
+ broken = rollback;
261
+ }
262
+ }
263
+ throw asDatabaseError(error);
264
+ }
265
+ finally {
266
+ client.release(broken);
267
+ }
268
+ },
269
+ };
270
+ }
271
+ /** A handler's database when the World binds none: every use is the refusal naming what to add. */
272
+ export function unboundDatabase(service) {
273
+ const refuse = () => {
274
+ throw new EngineUnavailableError(`This World binds no managed Postgres to the ${service} twin, so what it serves over one is refused, not served: add a postgres service to the World's managed infrastructure (architecture: managed infrastructure)`);
275
+ };
276
+ return { bound: false, batch: async () => refuse(), script: async () => refuse() };
277
+ }
278
+ /** Close every connection this process holds (a test's teardown; a served twin's connections end with it). */
279
+ export async function closeManagedDatabases() {
280
+ const held = [...pools.values()];
281
+ pools.clear();
282
+ await Promise.all(held.map((pool) => pool.end().catch(() => { })));
283
+ }
@@ -0,0 +1,11 @@
1
+ /** One part: its field name (possibly empty), its filename when it is a file, its media type as sent, its bytes. */
2
+ export type MultipartPart = {
3
+ name: string;
4
+ filename: string | null;
5
+ type: string | null;
6
+ body: Uint8Array;
7
+ };
8
+ /** The boundary a `multipart/form-data` content type names, or null (not multipart, or no boundary). */
9
+ export declare function multipartBoundary(contentType: string | null | undefined): string | null;
10
+ /** The parts of a body delimited by `boundary`, in order; a malformed tail ends the reading. */
11
+ export declare function multipartParts(bytes: Uint8Array, boundary: string): MultipartPart[];
@@ -0,0 +1,51 @@
1
+ // A multipart/form-data body's parts, read from its bytes (RFC 7578): the one reading every pack's uploads go through.
2
+ // A runtime's own form parser is not used: Bun's drops a part whose field name is empty, which a browser's or
3
+ // supabase-js's Blob upload sends. Dependency-free and side-effect free (a browser bundle imports it).
4
+ /** The boundary a `multipart/form-data` content type names, or null (not multipart, or no boundary). */
5
+ export function multipartBoundary(contentType) {
6
+ if (!contentType || !/^multipart\/form-data/i.test(contentType))
7
+ return null;
8
+ const hit = /boundary=(?:"([^"]+)"|([^;\s]+))/i.exec(contentType);
9
+ return hit ? (hit[1] ?? hit[2]) : null;
10
+ }
11
+ function indexOf(haystack, needle, from) {
12
+ outer: for (let i = from; i <= haystack.length - needle.length; i += 1) {
13
+ for (let j = 0; j < needle.length; j += 1)
14
+ if (haystack[i + j] !== needle[j])
15
+ continue outer;
16
+ return i;
17
+ }
18
+ return -1;
19
+ }
20
+ /** The parts of a body delimited by `boundary`, in order; a malformed tail ends the reading. */
21
+ export function multipartParts(bytes, boundary) {
22
+ const encoder = new TextEncoder();
23
+ const decoder = new TextDecoder();
24
+ const delimiter = encoder.encode(`--${boundary}`);
25
+ const blank = encoder.encode('\r\n\r\n');
26
+ const parts = [];
27
+ let at = indexOf(bytes, delimiter, 0);
28
+ while (at >= 0) {
29
+ const start = at + delimiter.length;
30
+ // `--` after the delimiter closes the body
31
+ if (bytes[start] === 0x2d && bytes[start + 1] === 0x2d)
32
+ break;
33
+ const headerEnd = indexOf(bytes, blank, start);
34
+ if (headerEnd < 0)
35
+ break;
36
+ const next = indexOf(bytes, delimiter, headerEnd + 4);
37
+ if (next < 0)
38
+ break;
39
+ const head = decoder.decode(bytes.subarray(start, headerEnd));
40
+ const disposition = /content-disposition:[^\r\n]*/i.exec(head)?.[0] ?? '';
41
+ parts.push({
42
+ name: /\bname="([^"]*)"/i.exec(disposition)?.[1] ?? '',
43
+ filename: /\bfilename="([^"]*)"/i.exec(disposition)?.[1] ?? null,
44
+ type: /content-type:\s*([^\r\n]+)/i.exec(head)?.[1]?.trim() ?? null,
45
+ // the CRLF before the next delimiter is the delimiter's, not the part's
46
+ body: bytes.slice(headerEnd + 4, next - 2),
47
+ });
48
+ at = next;
49
+ }
50
+ return parts;
51
+ }
@@ -1,9 +1,12 @@
1
1
  import { type SubjectFields } from './hash.js';
2
+ /** `adopts`: the local id of a subject a deploy settled with no vendor id, which this observation is (refresh's
3
+ * adoption, architecture "Refresh"): the observation lands as that subject's alias, and the local one stands. */
2
4
  export type ObservedResource = {
3
5
  type: string;
4
6
  id: string;
5
7
  fields: SubjectFields;
6
8
  deleted?: boolean;
9
+ adopts?: string;
7
10
  };
8
11
  export type ObserveReport = {
9
12
  observed: number;
@@ -18,7 +21,7 @@ export type Observation = {
18
21
  resource: ObservedResource;
19
22
  } | {
20
23
  resources: ObservedResource[];
21
- complete?: string[];
24
+ complete?: Completed[];
22
25
  });
23
26
  /** Run `fn` with every `observeResource` inside it COLLECTED instead of folded; the caller folds the
24
27
  * batch (`observeResources`) once `fn` returns. The kernel's refresh does this around an adapter. */
@@ -26,11 +29,18 @@ export declare function collectObservations<T>(fn: () => Promise<T>): Promise<{
26
29
  value: T;
27
30
  observations: Observation[];
28
31
  }>;
32
+ /** A type a batch listed in full: all of it, or (a type under a parent) the subjects whose `field` names one of the
33
+ * parents listed `within`. */
34
+ export type Completed = string | {
35
+ type: string;
36
+ field: string;
37
+ within: string[];
38
+ };
29
39
  /** Fold a collected batch: one `observeResources` per service, `complete` applied to each. */
30
40
  export declare function foldObservations(observations: Observation[], opts?: {
31
41
  root?: string;
32
42
  at?: string;
33
- complete?: string[];
43
+ complete?: Completed[];
34
44
  batch?: string;
35
45
  }): ObserveReport;
36
46
  /** One observed resource: collected when a refresh scope is open, else folded onto the root's log now. */
@@ -39,11 +49,11 @@ export declare function observeResource(service: string, resource: ObservedResou
39
49
  at?: string;
40
50
  }): ObserveReport;
41
51
  /** The fold: diff each resource against upstream state, append what changed to the root's log. `complete`
42
- * names the types this batch listed in full — a subject of such a type the batch does not hold is
43
- * tombstoned. Entry ids are content-addressed, so the same observation folds once. */
52
+ * names the types this batch listed in full (or in full under the parents it names) — a subject of such a type the
53
+ * batch does not hold is tombstoned. Entry ids are content-addressed, so the same observation folds once. */
44
54
  export declare function observeResources(service: string, resources: ObservedResource[], opts?: {
45
55
  root?: string;
46
56
  at?: string;
47
- complete?: string[];
57
+ complete?: Completed[];
48
58
  batch?: string;
49
59
  }): ObserveReport;
@@ -7,16 +7,17 @@
7
7
  // unchanged resource appends nothing; a listing the adapter declares complete tombstones what it
8
8
  // no longer holds. A refresh runs inside a scope that collects the adapter's observations and folds
9
9
  // them as one batch; an ingest observes directly, one resource at a time.
10
- import { withAncestryLock } from "./ancestry.js";
10
+ import { withHistoryLock } from "./ancestry.js";
11
11
  import { AsyncLocalStorage } from 'node:async_hooks';
12
12
  import { appendParentEntry, readParentTreeMap } from "./log.js";
13
13
  import { hashFieldValue } from "./hash.js";
14
14
  import { worldNow } from "./world-clock.js";
15
+ import { worldPaths } from "./storage.js";
15
16
  // ONE sink per process, whichever copy of the kernel a pack resolved: a pack installed beside a
16
17
  // world carries its own `@volter/world-core`, and its observations must reach the scope the runtime
17
18
  // opened. A module-level instance would be one per copy; a process-global symbol is one per process.
18
19
  const SINK = Symbol.for('volter.observe.sink');
19
- // created on first use, never at import: a mirror's CLIENT bundle (target browser) carries this module
20
+ // created on first use, never at import: a browser bundle carrying the kernel carries this module
20
21
  // through the pack's shared helpers, and a browser has no AsyncLocalStorage — constructing one at load
21
22
  // would kill the whole client ("not a constructor") for a scope the browser never opens
22
23
  const sink = () => (globalThis[SINK] ??= new AsyncLocalStorage());
@@ -75,8 +76,8 @@ function stable(value) {
75
76
  return `{${Object.keys(o).sort().map((k) => `${JSON.stringify(k)}:${stable(o[k])}`).join(',')}}`;
76
77
  }
77
78
  /** The fold: diff each resource against upstream state, append what changed to the root's log. `complete`
78
- * names the types this batch listed in full — a subject of such a type the batch does not hold is
79
- * tombstoned. Entry ids are content-addressed, so the same observation folds once. */
79
+ * names the types this batch listed in full (or in full under the parents it names) — a subject of such a type the
80
+ * batch does not hold is tombstoned. Entry ids are content-addressed, so the same observation folds once. */
80
81
  export function observeResources(service, resources, opts = {}) {
81
82
  const collecting = typeof AsyncLocalStorage === 'function' ? sink().getStore() : undefined;
82
83
  if (collecting) {
@@ -86,17 +87,17 @@ export function observeResources(service, resources, opts = {}) {
86
87
  return foldResources(service, resources, opts);
87
88
  }
88
89
  function foldResources(service, resources, opts) {
89
- return withAncestryLock(() => {
90
+ return withHistoryLock(worldPaths(service, opts.root).dir, () => {
90
91
  const at = opts.at ?? worldNow();
91
92
  // one look is one batch: every entry it appends carries the same id, and no position may split it
92
93
  const batch = opts.batch ?? `obs:${service}:${at}`;
93
94
  const tree = readParentTreeMap(service, opts.root);
94
95
  const report = { observed: resources.length, appended: 0, unchanged: 0, removed: 0 };
95
96
  const seen = new Set();
96
- const append = (subject, fields) => {
97
+ const append = (subject, fields, aliasOf) => {
97
98
  const entry = {
98
- id: `observed:${service}:${subject.type}:${subject.id}:${hashFieldValue({ subject, fields, at })}`,
99
- service, op: 'set', operation: 'observed', subject, occurredAt: at, actor: { kind: 'system', id: 'vendor' }, fields, batch,
99
+ id: `observed:${service}:${subject.type}:${subject.id}:${hashFieldValue({ subject, fields, at, ...(aliasOf ? { aliasOf } : {}) })}`,
100
+ service, op: 'set', operation: 'observed', subject, occurredAt: at, actor: { kind: 'system', id: 'vendor' }, fields, batch, ...(aliasOf ? { aliasOf } : {}),
100
101
  };
101
102
  const { appended } = appendParentEntry(entry, opts.root);
102
103
  const key = `${subject.type}:${subject.id}`;
@@ -110,6 +111,15 @@ function foldResources(service, resources, opts) {
110
111
  for (const r of resources) {
111
112
  const key = `${r.type}:${r.id}`;
112
113
  seen.add(key);
114
+ // an adoption: the vendor's subject is the local one a deploy made with no id, aliased to it
115
+ if (r.adopts !== undefined && !tree.has(key)) {
116
+ seen.add(`${r.type}:${r.adopts}`);
117
+ if (append({ type: r.type, id: r.id }, r.fields, r.adopts))
118
+ report.appended += 1;
119
+ else
120
+ report.unchanged += 1;
121
+ continue;
122
+ }
113
123
  const existing = tree.get(key);
114
124
  if (r.deleted) {
115
125
  if (existing && existing.fields.deleted !== true) {
@@ -135,10 +145,14 @@ function foldResources(service, resources, opts) {
135
145
  else
136
146
  report.unchanged += 1;
137
147
  }
138
- for (const type of opts.complete ?? []) {
148
+ for (const c of opts.complete ?? []) {
149
+ const type = typeof c === 'string' ? c : c.type;
150
+ const within = typeof c === 'string' ? undefined : new Set(c.within);
139
151
  for (const s of tree.values()) {
140
152
  if (s.type !== type || seen.has(`${s.type}:${s.id}`) || s.fields.deleted === true)
141
153
  continue;
154
+ if (within && typeof c !== 'string' && !within.has(String(s.fields[c.field])))
155
+ continue;
142
156
  if (append({ type: s.type, id: s.id }, { deleted: true }))
143
157
  report.removed += 1;
144
158
  }
@@ -0,0 +1,108 @@
1
+ import type { PackScenario } from './pack-fetch.js';
2
+ import type { ScenarioDecision } from './scenario.js';
3
+ type Row = Record<string, unknown>;
4
+ /** A turn: the text, or tool calls, the reasoning when there is some, and why it finished. */
5
+ export type WireTurn = {
6
+ text?: string | null;
7
+ toolCalls?: Array<{
8
+ name: string;
9
+ arguments: Row;
10
+ }>;
11
+ reasoning?: string | null;
12
+ finishReason?: string;
13
+ };
14
+ /** Tokens, estimated at about four characters a token: a twin's count, not a tokenizer's. */
15
+ export declare const estimateTokens: (text: string) => number;
16
+ /** A message's content as text: a string, or its text parts joined. */
17
+ export declare const chatText: (content: unknown) => string;
18
+ /** The last user message's text. */
19
+ export declare function chatLastUserText(messages: Row[]): string;
20
+ /** The tool results the last message carries (a `tool` message), by its name or call id. */
21
+ export declare const chatToolResults: (messages: Row[]) => string[];
22
+ /** The labeled stub for a chat request: with tools offered (`tool_choice` not `none`) and the last message not a tool's
23
+ * result, it calls the tool the choice names, else the first, with arguments its schema admits; with a JSON response
24
+ * format, it answers a value the schema admits; otherwise it echoes the last user message. `label` names the twin. */
25
+ export declare function chatStubTurn(body: Row, label: string): WireTurn;
26
+ export type ChatShape = {
27
+ id: string;
28
+ model: string;
29
+ created: number;
30
+ turn: WireTurn;
31
+ messages: Row[];
32
+ /** the request's max_tokens / max_completion_tokens: a longer text is cut there and finishes `length` */
33
+ maxTokens?: number;
34
+ /** each tool call's id */
35
+ callId: (i: number) => string;
36
+ /** fields a vendor adds at the top of the answer and of each chunk (OpenRouter's `provider`) */
37
+ extra?: Row;
38
+ /** top-level fields on interim stream chunks instead of extra; terminal chunks retain extra */
39
+ interimExtra?: Row;
40
+ /** fields a vendor adds to the usage (OpenRouter's `cost`) */
41
+ usageExtra?: Row;
42
+ /** the vendor also answers the provider's own finish reason (`native_finish_reason`) */
43
+ nativeFinish?: boolean;
44
+ /** the name the reasoning takes in a message (`reasoning`, or `reasoning_content`) */
45
+ reasoningField?: string;
46
+ };
47
+ /** A chat completion's body (`object: chat.completion`), one choice. */
48
+ export declare function chatCompletion(s: ChatShape): Row;
49
+ /** A chat completion streamed: its chunks (`object: chat.completion.chunk`) as Server-Sent Events, the text in two
50
+ * deltas, the last chunk carrying the finish reason and the usage, then `data: [DONE]`. `comment` is a keep-alive a
51
+ * vendor sends first (OpenRouter's `: OPENROUTER PROCESSING`). */
52
+ export declare function chatCompletionSSE(s: ChatShape & {
53
+ comment?: string;
54
+ }): string;
55
+ /** A V4 call's options as the chat completions request they mean: its prompt as messages, its tools, tool choice,
56
+ * response format and output limit; so the chat stub and the wire's scenario read it. */
57
+ export declare function aiSdkChatBody(call: Row, model: string): Row;
58
+ /** A V4 generate result: the turn's content (reasoning, text, tool calls with their input as JSON text), its finish
59
+ * reason (unified, and the raw one) and its usage; `extra` is what the server adds (the gateway's provider metadata). */
60
+ export declare function languageModelResult(s: ChatShape): Row;
61
+ /** A V4 stream: its parts as Server-Sent Events (stream-start, response-metadata, the reasoning, the text in two deltas,
62
+ * each tool call's input then the call, and the finish), with no `[DONE]`: the SDK reads every event as a part. */
63
+ export declare function languageModelSSE(s: ChatShape): string;
64
+ /** A Responses request's input as items: a string is one user message. */
65
+ export declare const responsesItems: (input: unknown) => Row[];
66
+ /** The last user message's text in a Responses input. */
67
+ export declare function responsesLastUserText(input: unknown): string;
68
+ /** The function-call outputs the input ends with. */
69
+ export declare const responsesToolResults: (input: unknown) => string[];
70
+ /** The labeled stub for a Responses request: a function tool is called (unless the input already carries its output or
71
+ * `tool_choice` is `none`), a JSON schema format answers a value it admits, otherwise the last user text is echoed. */
72
+ export declare function responsesStubTurn(body: Row, label: string): WireTurn;
73
+ export type ResponsesShape = {
74
+ id: string;
75
+ model: string;
76
+ createdAt: number;
77
+ turn: WireTurn;
78
+ body: Row;
79
+ /** ids for the output items: the message, the reasoning, each function call and its call id */
80
+ itemId: (kind: 'msg' | 'rs' | 'fc' | 'call', i: number) => string;
81
+ extra?: Row;
82
+ };
83
+ /** A Responses object (`object: response`, `status: completed`): its output items (the reasoning when there is some,
84
+ * the message with its output text, or function calls) and its usage. */
85
+ export declare function responsesAnswer(s: ResponsesShape): Row;
86
+ /** A Responses object streamed: the events the wire sends (`response.created`, `response.in_progress`, each output
87
+ * item's added, its text deltas and done, `response.completed`), each `event:` and `data:` with its sequence number. */
88
+ export declare function responsesSSE(s: ResponsesShape): string;
89
+ export type WireRequest = {
90
+ model: string;
91
+ text: string;
92
+ lastUser: string;
93
+ tools: string[];
94
+ results: string[];
95
+ toolsForbidden?: boolean;
96
+ };
97
+ /** Whether a request forbids every tool call: OpenAI's `tool_choice: 'none'`, Anthropic's `{ type: 'none' }`. */
98
+ export declare const forbidsTools: (choice: unknown) => boolean;
99
+ /** The turn a scenario's decision scripts (`respond`: text, toolCalls, reasoning, finishReason), or undefined. */
100
+ export declare function scenarioTurn(decision: ScenarioDecision | undefined): WireTurn | undefined;
101
+ /** A request as the scenario reads it: its model, its text, its last user text, the tools it offers and the tool
102
+ * results it ends with, from a chat completion's messages or a Responses input. */
103
+ export declare function wireRequest(body: Row): WireRequest;
104
+ /** The scenario of a vendor on this wire: its operations, and its error body for a scripted fault. `readers` reads an
105
+ * operation whose request is another wire's (a gateway's Anthropic Messages surface: anthropicWire's
106
+ * `messagesWireRequest`), so one scenario scripts every surface's turns alike. */
107
+ export declare function scenario(vendor: string, operations: string[], faultBody: (status: number, message: string | undefined) => unknown, readers?: Record<string, (body: Row) => WireRequest>): PackScenario<WireRequest>;
108
+ export {};