@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
@@ -114,7 +114,10 @@ export type CredentialCustody = {
114
114
  open: () => Promise<CredentialPayload>;
115
115
  seal: (credential: CredentialPayload) => Promise<void> | void;
116
116
  };
117
+ /** `vendor`: the vendor whose rate budget every call is charged to (D8): the call is priced and reserved before it is
118
+ * made, refused when the budget is spent, and settled with the vendor's answer (its back-off honoured). */
117
119
  export type RemoteExecuteOptions = {
118
120
  custody?: CredentialCustody;
121
+ vendor?: string;
119
122
  };
120
123
  export declare function buildRemoteExecute(origin: URL, credential: CredentialPayload, auth?: TwinAuthStrategy, vendorHosts?: readonly HostRule[], options?: RemoteExecuteOptions): RemoteExecute;
@@ -1,3 +1,4 @@
1
+ import { RateBudget } from "./rateBudget.js";
1
2
  const MAX_ORIGIN_LENGTH = 2048;
2
3
  function isLoopbackHost(hostname) {
3
4
  return hostname === '127.0.0.1' || hostname === 'localhost' || hostname === '[::1]' || hostname === '::1';
@@ -129,8 +130,24 @@ async function awsSigV4Headers(input) {
129
130
  };
130
131
  }
131
132
  export function buildRemoteExecute(origin, credential, auth, vendorHosts, options = {}) {
132
- if (auth?.in === 'exchange')
133
- return exchangeExecute(origin, credential, auth, vendorHosts, options.custody);
133
+ const execute = auth?.in === 'exchange' ? exchangeExecute(origin, credential, auth, vendorHosts, options.custody) : direct(origin, credential, auth, vendorHosts);
134
+ return options.vendor === undefined ? execute : budgeted(options.vendor, execute, options.custody?.key ?? JSON.stringify(credential.headers));
135
+ }
136
+ /** Every live call charged to the vendor's rate budget (rateBudget.ts, D8): a call the budget refuses is never made;
137
+ * the ledger is the credential's (only its hash names it). */
138
+ function budgeted(vendor, execute, token) {
139
+ const budget = new RateBudget({ vendor, token });
140
+ return async (request) => {
141
+ const url = new URL(request.path, 'http://budget.invalid');
142
+ const weight = budget.weightFor(`${request.method.toUpperCase()} ${url.pathname}`, Object.fromEntries(url.searchParams));
143
+ const reservation = budget.checkBudget(weight);
144
+ const answer = await execute(request);
145
+ budget.recordCall(weight, answer.headers, { status: answer.status, reservation });
146
+ return answer;
147
+ };
148
+ }
149
+ /** The call made as the root's credential is applied by the strategy (header replacement when none). */
150
+ function direct(origin, credential, auth, vendorHosts) {
134
151
  return async ({ method, path, headers, body, responseType, presigned }) => {
135
152
  // A PRESIGNED UPLOAD URL the vendor returned: its own signature is its authorization, so the
136
153
  // sealed credential never goes with it, whatever host it names. A host other than the root's
@@ -1,3 +1,9 @@
1
1
  /** The content type a file's extension implies; `application/octet-stream` for the rest. */
2
2
  export declare function contentTypeOf(path: string): string;
3
3
  export declare function fileResponse(path: string, init?: ResponseInit): Response;
4
+ /**
5
+ * Stored bytes as a response, honouring a single `Range: bytes=` as RFC 9110 §14 has it: a range that does not parse, a
6
+ * first-byte-pos past its last-byte-pos (`bytes=5-2`) included, is ignored and the whole body is a 200; a well-formed
7
+ * range that starts past the end is a 416. `nosniff` stops a browser second-guessing the content type the vendor gave.
8
+ */
9
+ export declare function bytesResponse(request: Request, bytes: Uint8Array, contentType: string, cacheControl?: string): Response;
@@ -20,3 +20,33 @@ export function fileResponse(path, init) {
20
20
  headers.set('content-type', contentTypeOf(path));
21
21
  return new Response(readFileSync(path), { ...init, headers });
22
22
  }
23
+ /**
24
+ * Stored bytes as a response, honouring a single `Range: bytes=` as RFC 9110 §14 has it: a range that does not parse, a
25
+ * first-byte-pos past its last-byte-pos (`bytes=5-2`) included, is ignored and the whole body is a 200; a well-formed
26
+ * range that starts past the end is a 416. `nosniff` stops a browser second-guessing the content type the vendor gave.
27
+ */
28
+ export function bytesResponse(request, bytes, contentType, cacheControl = 'public, max-age=604800, immutable') {
29
+ const total = bytes.length;
30
+ const base = { 'content-type': contentType, 'x-content-type-options': 'nosniff', 'accept-ranges': 'bytes', 'cache-control': cacheControl };
31
+ const range = /^bytes=(\d*)-(\d*)$/.exec((request.headers.get('range') ?? '').trim());
32
+ let start = 0;
33
+ let end = total - 1;
34
+ const valid = range !== null && (range[1] !== '' || range[2] !== '') && !(range[1] !== '' && range[2] !== '' && Number(range[1]) > Number(range[2]));
35
+ if (valid) {
36
+ if (range[1] === '') {
37
+ start = Math.max(0, total - Number(range[2]));
38
+ }
39
+ else {
40
+ start = Number(range[1]);
41
+ end = range[2] === '' ? total - 1 : Math.min(Number(range[2]), total - 1);
42
+ }
43
+ if (start >= total || (range[1] === '' && Number(range[2]) === 0))
44
+ return new Response(null, { status: 416, headers: { ...base, 'content-range': `bytes */${total}` } });
45
+ }
46
+ const partial = start !== 0 || end !== total - 1;
47
+ const body = bytes.slice(start, end + 1);
48
+ return new Response(request.method === 'HEAD' ? null : new Blob([body]), {
49
+ status: partial ? 206 : 200,
50
+ headers: { ...base, 'content-length': String(body.length), ...(partial ? { 'content-range': `bytes ${start}-${end}/${total}` } : {}) },
51
+ });
52
+ }
package/dist/src/fork.js CHANGED
@@ -3,7 +3,7 @@ import { captureHistory, historyEntries, historyLength, historyPrefix, readHisto
3
3
  // The kernel registers parent ownership before publishing the pointer. Lifecycle removal
4
4
  // refuses while a child depends on that storage; direct filesystem deletion bypasses it.
5
5
  import { dirname, join } from 'node:path';
6
- import { checkParent, withAncestryLock } from "./ancestry.js";
6
+ import { checkParent, withHistoryLock } from "./ancestry.js";
7
7
  import { getActiveWorldStore } from "./world-store.js";
8
8
  import { writeBranchMeta } from "./log.js";
9
9
  import { twinResources } from "./serve.js";
@@ -17,7 +17,8 @@ function forkMetaPath(service, root) {
17
17
  * References the base log at a position, then records the divergence baseline. `occurredAt` is supplied by the caller (deterministic; no clock).
18
18
  */
19
19
  export function forkTwin(opts) {
20
- return withAncestryLock(() => {
20
+ // under the new branch's history lock; capturing the base nests the base's inside it (child before ancestor)
21
+ return withHistoryLock(worldPaths(opts.service, opts.toRoot).dir, () => {
21
22
  // A BRANCH IS A POINTER (log.ts): the base root and the position in its logs, nothing copied.
22
23
  // The branch reads the base's parent view and branch log up to that position as its own
23
24
  // parent, and records only its own entries from there. The base keeps appending past the
@@ -34,3 +34,10 @@ export declare function diffFiles(store: GitObjectStore, baseSha: string, headSh
34
34
  /** Three-way line merge. Return null for overlapping edits; preserve exact line endings.
35
35
  * Binary payloads belong to the caller's conflict path, never a text decoder's replacement. */
36
36
  export declare function mergeText(base: string, ours: string, theirs: string): string | null;
37
+ /** The blob at a path in a commit (walking its trees segment by segment), or null when the commit, a directory on the
38
+ * way or the file is not there. A host that serves a file at a revision (the Hugging Face Hub's `resolve`) reads it. */
39
+ export declare function blobAt(store: GitObjectStore, commit: string, path: string): Promise<{
40
+ sha: string;
41
+ mode: string;
42
+ bytes: Uint8Array;
43
+ } | null>;
@@ -296,3 +296,27 @@ export function mergeText(base, ours, theirs) {
296
296
  result.splice(h.start, h.end - h.start, ...h.lines);
297
297
  return result.map(line => JSON.parse(line)).join('');
298
298
  }
299
+ /** The blob at a path in a commit (walking its trees segment by segment), or null when the commit, a directory on the
300
+ * way or the file is not there. A host that serves a file at a revision (the Hugging Face Hub's `resolve`) reads it. */
301
+ export async function blobAt(store, commit, path) {
302
+ const c = await store.read(commit);
303
+ if (!c || c.type !== 'commit')
304
+ return null;
305
+ let tree = decodeCommit(c.payload).tree;
306
+ const parts = path.split('/').filter(Boolean);
307
+ for (const [i, name] of parts.entries()) {
308
+ const t = await store.read(tree);
309
+ if (!t || t.type !== 'tree')
310
+ return null;
311
+ const e = decodeTree(t.payload).find((x) => x.name === name);
312
+ if (!e)
313
+ return null;
314
+ if (i < parts.length - 1) {
315
+ tree = e.sha;
316
+ continue;
317
+ }
318
+ const b = await store.read(e.sha);
319
+ return b && b.type === 'blob' ? { sha: e.sha, mode: e.mode, bytes: b.payload } : null;
320
+ }
321
+ return null;
322
+ }
@@ -4,3 +4,4 @@ export * from './pack.js';
4
4
  export * from './refs.js';
5
5
  export * from './smart-http.js';
6
6
  export * from './history.js';
7
+ export * from './lfs.js';
@@ -4,3 +4,4 @@ export * from "./pack.js";
4
4
  export * from "./refs.js";
5
5
  export * from "./smart-http.js";
6
6
  export * from "./history.js";
7
+ export * from "./lfs.js";
@@ -0,0 +1,28 @@
1
+ export declare const LFS_TYPE = "application/vnd.git-lfs+json";
2
+ export type LfsObject = {
3
+ oid: string;
4
+ size: number;
5
+ };
6
+ /** Where a pack keeps its objects: by oid. */
7
+ export type LfsStore = {
8
+ get(oid: string): Promise<Uint8Array | null>;
9
+ put(oid: string, bytes: Uint8Array): Promise<void>;
10
+ };
11
+ /** A pointer file's object (`version https://git-lfs.github.com/spec/v1`, `oid sha256:<hex>`, `size <n>`), or null
12
+ * for any other file. */
13
+ export declare function parseLfsPointer(file: Uint8Array | string): LfsObject | null;
14
+ /** A pointer file's text for an object. */
15
+ export declare const lfsPointer: (o: LfsObject) => string;
16
+ /** An object's sha256, as its oid. */
17
+ export declare function lfsOid(bytes: Uint8Array): Promise<string>;
18
+ /** `POST …/info/lfs/objects/batch`: for each object asked, an upload's action (none when the object is held already) or
19
+ * a download's (an error when it is not held), by the basic transfer. `href` names where an object goes up or comes
20
+ * down; `header` is sent with each action. */
21
+ export declare function lfsBatch(body: unknown, opts: {
22
+ store: LfsStore;
23
+ href: (operation: 'upload' | 'download', o: LfsObject) => string;
24
+ header?: Record<string, string>;
25
+ }): Promise<Response>;
26
+ /** A basic transfer's upload (`PUT` of the object's bytes): kept when they hash to the oid and have the declared size
27
+ * (the size is the request's Content-Length when the caller has no other), else refused and nothing kept. */
28
+ export declare function lfsUpload(bytes: Uint8Array, o: LfsObject, store: LfsStore): Promise<Response>;
@@ -0,0 +1,66 @@
1
+ // GIT LFS (architecture, "The tree contract": Git LFS is the git library's too): the batch API git-lfs speaks
2
+ // (https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md, the basic transfer), a pointer file read
3
+ // (https://github.com/git-lfs/git-lfs/blob/main/docs/spec.md), and an upload accepted only when its bytes hash to the
4
+ // object's oid and its size is the one declared. The objects are the caller's (a pack's resource blobs, by oid); the
5
+ // caller says where objects go up and come down (the vendor's own hosts) and decides who may do either before it calls.
6
+ // Importing it does nothing.
7
+ export const LFS_TYPE = 'application/vnd.git-lfs+json';
8
+ const OID = /^[0-9a-f]{64}$/;
9
+ const lfsJson = (status, body) => Response.json(body, { status, headers: { 'content-type': LFS_TYPE } });
10
+ /** A pointer file's object (`version https://git-lfs.github.com/spec/v1`, `oid sha256:<hex>`, `size <n>`), or null
11
+ * for any other file. */
12
+ export function parseLfsPointer(file) {
13
+ const text = typeof file === 'string' ? file : file.length > 1024 ? '' : new TextDecoder().decode(file);
14
+ if (!text.startsWith('version https://git-lfs.github.com/spec/'))
15
+ return null;
16
+ const oid = /^oid sha256:([0-9a-f]{64})$/m.exec(text)?.[1];
17
+ const size = /^size (\d+)$/m.exec(text)?.[1];
18
+ return oid && size ? { oid, size: Number(size) } : null;
19
+ }
20
+ /** A pointer file's text for an object. */
21
+ export const lfsPointer = (o) => `version https://git-lfs.github.com/spec/v1\noid sha256:${o.oid}\nsize ${o.size}\n`;
22
+ /** An object's sha256, as its oid. */
23
+ export async function lfsOid(bytes) {
24
+ const digest = await globalThis.crypto.subtle.digest('SHA-256', bytes);
25
+ return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, '0')).join('');
26
+ }
27
+ /** `POST …/info/lfs/objects/batch`: for each object asked, an upload's action (none when the object is held already) or
28
+ * a download's (an error when it is not held), by the basic transfer. `href` names where an object goes up or comes
29
+ * down; `header` is sent with each action. */
30
+ export async function lfsBatch(body, opts) {
31
+ const b = (body && typeof body === 'object' ? body : {});
32
+ // source: https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md "Should be download or upload."
33
+ if (b.operation !== 'upload' && b.operation !== 'download')
34
+ return lfsJson(422, { message: 'operation must be download or upload' });
35
+ if (Array.isArray(b.transfers) && b.transfers.length && !b.transfers.includes('basic'))
36
+ return lfsJson(422, { message: 'no transfer adapter in common: this server speaks basic' });
37
+ if (!Array.isArray(b.objects))
38
+ return lfsJson(422, { message: 'objects is required' });
39
+ const operation = b.operation;
40
+ const objects = [];
41
+ for (const raw of b.objects) {
42
+ const o = { oid: String(raw?.oid ?? ''), size: Number(raw?.size) };
43
+ // source: https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md "The object is invalid (for example, an invalid oid or size)."
44
+ if (!OID.test(o.oid) || !Number.isSafeInteger(o.size) || o.size < 0) {
45
+ objects.push({ ...o, error: { code: 422, message: 'Invalid object' } });
46
+ continue;
47
+ }
48
+ const held = await opts.store.get(o.oid);
49
+ if (operation === 'upload') {
50
+ objects.push(held ? { ...o, authenticated: true } : { ...o, authenticated: true, actions: { upload: { href: opts.href('upload', o), ...(opts.header ? { header: opts.header } : {}), expires_in: 3600 } } });
51
+ }
52
+ else {
53
+ // source: https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md "The object does not exist on the server."
54
+ objects.push(held ? { oid: o.oid, size: held.length, authenticated: true, actions: { download: { href: opts.href('download', o), ...(opts.header ? { header: opts.header } : {}), expires_in: 3600 } } } : { ...o, error: { code: 404, message: 'Object does not exist' } });
55
+ }
56
+ }
57
+ return lfsJson(200, { transfer: 'basic', objects, hash_algo: 'sha256' });
58
+ }
59
+ /** A basic transfer's upload (`PUT` of the object's bytes): kept when they hash to the oid and have the declared size
60
+ * (the size is the request's Content-Length when the caller has no other), else refused and nothing kept. */
61
+ export async function lfsUpload(bytes, o, store) {
62
+ if (bytes.length !== o.size || (await lfsOid(bytes)) !== o.oid)
63
+ return lfsJson(422, { message: `the object's bytes do not match ${o.oid} (${o.size} bytes)` });
64
+ await store.put(o.oid, bytes);
65
+ return new Response(null, { status: 200 });
66
+ }
@@ -2,9 +2,10 @@
2
2
  // <size>\0<payload>`, zlib-deflated, addressed by the SHA-1 of the uncompressed form — written
3
3
  // and read through the active BlobStore under `<prefix>/objects/<ab>/<rest>`, the exact layout
4
4
  // a bare repository keeps. Pure JS: SHA-1 through WebCrypto, loose objects stored uncompressed in zlib framing (storeZlib),
5
- // a pack deflated through the standard CompressionStream, inflate through ./inflate.ts (it reports consumed bytes, which a packfile
5
+ // a pack deflated through the kernel's `deflate` (bytes.ts), inflate through ./inflate.ts (it reports consumed bytes, which a packfile
6
6
  // reader needs). Bun and workerd alike; no `git` binary anywhere.
7
7
  import { inflateZlib } from "./inflate.js";
8
+ import { deflate } from "../bytes.js";
8
9
  const ENC = new TextEncoder();
9
10
  const DEC = new TextDecoder();
10
11
  export async function sha1Hex(bytes) {
@@ -31,13 +32,7 @@ export function decodeLoose(bytes) {
31
32
  return { type: type, payload };
32
33
  }
33
34
  export async function hashObject(type, payload) { return sha1Hex(encodeLoose(type, payload)); }
34
- export async function deflateZlib(bytes) {
35
- const cs = new CompressionStream('deflate');
36
- const w = cs.writable.getWriter();
37
- void w.write(new Uint8Array(bytes));
38
- void w.close();
39
- return new Uint8Array(await new Response(cs.readable).arrayBuffer());
40
- }
35
+ export async function deflateZlib(bytes) { return deflate(bytes); }
41
36
  /** zlib with stored (uncompressed) deflate blocks: what git writes at `core.compression 0`, and what every
42
37
  * inflater reads. A loose object is written on every commit a World makes; compressing each one through the
43
38
  * asynchronous CompressionStream made a write's cost the stream's round trips, not the object's size. */
@@ -16,7 +16,9 @@ export declare function readAllPktLines(bytes: Uint8Array): {
16
16
  export type Service = 'git-upload-pack' | 'git-receive-pack';
17
17
  /** `GET info/refs?service=<service>`: the ref advertisement, HEAD first as a symref. */
18
18
  export declare function advertisement(service: Service, refs: GitRefs): Uint8Array;
19
- /** `POST git-upload-pack`: wants and haves in, NAK and (once `done`) the pack out. */
19
+ /** `POST git-upload-pack`: wants and haves in, NAK and (once `done`) the pack out. A request with `deepen <n>` is shallow:
20
+ * its answer opens with the shallow-update section (`shallow <sha>` for each boundary commit, `unshallow <sha>` for a
21
+ * commit the client held shallow whose parents it now receives, then a flush), and its pack stops at the boundary. */
20
22
  export declare function uploadPack(body: Uint8Array, store: GitObjectStore): Promise<Uint8Array>;
21
23
  export type RefCommand = {
22
24
  old: string;
@@ -5,6 +5,7 @@
5
5
  // not advertised; a client that asks for it falls back to v0 on this answer, as git specifies.
6
6
  import { decodeCommit, decodeTag, decodeTree } from "./objects.js";
7
7
  import { readPack, writePack } from "./pack.js";
8
+ import { gunzip } from "../bytes.js";
8
9
  const ENC = new TextEncoder();
9
10
  const DEC = new TextDecoder();
10
11
  export const ZERO_SHA = '0'.repeat(40);
@@ -55,7 +56,7 @@ function cat(...parts) { const out = new Uint8Array(parts.reduce((n, p) => n + p
55
56
  out.set(p, o);
56
57
  o += p.length;
57
58
  } return out; }
58
- const CAPS = { 'git-upload-pack': 'ofs-delta no-progress agent=volter-twin', 'git-receive-pack': 'report-status delete-refs ofs-delta agent=volter-twin' };
59
+ const CAPS = { 'git-upload-pack': 'ofs-delta shallow no-progress agent=volter-twin', 'git-receive-pack': 'report-status delete-refs ofs-delta agent=volter-twin' };
59
60
  /** `GET info/refs?service=<service>`: the ref advertisement, HEAD first as a symref. */
60
61
  export function advertisement(service, refs) {
61
62
  const all = refs.list();
@@ -104,11 +105,47 @@ async function closure(store, roots, seen) {
104
105
  return out;
105
106
  }
106
107
  async function reachable(store, roots) { const seen = new Set(); await closure(store, roots, seen); return seen; }
107
- /** `POST git-upload-pack`: wants and haves in, NAK and (once `done`) the pack out. */
108
+ /**
109
+ * A shallow request's commits (gitprotocol-pack, "Shallow requests"): every commit within `depth` of a want (a want is
110
+ * depth 1), and the boundary, the commits whose parents the client will not have. The client's own shallow commits it
111
+ * already has are boundaries it keeps unless this walk passes through them.
112
+ */
113
+ async function deepened(store, wants, depth) {
114
+ const at = new Map();
115
+ const boundary = new Set();
116
+ let frontier = wants.map((sha) => ({ sha, d: 1 }));
117
+ while (frontier.length) {
118
+ const next = [];
119
+ for (const { sha, d } of frontier) {
120
+ if ((at.get(sha) ?? Infinity) <= d)
121
+ continue;
122
+ at.set(sha, d);
123
+ const obj = await store.read(sha);
124
+ if (obj?.type !== 'commit')
125
+ continue;
126
+ const parents = decodeCommit(obj.payload).parents;
127
+ if (d >= depth) {
128
+ if (parents.length)
129
+ boundary.add(sha);
130
+ continue;
131
+ }
132
+ boundary.delete(sha);
133
+ for (const p of parents)
134
+ next.push({ sha: p, d: d + 1 });
135
+ }
136
+ frontier = next;
137
+ }
138
+ return { commits: [...at.keys()], boundary: [...boundary].filter((sha) => at.get(sha) >= depth) };
139
+ }
140
+ /** `POST git-upload-pack`: wants and haves in, NAK and (once `done`) the pack out. A request with `deepen <n>` is shallow:
141
+ * its answer opens with the shallow-update section (`shallow <sha>` for each boundary commit, `unshallow <sha>` for a
142
+ * commit the client held shallow whose parents it now receives, then a flush), and its pack stops at the boundary. */
108
143
  export async function uploadPack(body, store) {
109
144
  const { lines } = readAllPktLines(body);
110
145
  const wants = lines.filter((l) => l.startsWith('want ')).map((l) => l.slice(5, 45)).filter((s) => SHA.test(s));
111
146
  const haves = lines.filter((l) => l.startsWith('have ')).map((l) => l.slice(5, 45)).filter((s) => SHA.test(s));
147
+ const clientShallow = lines.filter((l) => l.startsWith('shallow ')).map((l) => l.slice(8, 48)).filter((s) => SHA.test(s));
148
+ const depth = Number(/^deepen (\d+)/.exec(lines.find((l) => l.startsWith('deepen ')) ?? '')?.[1] ?? 0);
112
149
  const done = lines.some((l) => l.trim() === 'done');
113
150
  const known = [];
114
151
  for (const h of haves)
@@ -117,11 +154,35 @@ export async function uploadPack(body, store) {
117
154
  for (const w of wants)
118
155
  if (!(await store.has(w)))
119
156
  return pktLine(`ERR upload-pack: not our ref ${w}\n`);
157
+ const shallow = depth > 0 ? await deepened(store, wants, depth) : undefined;
158
+ const update = shallow
159
+ ? cat(...shallow.boundary.filter((sha) => !clientShallow.includes(sha)).map((sha) => pktLine(`shallow ${sha}\n`)), ...clientShallow.filter((sha) => shallow.commits.includes(sha) && !shallow.boundary.includes(sha)).map((sha) => pktLine(`unshallow ${sha}\n`)), FLUSH)
160
+ : new Uint8Array();
161
+ // a stateless client sends its wants and `deepen` alone first and reads only the shallow section back (git's
162
+ // fetch-pack, find_common); every later request carries haves or `done`, and its answer repeats the section before NAK
163
+ if (!done && shallow && haves.length === 0)
164
+ return update;
120
165
  if (!done)
121
- return pktLine('NAK\n');
166
+ return cat(update, pktLine('NAK\n'));
122
167
  const seen = await reachable(store, known);
123
- const objects = await closure(store, wants, seen);
124
- return cat(pktLine('NAK\n'), await writePack(objects));
168
+ let objects;
169
+ if (shallow) {
170
+ objects = [];
171
+ for (const sha of shallow.commits) {
172
+ if (seen.has(sha))
173
+ continue;
174
+ seen.add(sha);
175
+ const obj = await store.read(sha);
176
+ if (!obj)
177
+ continue;
178
+ objects.push({ ...obj, sha });
179
+ if (obj.type === 'commit')
180
+ objects.push(...await closure(store, [decodeCommit(obj.payload).tree], seen));
181
+ }
182
+ }
183
+ else
184
+ objects = await closure(store, wants, seen);
185
+ return cat(update, pktLine('NAK\n'), await writePack(objects));
125
186
  }
126
187
  /** Bounded ancestry walk: is `ancestor` reachable from `sha` through parents? */
127
188
  export async function isAncestor(store, ancestor, sha, limit = 5000) {
@@ -212,7 +273,7 @@ export async function serveSmartHttp(req, repo, tailPath) {
212
273
  * the size of the repository, so re-wrapping it in a Request only to read it back is a full copy. */
213
274
  export async function serveSmartHttpPost(body, contentEncoding, repo, tailPath) {
214
275
  if (contentEncoding.includes('gzip'))
215
- body = new Uint8Array(await new Response(new Response(body).body.pipeThrough(new DecompressionStream('gzip'))).arrayBuffer());
276
+ body = await gunzip(body);
216
277
  const headers = { 'content-type': `application/x-${tailPath}-result`, 'cache-control': 'no-cache' };
217
278
  if (tailPath === 'git-upload-pack')
218
279
  return new Response((await uploadPack(body, repo.store)), { headers });
@@ -0,0 +1,29 @@
1
+ /** A resolver of one field, `Type.field`: its parent object, its arguments, and the request's handler context. */
2
+ export type GraphqlResolver<C = unknown> = (source: Record<string, unknown>, args: Record<string, unknown>, ctx: C) => unknown;
3
+ /** What a pack's `semantics/graphql.ts` exports as `graphql`. */
4
+ export type GraphqlPart<C = unknown> = {
5
+ /** resolvers by `Type.field` (`Query.repository`, `PullRequest.state`) */
6
+ resolvers: Record<string, GraphqlResolver<C>>;
7
+ /** per type, a GraphQL field name → the object's field that holds it (`nameWithOwner` → `full_name`) */
8
+ aliases?: Record<string, Record<string, string>>;
9
+ /** per type, the object's fields the default resolver may read as they are */
10
+ stored?: Record<string, string[]>;
11
+ };
12
+ /** An error a resolver answers, with the vendor's `type` for it (GitHub's NOT_FOUND, UNPROCESSABLE). */
13
+ export declare class GraphqlError extends Error {
14
+ readonly type?: string | undefined;
15
+ constructor(message: string, type?: string | undefined);
16
+ }
17
+ /** A GraphQL endpoint over a schema and a pack's part: answers a request's `{query, variables, operationName}`. The
18
+ * context each resolver receives is made per request (the handler context). `unmodeled` answers a query selecting a
19
+ * field nothing models; `mutationRefused`, when given, a mutation the World may not make (a read-only twin). */
20
+ export declare function graphqlEndpoint<C>(options: {
21
+ sdl: string;
22
+ part: GraphqlPart<C>;
23
+ context: (request: Request) => Promise<C>;
24
+ unmodeled: (field: {
25
+ type: string;
26
+ field: string;
27
+ }) => Response;
28
+ mutationRefused?: (request: Request) => Response | undefined;
29
+ }): (request: Request) => Promise<Response>;
@@ -0,0 +1,101 @@
1
+ // THE GRAPHQL WIRE — a vendor's GraphQL API served from its published schema over the pack's state
2
+ // (docs/contributing/architecture.md, "Other wires: lanes, headers and line protocols": GraphQL). A query is parsed and
3
+ // validated against the vendor's own schema, so an unknown field is the GraphQL error the vendor gives. A field resolves
4
+ // from the pack's resolver when it writes one (`semantics/graphql.ts`), else from the object its parent resolved: the
5
+ // field's own name, its snake_case form, or the pack's alias for it. A list answering a `…Connection` field is a Relay
6
+ // connection, and an enum resolves case-insensitively. The wire takes a query only when every field it selects is
7
+ // modeled; any other is answered by the manifest's `graphql.unmodeled`, naming the first field nothing models. An error
8
+ // a resolver throws (`GraphqlError`) carries its `type`, as GitHub's GraphQL errors do.
9
+ //
10
+ // The `graphql` protocol library is loaded the first time a GraphQL request comes (installed by the pack's package.json,
11
+ // as node-postgres is for the managed database): the kernel's own dependencies stay B1's, and importing the kernel
12
+ // loads nothing.
13
+ /** An error a resolver answers, with the vendor's `type` for it (GitHub's NOT_FOUND, UNPROCESSABLE). */
14
+ export class GraphqlError extends Error {
15
+ type;
16
+ constructor(message, type) {
17
+ super(message);
18
+ this.type = type;
19
+ this.name = 'GraphqlError';
20
+ }
21
+ }
22
+ let lib;
23
+ const graphqlLib = () => (lib ??= import('graphql'));
24
+ const snake = (s) => s.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
25
+ /** A list as a Relay connection, paged by `first`/`last` and `after`. */
26
+ function connection(items, args) {
27
+ const first = typeof args.first === 'number' ? args.first : undefined;
28
+ const last = typeof args.last === 'number' ? args.last : undefined;
29
+ const after = typeof args.after === 'string' ? Number(atob(args.after)) + 1 : 0;
30
+ let slice = items.slice(after);
31
+ if (first !== undefined)
32
+ slice = slice.slice(0, first);
33
+ if (last !== undefined)
34
+ slice = slice.slice(-last);
35
+ const start = after + (last !== undefined ? Math.max(0, items.slice(after).length - last) : 0);
36
+ const cursor = (i) => btoa(String(start + i));
37
+ return {
38
+ totalCount: items.length, nodes: slice, edges: slice.map((node, i) => ({ node, cursor: cursor(i) })),
39
+ pageInfo: { hasNextPage: start + slice.length < items.length, hasPreviousPage: start > 0, startCursor: slice.length ? cursor(0) : null, endCursor: slice.length ? cursor(slice.length - 1) : null },
40
+ };
41
+ }
42
+ /** A GraphQL endpoint over a schema and a pack's part: answers a request's `{query, variables, operationName}`. The
43
+ * context each resolver receives is made per request (the handler context). `unmodeled` answers a query selecting a
44
+ * field nothing models; `mutationRefused`, when given, a mutation the World may not make (a read-only twin). */
45
+ export function graphqlEndpoint(options) {
46
+ let schema;
47
+ const { resolvers, aliases, stored } = options.part;
48
+ const modeled = (type, field) => field === '__typename' || `${type}.${field}` in resolvers || !!aliases?.[type]?.[field] || !!stored?.[type]?.includes(field) || !!stored?.[type]?.includes(snake(field));
49
+ return async (request) => {
50
+ const g = await graphqlLib();
51
+ schema ??= g.buildSchema(options.sdl, { assumeValidSDL: true });
52
+ const payload = (await request.clone().json().catch(() => ({})));
53
+ let document;
54
+ try {
55
+ document = g.parse(payload.query ?? '');
56
+ }
57
+ catch (error) {
58
+ return Response.json({ errors: [{ message: error instanceof Error ? error.message : String(error) }] });
59
+ }
60
+ const invalid = g.validate(schema, document);
61
+ if (invalid.length)
62
+ return Response.json({ errors: invalid.map((e) => ({ message: e.message, locations: e.locations })) });
63
+ const mutation = document.definitions.some((d) => d.kind === 'OperationDefinition' && d.operation === 'mutation');
64
+ if (mutation) {
65
+ const refused = options.mutationRefused?.(request);
66
+ if (refused)
67
+ return refused;
68
+ }
69
+ const typeInfo = new g.TypeInfo(schema);
70
+ let unmodeled;
71
+ g.visit(document, g.visitWithTypeInfo(typeInfo, {
72
+ Field(node) {
73
+ const parent = typeInfo.getParentType();
74
+ if (!unmodeled && parent && g.isObjectType(parent) && !parent.name.endsWith('Connection') && !parent.name.endsWith('Edge') && parent.name !== 'PageInfo' && !modeled(parent.name, node.name.value))
75
+ unmodeled = { type: parent.name, field: node.name.value };
76
+ },
77
+ }));
78
+ if (unmodeled)
79
+ return options.unmodeled(unmodeled);
80
+ const ctx = await options.context(request);
81
+ const result = await g.execute({
82
+ schema, document, variableValues: payload.variables ?? {}, ...(payload.operationName ? { operationName: payload.operationName } : {}), contextValue: ctx,
83
+ fieldResolver: (source, args, context, info) => {
84
+ const own = resolvers[`${info.parentType.name}.${info.fieldName}`];
85
+ const obj = (source ?? {});
86
+ let value = own ? own(obj, args, context) : (obj[aliases?.[info.parentType.name]?.[info.fieldName] ?? info.fieldName] ?? obj[snake(info.fieldName)] ?? null);
87
+ const named = g.getNamedType(info.returnType);
88
+ if (Array.isArray(value) && g.isObjectType(named) && named.name.endsWith('Connection'))
89
+ value = connection(value, args);
90
+ if (typeof value === 'string' && g.isEnumType(named))
91
+ value = named.getValues().find((v) => v.name.toLowerCase() === String(value).toLowerCase())?.name ?? value;
92
+ return value;
93
+ },
94
+ });
95
+ const errors = (result.errors ?? []).map((e) => {
96
+ const type = e.originalError instanceof GraphqlError ? e.originalError.type : typeof e.extensions?.type === 'string' ? e.extensions.type : undefined;
97
+ return { ...(type ? { type } : {}), message: e.message, ...(e.path ? { path: e.path } : {}), ...(e.locations ? { locations: e.locations } : {}) };
98
+ });
99
+ return Response.json({ ...(result.data !== undefined ? { data: result.data } : {}), ...(errors.length ? { errors } : {}) });
100
+ };
101
+ }
@@ -0,0 +1,67 @@
1
+ import { type H2Handler } from './h2.js';
2
+ import { type ProtoSchema } from './protobuf.js';
3
+ /** gRPC's status codes (https://grpc.github.io/grpc/core/md_doc_statuscodes.html). */
4
+ export declare const GrpcStatus: {
5
+ readonly OK: 0;
6
+ readonly CANCELLED: 1;
7
+ readonly UNKNOWN: 2;
8
+ readonly INVALID_ARGUMENT: 3;
9
+ readonly DEADLINE_EXCEEDED: 4;
10
+ readonly NOT_FOUND: 5;
11
+ readonly ALREADY_EXISTS: 6;
12
+ readonly PERMISSION_DENIED: 7;
13
+ readonly RESOURCE_EXHAUSTED: 8;
14
+ readonly FAILED_PRECONDITION: 9;
15
+ readonly ABORTED: 10;
16
+ readonly OUT_OF_RANGE: 11;
17
+ readonly UNIMPLEMENTED: 12;
18
+ readonly INTERNAL: 13;
19
+ readonly UNAVAILABLE: 14;
20
+ readonly DATA_LOSS: 15;
21
+ readonly UNAUTHENTICATED: 16;
22
+ };
23
+ /** A call answered with a gRPC status other than OK: its code and its message. */
24
+ export declare class GrpcError extends Error {
25
+ readonly code: number;
26
+ constructor(code: number, message: string);
27
+ }
28
+ export type GrpcCall = {
29
+ /** the operation the rpc is (the derive's name for it) and its path */
30
+ operation: string;
31
+ path: string;
32
+ /** the request message, in its proto3 JSON form */
33
+ request: Record<string, unknown>;
34
+ /** the call's metadata (its non-pseudo headers: `authorization`, `x-goog-api-client`, …) */
35
+ metadata: Record<string, string>;
36
+ /** aborted when the client cancels or its deadline passes */
37
+ signal: AbortSignal;
38
+ };
39
+ /** Answers one unary call with its response message's JSON form, or throws a GrpcError. */
40
+ export type GrpcHandler = (call: GrpcCall) => Promise<Record<string, unknown>>;
41
+ /** `grpc-timeout`: an integer and a unit (H M S m u n), PROTOCOL-HTTP2.md "Timeout". */
42
+ export declare function parseGrpcTimeout(value: string | undefined): number | undefined;
43
+ /** Length-prefixed messages out of a request body: [compressed flag][u32 length][bytes]… */
44
+ export declare function splitGrpcFrames(body: Uint8Array): Array<{
45
+ compressed: boolean;
46
+ message: Uint8Array;
47
+ }>;
48
+ export declare function grpcFrame(message: Uint8Array): Uint8Array;
49
+ /** grpc-message is percent-encoded (PROTOCOL-HTTP2.md "Status-Message"). */
50
+ export declare function encodeGrpcMessage(text: string): string;
51
+ /** The HTTP/2 stream handler that answers the unary rpcs of a proto unit's schema through `handler`. */
52
+ export declare function grpcStreamHandler(schema: ProtoSchema, handler: GrpcHandler): H2Handler;
53
+ /** A gRPC call answered by the pack's own fetch, as the derive serves a proto unit's rpc (`POST /<package>.<Service>/
54
+ * <Method>`, the request message's JSON form the body): its JSON answer is the response message; any other status its
55
+ * gRPC code, the error's message as the status message. */
56
+ export declare function grpcOverFetch(fetch: (request: Request) => Promise<Response>): GrpcHandler;
57
+ /** A pack that speaks gRPC, served on one port: HTTP/2 (gRPC, by the schema through the pack's fetch) and HTTP/1 (the
58
+ * pack's fetch: its doors, its REST) on the same socket, as the kernel's HTTP/2 server tells them apart. Inside a World
59
+ * it answers the World's boot probe as `serveHttp` does. */
60
+ export declare function serveGrpc(options: {
61
+ port?: number;
62
+ fetch: (request: Request) => Promise<Response>;
63
+ schema: ProtoSchema;
64
+ }): Promise<{
65
+ port: number;
66
+ stop: () => void;
67
+ }>;