@volter/world-core 3.0.38 → 3.0.40

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 (179) hide show
  1. package/app-route.cjs +26 -25
  2. package/app-route.d.cts +1 -0
  3. package/dist/app-route.cjs +26 -25
  4. package/dist/app-route.d.cts +1 -0
  5. package/dist/generated/pack-facts.json +409 -352
  6. package/dist/inject.cjs +474 -95
  7. package/dist/network-policy.cjs +110 -13
  8. package/dist/network-policy.d.cts +8 -3
  9. package/dist/src/actions.js +2 -1
  10. package/dist/src/anthropic-wire.d.ts +2 -0
  11. package/dist/src/anthropic-wire.js +5 -0
  12. package/dist/src/attestation.d.ts +6 -0
  13. package/dist/src/attestation.js +27 -0
  14. package/dist/src/bytes.d.ts +3 -0
  15. package/dist/src/bytes.js +17 -0
  16. package/dist/src/changeset.js +2 -1
  17. package/dist/src/clickhouse/sql.js +73 -14
  18. package/dist/src/derived-core.d.ts +210 -32
  19. package/dist/src/derived-core.js +510 -244
  20. package/dist/src/derived-real.d.ts +1 -1
  21. package/dist/src/derived-real.js +704 -93
  22. package/dist/src/derived.d.ts +18 -2
  23. package/dist/src/derived.js +21 -2
  24. package/dist/src/events.d.ts +28 -45
  25. package/dist/src/events.js +40 -58
  26. package/dist/src/exact-json.d.ts +10 -0
  27. package/dist/src/exact-json.js +98 -0
  28. package/dist/src/executor.d.ts +17 -0
  29. package/dist/src/executor.js +91 -30
  30. package/dist/src/file-response.js +1 -1
  31. package/dist/src/form.d.ts +8 -0
  32. package/dist/src/form.js +76 -0
  33. package/dist/src/git/format.d.ts +16 -0
  34. package/dist/src/git/format.js +110 -0
  35. package/dist/src/git/index.d.ts +1 -0
  36. package/dist/src/git/index.js +1 -0
  37. package/dist/src/git/lfs.d.ts +1 -1
  38. package/dist/src/git/lfs.js +4 -3
  39. package/dist/src/graphql-wire.d.ts +6 -0
  40. package/dist/src/graphql-wire.js +6 -3
  41. package/dist/src/grpc-wire.js +2 -1
  42. package/dist/src/head.d.ts +1 -0
  43. package/dist/src/head.js +15 -4
  44. package/dist/src/held-reads.d.ts +6 -0
  45. package/dist/src/held-reads.js +60 -0
  46. package/dist/src/history.js +5 -4
  47. package/dist/src/host-port.d.ts +5 -0
  48. package/dist/src/host-port.js +14 -0
  49. package/dist/src/index.d.ts +11 -3
  50. package/dist/src/index.js +9 -4
  51. package/dist/src/log.js +12 -7
  52. package/dist/src/machines.d.ts +81 -5
  53. package/dist/src/machines.js +125 -9
  54. package/dist/src/managed-database.d.ts +15 -11
  55. package/dist/src/managed-database.js +38 -37
  56. package/dist/src/multipart.d.ts +21 -3
  57. package/dist/src/multipart.js +112 -38
  58. package/dist/src/observe.d.ts +3 -0
  59. package/dist/src/observe.js +6 -2
  60. package/dist/src/openai-wire.d.ts +16 -0
  61. package/dist/src/openai-wire.js +43 -12
  62. package/dist/src/pack-fetch.d.ts +4 -6
  63. package/dist/src/pack-fetch.js +189 -57
  64. package/dist/src/packRegistry.d.ts +7 -1
  65. package/dist/src/packRegistry.js +20 -3
  66. package/dist/src/private-transport.d.ts +1 -0
  67. package/dist/src/private-transport.js +11 -0
  68. package/dist/src/protobuf.js +10 -4
  69. package/dist/src/rateBudget.d.ts +8 -1
  70. package/dist/src/rateBudget.js +1 -0
  71. package/dist/src/redis/engine.d.ts +14 -5
  72. package/dist/src/redis/engine.js +271 -72
  73. package/dist/src/redis/frames.d.ts +17 -0
  74. package/dist/src/redis/frames.js +123 -0
  75. package/dist/src/redis/index.d.ts +6 -0
  76. package/dist/src/redis/index.js +6 -0
  77. package/dist/src/redis/json.d.ts +2 -0
  78. package/dist/src/redis/json.js +14 -0
  79. package/dist/src/redis/lua-libs.d.ts +4 -0
  80. package/dist/src/redis/lua-libs.js +173 -0
  81. package/dist/src/redis/lua.d.ts +1 -0
  82. package/dist/src/redis/lua.js +36 -0
  83. package/dist/src/redis/member-storage.d.ts +5 -0
  84. package/dist/src/redis/member-storage.js +75 -0
  85. package/dist/src/redis/resp2.d.ts +4 -0
  86. package/dist/src/redis/resp2.js +29 -0
  87. package/dist/src/redis/stream.d.ts +14 -0
  88. package/dist/src/redis/stream.js +335 -0
  89. package/dist/src/remote-execute.d.ts +2 -2
  90. package/dist/src/request-body.d.ts +11 -0
  91. package/dist/src/request-body.js +11 -0
  92. package/dist/src/runtime.d.ts +6 -5
  93. package/dist/src/runtime.js +5 -5
  94. package/dist/src/s3/wire.d.ts +4 -4
  95. package/dist/src/s3/wire.js +5 -3
  96. package/dist/src/scenario.d.ts +22 -0
  97. package/dist/src/scenario.js +35 -31
  98. package/dist/src/serve-http.d.ts +2 -1
  99. package/dist/src/serve-http.js +10 -1
  100. package/dist/src/serve.js +7 -4
  101. package/dist/src/signing.d.ts +23 -5
  102. package/dist/src/signing.js +74 -11
  103. package/dist/src/sigv4.d.ts +3 -1
  104. package/dist/src/sigv4.js +4 -2
  105. package/dist/src/smtp.js +2 -1
  106. package/dist/src/sockets.js +8 -3
  107. package/dist/src/state-system.d.ts +3 -0
  108. package/dist/src/storage.js +5 -2
  109. package/dist/src/twin-fetch.d.ts +3 -1
  110. package/dist/src/twin-fetch.js +3 -2
  111. package/dist/src/vendor-call.d.ts +10 -3
  112. package/dist/src/vendor-call.js +58 -14
  113. package/dist/test-fixtures/attestation.SOURCE.md +1 -0
  114. package/dist/test-fixtures/attestation.json +50 -0
  115. package/generated/pack-facts.json +409 -352
  116. package/inject.cjs +474 -95
  117. package/network-policy.cjs +110 -13
  118. package/network-policy.d.cts +8 -3
  119. package/package.json +11 -4
  120. package/src/actions.ts +2 -1
  121. package/src/anthropic-wire.ts +6 -0
  122. package/src/attestation.ts +23 -0
  123. package/src/bytes.ts +21 -0
  124. package/src/changeset.ts +2 -1
  125. package/src/clickhouse/sql.ts +50 -16
  126. package/src/derived-core.ts +573 -249
  127. package/src/derived-real.ts +552 -42
  128. package/src/derived.ts +23 -4
  129. package/src/events.ts +54 -85
  130. package/src/exact-json.ts +71 -0
  131. package/src/executor.ts +86 -27
  132. package/src/file-response.ts +1 -1
  133. package/src/form.ts +66 -0
  134. package/src/git/format.ts +89 -0
  135. package/src/git/index.ts +1 -0
  136. package/src/git/lfs.ts +5 -4
  137. package/src/graphql-wire.ts +8 -3
  138. package/src/grpc-wire.ts +2 -1
  139. package/src/head.ts +15 -5
  140. package/src/held-reads.ts +54 -0
  141. package/src/history.ts +5 -4
  142. package/src/host-port.ts +9 -0
  143. package/src/index.ts +10 -4
  144. package/src/log.ts +11 -7
  145. package/src/machines.ts +135 -13
  146. package/src/managed-database.ts +48 -40
  147. package/src/multipart.ts +110 -35
  148. package/src/observe.ts +6 -3
  149. package/src/openai-wire.ts +45 -14
  150. package/src/pack-fetch.ts +173 -50
  151. package/src/packRegistry.ts +27 -4
  152. package/src/private-transport.ts +8 -0
  153. package/src/protobuf.ts +8 -4
  154. package/src/rateBudget.ts +6 -1
  155. package/src/redis/engine.ts +168 -63
  156. package/src/redis/frames.ts +53 -0
  157. package/src/redis/index.ts +6 -0
  158. package/src/redis/json.ts +13 -0
  159. package/src/redis/lua-libs.ts +62 -0
  160. package/src/redis/lua.ts +20 -0
  161. package/src/redis/member-storage.ts +58 -0
  162. package/src/redis/resp2.ts +25 -0
  163. package/src/redis/stream.ts +102 -0
  164. package/src/remote-execute.ts +2 -2
  165. package/src/request-body.ts +12 -0
  166. package/src/runtime.ts +6 -5
  167. package/src/s3/wire.ts +8 -5
  168. package/src/scenario.ts +34 -13
  169. package/src/serve-http.ts +11 -2
  170. package/src/serve.ts +4 -3
  171. package/src/signing.ts +71 -17
  172. package/src/sigv4.ts +5 -3
  173. package/src/smtp.ts +3 -2
  174. package/src/sockets.ts +8 -3
  175. package/src/state-system.ts +3 -0
  176. package/src/storage.ts +5 -2
  177. package/src/twin-fetch.ts +4 -3
  178. package/src/vendor-call.ts +59 -12
  179. package/test-fixtures/attestation.json +50 -0
@@ -1,3 +1,4 @@
1
+ import { parseExactJson } from "./exact-json.js";
1
2
  // Immutable views over shared append-only segments. A position is meaningful within a view.
2
3
  import { createHash } from 'node:crypto';
3
4
  import { join } from 'node:path';
@@ -37,7 +38,7 @@ const viewPath = (directory, id) => {
37
38
  return join(directory, 'views', `${id}.json`);
38
39
  };
39
40
  function rows(directory, file) {
40
- return getActiveWorldStore().readLines(join(directory, file)).filter(line => line.trim()).map(line => toEntry(JSON.parse(line)));
41
+ return getActiveWorldStore().readLines(join(directory, file)).filter(line => line.trim()).map(line => toEntry(parseExactJson(line)));
41
42
  }
42
43
  function sliceLayout(layout, at) {
43
44
  const base = layout.base ? historyLength(layout.base) : 0;
@@ -47,7 +48,7 @@ export function readHistoryView(directory, id) {
47
48
  const raw = getActiveWorldStore().read(viewPath(directory, id));
48
49
  if (raw === null)
49
50
  throw new Error(`Missing retained history view ${id}`);
50
- const { dependencies: _dependencies, ...view } = JSON.parse(raw);
51
+ const { dependencies: _dependencies, ...view } = parseExactJson(raw);
51
52
  if (view.version !== 1 || historyDigest({ ...view, dependencies: _dependencies }) !== id)
52
53
  throw new Error(`Invalid retained history view ${id}`);
53
54
  historyLength(view.layout);
@@ -124,7 +125,7 @@ export function inheritedHistoryKey(service, root) {
124
125
  }
125
126
  if (raw === null)
126
127
  return 'missing-view';
127
- const view = JSON.parse(raw);
128
+ const view = parseExactJson(raw);
128
129
  const directories = new Set([view.owner?.directory ?? '', ...(view.segments ?? []).map((sp) => sp.directory)]);
129
130
  if (!isUrlParent(parent.at))
130
131
  directories.add(worldPaths(service, parent.at).dir);
@@ -166,7 +167,7 @@ export function captureHistory(service, root) {
166
167
  }
167
168
  export function originHead(service, root) {
168
169
  const raw = getActiveWorldStore().read(join(worldPaths(service, root).dir, 'origin-head.json'));
169
- return raw === null ? null : JSON.parse(raw);
170
+ return raw === null ? null : parseExactJson(raw);
170
171
  }
171
172
  /** A complete remote view shares cached rows by content, never by mutable offsets or entry ID. */
172
173
  export function publishOriginHistory(service, root, remoteView, layout, entries) {
@@ -0,0 +1,5 @@
1
+ /** Read a Host authority with the platform URL parser; bracketed IPv6 retains its address. */
2
+ export declare function readHostPort(authority: string): {
3
+ host: string;
4
+ port: string;
5
+ } | undefined;
@@ -0,0 +1,14 @@
1
+ /** Read a Host authority with the platform URL parser; bracketed IPv6 retains its address. */
2
+ export function readHostPort(authority) {
3
+ try {
4
+ if (!authority || /[\s/\\?#@]/.test(authority))
5
+ return undefined;
6
+ const url = new URL(`http://${authority}`);
7
+ if (url.username || url.password)
8
+ return undefined;
9
+ return { host: url.hostname.toLowerCase(), port: url.port };
10
+ }
11
+ catch {
12
+ return undefined;
13
+ }
14
+ }
@@ -11,6 +11,11 @@ export declare const packOf: (manifest: {
11
11
  }, surface?: DerivedSurface | Record<string, {
12
12
  manifest: DerivedManifest;
13
13
  surface: DerivedSurface;
14
+ }>,
15
+ /** the lanes beside a pack that has an API of its own (Clerk's `fapi`, PlanetScale's `api`) */
16
+ beside?: Record<string, {
17
+ manifest: DerivedManifest;
18
+ surface: DerivedSurface;
14
19
  }>) => PackDescriptor;
15
20
  /** The pack registered, so the World finds it. */
16
21
  export declare const registerPack: (pack: PackDescriptor) => PackDescriptor;
@@ -20,7 +25,7 @@ export { verifyEvent } from './events.js';
20
25
  export { deriveStateSystem, deriveVendorStateSystem } from './derived-real.js';
21
26
  export type { IngestDecl } from './derived-core.js';
22
27
  export type { SocketDecl, SocketEngine, SocketSession } from './sockets.js';
23
- export { machinePool, type ExecSpec, type MachinePool, type MachineProcess, type MachineRun, type MachineSpec } from './machines.js';
28
+ export { machinePool, isMachineCapabilityGap, type ExecSpec, type MachinePool, type MachineProcess, type MachineRun, type MachineSpec, type MachineStatus, type MachineFiles, type MachineFileStat } from './machines.js';
24
29
  export type { CorsDecl } from './cors.js';
25
30
  export type { Handler, HandlerContext, WriteHookContext } from './derived-core.js';
26
31
  export type { MultipartPart } from './multipart.js';
@@ -36,18 +41,19 @@ export { VendorUnreachableError } from './vendor-call.js';
36
41
  export { isReadOnlyRequest, READ_ONLY_REQUEST_HEADER } from './request-scope.js';
37
42
  export { serveHttp, serveStream, type HttpHandler, type HttpServer, type ServeHttpOptions } from './serve-http.js';
38
43
  export type { TwinStream, TwinStreamConnection, TwinStreamSink } from './twin-fetch.js';
44
+ export { privateTransportKey } from './private-transport.js';
39
45
  export { siteLabel, siteUrlOf, twinSiteUrl, twinVendorUrl } from './twin-fetch.js';
40
46
  export { statefulTwinManifest, twinManifest } from './scenario.js';
41
47
  export { assetContentType, packAsset } from './pack-assets.js';
42
48
  export { bytesResponse, contentTypeOf, fileResponse } from './file-response.js';
43
- export { certificateOf, digest, digestBytes, equalSecrets, oauth1BaseString, oauth1Encode, oauth1Header, oauth1Signature, hmac, jwks, jwtDecode, jwtSign, jwtVerify, keyId, lettersFrom, md5, publicKeyOf, sha256, signWith, SIGNING_ALGORITHMS, uuidFrom, verifyWith } from './signing.js';
49
+ export { certificateOf, digest, digestBytes, equalSecrets, oauth1BaseString, oauth1Encode, oauth1Header, oauth1Signature, hmac, jwks, jwtDecode, jwtSign, jwtVerify, keyId, lettersFrom, base62From, md5, publicKeyOf, sha256, signWith, SIGNING_ALGORITHMS, uuidFrom, verifyWith } from './signing.js';
44
50
  export type { HandlerCrypto, Jwk, Jwks, JwtVerdict, SigningKey } from './signing.js';
45
51
  export { blobDigest } from './blob-store.js';
46
52
  export { gunzip, gzip } from './bytes.js';
47
53
  export { ownFields } from './log.js';
48
54
  export type { TwinResource } from './serve.js';
49
55
  export { passwordHash, personOf, personWith, recordPerson, type Person } from './people.js';
50
- export { EngineDatabaseError, EngineUnavailableError, quoteIdent, worldClockSql } from './managed-database.js';
56
+ export { EngineDatabaseError, EngineUnavailableError, quoteIdent } from './managed-database.js';
51
57
  export type { Batch as EngineBatch, ManagedDatabase, Statement as EngineStatement, StatementResult as EngineStatementResult } from './managed-database.js';
52
58
  export type { Mail, SmtpRoute } from './smtp.js';
53
59
  export * as s3 from './s3/wire.js';
@@ -64,5 +70,7 @@ export * as redis from './redis/index.js';
64
70
  export * as git from './git/index.js';
65
71
  export { parseScenarioDocument, scenarioFaultResult, ScenarioEngine, ScenarioError } from './scenario.js';
66
72
  export type { PackScenarioAdapter, ScenarioDecision, ScenarioDocument, ScenarioExtractorSpec, ScenarioFault, ScenarioFaultResult, ScenarioFeatures, ScenarioHandler, ScenarioMatcher, ScenarioMissRecord, ScenarioStatus } from './scenario.js';
73
+ export { verifyAttestationBundle } from './attestation.js';
67
74
  export { browserSessionToken, startBrowserSession, endBrowserSession } from './browser-session.js';
75
+ export { readHostPort } from './host-port.js';
68
76
  export { assertBrowserName, browserOrigin, validateBrowserState, validateBrowserStorageState, validateBrowserContext, browserAcceptLanguage, cookieMatchesHost, type BrowserCookie, type BrowserState, type BrowserStorageState, type BrowserContext, type BrowserIndexedDB } from './browser-state.js';
package/dist/src/index.js CHANGED
@@ -8,12 +8,14 @@ import { bindSemantics as bindAny } from "./derived-core.js";
8
8
  import { packOf as packOfAny, registerPack as registerAny } from "./packRegistry.js";
9
9
  /** The descriptor a manifest declares, with its vendor ("The descriptor"); given the pack's generated surface (a vendor of
10
10
  * lanes: each lane's manifest and surface), with the real-system adapters the kernel derives from them. */
11
- export const packOf = (manifest, surface) => packOfAny(manifest, surface);
11
+ export const packOf = (manifest, surface,
12
+ /** the lanes beside a pack that has an API of its own (Clerk's `fapi`, PlanetScale's `api`) */
13
+ beside) => packOfAny(manifest, surface, beside);
12
14
  /** The pack registered, so the World finds it. */
13
15
  export const registerPack = (pack) => registerAny(pack);
14
16
  export { verifyEvent } from "./events.js";
15
17
  export { deriveStateSystem, deriveVendorStateSystem } from "./derived-real.js";
16
- export { machinePool } from "./machines.js";
18
+ export { machinePool, isMachineCapabilityGap } from "./machines.js";
17
19
  /** A pack's handlers, by operationId, bound to the manifest: each is answered over the contract's context. */
18
20
  export const bindSemantics = (m, handlers, scope = {}) => bindAny(m, handlers, scope);
19
21
  // ── the derived fetch ──────────────────────────────────────────────────────────────────────────
@@ -28,13 +30,14 @@ export { VendorUnreachableError } from "./vendor-call.js";
28
30
  export { isReadOnlyRequest, READ_ONLY_REQUEST_HEADER } from "./request-scope.js";
29
31
  // ── the serve factory ──────────────────────────────────────────────────────────────────────────
30
32
  export { serveHttp, serveStream } from "./serve-http.js";
33
+ export { privateTransportKey } from "./private-transport.js";
31
34
  // where a page links a hostname its twin answers, inside the World that shows it
32
35
  export { siteLabel, siteUrlOf, twinSiteUrl, twinVendorUrl } from "./twin-fetch.js";
33
36
  export { statefulTwinManifest, twinManifest } from "./scenario.js";
34
37
  export { assetContentType, packAsset } from "./pack-assets.js";
35
38
  export { bytesResponse, contentTypeOf, fileResponse } from "./file-response.js";
36
39
  // ── the kernel's deterministic helpers ─────────────────────────────────────────────────────────
37
- export { certificateOf, digest, digestBytes, equalSecrets, oauth1BaseString, oauth1Encode, oauth1Header, oauth1Signature, hmac, jwks, jwtDecode, jwtSign, jwtVerify, keyId, lettersFrom, md5, publicKeyOf, sha256, signWith, SIGNING_ALGORITHMS, uuidFrom, verifyWith } from "./signing.js";
40
+ export { certificateOf, digest, digestBytes, equalSecrets, oauth1BaseString, oauth1Encode, oauth1Header, oauth1Signature, hmac, jwks, jwtDecode, jwtSign, jwtVerify, keyId, lettersFrom, base62From, md5, publicKeyOf, sha256, signWith, SIGNING_ALGORITHMS, uuidFrom, verifyWith } from "./signing.js";
38
41
  export { blobDigest } from "./blob-store.js";
39
42
  // compressed bytes a wire carries (a SARIF upload), over web streams
40
43
  export { gunzip, gzip } from "./bytes.js";
@@ -43,7 +46,7 @@ export { ownFields } from "./log.js";
43
46
  // the people a vendor's pages sign in (a `_person` row with its password's hash)
44
47
  export { passwordHash, personOf, personWith, recordPerson } from "./people.js";
45
48
  // the World's managed Postgres a handler reaches as `ctx.engine` (managed-database.ts), and a mail's route (`ctx.mail`)
46
- export { EngineDatabaseError, EngineUnavailableError, quoteIdent, worldClockSql } from "./managed-database.js";
49
+ export { EngineDatabaseError, EngineUnavailableError, quoteIdent } from "./managed-database.js";
47
50
  // S3's wire (contract "S3's wire is a kernel library too") and the git plane (contract "The git plane is a kernel library")
48
51
  export * as s3 from "./s3/wire.js";
49
52
  export * as clickhouse from "./clickhouse/index.js";
@@ -59,5 +62,7 @@ export * as redis from "./redis/index.js";
59
62
  export * as git from "./git/index.js";
60
63
  // ── the scenario grammar (architecture, "Behaviour") ───────────────────────────────────────────
61
64
  export { parseScenarioDocument, scenarioFaultResult, ScenarioEngine, ScenarioError } from "./scenario.js";
65
+ export { verifyAttestationBundle } from "./attestation.js";
62
66
  export { browserSessionToken, startBrowserSession, endBrowserSession } from "./browser-session.js";
67
+ export { readHostPort } from "./host-port.js";
63
68
  export { assertBrowserName, browserOrigin, validateBrowserState, validateBrowserStorageState, validateBrowserContext, browserAcceptLanguage, cookieMatchesHost } from "./browser-state.js";
package/dist/src/log.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { parseExactJson } from "./exact-json.js";
1
2
  // THE LOG (docs/concepts/the-model.md). One kind of entry, one log per
2
3
  // branch, and a branch is a parent plus a position in the parent's log:
3
4
  //
@@ -45,7 +46,7 @@ function parseLines(path, lines, offset, out) {
45
46
  if (!line.trim())
46
47
  continue;
47
48
  try {
48
- out.push(JSON.parse(line));
49
+ out.push(parseExactJson(line));
49
50
  }
50
51
  catch (error) {
51
52
  throw new Error(`${path}:${offset + i + 1}: invalid JSONL row: ${error.message}`);
@@ -115,7 +116,7 @@ export function readBranchMeta(service, root) {
115
116
  const raw = getActiveWorldStore().read(branchMetaPath(service, root));
116
117
  if (raw === null)
117
118
  return null;
118
- const meta = JSON.parse(raw);
119
+ const meta = parseExactJson(raw);
119
120
  // a pointer written before positions were one number: the parent's whole log is parent then branch
120
121
  if (meta.parent && meta.parent.at === undefined && meta.parent.root !== undefined)
121
122
  meta.parent = { at: meta.parent.root, position: (meta.parent.parentCount ?? 0) + (meta.parent.branchCount ?? 0) };
@@ -274,7 +275,7 @@ export function landedIds(parent) {
274
275
  export function aliasesFrom(parent) {
275
276
  const vendorHeld = new Set();
276
277
  for (const e of parent)
277
- if (e.batch !== undefined || e.operation === 'observed' || e.event !== undefined || e.landsId !== undefined)
278
+ if (e.batch !== undefined || e.operation === 'observed' || e.event !== undefined || (e.landsId !== undefined && e.receipt?.status === 'deployed'))
278
279
  vendorHeld.add(`${e.subject.type}:${e.subject.id}`);
279
280
  const out = new Map();
280
281
  for (const e of parent) {
@@ -355,7 +356,7 @@ export function treeResources(tree) {
355
356
  for (const [k, v] of Object.entries(s.fields)) {
356
357
  if (!META.has(k))
357
358
  out[k] = v;
358
- else if (v !== undefined && v !== out[k])
359
+ else if (v !== undefined)
359
360
  own[k] = v;
360
361
  }
361
362
  if (Object.keys(own).length)
@@ -376,7 +377,11 @@ export function ownFields(r) {
376
377
  const { id: _id, type: _type, updatedAt: _at, ...rest } = r;
377
378
  const own = (r[OWN] ?? {});
378
379
  // restored first: a vendor's object leads with its id, and a reader's grep may count on the order
379
- return { ...(own.id !== undefined ? { id: own.id } : {}), ...(own.type !== undefined ? { type: own.type } : {}), ...rest, ...(own.updatedAt !== undefined ? { updatedAt: own.updatedAt } : {}) };
380
+ const out = { ...(own.id !== undefined ? { id: own.id } : {}), ...(own.type !== undefined ? { type: own.type } : {}), ...rest, ...(own.updatedAt !== undefined ? { updatedAt: own.updatedAt } : {}) };
381
+ // the own record rides along unseen, so an own view taken of an own view is the same view (never one that drops them)
382
+ if (r[OWN] !== undefined)
383
+ Object.defineProperty(out, OWN, { value: own, enumerable: false });
384
+ return out;
380
385
  }
381
386
  export function dropCheckpoint(service, root) { getActiveWorldStore().remove(checkpointPath(service, root)); }
382
387
  /** Preserve each ancestor's observed-then-local overlay, including landed IDs and aliases. */
@@ -579,7 +584,7 @@ function readTreeMapFull(service, root, opts = {}) {
579
584
  const facts = opts.until === undefined ? `${parentKeyOf(service, root)}#${statKey(branchLogPath(service, root))}` : null;
580
585
  if (facts !== null) {
581
586
  const held = getActiveWorldStore().read(checkpointPath(service, root));
582
- const cut = held === null ? null : JSON.parse(held);
587
+ const cut = held === null ? null : parseExactJson(held);
583
588
  if (cut?.facts === facts)
584
589
  return new Map(cut.subjects.map(s => [`${s.type}:${s.id}`, { ...s, fields: { ...s.fields } }]));
585
590
  }
@@ -593,7 +598,7 @@ function readTreeMapFull(service, root, opts = {}) {
593
598
  const parentDigest = hashFieldValue({ inherited, own });
594
599
  const branchDigest = hashFieldValue(branch);
595
600
  const raw = opts.until === undefined ? getActiveWorldStore().read(checkpointPath(service, root)) : null;
596
- const cp = raw === null ? null : JSON.parse(raw);
601
+ const cp = raw === null ? null : parseExactJson(raw);
597
602
  if (cp?.parentDigest === parentDigest && cp.branchDigest === branchDigest)
598
603
  return new Map(cp.subjects.map(s => [`${s.type}:${s.id}`, { ...s, fields: { ...s.fields } }]));
599
604
  const layout = { ...(inherited ? { base: inherited.layout } : {}), parent: own.length, branch: branch.length };
@@ -4,13 +4,61 @@ export type MachineSpec = {
4
4
  env?: Record<string, string>;
5
5
  ports?: number[];
6
6
  command?: string[];
7
+ checks?: Array<{
8
+ port: number;
9
+ type: 'http' | 'tcp';
10
+ path?: string;
11
+ }>;
12
+ resetOnStop?: boolean;
13
+ mounts?: Array<{
14
+ name: string;
15
+ path: string;
16
+ }>;
17
+ signal?: AbortSignal;
18
+ startTimeoutMs?: number;
19
+ };
20
+ export type GuestImage = {
21
+ context: string;
22
+ dockerfile?: string;
23
+ runtime?: 'node' | 'bun' | 'python' | 'deno';
24
+ command?: string[];
25
+ cwd?: string;
26
+ env?: Record<string, string>;
27
+ prepared?: boolean;
28
+ };
29
+ export type LightPoolOptions = {
30
+ images?: Record<string, GuestImage>;
7
31
  };
8
32
  export type MachineRun = {
9
33
  ports: Array<{
10
34
  internal: number;
11
35
  host: number;
36
+ url?: string;
12
37
  }>;
13
38
  };
39
+ export type MachineStatus = {
40
+ state: 'started' | 'paused' | 'stopped' | 'exited' | 'failed';
41
+ exitCode?: number;
42
+ reason?: string;
43
+ };
44
+ export type MachineFileStat = {
45
+ type: 'file' | 'directory';
46
+ size: number;
47
+ mode: number;
48
+ mtime: string;
49
+ };
50
+ export type MachineFiles = {
51
+ read(path: string): Promise<Uint8Array>;
52
+ write(path: string, bytes: Uint8Array): Promise<unknown>;
53
+ list(path: string): Promise<Array<{
54
+ name: string;
55
+ type: 'file' | 'directory';
56
+ size: number;
57
+ }>>;
58
+ mkdir(path: string): Promise<unknown>;
59
+ remove(path: string): Promise<unknown>;
60
+ stat(path: string): Promise<MachineFileStat>;
61
+ };
14
62
  /** A process started in a machine: its pid, and what it writes and how it ends, as they happen. */
15
63
  export type MachineProcess = {
16
64
  pid: number;
@@ -27,24 +75,52 @@ export type ExecSpec = {
27
75
  env?: Record<string, string>;
28
76
  cwd?: string;
29
77
  };
78
+ export type MachineHttpReply = {
79
+ port: number;
80
+ method: string;
81
+ path: string;
82
+ requestText?: string;
83
+ status: number;
84
+ headers?: Record<string, string>;
85
+ body?: string;
86
+ };
30
87
  export type MachinePool = {
31
- readonly kind: 'docker' | 'none' | 'fake';
88
+ readonly kind: 'docker' | 'none' | 'fake' | 'light';
89
+ /** Optional capability admission, before vendor resource creation; starts no process. */
90
+ admitRun?(spec: MachineSpec): Promise<void>;
32
91
  /** runs the image; a failure (no container runtime, an image that will not start) rejects with the runtime's message */
33
92
  run(spec: MachineSpec): Promise<MachineRun>;
34
93
  /** starts a process in a running machine; a machine the pool does not run rejects */
35
94
  exec(name: string, spec: ExecSpec): Promise<MachineProcess>;
95
+ request(name: string, port: number, request: Request): Promise<Response>;
96
+ /** Undefined means the metadata-only/fake backing has no process lifecycle to observe. */
97
+ status(name: string): Promise<MachineStatus | undefined>;
98
+ ports(name: string): Promise<number[]>;
99
+ files(name: string): Promise<MachineFiles>;
100
+ pause(name: string): Promise<void>;
101
+ resume(name: string): Promise<void>;
36
102
  stop(name: string): Promise<void>;
37
103
  remove(name: string): Promise<void>;
38
104
  /** the fake pool's record of what it was asked */
39
105
  readonly asked?: Array<{
40
- act: 'run' | 'exec' | 'stop' | 'remove';
106
+ act: 'run' | 'exec' | 'request' | 'pause' | 'resume' | 'stop' | 'remove';
41
107
  name: string;
42
108
  spec?: MachineSpec;
43
109
  exec?: ExecSpec;
110
+ port?: number;
111
+ path?: string;
112
+ requestText?: string;
44
113
  }>;
45
114
  };
115
+ /** A declared compute capability gap, distinct from a failed admitted guest. */
116
+ export declare function isMachineCapabilityGap(error: unknown): boolean;
46
117
  export declare const MACHINE_POOL = "_machine_pool";
47
- export declare const POOL_KINDS: readonly ["docker", "none", "fake"];
118
+ export declare const POOL_KINDS: readonly ["docker", "none", "fake", "light"];
48
119
  /** The pool of a kind, for a World (`world`: the mark its containers carry). */
49
- export declare function machinePool(kind: MachinePool['kind'], world?: string): MachinePool;
50
- export declare function poolFor(service: string, root: string | undefined, kind: MachinePool['kind']): MachinePool;
120
+ export declare function machinePool(kind: MachinePool['kind'], world?: string, httpReplies?: readonly MachineHttpReply[], light?: LightPoolOptions, root?: string): MachinePool;
121
+ export declare function poolFor(service: string, root: string | undefined, kind: MachinePool['kind'], httpReplies?: readonly MachineHttpReply[], light?: LightPoolOptions): MachinePool;
122
+ /** Re-enrolling a verification pool starts a fresh fake execution record. */
123
+ export declare function resetFakePool(service: string, root: string | undefined): void;
124
+ /** Every machine mutation shares the tree's refusal seam. HTTP/RPC reads must be explicitly
125
+ * classified by the pack; a computed operation may execute arbitrary guest code. */
126
+ export declare function guardedMachinePool(pool: MachinePool, service: string, safeRead: boolean): MachinePool;
@@ -1,12 +1,51 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
1
9
  // The World's machine pool (architecture, "Other wires: … machines"): what runs a vendor's customer's image when the
2
10
  // vendor is one that runs images (Fly's Machines). It is a platform provider enrolled through the kernel's door
3
11
  // `POST /_twin/machine-pool`, recorded in the pack's own tree; a handler reaches it as `ctx.machines`. `docker` runs
4
12
  // each machine as a local container with its internal ports published on loopback; `none` runs nothing; `fake`
5
13
  // records what it was asked (capability verification). Nothing here runs at module scope, and `node:child_process`
6
- // is reached only when a docker pool is asked to act.
14
+ // is reached only when a compute pool is asked to act. The light pool delegates to the host's Substrate adapter.
15
+ import { refuseReadOnlyWrite } from "./request-scope.js";
7
16
  import { nodeBuiltin } from "./serve-http.js";
17
+ /** A declared compute capability gap, distinct from a failed admitted guest. */
18
+ export function isMachineCapabilityGap(error) {
19
+ return typeof error === 'object' && error !== null && 'code' in error && error.code === 'WORLD_MACHINE_CAPABILITY_GAP';
20
+ }
8
21
  export const MACHINE_POOL = '_machine_pool';
9
- export const POOL_KINDS = ['docker', 'none', 'fake'];
22
+ export const POOL_KINDS = ['docker', 'none', 'fake', 'light'];
23
+ /** The host owns provider bootstrapping. Browser/kernel imports stay inert, and a missing
24
+ * host provider refuses rather than falling back to metadata-only success. */
25
+ function lightPool(world, root, options) {
26
+ let ready;
27
+ const load = () => ready ??= (async () => {
28
+ const entry = typeof process === 'undefined' ? undefined : process.env.VOLTER_WORLD_GUEST_RUNNER;
29
+ if (!entry)
30
+ throw new Error('light guest runner is unavailable: run this twin inside a World');
31
+ const provider = await import(__rewriteRelativeImportExtension(entry));
32
+ return provider.createLightGuestPool({ world, root, ...options });
33
+ })();
34
+ return {
35
+ kind: 'light',
36
+ admitRun: async (spec) => { await (await load()).admitRun?.(spec); },
37
+ run: async (spec) => (await load()).run(spec),
38
+ exec: async (name, spec) => (await load()).exec(name, spec),
39
+ request: async (name, port, request) => (await load()).request(name, port, request),
40
+ status: async (name) => (await load()).status(name),
41
+ ports: async (name) => (await load()).ports(name),
42
+ files: async (name) => (await load()).files(name),
43
+ pause: async (name) => (await load()).pause(name),
44
+ resume: async (name) => (await load()).resume(name),
45
+ stop: async (name) => (await load()).stop(name),
46
+ remove: async (name) => (await load()).remove(name),
47
+ };
48
+ }
10
49
  /** A container's name for a machine: the World's prefix, the World's own mark (a machine id repeats across Worlds and
11
50
  * branches, which mint alike), then the vendor's machine name made safe for docker. */
12
51
  const containerName = (world, name) => `volter-world-machine-${world}-${name.replace(/[^A-Za-z0-9_.-]/g, '-')}`;
@@ -21,6 +60,12 @@ function dockerPool(world) {
21
60
  });
22
61
  return {
23
62
  kind: 'docker',
63
+ async status(name) {
64
+ const state = JSON.parse(await docker(['inspect', '--format', '{{json .State}}', containerName(world, name)]));
65
+ return state.Running ? { state: 'started' } : { state: 'exited', exitCode: state.ExitCode, ...(state.Error ? { reason: state.Error } : {}) };
66
+ },
67
+ async ports() { throw new Error('port discovery requires the light guest backing'); },
68
+ async files() { throw new Error('file access requires the light guest backing'); },
24
69
  async run(spec) {
25
70
  const name = containerName(world, spec.name);
26
71
  await docker(['rm', '-f', name]).catch(() => undefined);
@@ -105,47 +150,118 @@ function dockerPool(world) {
105
150
  clearTimeout(timer);
106
151
  }
107
152
  },
153
+ async request(name, port, request) {
154
+ if (!Number.isInteger(port) || port < 1 || port > 65535)
155
+ throw new Error('invalid machine port');
156
+ const line = (await docker(['port', containerName(world, name), String(port)])).split('\n')[0] ?? '';
157
+ const host = Number(line.split(':').at(-1));
158
+ if (!Number.isInteger(host) || host < 1 || host > 65535 || !line.startsWith('127.0.0.1:'))
159
+ throw new Error('the machine port is not published on loopback');
160
+ const source = new URL(request.url);
161
+ const headers = new Headers(request.headers);
162
+ for (const key of ['host', 'connection', 'content-length', 'transfer-encoding'])
163
+ headers.delete(key);
164
+ const body = ['GET', 'HEAD'].includes(request.method) ? undefined : await request.clone().arrayBuffer();
165
+ return fetch(`http://127.0.0.1:${host}${source.pathname}${source.search}`, { method: request.method, headers, body, redirect: 'manual', signal: request.signal });
166
+ },
167
+ async pause(name) { await docker(['pause', containerName(world, name)]); },
168
+ async resume(name) { await docker(['unpause', containerName(world, name)]); },
108
169
  async stop(name) { await docker(['stop', containerName(world, name)]).catch(() => undefined); },
109
170
  async remove(name) { await docker(['rm', '-f', containerName(world, name)]).catch(() => undefined); },
110
171
  };
111
172
  }
112
173
  /** The pool of a kind, for a World (`world`: the mark its containers carry). */
113
- export function machinePool(kind, world = 'world') {
174
+ export function machinePool(kind, world = 'world', httpReplies = [], light = {}, root) {
175
+ if (kind === 'light')
176
+ return lightPool(world, root, light);
114
177
  if (kind === 'docker')
115
178
  return dockerPool(world);
116
179
  if (kind === 'fake') {
117
180
  const asked = [];
181
+ const running = new Map();
118
182
  return {
119
183
  kind, asked,
120
- async run(spec) { asked.push({ act: 'run', name: spec.name, spec }); return { ports: (spec.ports ?? []).map((internal) => ({ internal, host: 40_000 + internal })) }; },
184
+ async status() { return undefined; },
185
+ async ports(name) { return running.get(name)?.ports ?? []; },
186
+ async files() { throw new Error('the fake pool has no filesystem'); },
187
+ async run(spec) { running.set(spec.name, { ports: spec.ports ?? [], paused: false }); asked.push({ act: 'run', name: spec.name, spec }); return { ports: (spec.ports ?? []).map((internal) => ({ internal, host: 40_000 + internal })) }; },
121
188
  async exec(name, spec) {
189
+ if (!running.has(name) || running.get(name).paused)
190
+ throw new Error('the fake machine is not running');
122
191
  asked.push({ act: 'exec', name, exec: spec });
123
192
  // a fake process: its pid the count of processes asked, and it ends at once
124
193
  const pid = asked.filter((a) => a.act === 'exec').length + 100;
125
194
  return { pid, events: (async function* () { yield { exit: 0 }; })() };
126
195
  },
127
- async stop(name) { asked.push({ act: 'stop', name }); },
128
- async remove(name) { asked.push({ act: 'remove', name }); },
196
+ async request(name, port, request) {
197
+ const machine = running.get(name);
198
+ if (!machine || machine.paused || !machine.ports.includes(port))
199
+ throw new Error('the fake machine port is not running');
200
+ const url = new URL(request.url), path = url.pathname + url.search;
201
+ const requestText = ['GET', 'HEAD'].includes(request.method) ? '' : await request.clone().text();
202
+ asked.push({ act: 'request', name, port, path, requestText });
203
+ const reply = httpReplies.find((r) => r.port === port && r.method === request.method && r.path === path && (r.requestText === undefined || r.requestText === requestText));
204
+ if (!reply)
205
+ throw new Error('no fake HTTP reply matches');
206
+ return new Response(reply.body ?? null, { status: reply.status, headers: reply.headers });
207
+ },
208
+ async pause(name) { if (!running.has(name))
209
+ throw new Error('no fake machine'); running.get(name).paused = true; asked.push({ act: 'pause', name }); },
210
+ async resume(name) { if (!running.has(name))
211
+ throw new Error('no fake machine'); running.get(name).paused = false; asked.push({ act: 'resume', name }); },
212
+ async stop(name) { running.delete(name); asked.push({ act: 'stop', name }); },
213
+ async remove(name) { running.delete(name); asked.push({ act: 'remove', name }); },
129
214
  };
130
215
  }
131
216
  return {
132
217
  kind: 'none', async run() { return { ports: [] }; },
218
+ async status() { return undefined; },
219
+ async ports() { throw new Error('no guest ports'); },
220
+ async files() { throw new Error('no guest files'); },
133
221
  async exec() { throw new Error('no machine runs in this World: its machine pool is none'); },
222
+ async request() { throw new Error('no machine runs in this World: its machine pool is none'); },
223
+ async pause() { },
224
+ async resume() { },
134
225
  async stop() { }, async remove() { },
135
226
  };
136
227
  }
137
228
  /** A vendor's pool in a World, one per World and kind for the life of the process (a fake pool keeps what it was asked). */
138
229
  const pools = new Map();
139
- export function poolFor(service, root, kind) {
140
- const key = `${service}\0${root ?? ''}\0${kind}`;
230
+ export function poolFor(service, root, kind, httpReplies = [], light = {}) {
231
+ const key = `${service}\0${root ?? ''}\0${kind}${kind === 'light' ? JSON.stringify(light) : ''}`;
141
232
  let pool = pools.get(key);
142
233
  if (!pool) {
143
234
  // the World's mark: a short digest of the vendor and the World's tree, so two Worlds' machines never share a name
144
235
  let h = 0;
145
236
  for (const ch of `${service}:${root ?? ''}`)
146
237
  h = (Math.imul(h, 31) + ch.charCodeAt(0)) >>> 0;
147
- pool = machinePool(kind, h.toString(16).padStart(8, '0'));
238
+ pool = machinePool(kind, h.toString(16).padStart(8, '0'), httpReplies, light, root);
148
239
  pools.set(key, pool);
149
240
  }
150
241
  return pool;
151
242
  }
243
+ /** Re-enrolling a verification pool starts a fresh fake execution record. */
244
+ export function resetFakePool(service, root) { pools.delete(`${service}\0${root ?? ''}\0fake`); }
245
+ /** Every machine mutation shares the tree's refusal seam. HTTP/RPC reads must be explicitly
246
+ * classified by the pack; a computed operation may execute arbitrary guest code. */
247
+ export function guardedMachinePool(pool, service, safeRead) {
248
+ const guard = () => refuseReadOnlyWrite(service);
249
+ return {
250
+ kind: pool.kind, asked: pool.asked,
251
+ ...(pool.admitRun ? { admitRun: (spec) => { guard(); return pool.admitRun(spec); } } : {}),
252
+ run: (spec) => { guard(); return pool.run(spec); },
253
+ exec: (name, spec) => { guard(); return pool.exec(name, spec); },
254
+ request: (name, port, request) => { if (!safeRead)
255
+ guard(); return pool.request(name, port, request); },
256
+ status: (name) => pool.status(name),
257
+ ports: (name) => pool.ports(name),
258
+ files: async (name) => {
259
+ const files = await pool.files(name);
260
+ return { ...files, write: (path, bytes) => { guard(); return files.write(path, bytes); }, mkdir: (path) => { guard(); return files.mkdir(path); }, remove: (path) => { guard(); return files.remove(path); } };
261
+ },
262
+ pause: (name) => { guard(); return pool.pause(name); },
263
+ resume: (name) => { guard(); return pool.resume(name); },
264
+ stop: (name) => { guard(); return pool.stop(name); },
265
+ remove: (name) => { guard(); return pool.remove(name); },
266
+ };
267
+ }
@@ -37,19 +37,14 @@ export type Batch = {
37
37
  export interface ManagedDatabase {
38
38
  /** Whether the World binds a database at all (a World with none refuses every batch). */
39
39
  readonly bound: boolean;
40
+ /** The URL the World binds it at, what an application connects to (a credential door's database URL); none when
41
+ * unbound. */
42
+ readonly url?: string;
40
43
  /** One transaction as one pipeline and one Sync; the answer is the batch's own statements', in order. */
41
44
  batch(batch: Batch): Promise<StatementResult[]>;
42
45
  /** A whole SQL script as one simple Query (a vendor's migrations, several statements to a file). */
43
46
  script(text: string): Promise<void>;
44
47
  }
45
- /** The transaction-local setting every batch a handler makes carries: the World clock at the call (ctx.occurredAt). */
46
- export declare const WORLD_CLOCK_SETTING = "volter.world_clock";
47
- /** The World clock inside the database, for the schemas a vendor lays (its migrations stamp rows with Postgres's
48
- * `now()`, which reads the wall clock): `volter.now()`, the World clock the batch set or the wall clock where none is
49
- * set, and every column default in `schemas` that reads `now()` or `CURRENT_TIMESTAMP` (alone or inside an expression,
50
- * GoTrue's `timezone('utc', now())`) rewritten to read it. Run after the schemas are laid; running it again changes
51
- * nothing. A trigger or function that calls `now()` itself is the pack's to redefine. */
52
- export declare function worldClockSql(schemas: string[]): string;
53
48
  /** Quote an identifier the way Postgres's quote_ident does for a name that needs it. */
54
49
  export declare function quoteIdent(name: string): string;
55
50
  /** The part of a Postgres driver the kernel uses: node-postgres's (`pg`) Pool and its clients. */
@@ -73,12 +68,21 @@ export type DatabaseDriver = {
73
68
  /** A Postgres driver a host registers in place of node-postgres. */
74
69
  export declare function useDatabaseDriver(given: DatabaseDriver): void;
75
70
  /** The World's Postgres at `url` (the runtime's binding, never the environment); with `readOnly`, every transaction is
76
- * READ ONLY whatever the batch asks (a read-only twin or request: Postgres itself refuses a write, 25006); with `clock`,
77
- * every batch carries the World clock as `volter.world_clock`. */
71
+ * READ ONLY whatever the batch asks (a read-only twin or request: Postgres itself refuses a write, 25006). */
78
72
  export declare function managedDatabase(url: string, options?: {
79
73
  readOnly?: boolean;
80
- clock?: string;
81
74
  }): ManagedDatabase;
75
+ /** One connection of an application's own to the World's Postgres (a journey's SQL steps: twin-standard
76
+ * walk/journey-kit.ts), its statements sent as its client sends them: a text alone as one simple Query (a migration
77
+ * file, a `BEGIN`), a text with values as one extended-protocol statement, the values bind parameters. A transaction it
78
+ * opens stays open across its statements until it ends it, as on the application's own connection. Each statement's
79
+ * answer is its results in Postgres's text format, untyped. */
80
+ export type DatabaseSession = {
81
+ query(text: string, values?: ReadonlyArray<string | null>): Promise<StatementResult[]>;
82
+ end(): Promise<void>;
83
+ };
84
+ /** The World's Postgres at `url` as one connection held until `end()`. */
85
+ export declare function databaseSession(url: string): Promise<DatabaseSession>;
82
86
  /** A handler's database when the World binds none: every use is the refusal naming what to add. */
83
87
  export declare function unboundDatabase(service: string): ManagedDatabase;
84
88
  /** Close every connection this process holds (a test's teardown; a served twin's connections end with it). */