@openclaw/crabline 0.1.9 → 0.1.10

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 (189) hide show
  1. package/README.md +47 -22
  2. package/assets/crabline-banner.svg +20 -0
  3. package/dist/src/bin/crabline.js.map +1 -1
  4. package/dist/src/cli/program.d.ts +25 -1
  5. package/dist/src/cli/program.js +579 -82
  6. package/dist/src/cli/program.js.map +1 -1
  7. package/dist/src/config/load.js +59 -6
  8. package/dist/src/config/load.js.map +1 -1
  9. package/dist/src/config/schema.d.ts +116 -92
  10. package/dist/src/config/schema.js +179 -73
  11. package/dist/src/config/schema.js.map +1 -1
  12. package/dist/src/core/errors.js +9 -2
  13. package/dist/src/core/errors.js.map +1 -1
  14. package/dist/src/core/exit-codes.js.map +1 -1
  15. package/dist/src/core/matcher.js +7 -6
  16. package/dist/src/core/matcher.js.map +1 -1
  17. package/dist/src/core/message-template.js.map +1 -1
  18. package/dist/src/core/nonces.d.ts +1 -0
  19. package/dist/src/core/nonces.js +4 -2
  20. package/dist/src/core/nonces.js.map +1 -1
  21. package/dist/src/core/reporters.d.ts +1 -0
  22. package/dist/src/core/reporters.js +42 -4
  23. package/dist/src/core/reporters.js.map +1 -1
  24. package/dist/src/core/run.d.ts +3 -0
  25. package/dist/src/core/run.js +415 -107
  26. package/dist/src/core/run.js.map +1 -1
  27. package/dist/src/core/safe-regex.d.ts +3 -0
  28. package/dist/src/core/safe-regex.js +21 -0
  29. package/dist/src/core/safe-regex.js.map +1 -0
  30. package/dist/src/index.d.ts +2 -2
  31. package/dist/src/index.js +1 -1
  32. package/dist/src/index.js.map +1 -1
  33. package/dist/src/openclaw/artifact-generation.d.ts +38 -0
  34. package/dist/src/openclaw/artifact-generation.js +314 -0
  35. package/dist/src/openclaw/artifact-generation.js.map +1 -0
  36. package/dist/src/openclaw/bridges/matrix.js +97 -12
  37. package/dist/src/openclaw/bridges/matrix.js.map +1 -1
  38. package/dist/src/openclaw/bridges/mattermost.js +32 -10
  39. package/dist/src/openclaw/bridges/mattermost.js.map +1 -1
  40. package/dist/src/openclaw/bridges/signal.js +55 -18
  41. package/dist/src/openclaw/bridges/signal.js.map +1 -1
  42. package/dist/src/openclaw/bridges/slack.js +85 -7
  43. package/dist/src/openclaw/bridges/slack.js.map +1 -1
  44. package/dist/src/openclaw/bridges/telegram.js +124 -30
  45. package/dist/src/openclaw/bridges/telegram.js.map +1 -1
  46. package/dist/src/openclaw/bridges/whatsapp.js +78 -17
  47. package/dist/src/openclaw/bridges/whatsapp.js.map +1 -1
  48. package/dist/src/openclaw/bridges/zalo.js +14 -5
  49. package/dist/src/openclaw/bridges/zalo.js.map +1 -1
  50. package/dist/src/openclaw/outbound-contract.d.ts +5 -0
  51. package/dist/src/openclaw/outbound-contract.js +40 -0
  52. package/dist/src/openclaw/outbound-contract.js.map +1 -0
  53. package/dist/src/openclaw/private-file.d.ts +26 -0
  54. package/dist/src/openclaw/private-file.js +411 -0
  55. package/dist/src/openclaw/private-file.js.map +1 -0
  56. package/dist/src/openclaw/shared.d.ts +21 -2
  57. package/dist/src/openclaw/shared.js +115 -18
  58. package/dist/src/openclaw/shared.js.map +1 -1
  59. package/dist/src/openclaw/smoke-lock.d.ts +63 -0
  60. package/dist/src/openclaw/smoke-lock.js +949 -0
  61. package/dist/src/openclaw/smoke-lock.js.map +1 -0
  62. package/dist/src/openclaw.d.ts +16 -8
  63. package/dist/src/openclaw.js +194 -52
  64. package/dist/src/openclaw.js.map +1 -1
  65. package/dist/src/providers/builtin/discord.d.ts +10 -1
  66. package/dist/src/providers/builtin/discord.js +144 -18
  67. package/dist/src/providers/builtin/discord.js.map +1 -1
  68. package/dist/src/providers/builtin/external-webhook-auth.d.ts +11 -0
  69. package/dist/src/providers/builtin/external-webhook-auth.js +10 -0
  70. package/dist/src/providers/builtin/external-webhook-auth.js.map +1 -0
  71. package/dist/src/providers/builtin/feishu.d.ts +19 -2
  72. package/dist/src/providers/builtin/feishu.js +307 -25
  73. package/dist/src/providers/builtin/feishu.js.map +1 -1
  74. package/dist/src/providers/builtin/googlechat.d.ts +20 -1
  75. package/dist/src/providers/builtin/googlechat.js +178 -19
  76. package/dist/src/providers/builtin/googlechat.js.map +1 -1
  77. package/dist/src/providers/builtin/imessage.d.ts +4 -0
  78. package/dist/src/providers/builtin/imessage.js +26 -11
  79. package/dist/src/providers/builtin/imessage.js.map +1 -1
  80. package/dist/src/providers/builtin/loopback.js +92 -30
  81. package/dist/src/providers/builtin/loopback.js.map +1 -1
  82. package/dist/src/providers/builtin/matrix.d.ts +11 -1
  83. package/dist/src/providers/builtin/matrix.js +53 -29
  84. package/dist/src/providers/builtin/matrix.js.map +1 -1
  85. package/dist/src/providers/builtin/mattermost.d.ts +10 -0
  86. package/dist/src/providers/builtin/mattermost.js +38 -15
  87. package/dist/src/providers/builtin/mattermost.js.map +1 -1
  88. package/dist/src/providers/builtin/msteams.d.ts +17 -1
  89. package/dist/src/providers/builtin/msteams.js +113 -14
  90. package/dist/src/providers/builtin/msteams.js.map +1 -1
  91. package/dist/src/providers/builtin/native-local-mock.d.ts +2 -9
  92. package/dist/src/providers/builtin/native-local-mock.js +12 -35
  93. package/dist/src/providers/builtin/native-local-mock.js.map +1 -1
  94. package/dist/src/providers/builtin/script.d.ts +11 -15
  95. package/dist/src/providers/builtin/script.js +566 -104
  96. package/dist/src/providers/builtin/script.js.map +1 -1
  97. package/dist/src/providers/builtin/slack.d.ts +6 -0
  98. package/dist/src/providers/builtin/slack.js +99 -40
  99. package/dist/src/providers/builtin/slack.js.map +1 -1
  100. package/dist/src/providers/builtin/telegram.d.ts +6 -0
  101. package/dist/src/providers/builtin/telegram.js +64 -43
  102. package/dist/src/providers/builtin/telegram.js.map +1 -1
  103. package/dist/src/providers/builtin/whatsapp.d.ts +28 -8
  104. package/dist/src/providers/builtin/whatsapp.js +567 -30
  105. package/dist/src/providers/builtin/whatsapp.js.map +1 -1
  106. package/dist/src/providers/builtin/zalo.js +30 -19
  107. package/dist/src/providers/builtin/zalo.js.map +1 -1
  108. package/dist/src/providers/catalog.d.ts +3 -3
  109. package/dist/src/providers/catalog.js +3 -3
  110. package/dist/src/providers/catalog.js.map +1 -1
  111. package/dist/src/providers/local-mock.d.ts +11 -2
  112. package/dist/src/providers/local-mock.js +322 -96
  113. package/dist/src/providers/local-mock.js.map +1 -1
  114. package/dist/src/providers/native-ids.d.ts +1 -0
  115. package/dist/src/providers/native-ids.js +6 -1
  116. package/dist/src/providers/native-ids.js.map +1 -1
  117. package/dist/src/providers/recorder.d.ts +30 -2
  118. package/dist/src/providers/recorder.js +539 -70
  119. package/dist/src/providers/recorder.js.map +1 -1
  120. package/dist/src/providers/registry.d.ts +25 -1
  121. package/dist/src/providers/registry.js +280 -40
  122. package/dist/src/providers/registry.js.map +1 -1
  123. package/dist/src/providers/signed-jwt.d.ts +29 -0
  124. package/dist/src/providers/signed-jwt.js +202 -0
  125. package/dist/src/providers/signed-jwt.js.map +1 -0
  126. package/dist/src/providers/slack-ids.js.map +1 -1
  127. package/dist/src/providers/target-normalizers.d.ts +26 -0
  128. package/dist/src/providers/target-normalizers.js +276 -0
  129. package/dist/src/providers/target-normalizers.js.map +1 -0
  130. package/dist/src/providers/types.d.ts +5 -0
  131. package/dist/src/providers/types.js.map +1 -1
  132. package/dist/src/providers/webhook-server.d.ts +2 -0
  133. package/dist/src/providers/webhook-server.js +72 -25
  134. package/dist/src/providers/webhook-server.js.map +1 -1
  135. package/dist/src/servers/http.d.ts +23 -3
  136. package/dist/src/servers/http.js +213 -20
  137. package/dist/src/servers/http.js.map +1 -1
  138. package/dist/src/servers/index.js +20 -21
  139. package/dist/src/servers/index.js.map +1 -1
  140. package/dist/src/servers/matrix.js +465 -51
  141. package/dist/src/servers/matrix.js.map +1 -1
  142. package/dist/src/servers/mattermost.d.ts +5 -0
  143. package/dist/src/servers/mattermost.js +306 -58
  144. package/dist/src/servers/mattermost.js.map +1 -1
  145. package/dist/src/servers/pending-events.d.ts +2 -0
  146. package/dist/src/servers/pending-events.js +11 -0
  147. package/dist/src/servers/pending-events.js.map +1 -0
  148. package/dist/src/servers/recorder.d.ts +5 -0
  149. package/dist/src/servers/recorder.js +46 -3
  150. package/dist/src/servers/recorder.js.map +1 -1
  151. package/dist/src/servers/signal.d.ts +2 -0
  152. package/dist/src/servers/signal.js +444 -56
  153. package/dist/src/servers/signal.js.map +1 -1
  154. package/dist/src/servers/slack.d.ts +5 -0
  155. package/dist/src/servers/slack.js +515 -49
  156. package/dist/src/servers/slack.js.map +1 -1
  157. package/dist/src/servers/telegram.d.ts +111 -0
  158. package/dist/src/servers/telegram.js +811 -152
  159. package/dist/src/servers/telegram.js.map +1 -1
  160. package/dist/src/servers/webhook-target.d.ts +40 -0
  161. package/dist/src/servers/webhook-target.js +349 -0
  162. package/dist/src/servers/webhook-target.js.map +1 -0
  163. package/dist/src/servers/websocket.d.ts +2 -0
  164. package/dist/src/servers/websocket.js +29 -0
  165. package/dist/src/servers/websocket.js.map +1 -0
  166. package/dist/src/servers/whatsapp-baileys-websocket.d.ts +65 -1
  167. package/dist/src/servers/whatsapp-baileys-websocket.js +723 -99
  168. package/dist/src/servers/whatsapp-baileys-websocket.js.map +1 -1
  169. package/dist/src/servers/whatsapp-jid.d.ts +5 -0
  170. package/dist/src/servers/whatsapp-jid.js +32 -0
  171. package/dist/src/servers/whatsapp-jid.js.map +1 -0
  172. package/dist/src/servers/whatsapp-wire/binary-node.d.ts +5 -0
  173. package/dist/src/servers/whatsapp-wire/binary-node.js +87 -20
  174. package/dist/src/servers/whatsapp-wire/binary-node.js.map +1 -1
  175. package/dist/src/servers/whatsapp-wire/crypto.d.ts +6 -1
  176. package/dist/src/servers/whatsapp-wire/crypto.js +99 -45
  177. package/dist/src/servers/whatsapp-wire/crypto.js.map +1 -1
  178. package/dist/src/servers/whatsapp-wire/handshake.js +39 -4
  179. package/dist/src/servers/whatsapp-wire/handshake.js.map +1 -1
  180. package/dist/src/servers/whatsapp-wire/signal.js.map +1 -1
  181. package/dist/src/servers/whatsapp.d.ts +12 -7
  182. package/dist/src/servers/whatsapp.js +269 -107
  183. package/dist/src/servers/whatsapp.js.map +1 -1
  184. package/dist/src/servers/zalo.d.ts +14 -0
  185. package/dist/src/servers/zalo.js +521 -88
  186. package/dist/src/servers/zalo.js.map +1 -1
  187. package/docs/channel-setup.md +197 -19
  188. package/fixtures/examples/crabline.example.yaml +10 -0
  189. package/package.json +20 -15
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/providers/types.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/providers/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n FixtureDefinition,\n FixtureMode,\n InboundAuthor,\n ProviderConfig,\n ProviderPlatform,\n} from \"../config/schema.js\";\n\nexport type ProviderSupportStatus = \"bridge\" | \"planned\" | \"ready\";\n\nexport type InboundMatchConfig = {\n author: InboundAuthor;\n nonce: \"contains\" | \"exact\" | \"ignore\";\n pattern?: string | undefined;\n strategy: \"contains\" | \"exact\" | \"regex\";\n};\n\nexport type NormalizedTarget = {\n channelId?: string | undefined;\n id: string;\n metadata: Record<string, string>;\n threadId?: string | undefined;\n};\n\nexport type InboundEnvelope = {\n author: Exclude<InboundAuthor, \"any\">;\n id: string;\n provider: string;\n raw?: unknown;\n sentAt: string;\n text: string;\n threadId: string;\n};\n\nexport type ProbeResult = {\n details: string[];\n healthy: boolean;\n};\n\nexport type SendResult = {\n accepted: boolean;\n messageId: string;\n threadId: string;\n};\n\nexport type ProviderContext = {\n config: ProviderConfig;\n fixture: FixtureDefinition;\n manifestPath: string;\n providerId: string;\n signal?: AbortSignal | undefined;\n userName: string;\n};\n\nexport type SendContext = ProviderContext & {\n mode: Extract<FixtureMode, \"agent\" | \"roundtrip\" | \"send\">;\n nonce: string;\n text: string;\n};\n\nexport type WaitContext = ProviderContext & {\n excludeIds?: readonly string[] | undefined;\n nonce: string;\n since: string;\n threadId?: string | undefined;\n timeoutMs: number;\n};\n\nexport type WatchContext = ProviderContext & {\n signal?: AbortSignal | undefined;\n since?: string;\n};\n\nexport interface ProviderAdapter {\n readonly id: string;\n readonly platform: ProviderPlatform;\n readonly status: ProviderSupportStatus;\n readonly supports: readonly FixtureMode[];\n normalizeTarget(target: FixtureDefinition[\"target\"]): NormalizedTarget;\n probe(context: ProviderContext): Promise<ProbeResult>;\n send(context: SendContext): Promise<SendResult>;\n waitForInbound(context: WaitContext): Promise<InboundEnvelope | null>;\n watch?(context: WatchContext): AsyncIterable<InboundEnvelope>;\n /** Stops external admission synchronously before cleanup drains accepted work. */\n beginCleanup?(): void;\n cleanup?(): Promise<void>;\n}\n\nexport type LoopbackMessage = {\n author: {\n isMe: boolean;\n userName: string;\n };\n formatted: string;\n id: string;\n metadata: {\n dateSent: Date;\n edited: boolean;\n editedAt?: Date;\n };\n raw: {\n author: \"assistant\" | \"user\";\n id: string;\n text: string;\n threadId: string;\n timestamp: string;\n };\n text: string;\n threadId: string;\n};\n\nexport type LoopbackRawMessage = {\n author: \"assistant\" | \"user\";\n id: string;\n text: string;\n threadId: string;\n timestamp: string;\n};\n"]}
@@ -4,9 +4,11 @@ export type StartedWebhookServer = {
4
4
  };
5
5
  export declare function startWebhookServer(params: {
6
6
  handle(request: Request): Promise<Response>;
7
+ bodyTimeoutMs?: number | undefined;
7
8
  host: string;
8
9
  maxBodyBytes?: number;
9
10
  methods?: readonly string[] | undefined;
11
+ onError?: ((error: unknown) => void) | undefined;
10
12
  path: string;
11
13
  port: number;
12
14
  }): Promise<StartedWebhookServer>;
@@ -1,48 +1,69 @@
1
1
  import { createServer } from "node:http";
2
2
  const DEFAULT_MAX_BODY_BYTES = 1024 * 1024;
3
+ const DEFAULT_BODY_TIMEOUT_MS = 5_000;
3
4
  class RequestBodyTooLargeError extends Error {
4
5
  }
5
- async function readRequestBody(request, maxBodyBytes) {
6
+ class RequestBodyTimeoutError extends Error {
7
+ }
8
+ async function readRequestBody(request, maxBodyBytes, bodyTimeoutMs) {
6
9
  return await new Promise((resolve, reject) => {
7
10
  const chunks = [];
8
11
  let bodyBytes = 0;
9
12
  let settled = false;
10
- request.on("data", (chunk) => {
13
+ const cleanup = (preserveErrorListener = false) => {
14
+ clearTimeout(timeout);
15
+ request.off("data", onData);
16
+ request.off("end", onEnd);
17
+ if (!preserveErrorListener) {
18
+ request.off("error", onError);
19
+ }
20
+ request.off("aborted", onAborted);
21
+ };
22
+ const fail = (error) => {
23
+ if (settled) {
24
+ return;
25
+ }
26
+ settled = true;
27
+ cleanup(true);
28
+ reject(error);
29
+ };
30
+ const onData = (chunk) => {
11
31
  if (settled) {
12
32
  return;
13
33
  }
14
34
  const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
15
35
  bodyBytes += buffer.length;
16
36
  if (bodyBytes > maxBodyBytes) {
17
- settled = true;
18
37
  request.resume();
19
- reject(new RequestBodyTooLargeError());
38
+ fail(new RequestBodyTooLargeError());
20
39
  return;
21
40
  }
22
41
  chunks.push(buffer);
23
- });
24
- request.once("end", () => {
42
+ };
43
+ const onEnd = () => {
25
44
  if (settled) {
26
45
  return;
27
46
  }
28
47
  settled = true;
48
+ cleanup();
29
49
  resolve(Buffer.concat(chunks));
30
- });
31
- request.once("error", (error) => {
32
- if (settled) {
33
- return;
34
- }
35
- settled = true;
36
- reject(error);
37
- });
50
+ };
51
+ const onError = (error) => fail(error);
52
+ const onAborted = () => fail(new Error("request body aborted"));
53
+ const timeout = setTimeout(() => {
54
+ request.resume();
55
+ fail(new RequestBodyTimeoutError());
56
+ }, bodyTimeoutMs);
57
+ request.on("data", onData);
58
+ request.once("end", onEnd);
59
+ request.once("error", onError);
60
+ request.once("aborted", onAborted);
38
61
  });
39
62
  }
40
- async function toFetchRequest(request, maxBodyBytes) {
41
- const host = request.headers.host ?? "127.0.0.1";
42
- const url = new URL(request.url ?? "/", `http://${host}`);
63
+ async function toFetchRequest(request, url, maxBodyBytes, bodyTimeoutMs) {
43
64
  const body = request.method === "GET" || request.method === "HEAD"
44
65
  ? undefined
45
- : await readRequestBody(request, maxBodyBytes);
66
+ : await readRequestBody(request, maxBodyBytes, bodyTimeoutMs);
46
67
  const init = {
47
68
  headers: request.headers,
48
69
  };
@@ -76,25 +97,51 @@ function closeServer(server) {
76
97
  }
77
98
  resolve();
78
99
  });
100
+ server.closeAllConnections();
79
101
  });
80
102
  }
103
+ function formatUrlHost(host) {
104
+ return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
105
+ }
81
106
  export async function startWebhookServer(params) {
82
107
  const methods = new Set(params.methods ?? ["POST"]);
83
108
  const maxBodyBytes = params.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
109
+ const bodyTimeoutMs = params.bodyTimeoutMs ?? DEFAULT_BODY_TIMEOUT_MS;
84
110
  const server = createServer(async (request, response) => {
85
111
  try {
86
- const fetchRequest = await toFetchRequest(request, maxBodyBytes);
87
- const pathname = new URL(fetchRequest.url).pathname;
88
- if (!methods.has(fetchRequest.method) || pathname !== params.path) {
112
+ const method = request.method ?? "GET";
113
+ const host = request.headers.host ?? "127.0.0.1";
114
+ const url = new URL(request.url ?? "/", `http://${host}`);
115
+ if (!methods.has(method) || url.pathname !== params.path) {
116
+ request.resume();
89
117
  await writeFetchResponse(response, new Response("not found", { status: 404 }));
90
118
  return;
91
119
  }
120
+ const fetchRequest = await toFetchRequest(request, url, maxBodyBytes, bodyTimeoutMs);
92
121
  await writeFetchResponse(response, await params.handle(fetchRequest));
93
122
  }
94
123
  catch (error) {
95
- const message = error instanceof Error ? error.message : String(error);
96
- const status = error instanceof RequestBodyTooLargeError ? 413 : 500;
97
- await writeFetchResponse(response, new Response(status === 413 ? "request body too large" : message, { status }));
124
+ const status = error instanceof RequestBodyTooLargeError
125
+ ? 413
126
+ : error instanceof RequestBodyTimeoutError
127
+ ? 408
128
+ : 500;
129
+ if (status === 500) {
130
+ try {
131
+ params.onError?.(error);
132
+ }
133
+ catch {
134
+ // Error reporting must not change the public response.
135
+ }
136
+ }
137
+ await writeFetchResponse(response, new Response(status === 413
138
+ ? "request body too large"
139
+ : status === 408
140
+ ? "request body timeout"
141
+ : "internal server error", {
142
+ ...(status === 408 ? { headers: { connection: "close" } } : {}),
143
+ status,
144
+ }));
98
145
  }
99
146
  });
100
147
  await new Promise((resolve, reject) => {
@@ -112,7 +159,7 @@ export async function startWebhookServer(params) {
112
159
  async close() {
113
160
  await closeServer(server);
114
161
  },
115
- endpointUrl: `http://${params.host}:${address.port}${params.path}`,
162
+ endpointUrl: `http://${formatUrlHost(params.host)}:${address.port}${params.path}`,
116
163
  };
117
164
  }
118
165
  //# sourceMappingURL=webhook-server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-server.js","sourceRoot":"","sources":["../../../src/providers/webhook-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webhook-server.js","sourceRoot":"","sources":["../../../src/providers/webhook-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\n\nexport type StartedWebhookServer = {\n close(): Promise<void>;\n endpointUrl: string;\n};\n\nconst DEFAULT_MAX_BODY_BYTES = 1024 * 1024;\nconst DEFAULT_BODY_TIMEOUT_MS = 5_000;\n\nclass RequestBodyTooLargeError extends Error {}\nclass RequestBodyTimeoutError extends Error {}\n\nasync function readRequestBody(\n request: IncomingMessage,\n maxBodyBytes: number,\n bodyTimeoutMs: number,\n): Promise<Buffer> {\n return await new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let bodyBytes = 0;\n let settled = false;\n\n const cleanup = (preserveErrorListener = false) => {\n clearTimeout(timeout);\n request.off(\"data\", onData);\n request.off(\"end\", onEnd);\n if (!preserveErrorListener) {\n request.off(\"error\", onError);\n }\n request.off(\"aborted\", onAborted);\n };\n const fail = (error: unknown) => {\n if (settled) {\n return;\n }\n settled = true;\n cleanup(true);\n reject(error);\n };\n const onData = (chunk: Buffer | string) => {\n if (settled) {\n return;\n }\n\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n bodyBytes += buffer.length;\n if (bodyBytes > maxBodyBytes) {\n request.resume();\n fail(new RequestBodyTooLargeError());\n return;\n }\n chunks.push(buffer);\n };\n const onEnd = () => {\n if (settled) {\n return;\n }\n settled = true;\n cleanup();\n resolve(Buffer.concat(chunks));\n };\n const onError = (error: Error) => fail(error);\n const onAborted = () => fail(new Error(\"request body aborted\"));\n const timeout = setTimeout(() => {\n request.resume();\n fail(new RequestBodyTimeoutError());\n }, bodyTimeoutMs);\n\n request.on(\"data\", onData);\n request.once(\"end\", onEnd);\n request.once(\"error\", onError);\n request.once(\"aborted\", onAborted);\n });\n}\n\nasync function toFetchRequest(\n request: IncomingMessage,\n url: URL,\n maxBodyBytes: number,\n bodyTimeoutMs: number,\n): Promise<Request> {\n const body =\n request.method === \"GET\" || request.method === \"HEAD\"\n ? undefined\n : await readRequestBody(request, maxBodyBytes, bodyTimeoutMs);\n\n const init: RequestInit = {\n headers: request.headers as Record<string, string>,\n };\n if (request.method) {\n init.method = request.method;\n }\n if (body) {\n init.body = body;\n init.duplex = \"half\";\n }\n\n return new Request(url, init);\n}\n\nasync function writeFetchResponse(\n response: ServerResponse<IncomingMessage>,\n fetchResponse: Response,\n): Promise<void> {\n response.statusCode = fetchResponse.status;\n\n for (const [name, value] of fetchResponse.headers) {\n response.setHeader(name, value);\n }\n\n if (!fetchResponse.body) {\n response.end();\n return;\n }\n\n const body = Buffer.from(await fetchResponse.arrayBuffer());\n response.end(body);\n}\n\nfunction closeServer(server: Server): Promise<void> {\n return new Promise((resolve, reject) => {\n server.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n server.closeAllConnections();\n });\n}\n\nfunction formatUrlHost(host: string): string {\n return host.includes(\":\") && !host.startsWith(\"[\") ? `[${host}]` : host;\n}\n\nexport async function startWebhookServer(params: {\n handle(request: Request): Promise<Response>;\n bodyTimeoutMs?: number | undefined;\n host: string;\n maxBodyBytes?: number;\n methods?: readonly string[] | undefined;\n onError?: ((error: unknown) => void) | undefined;\n path: string;\n port: number;\n}): Promise<StartedWebhookServer> {\n const methods = new Set(params.methods ?? [\"POST\"]);\n const maxBodyBytes = params.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;\n const bodyTimeoutMs = params.bodyTimeoutMs ?? DEFAULT_BODY_TIMEOUT_MS;\n const server = createServer(async (request, response) => {\n try {\n const method = request.method ?? \"GET\";\n const host = request.headers.host ?? \"127.0.0.1\";\n const url = new URL(request.url ?? \"/\", `http://${host}`);\n if (!methods.has(method) || url.pathname !== params.path) {\n request.resume();\n await writeFetchResponse(response, new Response(\"not found\", { status: 404 }));\n return;\n }\n\n const fetchRequest = await toFetchRequest(request, url, maxBodyBytes, bodyTimeoutMs);\n await writeFetchResponse(response, await params.handle(fetchRequest));\n } catch (error) {\n const status =\n error instanceof RequestBodyTooLargeError\n ? 413\n : error instanceof RequestBodyTimeoutError\n ? 408\n : 500;\n if (status === 500) {\n try {\n params.onError?.(error);\n } catch {\n // Error reporting must not change the public response.\n }\n }\n await writeFetchResponse(\n response,\n new Response(\n status === 413\n ? \"request body too large\"\n : status === 408\n ? \"request body timeout\"\n : \"internal server error\",\n {\n ...(status === 408 ? { headers: { connection: \"close\" } } : {}),\n status,\n },\n ),\n );\n }\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(params.port, params.host, () => {\n server.off(\"error\", reject);\n resolve();\n });\n });\n\n const address = server.address();\n if (!address || typeof address === \"string\") {\n throw new Error(\"Unable to resolve webhook server address.\");\n }\n\n return {\n async close() {\n await closeServer(server);\n },\n endpointUrl: `http://${formatUrlHost(params.host)}:${address.port}${params.path}`,\n };\n}\n"]}
@@ -7,15 +7,35 @@ export type ServerRequestEvent = {
7
7
  query: Record<string, string>;
8
8
  type: "admin" | "api";
9
9
  };
10
+ export type HttpJsonHandlerResult = Response | {
11
+ onWriteFailure?(): Promise<void> | void;
12
+ onWriteSuccess?(): Promise<void> | void;
13
+ response: Response;
14
+ };
10
15
  export declare const ADMIN_TOKEN_HEADER = "x-crabline-admin-token";
16
+ export declare const DEFAULT_MAX_REQUEST_BODY_BYTES: number;
17
+ export declare const DEFAULT_SERVER_SHUTDOWN_GRACE_MS = 250;
18
+ export declare class InvalidJsonBodyError extends Error {
19
+ constructor(cause: unknown);
20
+ }
21
+ export declare class RequestBodyTooLargeError extends Error {
22
+ readonly maxBytes: number;
23
+ constructor(maxBytes: number);
24
+ }
11
25
  export declare function jsonResponse(value: unknown, status?: number): Response;
12
- export declare function readBody(request: IncomingMessage): Promise<Buffer>;
26
+ export declare function drainRequestBody(request: IncomingMessage): void;
27
+ export declare function readBody(request: IncomingMessage, maxBytes?: number): Promise<Buffer>;
28
+ export declare function parseUnknownRequestBody(request: IncomingMessage, maxBytes?: number): Promise<unknown>;
13
29
  export declare function parseRequestBody(request: IncomingMessage): Promise<Record<string, unknown>>;
30
+ export declare function isJsonObject(value: unknown): value is Record<string, unknown>;
14
31
  export declare function queryRecord(url: URL): Record<string, string>;
32
+ export declare function formatUrlHost(host: string): string;
33
+ export declare function isLoopbackHost(host: string): boolean;
15
34
  export declare function writeResponse(response: ServerResponse, fetchResponse: Response): Promise<void>;
16
- export declare function closeServer(server: Server): Promise<void>;
35
+ export declare function closeServer(server: Server, graceMs?: number): Promise<void>;
17
36
  export declare function startHttpJsonServer(params: {
18
- handle: (request: IncomingMessage) => Promise<Response>;
37
+ handle: (request: IncomingMessage, response: ServerResponse) => Promise<HttpJsonHandlerResult>;
38
+ handleError?: (error: unknown, request: IncomingMessage) => Response | undefined;
19
39
  host: string;
20
40
  port: number;
21
41
  serverName: string;
@@ -2,63 +2,255 @@ import { createServer } from "node:http";
2
2
  import { timingSafeEqual } from "node:crypto";
3
3
  import { CrablineError } from "../core/errors.js";
4
4
  export const ADMIN_TOKEN_HEADER = "x-crabline-admin-token";
5
+ export const DEFAULT_MAX_REQUEST_BODY_BYTES = 1024 * 1024;
6
+ export const DEFAULT_SERVER_SHUTDOWN_GRACE_MS = 250;
7
+ export class InvalidJsonBodyError extends Error {
8
+ constructor(cause) {
9
+ super("Request body is not valid JSON.", { cause });
10
+ this.name = "InvalidJsonBodyError";
11
+ }
12
+ }
13
+ export class RequestBodyTooLargeError extends Error {
14
+ maxBytes;
15
+ constructor(maxBytes) {
16
+ super(`Request body exceeds the ${maxBytes} byte limit.`);
17
+ this.maxBytes = maxBytes;
18
+ this.name = "RequestBodyTooLargeError";
19
+ }
20
+ }
5
21
  export function jsonResponse(value, status = 200) {
6
22
  return Response.json(value, { status });
7
23
  }
8
- export async function readBody(request) {
9
- const chunks = [];
10
- for await (const chunk of request) {
11
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
24
+ export function drainRequestBody(request) {
25
+ const ignoreError = () => { };
26
+ request.on("error", ignoreError);
27
+ if (request.destroyed || request.readableEnded) {
28
+ return;
12
29
  }
13
- return Buffer.concat(chunks);
30
+ const cleanup = () => {
31
+ request.off("close", cleanup);
32
+ request.off("end", cleanup);
33
+ request.off("error", ignoreError);
34
+ };
35
+ request.once("close", cleanup);
36
+ request.once("end", cleanup);
37
+ request.resume();
14
38
  }
15
- export async function parseRequestBody(request) {
16
- const body = await readBody(request);
39
+ export async function readBody(request, maxBytes = DEFAULT_MAX_REQUEST_BODY_BYTES) {
40
+ const contentLengthHeader = request.headers["content-length"];
41
+ const contentLengthValue = Array.isArray(contentLengthHeader)
42
+ ? contentLengthHeader[0]
43
+ : contentLengthHeader;
44
+ if (contentLengthValue && /^\d+$/u.test(contentLengthValue)) {
45
+ const contentLength = Number(contentLengthValue);
46
+ if (!Number.isSafeInteger(contentLength) || contentLength > maxBytes) {
47
+ drainRequestBody(request);
48
+ throw new RequestBodyTooLargeError(maxBytes);
49
+ }
50
+ }
51
+ return await new Promise((resolve, reject) => {
52
+ const chunks = [];
53
+ let length = 0;
54
+ let settled = false;
55
+ const cleanup = () => {
56
+ request.off("aborted", onAborted);
57
+ request.off("data", onData);
58
+ request.off("end", onEnd);
59
+ request.off("error", onError);
60
+ };
61
+ const fail = (error) => {
62
+ if (settled) {
63
+ return;
64
+ }
65
+ settled = true;
66
+ drainRequestBody(request);
67
+ cleanup();
68
+ reject(error);
69
+ };
70
+ const onAborted = () => fail(new Error("Request body stream was aborted."));
71
+ const onData = (chunk) => {
72
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
73
+ length += buffer.length;
74
+ if (length > maxBytes) {
75
+ fail(new RequestBodyTooLargeError(maxBytes));
76
+ return;
77
+ }
78
+ chunks.push(buffer);
79
+ };
80
+ const onEnd = () => {
81
+ if (settled) {
82
+ return;
83
+ }
84
+ settled = true;
85
+ cleanup();
86
+ resolve(Buffer.concat(chunks, length));
87
+ };
88
+ const onError = (error) => fail(error);
89
+ request.on("aborted", onAborted);
90
+ request.on("data", onData);
91
+ request.on("end", onEnd);
92
+ request.on("error", onError);
93
+ });
94
+ }
95
+ export async function parseUnknownRequestBody(request, maxBytes = DEFAULT_MAX_REQUEST_BODY_BYTES) {
96
+ const body = await readBody(request, maxBytes);
17
97
  if (body.length === 0) {
18
98
  return {};
19
99
  }
20
100
  const contentType = request.headers["content-type"] ?? "";
21
101
  const includesJson = Array.isArray(contentType)
22
- ? contentType.some((entry) => entry.includes("json"))
23
- : contentType.includes("json");
102
+ ? contentType.some((entry) => entry.toLowerCase().includes("json"))
103
+ : contentType.toLowerCase().includes("json");
24
104
  if (includesJson) {
25
- return JSON.parse(body.toString("utf8"));
105
+ try {
106
+ return JSON.parse(body.toString("utf8"));
107
+ }
108
+ catch (error) {
109
+ throw new InvalidJsonBodyError(error);
110
+ }
26
111
  }
27
112
  const params = new URLSearchParams(body.toString("utf8"));
28
113
  return Object.fromEntries(params.entries());
29
114
  }
115
+ export async function parseRequestBody(request) {
116
+ return (await parseUnknownRequestBody(request, Number.MAX_SAFE_INTEGER));
117
+ }
118
+ export function isJsonObject(value) {
119
+ return value !== null && typeof value === "object" && !Array.isArray(value);
120
+ }
30
121
  export function queryRecord(url) {
31
122
  return Object.fromEntries(url.searchParams.entries());
32
123
  }
124
+ export function formatUrlHost(host) {
125
+ return host.includes(":") ? `[${host}]` : host;
126
+ }
127
+ export function isLoopbackHost(host) {
128
+ const normalized = host
129
+ .trim()
130
+ .replace(/^\[(.*)\]$/u, "$1")
131
+ .toLowerCase();
132
+ return (normalized === "localhost" ||
133
+ normalized.endsWith(".localhost") ||
134
+ normalized === "::1" ||
135
+ normalized.startsWith("::ffff:127.") ||
136
+ /^127(?:\.\d{1,3}){3}$/u.test(normalized));
137
+ }
33
138
  export async function writeResponse(response, fetchResponse) {
34
139
  response.statusCode = fetchResponse.status;
35
140
  for (const [name, value] of fetchResponse.headers) {
141
+ if (name.toLowerCase() === "set-cookie") {
142
+ continue;
143
+ }
36
144
  response.setHeader(name, value);
37
145
  }
38
- response.end(Buffer.from(await fetchResponse.arrayBuffer()));
146
+ const setCookies = fetchResponse.headers.getSetCookie();
147
+ if (setCookies.length > 0) {
148
+ response.setHeader("set-cookie", setCookies);
149
+ }
150
+ const body = Buffer.from(await fetchResponse.arrayBuffer());
151
+ await new Promise((resolve, reject) => {
152
+ if (response.destroyed ||
153
+ response.req?.aborted ||
154
+ response.req?.socket.destroyed ||
155
+ response.socket?.destroyed) {
156
+ reject(new Error("HTTP response closed before delivery completed."));
157
+ return;
158
+ }
159
+ const cleanup = () => {
160
+ response.off("close", onClose);
161
+ response.off("error", onError);
162
+ response.off("finish", onFinish);
163
+ };
164
+ const onClose = () => {
165
+ if (response.writableFinished) {
166
+ cleanup();
167
+ resolve();
168
+ return;
169
+ }
170
+ cleanup();
171
+ reject(new Error("HTTP response closed before delivery completed."));
172
+ };
173
+ const onError = (error) => {
174
+ cleanup();
175
+ reject(error);
176
+ };
177
+ const onFinish = () => {
178
+ cleanup();
179
+ resolve();
180
+ };
181
+ response.once("close", onClose);
182
+ response.once("error", onError);
183
+ response.once("finish", onFinish);
184
+ try {
185
+ response.end(body);
186
+ }
187
+ catch (error) {
188
+ cleanup();
189
+ reject(error);
190
+ }
191
+ });
39
192
  }
40
- export function closeServer(server) {
193
+ export function closeServer(server, graceMs = DEFAULT_SERVER_SHUTDOWN_GRACE_MS) {
41
194
  return new Promise((resolve, reject) => {
195
+ const closeIdleInterval = setInterval(() => server.closeIdleConnections(), 25);
196
+ closeIdleInterval.unref();
197
+ const forceCloseTimer = setTimeout(() => {
198
+ server.closeAllConnections();
199
+ }, graceMs);
200
+ forceCloseTimer.unref();
42
201
  server.close((error) => {
202
+ clearInterval(closeIdleInterval);
203
+ clearTimeout(forceCloseTimer);
43
204
  if (error) {
44
205
  reject(error);
45
206
  return;
46
207
  }
47
208
  resolve();
48
209
  });
210
+ server.closeIdleConnections();
49
211
  });
50
212
  }
51
213
  export async function startHttpJsonServer(params) {
52
- const server = createServer(async (request, response) => {
214
+ const handleRequest = async (request, response) => {
53
215
  try {
54
- await writeResponse(response, await params.handle(request));
216
+ const result = await params.handle(request, response);
217
+ const handled = result instanceof Response ? { response: result } : result;
218
+ try {
219
+ await writeResponse(response, handled.response);
220
+ }
221
+ catch (error) {
222
+ await handled.onWriteFailure?.();
223
+ throw error;
224
+ }
225
+ try {
226
+ await handled.onWriteSuccess?.();
227
+ }
228
+ catch {
229
+ // Delivery is already committed; lifecycle failures must not trigger another response.
230
+ }
55
231
  }
56
232
  catch (error) {
57
- await writeResponse(response, jsonResponse({
58
- error: error instanceof Error ? error.message : String(error),
59
- ok: false,
60
- }, 500));
233
+ let handled;
234
+ try {
235
+ handled = params.handleError?.(error, request);
236
+ }
237
+ catch {
238
+ handled = undefined;
239
+ }
240
+ try {
241
+ await writeResponse(response, handled ??
242
+ jsonResponse({
243
+ error: "internal server error",
244
+ ok: false,
245
+ }, 500));
246
+ }
247
+ catch {
248
+ response.destroy();
249
+ }
61
250
  }
251
+ };
252
+ const server = createServer((request, response) => {
253
+ void handleRequest(request, response);
62
254
  });
63
255
  await new Promise((resolve, reject) => {
64
256
  server.once("error", reject);
@@ -74,7 +266,7 @@ export async function startHttpJsonServer(params) {
74
266
  });
75
267
  }
76
268
  return {
77
- baseUrl: `http://${params.host.includes(":") ? `[${params.host}]` : params.host}:${address.port}`,
269
+ baseUrl: `http://${formatUrlHost(params.host)}:${address.port}`,
78
270
  async close() {
79
271
  await closeServer(server);
80
272
  },
@@ -99,7 +291,8 @@ export function readInteger(value) {
99
291
  if (!stringValue || !/^-?\d+$/u.test(stringValue)) {
100
292
  return undefined;
101
293
  }
102
- return Number(stringValue);
294
+ const parsed = Number(stringValue);
295
+ return Number.isSafeInteger(parsed) ? parsed : undefined;
103
296
  }
104
297
  function readBearerToken(authorization) {
105
298
  if (!authorization) {
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/servers/http.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/servers/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA0D,MAAM,WAAW,CAAC;AACjG,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAmBlD,MAAM,CAAC,MAAM,kBAAkB,GAAG,wBAAwB,CAAC;AAC3D,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,GAAG,IAAI,CAAC;AAC1D,MAAM,CAAC,MAAM,gCAAgC,GAAG,GAAG,CAAC;AAEpD,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C,YAAY,KAAc;QACxB,KAAK,CAAC,iCAAiC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IAC5B,QAAQ;IAA7B,YAAqB,QAAgB;QACnC,KAAK,CAAC,4BAA4B,QAAQ,cAAc,CAAC,CAAC;wBADvC,QAAQ;QAE3B,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAED,MAAM,UAAU,YAAY,CAAC,KAAc,EAAE,MAAM,GAAG,GAAG;IACvD,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAwB;IACvD,MAAM,WAAW,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACjC,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAC/C,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACpC,CAAC,CAAC;IAEF,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,CAAC,MAAM,EAAE,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,OAAwB,EACxB,QAAQ,GAAG,8BAA8B;IAEzC,MAAM,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC9D,MAAM,kBAAkB,GAAG,KAAK,CAAC,OAAO,CAAC,mBAAmB,CAAC;QAC3D,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC;QACxB,CAAC,CAAC,mBAAmB,CAAC;IACxB,IAAI,kBAAkB,IAAI,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAC5D,MAAM,aAAa,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,IAAI,aAAa,GAAG,QAAQ,EAAE,CAAC;YACrE,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAC1B,MAAM,IAAI,wBAAwB,CAAC,QAAQ,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,OAAO,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnD,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAChC,CAAC,CAAC;QACF,MAAM,IAAI,GAAG,CAAC,KAAY,EAAE,EAAE;YAC5B,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO;YACT,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YACf,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAC1B,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CAAC;QACF,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC,CAAC;QAC5E,MAAM,MAAM,GAAG,CAAC,KAAsB,EAAE,EAAE;YACxC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnE,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC;YACxB,IAAI,MAAM,GAAG,QAAQ,EAAE,CAAC;gBACtB,IAAI,CAAC,IAAI,wBAAwB,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAC7C,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,GAAG,EAAE;YACjB,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO;YACT,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,EAAE,CAAC;YACV,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QACzC,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,CAAC,KAAY,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAE9C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACjC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACzB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,OAAwB,EACxB,QAAQ,GAAG,8BAA8B;IAEzC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC/C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IAC1D,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC;QAC7C,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnE,CAAC,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAY,CAAC;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,oBAAoB,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAwB;IAC7D,OAAO,CAAC,MAAM,uBAAuB,CAAC,OAAO,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAGtE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAQ;IAClC,OAAO,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,UAAU,GAAG,IAAI;SACpB,IAAI,EAAE;SACN,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC;SAC5B,WAAW,EAAE,CAAC;IACjB,OAAO,CACL,UAAU,KAAK,WAAW;QAC1B,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAC;QACjC,UAAU,KAAK,KAAK;QACpB,UAAU,CAAC,UAAU,CAAC,aAAa,CAAC;QACpC,wBAAwB,CAAC,IAAI,CAAC,UAAU,CAAC,CAC1C,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAAwB,EACxB,aAAuB;IAEvB,QAAQ,CAAC,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;QAClD,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,YAAY,EAAE,CAAC;YACxC,SAAS;QACX,CAAC;QACD,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IACD,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;IACxD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,QAAQ,CAAC,SAAS,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,aAAa,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,IACE,QAAQ,CAAC,SAAS;YAClB,QAAQ,CAAC,GAAG,EAAE,OAAO;YACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,SAAS;YAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,EAC1B,CAAC;YACD,MAAM,CAAC,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC,CAAC;YACrE,OAAO;QACT,CAAC;QACD,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC/B,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC/B,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACnC,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,IAAI,QAAQ,CAAC,gBAAgB,EAAE,CAAC;gBAC9B,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YACD,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC,CAAC;QACvE,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,CAAC,KAAY,EAAE,EAAE;YAC/B,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC,CAAC;QACF,MAAM,QAAQ,GAAG,GAAG,EAAE;YACpB,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAChC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAChC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAClC,IAAI,CAAC;YACH,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,MAAc,EACd,OAAO,GAAG,gCAAgC;IAE1C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,iBAAiB,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,oBAAoB,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/E,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC1B,MAAM,eAAe,GAAG,UAAU,CAAC,GAAG,EAAE;YACtC,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAC/B,CAAC,EAAE,OAAO,CAAC,CAAC;QACZ,eAAe,CAAC,KAAK,EAAE,CAAC;QACxB,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACrB,aAAa,CAAC,iBAAiB,CAAC,CAAC;YACjC,YAAY,CAAC,eAAe,CAAC,CAAC;YAC9B,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,CAAC,KAAK,CAAC,CAAC;gBACd,OAAO;YACT,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,oBAAoB,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAMzC;IACC,MAAM,aAAa,GAAG,KAAK,EAAE,OAAwB,EAAE,QAAwB,EAAE,EAAE;QACjF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACtD,MAAM,OAAO,GAAG,MAAM,YAAY,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;YAC3E,IAAI,CAAC;gBACH,MAAM,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;YAClD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;gBACjC,MAAM,KAAK,CAAC;YACd,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;YACnC,CAAC;YAAC,MAAM,CAAC;gBACP,uFAAuF;YACzF,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,OAA6B,CAAC;YAClC,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACjD,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,GAAG,SAAS,CAAC;YACtB,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,aAAa,CACjB,QAAQ,EACR,OAAO;oBACL,YAAY,CACV;wBACE,KAAK,EAAE,uBAAuB;wBAC9B,EAAE,EAAE,KAAK;qBACV,EACD,GAAG,CACJ,CACJ,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,QAAQ,CAAC,OAAO,EAAE,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;QAChD,KAAK,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YAC3C,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5B,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,MAAM,IAAI,aAAa,CAAC,qBAAqB,MAAM,CAAC,UAAU,wBAAwB,EAAE;YACtF,IAAI,EAAE,cAAc;SACrB,CAAC,CAAC;IACL,CAAC;IACD,OAAO;QACL,OAAO,EAAE,UAAU,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE;QAC/D,KAAK,CAAC,KAAK;YACT,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QACD,MAAM;KACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC3D,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC1B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IAC9C,OAAO,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,MAAM,WAAW,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC7C,IAAI,CAAC,WAAW,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QAClD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3D,CAAC;AAED,SAAS,eAAe,CAAC,aAAiC;IACxD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,aAAa,CAAC,SAAS,EAAE,CAAC;IAC1C,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,SAAS,EAAE,CAAC;QACpD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAwB,EAAE,aAAqB;IAC3E,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACnD,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC/D,MAAM,aAAa,GAAG,WAAW,IAAI,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACpF,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC5C,OAAO,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,IAAI,QAAQ,CAAC,cAAc,EAAE;QAClC,OAAO,EAAE,EAAE,kBAAkB,EAAE,QAAQ,EAAE;QACzC,MAAM,EAAE,GAAG;KACZ,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport { timingSafeEqual } from \"node:crypto\";\nimport { CrablineError } from \"../core/errors.js\";\n\nexport type ServerRequestEvent = {\n at: string;\n body?: unknown;\n method: string;\n path: string;\n query: Record<string, string>;\n type: \"admin\" | \"api\";\n};\n\nexport type HttpJsonHandlerResult =\n | Response\n | {\n onWriteFailure?(): Promise<void> | void;\n onWriteSuccess?(): Promise<void> | void;\n response: Response;\n };\n\nexport const ADMIN_TOKEN_HEADER = \"x-crabline-admin-token\";\nexport const DEFAULT_MAX_REQUEST_BODY_BYTES = 1024 * 1024;\nexport const DEFAULT_SERVER_SHUTDOWN_GRACE_MS = 250;\n\nexport class InvalidJsonBodyError extends Error {\n constructor(cause: unknown) {\n super(\"Request body is not valid JSON.\", { cause });\n this.name = \"InvalidJsonBodyError\";\n }\n}\n\nexport class RequestBodyTooLargeError extends Error {\n constructor(readonly maxBytes: number) {\n super(`Request body exceeds the ${maxBytes} byte limit.`);\n this.name = \"RequestBodyTooLargeError\";\n }\n}\n\nexport function jsonResponse(value: unknown, status = 200): Response {\n return Response.json(value, { status });\n}\n\nexport function drainRequestBody(request: IncomingMessage): void {\n const ignoreError = () => {};\n request.on(\"error\", ignoreError);\n if (request.destroyed || request.readableEnded) {\n return;\n }\n const cleanup = () => {\n request.off(\"close\", cleanup);\n request.off(\"end\", cleanup);\n request.off(\"error\", ignoreError);\n };\n\n request.once(\"close\", cleanup);\n request.once(\"end\", cleanup);\n request.resume();\n}\n\nexport async function readBody(\n request: IncomingMessage,\n maxBytes = DEFAULT_MAX_REQUEST_BODY_BYTES,\n): Promise<Buffer> {\n const contentLengthHeader = request.headers[\"content-length\"];\n const contentLengthValue = Array.isArray(contentLengthHeader)\n ? contentLengthHeader[0]\n : contentLengthHeader;\n if (contentLengthValue && /^\\d+$/u.test(contentLengthValue)) {\n const contentLength = Number(contentLengthValue);\n if (!Number.isSafeInteger(contentLength) || contentLength > maxBytes) {\n drainRequestBody(request);\n throw new RequestBodyTooLargeError(maxBytes);\n }\n }\n\n return await new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let length = 0;\n let settled = false;\n\n const cleanup = () => {\n request.off(\"aborted\", onAborted);\n request.off(\"data\", onData);\n request.off(\"end\", onEnd);\n request.off(\"error\", onError);\n };\n const fail = (error: Error) => {\n if (settled) {\n return;\n }\n settled = true;\n drainRequestBody(request);\n cleanup();\n reject(error);\n };\n const onAborted = () => fail(new Error(\"Request body stream was aborted.\"));\n const onData = (chunk: Buffer | string) => {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n length += buffer.length;\n if (length > maxBytes) {\n fail(new RequestBodyTooLargeError(maxBytes));\n return;\n }\n chunks.push(buffer);\n };\n const onEnd = () => {\n if (settled) {\n return;\n }\n settled = true;\n cleanup();\n resolve(Buffer.concat(chunks, length));\n };\n const onError = (error: Error) => fail(error);\n\n request.on(\"aborted\", onAborted);\n request.on(\"data\", onData);\n request.on(\"end\", onEnd);\n request.on(\"error\", onError);\n });\n}\n\nexport async function parseUnknownRequestBody(\n request: IncomingMessage,\n maxBytes = DEFAULT_MAX_REQUEST_BODY_BYTES,\n): Promise<unknown> {\n const body = await readBody(request, maxBytes);\n if (body.length === 0) {\n return {};\n }\n const contentType = request.headers[\"content-type\"] ?? \"\";\n const includesJson = Array.isArray(contentType)\n ? contentType.some((entry) => entry.toLowerCase().includes(\"json\"))\n : contentType.toLowerCase().includes(\"json\");\n if (includesJson) {\n try {\n return JSON.parse(body.toString(\"utf8\")) as unknown;\n } catch (error) {\n throw new InvalidJsonBodyError(error);\n }\n }\n const params = new URLSearchParams(body.toString(\"utf8\"));\n return Object.fromEntries(params.entries());\n}\n\nexport async function parseRequestBody(request: IncomingMessage): Promise<Record<string, unknown>> {\n return (await parseUnknownRequestBody(request, Number.MAX_SAFE_INTEGER)) as Record<\n string,\n unknown\n >;\n}\n\nexport function isJsonObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nexport function queryRecord(url: URL): Record<string, string> {\n return Object.fromEntries(url.searchParams.entries());\n}\n\nexport function formatUrlHost(host: string): string {\n return host.includes(\":\") ? `[${host}]` : host;\n}\n\nexport function isLoopbackHost(host: string): boolean {\n const normalized = host\n .trim()\n .replace(/^\\[(.*)\\]$/u, \"$1\")\n .toLowerCase();\n return (\n normalized === \"localhost\" ||\n normalized.endsWith(\".localhost\") ||\n normalized === \"::1\" ||\n normalized.startsWith(\"::ffff:127.\") ||\n /^127(?:\\.\\d{1,3}){3}$/u.test(normalized)\n );\n}\n\nexport async function writeResponse(\n response: ServerResponse,\n fetchResponse: Response,\n): Promise<void> {\n response.statusCode = fetchResponse.status;\n for (const [name, value] of fetchResponse.headers) {\n if (name.toLowerCase() === \"set-cookie\") {\n continue;\n }\n response.setHeader(name, value);\n }\n const setCookies = fetchResponse.headers.getSetCookie();\n if (setCookies.length > 0) {\n response.setHeader(\"set-cookie\", setCookies);\n }\n const body = Buffer.from(await fetchResponse.arrayBuffer());\n await new Promise<void>((resolve, reject) => {\n if (\n response.destroyed ||\n response.req?.aborted ||\n response.req?.socket.destroyed ||\n response.socket?.destroyed\n ) {\n reject(new Error(\"HTTP response closed before delivery completed.\"));\n return;\n }\n const cleanup = () => {\n response.off(\"close\", onClose);\n response.off(\"error\", onError);\n response.off(\"finish\", onFinish);\n };\n const onClose = () => {\n if (response.writableFinished) {\n cleanup();\n resolve();\n return;\n }\n cleanup();\n reject(new Error(\"HTTP response closed before delivery completed.\"));\n };\n const onError = (error: Error) => {\n cleanup();\n reject(error);\n };\n const onFinish = () => {\n cleanup();\n resolve();\n };\n response.once(\"close\", onClose);\n response.once(\"error\", onError);\n response.once(\"finish\", onFinish);\n try {\n response.end(body);\n } catch (error) {\n cleanup();\n reject(error);\n }\n });\n}\n\nexport function closeServer(\n server: Server,\n graceMs = DEFAULT_SERVER_SHUTDOWN_GRACE_MS,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const closeIdleInterval = setInterval(() => server.closeIdleConnections(), 25);\n closeIdleInterval.unref();\n const forceCloseTimer = setTimeout(() => {\n server.closeAllConnections();\n }, graceMs);\n forceCloseTimer.unref();\n server.close((error) => {\n clearInterval(closeIdleInterval);\n clearTimeout(forceCloseTimer);\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n server.closeIdleConnections();\n });\n}\n\nexport async function startHttpJsonServer(params: {\n handle: (request: IncomingMessage, response: ServerResponse) => Promise<HttpJsonHandlerResult>;\n handleError?: (error: unknown, request: IncomingMessage) => Response | undefined;\n host: string;\n port: number;\n serverName: string;\n}): Promise<{ baseUrl: string; close(): Promise<void>; server: Server }> {\n const handleRequest = async (request: IncomingMessage, response: ServerResponse) => {\n try {\n const result = await params.handle(request, response);\n const handled = result instanceof Response ? { response: result } : result;\n try {\n await writeResponse(response, handled.response);\n } catch (error) {\n await handled.onWriteFailure?.();\n throw error;\n }\n try {\n await handled.onWriteSuccess?.();\n } catch {\n // Delivery is already committed; lifecycle failures must not trigger another response.\n }\n } catch (error) {\n let handled: Response | undefined;\n try {\n handled = params.handleError?.(error, request);\n } catch {\n handled = undefined;\n }\n try {\n await writeResponse(\n response,\n handled ??\n jsonResponse(\n {\n error: \"internal server error\",\n ok: false,\n },\n 500,\n ),\n );\n } catch {\n response.destroy();\n }\n }\n };\n const server = createServer((request, response) => {\n void handleRequest(request, response);\n });\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(params.port, params.host, () => {\n server.off(\"error\", reject);\n resolve();\n });\n });\n const address = server.address();\n if (!address || typeof address === \"string\") {\n throw new CrablineError(`Unable to resolve ${params.serverName} local server address.`, {\n kind: \"connectivity\",\n });\n }\n return {\n baseUrl: `http://${formatUrlHost(params.host)}:${address.port}`,\n async close() {\n await closeServer(server);\n },\n server,\n };\n}\n\nexport function readString(value: unknown): string | undefined {\n if (typeof value === \"string\" && value.length > 0) {\n return value;\n }\n if (typeof value === \"number\" || typeof value === \"bigint\") {\n return value.toString();\n }\n return undefined;\n}\n\nexport function readTrimmedString(value: unknown): string | undefined {\n const stringValue = readString(value)?.trim();\n return stringValue ? stringValue : undefined;\n}\n\nexport function readInteger(value: unknown): number | undefined {\n const stringValue = readTrimmedString(value);\n if (!stringValue || !/^-?\\d+$/u.test(stringValue)) {\n return undefined;\n }\n const parsed = Number(stringValue);\n return Number.isSafeInteger(parsed) ? parsed : undefined;\n}\n\nfunction readBearerToken(authorization: string | undefined): string | undefined {\n if (!authorization) {\n return undefined;\n }\n const trimmed = authorization.trimStart();\n if (trimmed.slice(0, 7).toLowerCase() !== \"bearer \") {\n return undefined;\n }\n return trimmed.slice(7);\n}\n\nexport function hasAdminToken(request: IncomingMessage, expectedToken: string): boolean {\n const header = request.headers[ADMIN_TOKEN_HEADER];\n const directToken = Array.isArray(header) ? header[0] : header;\n const providedToken = directToken ?? readBearerToken(request.headers.authorization);\n if (!providedToken) {\n return false;\n }\n\n const provided = Buffer.from(providedToken);\n const expected = Buffer.from(expectedToken);\n return provided.length === expected.length && timingSafeEqual(provided, expected);\n}\n\nexport function adminAuthError(): Response {\n return new Response(\"unauthorized\", {\n headers: { \"www-authenticate\": \"Bearer\" },\n status: 401,\n });\n}\n"]}