@suveren/gateway 0.7.3 → 0.7.6

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 (206) hide show
  1. package/content/integrations/gmail.json +3 -6
  2. package/dist/control-plane/index.mjs +294 -62
  3. package/dist/mcp-server/http.mjs +467 -73
  4. package/dist/ui/assets/{index-CkWaRck1.css → index-BYVb_0oG.css} +1 -1
  5. package/dist/ui/assets/index-CsrM6RQr.js +110 -0
  6. package/dist/ui/index.html +2 -2
  7. package/node_modules/@hap/core/dist/index.d.mts +258 -9
  8. package/node_modules/@hap/core/dist/index.d.ts +258 -9
  9. package/node_modules/@hap/core/dist/index.js +227 -5
  10. package/node_modules/@hap/core/dist/index.mjs +213 -4
  11. package/node_modules/@hap/core/package.json +3 -2
  12. package/node_modules/@hap/core/src/did-key.ts +126 -0
  13. package/node_modules/@hap/core/src/identity.ts +21 -5
  14. package/node_modules/@hap/core/src/index.ts +3 -0
  15. package/node_modules/@hap/core/src/mandate.ts +207 -0
  16. package/node_modules/@hap/core/src/receipt.ts +74 -0
  17. package/node_modules/@hap/core/src/types.ts +81 -5
  18. package/node_modules/@hono/node-server/dist/index.cjs +151 -9
  19. package/node_modules/@hono/node-server/dist/index.mjs +151 -9
  20. package/node_modules/@hono/node-server/package.json +1 -1
  21. package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/README.md +2 -0
  22. package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/dist/index.d.ts +20 -0
  23. package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/dist/index.js +20 -14
  24. package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/dist/index.js.map +1 -1
  25. package/node_modules/@modelcontextprotocol/sdk/node_modules/body-parser/node_modules/content-type/package.json +1 -1
  26. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/accept.js +54 -0
  27. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/charset.js +10 -34
  28. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/encoding.js +9 -30
  29. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/language.js +15 -36
  30. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/mediaType.js +16 -134
  31. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
  32. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/README.md +71 -0
  33. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
  34. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
  35. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
  36. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/package.json +52 -0
  37. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/package.json +16 -10
  38. package/node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type/README.md +2 -0
  39. package/node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type/dist/index.d.ts +20 -0
  40. package/node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type/dist/index.js +20 -14
  41. package/node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type/dist/index.js.map +1 -1
  42. package/node_modules/@modelcontextprotocol/sdk/node_modules/type-is/node_modules/content-type/package.json +1 -1
  43. package/node_modules/@types/node/README.md +1 -1
  44. package/node_modules/@types/node/buffer.buffer.d.ts +5 -5
  45. package/node_modules/@types/node/crypto.d.ts +2 -2
  46. package/node_modules/@types/node/http.d.ts +16 -0
  47. package/node_modules/@types/node/http2.d.ts +10 -2
  48. package/node_modules/@types/node/package.json +2 -2
  49. package/node_modules/@types/node/process.d.ts +52 -0
  50. package/node_modules/@types/node/quic.d.ts +210 -28
  51. package/node_modules/@types/node/sqlite.d.ts +6 -2
  52. package/node_modules/@types/node/test.d.ts +36 -0
  53. package/node_modules/eventsource-parser/dist/index.cjs +2 -2
  54. package/node_modules/eventsource-parser/dist/index.cjs.map +1 -1
  55. package/node_modules/eventsource-parser/dist/index.js +2 -2
  56. package/node_modules/eventsource-parser/dist/index.js.map +1 -1
  57. package/node_modules/eventsource-parser/package.json +1 -1
  58. package/node_modules/eventsource-parser/src/parse.ts +4 -2
  59. package/node_modules/fast-uri/index.js +201 -48
  60. package/node_modules/fast-uri/lib/schemes.js +9 -4
  61. package/node_modules/fast-uri/lib/utils.js +388 -91
  62. package/node_modules/fast-uri/package.json +1 -1
  63. package/node_modules/fast-uri/test/component-safe-serialization.test.js +105 -0
  64. package/node_modules/fast-uri/test/equal.test.js +31 -3
  65. package/node_modules/fast-uri/test/fixtures/uri-js-parse.json +2 -0
  66. package/node_modules/fast-uri/test/ipv6-canonical.test.js +34 -0
  67. package/node_modules/fast-uri/test/ipv6-validation.test.js +90 -0
  68. package/node_modules/fast-uri/test/malformed-percent.test.js +77 -0
  69. package/node_modules/fast-uri/test/malformed-urn.test.js +61 -0
  70. package/node_modules/fast-uri/test/parse.test.js +7 -3
  71. package/node_modules/fast-uri/test/query-fragment-normalization.test.js +33 -0
  72. package/node_modules/fast-uri/test/reserved-path-normalization.test.js +109 -0
  73. package/node_modules/fast-uri/test/scheme-validation.test.js +124 -0
  74. package/node_modules/fast-uri/test/security-normalization.test.js +101 -0
  75. package/node_modules/fast-uri/test/security.test.js +75 -3
  76. package/node_modules/fast-uri/test/urn-full-input.test.js +29 -0
  77. package/node_modules/fast-uri/test/websocket-query-preservation.test.js +24 -0
  78. package/node_modules/hono/dist/cjs/client/client.js +27 -13
  79. package/node_modules/hono/dist/cjs/client/utils.js +4 -1
  80. package/node_modules/hono/dist/cjs/context.js +4 -0
  81. package/node_modules/hono/dist/cjs/helper/accepts/accepts.js +36 -2
  82. package/node_modules/hono/dist/cjs/helper/ssg/ssg.js +1 -1
  83. package/node_modules/hono/dist/cjs/helper/ssg/utils.js +30 -10
  84. package/node_modules/hono/dist/cjs/jsx/dom/render.js +2 -0
  85. package/node_modules/hono/dist/cjs/jsx/intrinsic-element/components.js +1 -1
  86. package/node_modules/hono/dist/cjs/middleware/cache/index.js +1 -1
  87. package/node_modules/hono/dist/cjs/middleware/cors/index.js +16 -13
  88. package/node_modules/hono/dist/cjs/middleware/csrf/index.js +1 -1
  89. package/node_modules/hono/dist/cjs/middleware/etag/digest.js +47 -1
  90. package/node_modules/hono/dist/cjs/middleware/etag/index.js +3 -3
  91. package/node_modules/hono/dist/cjs/middleware/pretty-json/index.js +3 -1
  92. package/node_modules/hono/dist/cjs/middleware/secure-headers/secure-headers.js +6 -3
  93. package/node_modules/hono/dist/cjs/request.js +8 -4
  94. package/node_modules/hono/dist/cjs/router/linear-router/router.js +7 -2
  95. package/node_modules/hono/dist/cjs/router/pattern-router/router.js +3 -9
  96. package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +10 -3
  97. package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +47 -83
  98. package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +2 -1
  99. package/node_modules/hono/dist/cjs/router/trie-router/node.js +46 -69
  100. package/node_modules/hono/dist/cjs/router/trie-router/router.js +3 -11
  101. package/node_modules/hono/dist/cjs/router/utils.js +27 -0
  102. package/node_modules/hono/dist/cjs/utils/body.js +15 -3
  103. package/node_modules/hono/dist/cjs/utils/cookie.js +1 -1
  104. package/node_modules/hono/dist/cjs/utils/ipaddr.js +5 -3
  105. package/node_modules/hono/dist/cjs/utils/stream.js +7 -1
  106. package/node_modules/hono/dist/cjs/utils/url.js +11 -3
  107. package/node_modules/hono/dist/client/client.js +27 -13
  108. package/node_modules/hono/dist/client/utils.js +4 -1
  109. package/node_modules/hono/dist/context.js +4 -0
  110. package/node_modules/hono/dist/helper/accepts/accepts.js +36 -2
  111. package/node_modules/hono/dist/helper/ssg/ssg.js +1 -1
  112. package/node_modules/hono/dist/helper/ssg/utils.js +30 -10
  113. package/node_modules/hono/dist/jsx/dom/render.js +2 -0
  114. package/node_modules/hono/dist/jsx/intrinsic-element/components.js +1 -1
  115. package/node_modules/hono/dist/middleware/cache/index.js +1 -1
  116. package/node_modules/hono/dist/middleware/cors/index.js +16 -13
  117. package/node_modules/hono/dist/middleware/csrf/index.js +1 -1
  118. package/node_modules/hono/dist/middleware/etag/digest.js +47 -1
  119. package/node_modules/hono/dist/middleware/etag/index.js +3 -3
  120. package/node_modules/hono/dist/middleware/pretty-json/index.js +3 -1
  121. package/node_modules/hono/dist/middleware/secure-headers/secure-headers.js +6 -3
  122. package/node_modules/hono/dist/request.js +8 -4
  123. package/node_modules/hono/dist/router/linear-router/router.js +7 -2
  124. package/node_modules/hono/dist/router/pattern-router/router.js +3 -9
  125. package/node_modules/hono/dist/router/reg-exp-router/node.js +6 -2
  126. package/node_modules/hono/dist/router/reg-exp-router/router.js +53 -84
  127. package/node_modules/hono/dist/router/reg-exp-router/trie.js +2 -1
  128. package/node_modules/hono/dist/router/trie-router/node.js +46 -69
  129. package/node_modules/hono/dist/router/trie-router/router.js +3 -11
  130. package/node_modules/hono/dist/router/utils.js +5 -0
  131. package/node_modules/hono/dist/types/context.d.ts +4 -0
  132. package/node_modules/hono/dist/types/middleware/secure-headers/permissions-policy.d.ts +3 -3
  133. package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +3 -0
  134. package/node_modules/hono/dist/types/router/trie-router/node.d.ts +1 -2
  135. package/node_modules/hono/dist/types/router/trie-router/router.d.ts +0 -1
  136. package/node_modules/hono/dist/types/router/utils.d.ts +1 -0
  137. package/node_modules/hono/dist/types/utils/url.d.ts +4 -0
  138. package/node_modules/hono/dist/utils/body.js +15 -3
  139. package/node_modules/hono/dist/utils/cookie.js +1 -1
  140. package/node_modules/hono/dist/utils/ipaddr.js +5 -3
  141. package/node_modules/hono/dist/utils/stream.js +7 -1
  142. package/node_modules/hono/dist/utils/url.js +11 -3
  143. package/node_modules/hono/package.json +1 -1
  144. package/node_modules/ip-address/README.md +134 -134
  145. package/node_modules/ip-address/dist/address-error.d.ts +11 -0
  146. package/node_modules/ip-address/dist/address-error.js.map +1 -1
  147. package/node_modules/ip-address/dist/ipv4.d.ts +22 -6
  148. package/node_modules/ip-address/dist/ipv4.js +22 -6
  149. package/node_modules/ip-address/dist/ipv4.js.map +1 -1
  150. package/node_modules/ip-address/dist/ipv6.d.ts +10 -3
  151. package/node_modules/ip-address/dist/ipv6.js +26 -17
  152. package/node_modules/ip-address/dist/ipv6.js.map +1 -1
  153. package/node_modules/ip-address/package.json +1 -1
  154. package/node_modules/is-plain-object/README.md +39 -95
  155. package/node_modules/is-plain-object/dist/is-plain-object.js +0 -2
  156. package/node_modules/is-plain-object/is-plain-object.d.ts +6 -1
  157. package/node_modules/is-plain-object/package.json +8 -34
  158. package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +4 -3
  159. package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +4 -3
  160. package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +4 -3
  161. package/node_modules/jose/dist/types/jwks/remote.d.ts +4 -4
  162. package/node_modules/jose/dist/types/jws/general/verify.d.ts +3 -3
  163. package/node_modules/jose/dist/types/jwt/encrypt.d.ts +12 -17
  164. package/node_modules/jose/dist/types/jwt/sign.d.ts +8 -14
  165. package/node_modules/jose/dist/types/jwt/unsecured.d.ts +9 -15
  166. package/node_modules/jose/dist/types/types.d.ts +4 -2
  167. package/node_modules/jose/dist/webapi/jwe/compact/encrypt.js +30 -8
  168. package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +8 -4
  169. package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +6 -13
  170. package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +19 -19
  171. package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +32 -21
  172. package/node_modules/jose/dist/webapi/jwk/embedded.js +15 -1
  173. package/node_modules/jose/dist/webapi/jwk/thumbprint.js +11 -5
  174. package/node_modules/jose/dist/webapi/jwks/local.js +45 -53
  175. package/node_modules/jose/dist/webapi/jwks/remote.js +82 -103
  176. package/node_modules/jose/dist/webapi/jws/compact/sign.js +10 -9
  177. package/node_modules/jose/dist/webapi/jws/flattened/sign.js +2 -1
  178. package/node_modules/jose/dist/webapi/jws/flattened/verify.js +8 -7
  179. package/node_modules/jose/dist/webapi/jws/general/sign.js +5 -3
  180. package/node_modules/jose/dist/webapi/jws/general/verify.js +51 -21
  181. package/node_modules/jose/dist/webapi/jwt/decrypt.js +7 -9
  182. package/node_modules/jose/dist/webapi/jwt/encrypt.js +21 -50
  183. package/node_modules/jose/dist/webapi/jwt/sign.js +8 -41
  184. package/node_modules/jose/dist/webapi/jwt/unsecured.js +21 -42
  185. package/node_modules/jose/dist/webapi/key/generate_key_pair.js +6 -2
  186. package/node_modules/jose/dist/webapi/key/generate_secret.js +7 -6
  187. package/node_modules/jose/dist/webapi/key/import.js +16 -12
  188. package/node_modules/jose/dist/webapi/lib/asn1.js +7 -2
  189. package/node_modules/jose/dist/webapi/lib/content_encryption.js +7 -15
  190. package/node_modules/jose/dist/webapi/lib/deflate.js +8 -0
  191. package/node_modules/jose/dist/webapi/lib/helpers.js +1 -1
  192. package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +85 -28
  193. package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +45 -13
  194. package/node_modules/jose/dist/webapi/lib/jwk_metadata.js +20 -0
  195. package/node_modules/jose/dist/webapi/lib/jws_sign.js +47 -36
  196. package/node_modules/jose/dist/webapi/lib/jws_verify.js +77 -45
  197. package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +82 -47
  198. package/node_modules/jose/dist/webapi/lib/key.js +24 -9
  199. package/node_modules/jose/dist/webapi/lib/key_management.js +10 -4
  200. package/node_modules/jose/dist/webapi/lib/key_options.js +6 -0
  201. package/node_modules/jose/dist/webapi/lib/options.js +26 -0
  202. package/node_modules/jose/dist/webapi/lib/type_checks.js +11 -13
  203. package/node_modules/jose/package.json +1 -1
  204. package/package.json +2 -2
  205. package/dist/ui/assets/index-DdJeSfV9.js +0 -110
  206. package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/HISTORY.md +0 -114
@@ -2,7 +2,7 @@ declare module "node:quic" {
2
2
  import { NonSharedBuffer } from "node:buffer";
3
3
  import { KeyObject } from "node:crypto";
4
4
  import { FileHandle } from "node:fs/promises";
5
- import { SocketAddress } from "node:net";
5
+ import { BlockList, SocketAddress } from "node:net";
6
6
  import { Writer } from "node:stream/iter";
7
7
  import { EphemeralKeyInfo, PeerCertificate } from "node:tls";
8
8
  /**
@@ -192,34 +192,40 @@ declare module "node:quic" {
192
192
  */
193
193
  authoritative?: boolean | undefined;
194
194
  }
195
+ /**
196
+ * @since v26.3.0
197
+ */
195
198
  interface ApplicationOptions {
196
199
  /**
197
- * Maximum number of header name-value pairs accepted per header block. Headers beyond this limit are silently
198
- * dropped. **Default:** `128`
200
+ * Maximum number of header name-value pairs accepted per header block.
201
+ * Headers beyond this limit are silently dropped. **Default:** `128`
199
202
  */
200
- maxHeaderPairs?: number | undefined;
203
+ maxHeaderPairs?: bigint | number | undefined;
201
204
  /**
202
- * Maximum total byte length of all header names and values combined per header block. Headers that would push
203
- * the total over this limit are silently dropped. **Default:** `8192`
205
+ * Maximum total byte length of all header names and values combined per header
206
+ * block. Headers that would push the total over this limit are silently
207
+ * dropped. **Default:** `8192`
204
208
  */
205
- maxHeaderLength?: number | undefined;
209
+ maxHeaderLength?: bigint | number | undefined;
206
210
  /**
207
- * Maximum size of a compressed header field section (QPACK). `0` means unlimited. **Default:** `0`
211
+ * Maximum size of a compressed header field section (QPACK). `0` means
212
+ * unlimited. **Default:** `0`
208
213
  */
209
- maxFieldSectionSize?: number | undefined;
214
+ maxFieldSectionSize?: bigint | number | undefined;
210
215
  /**
211
- * QPACK dynamic table capacity in bytes. Set to `0` to disable the dynamic table. **Default:** `4096`
216
+ * QPACK dynamic table capacity in bytes. Set to `0` to disable the dynamic
217
+ * table. **Default:** `4096`
212
218
  */
213
- qpackMaxDTableCapacity?: number | undefined;
219
+ qpackMaxDTableCapacity?: bigint | number | undefined;
214
220
  /**
215
221
  * QPACK encoder maximum dynamic table capacity. **Default:** `4096`
216
222
  */
217
- qpackEncoderMaxDTableCapacity?: number | undefined;
223
+ qpackEncoderMaxDTableCapacity?: bigint | number | undefined;
218
224
  /**
219
- * Maximum number of streams that can be blocked waiting for QPACK dynamic table updates.
220
- * **Default:** `100`
225
+ * Maximum number of streams that can e blocked waiting for QPACK dynamic table
226
+ * updates. **Default:** `100`
221
227
  */
222
- qpackBlockedStreams?: number | undefined;
228
+ qpackBlockedStreams?: bigint | number | undefined;
223
229
  /**
224
230
  * Enable the extended CONNECT protocol (RFC 9220). **Default:** `false`
225
231
  */
@@ -256,8 +262,7 @@ declare module "node:quic" {
256
262
  */
257
263
  alpn?: string | readonly string[] | undefined;
258
264
  /**
259
- * HTTP/3 application-specific options. These only apply when the negotiated
260
- * ALPN selects the HTTP/3 application (`'h3'`).
265
+ * Application-specific options.
261
266
  * @since v26.2.0
262
267
  */
263
268
  application?: ApplicationOptions | undefined;
@@ -342,7 +347,12 @@ declare module "node:quic" {
342
347
  minVersion?: number | undefined;
343
348
  /**
344
349
  * When the remote peer advertises a preferred address, this option specifies whether
345
- * to use it or ignore it.
350
+ * to use it or ignore it. The default is `'ignore'` because honoring a server's
351
+ * preferred address causes the client to migrate its connection to a different IP
352
+ * address, which can be exploited for data exfiltration attacks that are
353
+ * indistinguishable from legitimate QUIC connection migration at the network level.
354
+ * Set to `'use'` only when connecting to trusted servers that require preferred
355
+ * address migration.
346
356
  * @since v23.8.0
347
357
  */
348
358
  preferredAddressPolicy?: "use" | "ignore" | "default" | undefined;
@@ -370,6 +380,21 @@ declare module "node:quic" {
370
380
  * @since v26.2.0
371
381
  */
372
382
  datagramDropPolicy?: "drop-oldest" | "drop-newest" | undefined;
383
+ /**
384
+ * The maximum time in milliseconds that a peer-initiated stream can be idle
385
+ * (no data received) before it is automatically destroyed. This protects
386
+ * against slowloris-style attacks where a remote peer opens streams but never
387
+ * sends data, holding server resources indefinitely. Only peer-initiated
388
+ * streams are checked — locally-initiated streams are the application's
389
+ * responsibility. Set to `0` to disable.
390
+ *
391
+ * The idle check runs as part of the normal send processing loop, so it adds
392
+ * no additional timers or event loop overhead. The
393
+ * `session.stats.streamsIdleTimedOut` counter tracks how many streams have been
394
+ * destroyed by this mechanism.
395
+ * @since v26.3.0
396
+ */
397
+ streamIdleTimeout?: bigint | number | undefined;
373
398
  /**
374
399
  * The maximum number of `SendPendingData` cycles a datagram can survive
375
400
  * without being sent before it is abandoned. When a datagram cannot be
@@ -397,6 +422,30 @@ declare module "node:quic" {
397
422
  * @since v23.8.0
398
423
  */
399
424
  handshakeTimeout?: bigint | number | undefined;
425
+ /**
426
+ * Controls how the client handles server certificate validation:
427
+ *
428
+ * * `'strict'` — OpenSSL aborts the TLS handshake immediately if the server's
429
+ * certificate fails validation. The `session.opened` promise rejects with a
430
+ * TLS error. The application cannot inspect the certificate or the error
431
+ * details. This is the most secure mode.
432
+ *
433
+ * * `'auto'` — The TLS handshake completes regardless of validation result.
434
+ * If validation fails, the `session.opened` promise is rejected with an error
435
+ * containing the validation reason, and the session is destroyed. The
436
+ * `onhandshake` callback (if set) fires before rejection, allowing diagnostic
437
+ * logging. This is the default and matches the behavior of `tls.connect()`
438
+ * with `rejectUnauthorized: true`.
439
+ *
440
+ * * `'manual'` — The TLS handshake completes regardless of validation result.
441
+ * The `session.opened` promise resolves with the handshake info, which includes
442
+ * `validationErrorReason` and `validationErrorCode` if validation failed. The
443
+ * application is responsible for checking these values and deciding whether to
444
+ * continue. Use this mode for custom validation logic, certificate pinning, or
445
+ * intentionally accepting self-signed certificates.
446
+ * @since v26.3.0
447
+ */
448
+ verifyPeer?: "strict" | "auto" | "manual" | undefined;
400
449
  /**
401
450
  * The peer server name to target (SNI). Defaults to `'localhost'`.
402
451
  * @since v26.1.0
@@ -569,6 +618,31 @@ declare module "node:quic" {
569
618
  * @since v23.8.0
570
619
  */
571
620
  address?: SocketAddress | string | undefined;
621
+ /**
622
+ * An optional `net.BlockList` instance for filtering incoming packets by
623
+ * source address. When configured, every received UDP packet is checked against
624
+ * the block list before any QUIC processing occurs, minimizing resource
625
+ * expenditure on blocked sources. The block list is evaluated live — rules
626
+ * added to the `BlockList` object after the endpoint is created take effect
627
+ * immediately.
628
+ *
629
+ * See `endpointOptions.blockListPolicy` for how matches are interpreted.
630
+ * @since v26.3.0
631
+ */
632
+ blockList?: BlockList | undefined;
633
+ /**
634
+ * Controls how the `endpointOptions.blockList` is interpreted:
635
+ *
636
+ * * `'deny'` — Packets from addresses matching the block list are dropped.
637
+ * All other addresses are accepted. This is the typical blocklist mode.
638
+ * * `'allow'` — Only packets from addresses matching the block list are
639
+ * accepted. All other addresses are dropped. This is an allowlist mode
640
+ * for restricting access to known clients.
641
+ *
642
+ * If no block list is configured, this option has no effect.
643
+ * @since v26.3.0
644
+ */
645
+ blockListPolicy?: "deny" | "allow" | undefined;
572
646
  /**
573
647
  * The endpoint maintains an internal cache of validated socket addresses as a
574
648
  * performance optimization. This option sets the maximum number of addresses
@@ -625,15 +699,69 @@ declare module "node:quic" {
625
699
  */
626
700
  maxConnectionsTotal?: number | undefined;
627
701
  /**
628
- * Specifies the maximum number of QUIC retry attempts allowed per remote peer address.
629
- * @since v23.8.0
702
+ * The maximum number of QUIC retry packets the endpoint will send per second.
703
+ * This is a global rate limit (not per-host) that caps the total server-wide
704
+ * retry response rate, preventing spoofed-source floods from consuming unbounded
705
+ * resources.
706
+ * @since v26.3.0
630
707
  */
631
- maxRetries?: bigint | number | undefined;
708
+ retryRate?: number | undefined;
632
709
  /**
633
- * Specifies the maximum number of stateless resets that are allowed per remote peer address.
634
- * @since v23.8.0
710
+ * The maximum burst of retry packets allowed before rate limiting takes effect.
711
+ * @since v26.3.0
635
712
  */
636
- maxStatelessResetsPerHost?: bigint | number | undefined;
713
+ retryBurst?: number | undefined;
714
+ /**
715
+ * The maximum number of stateless reset packets the endpoint will send per second.
716
+ * @since v26.3.0
717
+ */
718
+ statelessResetRate?: number | undefined;
719
+ /**
720
+ * The maximum burst of stateless reset packets allowed before rate limiting
721
+ * takes effect.
722
+ * @since v26.3.0
723
+ */
724
+ statelessResetBurst?: number | undefined;
725
+ /**
726
+ * The maximum number of version negotiation packets the endpoint will send per
727
+ * second.
728
+ * @since v26.3.0
729
+ */
730
+ versionNegotiationRate?: number | undefined;
731
+ /**
732
+ * The maximum number of immediate connection close packets the endpoint will
733
+ * send per second.
734
+ * @since v26.3.0
735
+ */
736
+ versionNegotiationBurst?: number | undefined;
737
+ /**
738
+ * The maximum number of immediate connection close packets the endpoint will
739
+ * send per second.
740
+ * @since v26.3.0
741
+ */
742
+ immediateCloseRate?: number | undefined;
743
+ /**
744
+ * The maximum burst of immediate connection close packets allowed before rate
745
+ * limiting takes effect.
746
+ * @since v26.3.0
747
+ */
748
+ immediateCloseBurst?: number | undefined;
749
+ /**
750
+ * The maximum number of new sessions that a single remote address can create per
751
+ * second. This is a per-host rate limit tracked in the address validation LRU
752
+ * cache. It prevents a validated remote address from churning through sessions
753
+ * (rapidly opening and abandoning connections) faster than the server can handle.
754
+ * For benchmarking where traffic comes from a single source, set this to a high
755
+ * value.
756
+ * @since v26.3.0
757
+ */
758
+ sessionCreationRate?: number | undefined;
759
+ /**
760
+ * The maximum burst of new session creations allowed from a single remote address
761
+ * before rate limiting takes effect.
762
+ * @since v26.3.0
763
+ */
764
+ sessionCreationBurst?: number | undefined;
637
765
  /**
638
766
  * Specifies the length of time a QUIC retry token is considered valid.
639
767
  * @since v23.8.0
@@ -846,25 +974,66 @@ declare module "node:quic" {
846
974
  */
847
975
  readonly serverBusyCount: bigint;
848
976
  /**
849
- * The total number of QUIC retry attempts on this endpoint. Read only.
977
+ * The total number of retry packets sent by this endpoint. Read only.
850
978
  * @since v23.8.0
851
979
  */
852
980
  readonly retryCount: bigint;
853
981
  /**
854
- * The total number of sessions rejected due to QUIC version mismatch. Read only.
982
+ * The total number of retry packets dropped by the global rate
983
+ * limiter. Read only. A non-zero value indicates the endpoint is under retry
984
+ * flood pressure.
985
+ * @since v26.3.0
986
+ */
987
+ readonly retryRateLimited: bigint;
988
+ /**
989
+ * The total number of version negotiation packets sent by this
990
+ * endpoint. Read only.
855
991
  * @since v23.8.0
856
992
  */
857
993
  readonly versionNegotiationCount: bigint;
858
994
  /**
859
- * The total number of stateless resets handled by this endpoint. Read only.
995
+ * The total number of version negotiation packets dropped by
996
+ * the global rate limiter. Read only.
997
+ * @since v26.3.0
998
+ */
999
+ readonly versionNegotiationRateLimited: bigint;
1000
+ /**
1001
+ * The total number of stateless reset packets sent by this
1002
+ * endpoint. Read only.
860
1003
  * @since v23.8.0
861
1004
  */
862
1005
  readonly statelessResetCount: bigint;
863
1006
  /**
864
- * The total number of sessions that were closed before handshake completed. Read only.
1007
+ * The total number of stateless reset packets dropped by the
1008
+ * global rate limiter. Read only.
1009
+ * @since v26.3.0
1010
+ */
1011
+ readonly statelessResetRateLimited: bigint;
1012
+ /**
1013
+ * The total number of immediate connection close packets sent
1014
+ * by this endpoint. Read only.
865
1015
  * @since v23.8.0
866
1016
  */
867
1017
  readonly immediateCloseCount: bigint;
1018
+ /**
1019
+ * The total number of immediate connection close packets
1020
+ * dropped by the global rate limiter. Read only.
1021
+ * @since v26.3.0
1022
+ */
1023
+ readonly immediateCloseRateLimited: bigint;
1024
+ /**
1025
+ * The total number of session creation attempts dropped by the
1026
+ * per-host rate limiter. Read only. A non-zero value indicates one or more
1027
+ * remote addresses are creating sessions faster than the configured rate allows.
1028
+ * @since v26.3.0
1029
+ */
1030
+ readonly sessionCreationRateLimited: bigint;
1031
+ /**
1032
+ * The total number of incoming packets dropped by the
1033
+ * block list filter. Read only.
1034
+ * @since v26.3.0
1035
+ */
1036
+ readonly packetsBlocked: bigint;
868
1037
  }
869
1038
  }
870
1039
  interface CreateStreamOptions {
@@ -997,6 +1166,13 @@ declare module "node:quic" {
997
1166
  */
998
1167
  class QuicSession implements AsyncDisposable {
999
1168
  private constructor();
1169
+ /**
1170
+ * The current application-level options for this session. These include settings
1171
+ * that are specific to the negotiated application protocol (e.g. HTTP/3) and may
1172
+ * be negotiated separately from the transport parameters. Read only.
1173
+ * @since v26.3.0
1174
+ */
1175
+ readonly applicationOptions: { [K in keyof ApplicationOptions]-?: ApplicationOptions[K] & (bigint | boolean) };
1000
1176
  /**
1001
1177
  * Initiate a graceful close of the session. Existing streams will be allowed
1002
1178
  * to complete but no new streams will be opened. Once all streams have closed,
@@ -1395,6 +1571,12 @@ declare module "node:quic" {
1395
1571
  * @since v23.8.0
1396
1572
  */
1397
1573
  readonly datagramsLost: bigint;
1574
+ /**
1575
+ * The total number of peer-initiated streams destroyed by the
1576
+ * stream idle timeout. Read only.
1577
+ * @since v26.3.0
1578
+ */
1579
+ readonly streamsIdleTimedOut: bigint;
1398
1580
  }
1399
1581
  }
1400
1582
  interface QuicErrorOptions {
@@ -127,8 +127,12 @@ declare module "node:sqlite" {
127
127
  }
128
128
  interface ApplyChangesetOptions {
129
129
  /**
130
- * Skip changes that, when targeted table name is supplied to this function, return a truthy value.
131
- * By default, all changes are attempted.
130
+ * for each table affected by at least
131
+ * one change in the changeset, the `filter` callback is invoked with the
132
+ * table name as the first argument. If the return value is falsy, then no
133
+ * attempt is made to apply any changes to the table.
134
+ * Otherwise, if the return value is truthy or no `filter` callback is provided,
135
+ * all changes related to the table are attempted.
132
136
  * @since v22.12.0
133
137
  */
134
138
  filter?: ((tableName: string) => boolean) | undefined;
@@ -677,6 +677,12 @@ declare module "node:test" {
677
677
  * The nesting level of the test.
678
678
  */
679
679
  nesting: number;
680
+ /**
681
+ * The `testId` of the enclosing test, or
682
+ * `undefined` for top-level tests. Lets custom reporters track lineage
683
+ * when concurrent siblings at the same nesting level interleave.
684
+ */
685
+ parentId: number | undefined;
680
686
  /**
681
687
  * The flattened lowercased tags declared on the test
682
688
  * and its ancestor suites, in declaration order. Empty for untagged tests.
@@ -711,6 +717,12 @@ declare module "node:test" {
711
717
  * The nesting level of the test.
712
718
  */
713
719
  nesting: number;
720
+ /**
721
+ * The `testId` of the enclosing test, or
722
+ * `undefined` for top-level tests. Lets custom reporters track lineage
723
+ * when concurrent siblings at the same nesting level interleave.
724
+ */
725
+ parentId: number | undefined;
714
726
  /**
715
727
  * The flattened lowercased tags declared on the test
716
728
  * and its ancestor suites, in declaration order. Empty for untagged tests.
@@ -738,6 +750,12 @@ declare module "node:test" {
738
750
  * The nesting level of the test.
739
751
  */
740
752
  nesting: number;
753
+ /**
754
+ * The `testId` of the enclosing test, or
755
+ * `undefined` for top-level tests. Lets custom reporters track lineage
756
+ * when concurrent siblings at the same nesting level interleave.
757
+ */
758
+ parentId: number | undefined;
741
759
  /**
742
760
  * The flattened lowercased tags declared on the test
743
761
  * and its ancestor suites, in declaration order. Empty for untagged tests.
@@ -789,6 +807,12 @@ declare module "node:test" {
789
807
  * The nesting level of the test.
790
808
  */
791
809
  nesting: number;
810
+ /**
811
+ * The `testId` of the enclosing test, or
812
+ * `undefined` for top-level tests. Lets custom reporters track lineage
813
+ * when concurrent siblings at the same nesting level interleave.
814
+ */
815
+ parentId: number | undefined;
792
816
  /**
793
817
  * The flattened lowercased tags declared on the test
794
818
  * and its ancestor suites, in declaration order. Empty for untagged tests.
@@ -856,6 +880,12 @@ declare module "node:test" {
856
880
  * The nesting level of the test.
857
881
  */
858
882
  nesting: number;
883
+ /**
884
+ * The `testId` of the enclosing test, or
885
+ * `undefined` for top-level tests. Lets custom reporters track lineage
886
+ * when concurrent siblings at the same nesting level interleave.
887
+ */
888
+ parentId: number | undefined;
859
889
  /**
860
890
  * The flattened lowercased tags declared on the test
861
891
  * and its ancestor suites, in declaration order. Empty for untagged tests.
@@ -900,6 +930,12 @@ declare module "node:test" {
900
930
  * The nesting level of the test.
901
931
  */
902
932
  nesting: number;
933
+ /**
934
+ * The `testId` of the enclosing test, or
935
+ * `undefined` for top-level tests. Lets custom reporters track lineage
936
+ * when concurrent siblings at the same nesting level interleave.
937
+ */
938
+ parentId: number | undefined;
903
939
  /**
904
940
  * The flattened lowercased tags declared on the test
905
941
  * and its ancestor suites, in declaration order. Empty for untagged tests.
@@ -101,7 +101,7 @@ ${value2}`, dataLines++;
101
101
  }
102
102
  if (firstCharCode === 105 && chunk.charCodeAt(start + 1) === 100 && chunk.charCodeAt(start + 2) === 58) {
103
103
  const value2 = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);
104
- id = value2.includes("\0") ? void 0 : value2;
104
+ value2.includes("\0") || (id = value2);
105
105
  return;
106
106
  }
107
107
  if (firstCharCode === 58) {
@@ -129,7 +129,7 @@ ${value2}`, dataLines++;
129
129
  ${value}`, dataLines++;
130
130
  break;
131
131
  case "id":
132
- id = value.includes("\0") ? void 0 : value;
132
+ value.includes("\0") || (id = value);
133
133
  break;
134
134
  case "retry":
135
135
  /^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/errors.ts","../src/parse.ts"],"sourcesContent":["/**\n * The type of error that occurred.\n * @public\n */\nexport type ErrorType = 'invalid-retry' | 'unknown-field' | 'max-buffer-size-exceeded'\n\n/**\n * Error thrown when encountering an issue during parsing.\n *\n * @public\n */\nexport class ParseError extends Error {\n /**\n * The type of error that occurred.\n */\n type: ErrorType\n\n /**\n * In the case of an unknown field encountered in the stream, this will be the field name.\n */\n field?: string | undefined\n\n /**\n * In the case of an unknown field encountered in the stream, this will be the value of the field.\n */\n value?: string | undefined\n\n /**\n * The line that caused the error, if available.\n */\n line?: string | undefined\n\n constructor(\n message: string,\n options: {type: ErrorType; field?: string; value?: string; line?: string},\n ) {\n super(message)\n this.name = 'ParseError'\n this.type = options.type\n this.field = options.field\n this.value = options.value\n this.line = options.line\n }\n}\n","/**\n * EventSource/Server-Sent Events parser\n * @see https://html.spec.whatwg.org/multipage/server-sent-events.html\n */\nimport {ParseError} from './errors.ts'\nimport type {EventSourceParser, ParserConfig} from './types.ts'\n\n// ASCII codes used in the hot parsing paths.\nconst LF = 10\nconst CR = 13\nconst SPACE = 32\n\n// oxlint-disable-next-line no-unused-vars\nfunction noop(_arg: unknown) {\n // intentional noop\n}\n\n/**\n * Creates a new EventSource parser.\n *\n * @param config - Parser configuration. Accepts callbacks (see {@link ParserCallbacks})\n * and options like `maxBufferSize` (see {@link ParserConfig}).\n *\n * @returns A new EventSource parser, with `feed` and `reset` methods.\n * @public\n */\nexport function createParser(config: ParserConfig): EventSourceParser {\n if (typeof config === 'function') {\n throw new TypeError(\n '`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?',\n )\n }\n\n const {onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize} = config\n\n // Trailing bytes from prior `feed()` calls that did not yet form a complete line.\n // Stored as an array of fragments and only joined when a line terminator arrives.\n // Concatenating per-feed (`prefix + chunk`) is O(N²) when a single SSE line spans\n // many chunks (e.g. a large `data:` payload streamed in tiny slices, or an MCP-style\n // server that emits one giant content block). Buffering as fragments + joining once\n // makes the same workload linear.\n const pendingFragments: string[] = []\n\n // Running total of `pendingFragments` lengths, kept in sync with the array so the\n // `maxBufferSize` check doesn't have to walk the fragment list on every feed.\n let pendingFragmentsLength = 0\n\n let isFirstChunk = true\n let id: string | undefined\n let data = ''\n let dataLines = 0\n let eventType: string | undefined\n\n // Set after a `maxBufferSize` overflow. Once tripped, `feed()` throws until\n // `reset()` is called — see the comment on `maxBufferSize` in `ParserConfig`.\n let terminated = false\n\n /**\n * Feeds a chunk of the SSE stream to the parser. Any trailing bytes that do\n * not yet form a complete line are held back and prepended to the next chunk,\n * so callers can pass arbitrary slices of the stream without worrying about\n * line boundaries.\n *\n * Per the SSE spec, a UTF-8 BOM (0xEF 0xBB 0xBF) at the start of the very\n * first chunk is stripped before parsing.\n *\n * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream\n */\n function feed(chunk: string) {\n if (terminated) {\n throw new Error(\n 'Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.',\n )\n }\n\n if (isFirstChunk) {\n isFirstChunk = false\n // Match and strip UTF-8 BOM from the start of the stream, if present.\n // (Per the spec, this is only valid at the very start of the stream)\n if (\n chunk.charCodeAt(0) === 0xef &&\n chunk.charCodeAt(1) === 0xbb &&\n chunk.charCodeAt(2) === 0xbf\n ) {\n chunk = chunk.slice(3)\n }\n }\n\n // Hot path: no buffered prefix from a prior partial line. Hand the chunk\n // straight to `processLines`, exactly like the original implementation.\n // Zero new work in the common case (every chunk ends with `\\n\\n`).\n if (pendingFragments.length === 0) {\n const trailing = processLines(chunk)\n if (trailing !== '') {\n pendingFragments.push(trailing)\n pendingFragmentsLength = trailing.length\n }\n checkBufferSize()\n return\n }\n\n // We have a buffered prefix. If this chunk also has no terminator, append\n // to the buffer without concatenating — that's the O(N²) trap we're\n // avoiding (large single `data:` payload split across many tiny chunks).\n if (chunk.indexOf('\\n') === -1 && chunk.indexOf('\\r') === -1) {\n pendingFragments.push(chunk)\n pendingFragmentsLength += chunk.length\n checkBufferSize()\n return\n }\n\n // Terminator arrived. Join the accumulated fragments + this chunk once,\n // process, and buffer any new trailing partial line.\n pendingFragments.push(chunk)\n const input = pendingFragments.join('')\n pendingFragments.length = 0\n pendingFragmentsLength = 0\n const trailing = processLines(input)\n if (trailing !== '') {\n pendingFragments.push(trailing)\n pendingFragmentsLength = trailing.length\n }\n checkBufferSize()\n }\n\n function checkBufferSize() {\n if (maxBufferSize === undefined) return\n if (pendingFragmentsLength + data.length <= maxBufferSize) return\n\n terminated = true\n pendingFragments.length = 0\n pendingFragmentsLength = 0\n id = undefined\n data = ''\n dataLines = 0\n eventType = undefined\n onError(\n new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {\n type: 'max-buffer-size-exceeded',\n }),\n )\n }\n\n /**\n * Splits `chunk` into SSE lines and dispatches each to the appropriate handler.\n * Returns any trailing bytes that did not terminate with a line break, so the\n * caller can prepend them to the next chunk.\n *\n * The SSE spec permits three line terminators: `\\n`, `\\r`, and `\\r\\n`. Real-world\n * streams almost always use plain `\\n`, so we take a fast path when no `\\r` is\n * present in the chunk. The slow path is spec-correct but does more work per line.\n */\n function processLines(chunk: string): string {\n let searchIndex = 0\n\n // Fast path: LF-only chunk (the common case for typical SSE servers).\n // We can scan forward with a single `indexOf('\\n')` per line and inline\n // the hot-path branches for `data:` and `event:` without the CR bookkeeping\n // the slow path needs.\n if (chunk.indexOf('\\r') === -1) {\n let lfIndex = chunk.indexOf('\\n', searchIndex)\n while (lfIndex !== -1) {\n // Blank line: end-of-event marker. Dispatch the accumulated event (if any)\n // and reset the buffered fields. This is hoisted out of `parseLine` because\n // it's the single most common line shape after `data:` lines.\n if (searchIndex === lfIndex) {\n if (dataLines > 0) {\n onEvent({id, event: eventType, data})\n }\n id = undefined\n data = ''\n dataLines = 0\n eventType = undefined\n searchIndex = lfIndex + 1\n lfIndex = chunk.indexOf('\\n', searchIndex)\n continue\n }\n const firstCharCode = chunk.charCodeAt(searchIndex)\n if (isDataPrefix(chunk, searchIndex, firstCharCode)) {\n // `data:` line — append the value to the event's data buffer.\n // 'data:'.length === 5, 'data: '.length === 6\n const valueStart =\n chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5\n const value = chunk.slice(valueStart, lfIndex)\n // Fast path within a fast path: if this is the first data line AND the\n // next char is another LF (i.e. `data:foo\\n\\n`), dispatch immediately\n // without ever writing to the `data` buffer. This is the shape of a\n // typical single-line SSE event (ChatGPT-style streams, etc.) and is\n // hot enough to be worth the duplication.\n if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {\n onEvent({id, event: eventType, data: value})\n id = undefined\n data = ''\n eventType = undefined\n searchIndex = lfIndex + 2\n lfIndex = chunk.indexOf('\\n', searchIndex)\n continue\n }\n // Multi-line data: concatenate with newline separator per spec.\n data = dataLines === 0 ? value : `${data}\\n${value}`\n dataLines++\n } else if (isEventPrefix(chunk, searchIndex, firstCharCode)) {\n // `event:` line — set the event type for the next dispatch. Per spec,\n // an empty value resets `event type` to its default (undefined here).\n // 'event:'.length === 6, 'event: '.length === 7\n eventType =\n chunk.slice(\n chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6,\n lfIndex,\n ) || undefined\n } else {\n // Everything else: `id:`, `retry:`, comment lines (`:` prefix), unknown\n // fields, or malformed lines. These are rarer and go through the full\n // per-line parser, which handles the SSE field grammar in detail.\n parseLine(chunk, searchIndex, lfIndex)\n }\n searchIndex = lfIndex + 1\n lfIndex = chunk.indexOf('\\n', searchIndex)\n }\n return chunk.slice(searchIndex)\n }\n\n // Slow path: the chunk contains at least one `\\r`, so lines may be terminated\n // by `\\r`, `\\n`, or `\\r\\n`. We locate the next terminator by looking at both\n // the nearest `\\r` and `\\n` and picking whichever comes first.\n while (searchIndex < chunk.length) {\n const crIndex = chunk.indexOf('\\r', searchIndex)\n const lfIndex = chunk.indexOf('\\n', searchIndex)\n\n let lineEnd = -1\n if (crIndex !== -1 && lfIndex !== -1) {\n lineEnd = crIndex < lfIndex ? crIndex : lfIndex\n } else if (crIndex !== -1) {\n // A trailing `\\r` at the very end of the chunk is ambiguous: it could be\n // a bare-CR terminator, or the first half of a `\\r\\n` whose `\\n` arrives\n // in the next chunk. Defer until we see more input.\n if (crIndex === chunk.length - 1) {\n lineEnd = -1\n } else {\n lineEnd = crIndex\n }\n } else if (lfIndex !== -1) {\n lineEnd = lfIndex\n }\n\n if (lineEnd === -1) {\n break\n }\n\n parseLine(chunk, searchIndex, lineEnd)\n searchIndex = lineEnd + 1\n // If we just consumed a `\\r` and the next char is `\\n`, skip it so the\n // pair is treated as a single terminator rather than an empty line.\n if (chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF) {\n searchIndex++\n }\n }\n\n return chunk.slice(searchIndex)\n }\n\n function parseLine(chunk: string, start: number, end: number) {\n if (start === end) {\n dispatchEvent()\n return\n }\n\n const firstCharCode = chunk.charCodeAt(start)\n\n if (isDataPrefix(chunk, start, firstCharCode)) {\n // 'data:'.length === 5, 'data: '.length === 6\n const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5\n const value = chunk.slice(valueStart, end)\n data = dataLines === 0 ? value : `${data}\\n${value}`\n dataLines++\n return\n }\n\n if (isEventPrefix(chunk, start, firstCharCode)) {\n // 'event:'.length === 6, 'event: '.length === 7\n eventType =\n chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || undefined\n return\n }\n\n // Fast path for \"id:\" — 'i' = 105, 'd' = 100, ':' = 58\n if (\n firstCharCode === 105 &&\n chunk.charCodeAt(start + 1) === 100 &&\n chunk.charCodeAt(start + 2) === 58\n ) {\n // 'id:'.length === 3, 'id: '.length === 4\n const value = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end)\n id = value.includes('\\0') ? undefined : value\n return\n }\n\n // Comment line — ':' = 58\n if (firstCharCode === 58) {\n if (onComment) {\n const line = chunk.slice(start, end)\n // skip ':' (+1), or ': ' (+2) when a space follows\n onComment(line.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1))\n }\n return\n }\n\n const line = chunk.slice(start, end)\n const fieldSeparatorIndex = line.indexOf(':')\n if (fieldSeparatorIndex === -1) {\n processField(line, '', line)\n return\n }\n\n const field = line.slice(0, fieldSeparatorIndex)\n // skip ':' (+1), or ': ' (+2) when a space follows\n const offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1\n const value = line.slice(fieldSeparatorIndex + offset)\n processField(field, value, line)\n }\n\n function processField(field: string, value: string, line: string) {\n // Field names must be compared literally, with no case folding performed.\n switch (field) {\n case 'event':\n // Set the `event type` buffer to field value\n eventType = value || undefined\n break\n case 'data':\n data = dataLines === 0 ? value : `${data}\\n${value}`\n dataLines++\n break\n case 'id':\n // If the field value does not contain U+0000 NULL, then set the `ID` buffer to\n // the field value. Otherwise, ignore the field.\n id = value.includes('\\0') ? undefined : value\n break\n case 'retry':\n // If the field value consists of only ASCII digits, then interpret the field value as an\n // integer in base ten, and set the event stream's reconnection time to that integer.\n // Otherwise, ignore the field.\n if (/^\\d+$/.test(value)) {\n onRetry(parseInt(value, 10))\n } else {\n onError(\n new ParseError(`Invalid \\`retry\\` value: \"${value}\"`, {\n type: 'invalid-retry',\n value,\n line,\n }),\n )\n }\n break\n default:\n // Otherwise, the field is ignored.\n onError(\n new ParseError(\n `Unknown field \"${field.length > 20 ? `${field.slice(0, 20)}…` : field}\"`,\n {type: 'unknown-field', field, value, line},\n ),\n )\n break\n }\n }\n\n function dispatchEvent() {\n if (dataLines > 0) {\n onEvent({\n id,\n event: eventType,\n data,\n })\n }\n\n id = undefined\n data = ''\n dataLines = 0\n eventType = undefined\n }\n\n function reset(options: {consume?: boolean} = {}) {\n if (options.consume && pendingFragments.length > 0) {\n const incompleteLine = pendingFragments.join('')\n parseLine(incompleteLine, 0, incompleteLine.length)\n }\n\n isFirstChunk = true\n id = undefined\n data = ''\n dataLines = 0\n eventType = undefined\n pendingFragments.length = 0\n pendingFragmentsLength = 0\n terminated = false\n }\n\n return {feed, reset}\n}\n\n/**\n * Checks if `chunk` starts with the literal `data:` at index `i`.\n *\n * Equivalent to `chunk.startsWith('data:', i)`, but benchmarks show this\n * hand-unrolled char-code comparison is ~20% faster on common event types.\n * The caller passes `firstCharCode` (the code at `i`) so it can be reused\n * across prefix checks.\n *\n * ASCII: 'd' = 100, 'a' = 97, 't' = 116, 'a' = 97, ':' = 58\n */\nfunction isDataPrefix(chunk: string, i: number, firstCharCode: number): boolean {\n return (\n firstCharCode === 100 &&\n chunk.charCodeAt(i + 1) === 97 &&\n chunk.charCodeAt(i + 2) === 116 &&\n chunk.charCodeAt(i + 3) === 97 &&\n chunk.charCodeAt(i + 4) === 58\n )\n}\n\n/**\n * Checks if `chunk` starts with the literal `event:` at index `i`.\n *\n * See {@link isDataPrefix} for why this is hand-unrolled rather than using\n * `String.prototype.startsWith`.\n *\n * ASCII: 'e' = 101, 'v' = 118, 'e' = 101, 'n' = 110, 't' = 116, ':' = 58\n */\nfunction isEventPrefix(chunk: string, i: number, firstCharCode: number): boolean {\n return (\n firstCharCode === 101 &&\n chunk.charCodeAt(i + 1) === 118 &&\n chunk.charCodeAt(i + 2) === 101 &&\n chunk.charCodeAt(i + 3) === 110 &&\n chunk.charCodeAt(i + 4) === 116 &&\n chunk.charCodeAt(i + 5) === 58\n )\n}\n"],"names":["trailing","value","line"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/errors.ts","../src/parse.ts"],"sourcesContent":["/**\n * The type of error that occurred.\n * @public\n */\nexport type ErrorType = 'invalid-retry' | 'unknown-field' | 'max-buffer-size-exceeded'\n\n/**\n * Error thrown when encountering an issue during parsing.\n *\n * @public\n */\nexport class ParseError extends Error {\n /**\n * The type of error that occurred.\n */\n type: ErrorType\n\n /**\n * In the case of an unknown field encountered in the stream, this will be the field name.\n */\n field?: string | undefined\n\n /**\n * In the case of an unknown field encountered in the stream, this will be the value of the field.\n */\n value?: string | undefined\n\n /**\n * The line that caused the error, if available.\n */\n line?: string | undefined\n\n constructor(\n message: string,\n options: {type: ErrorType; field?: string; value?: string; line?: string},\n ) {\n super(message)\n this.name = 'ParseError'\n this.type = options.type\n this.field = options.field\n this.value = options.value\n this.line = options.line\n }\n}\n","/**\n * EventSource/Server-Sent Events parser\n * @see https://html.spec.whatwg.org/multipage/server-sent-events.html\n */\nimport {ParseError} from './errors.ts'\nimport type {EventSourceParser, ParserConfig} from './types.ts'\n\n// ASCII codes used in the hot parsing paths.\nconst LF = 10\nconst CR = 13\nconst SPACE = 32\n\n// oxlint-disable-next-line no-unused-vars\nfunction noop(_arg: unknown) {\n // intentional noop\n}\n\n/**\n * Creates a new EventSource parser.\n *\n * @param config - Parser configuration. Accepts callbacks (see {@link ParserCallbacks})\n * and options like `maxBufferSize` (see {@link ParserConfig}).\n *\n * @returns A new EventSource parser, with `feed` and `reset` methods.\n * @public\n */\nexport function createParser(config: ParserConfig): EventSourceParser {\n if (typeof config === 'function') {\n throw new TypeError(\n '`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?',\n )\n }\n\n const {onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize} = config\n\n // Trailing bytes from prior `feed()` calls that did not yet form a complete line.\n // Stored as an array of fragments and only joined when a line terminator arrives.\n // Concatenating per-feed (`prefix + chunk`) is O(N²) when a single SSE line spans\n // many chunks (e.g. a large `data:` payload streamed in tiny slices, or an MCP-style\n // server that emits one giant content block). Buffering as fragments + joining once\n // makes the same workload linear.\n const pendingFragments: string[] = []\n\n // Running total of `pendingFragments` lengths, kept in sync with the array so the\n // `maxBufferSize` check doesn't have to walk the fragment list on every feed.\n let pendingFragmentsLength = 0\n\n let isFirstChunk = true\n let id: string | undefined\n let data = ''\n let dataLines = 0\n let eventType: string | undefined\n\n // Set after a `maxBufferSize` overflow. Once tripped, `feed()` throws until\n // `reset()` is called — see the comment on `maxBufferSize` in `ParserConfig`.\n let terminated = false\n\n /**\n * Feeds a chunk of the SSE stream to the parser. Any trailing bytes that do\n * not yet form a complete line are held back and prepended to the next chunk,\n * so callers can pass arbitrary slices of the stream without worrying about\n * line boundaries.\n *\n * Per the SSE spec, a UTF-8 BOM (0xEF 0xBB 0xBF) at the start of the very\n * first chunk is stripped before parsing.\n *\n * @see https://html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream\n */\n function feed(chunk: string) {\n if (terminated) {\n throw new Error(\n 'Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.',\n )\n }\n\n if (isFirstChunk) {\n isFirstChunk = false\n // Match and strip UTF-8 BOM from the start of the stream, if present.\n // (Per the spec, this is only valid at the very start of the stream)\n if (\n chunk.charCodeAt(0) === 0xef &&\n chunk.charCodeAt(1) === 0xbb &&\n chunk.charCodeAt(2) === 0xbf\n ) {\n chunk = chunk.slice(3)\n }\n }\n\n // Hot path: no buffered prefix from a prior partial line. Hand the chunk\n // straight to `processLines`, exactly like the original implementation.\n // Zero new work in the common case (every chunk ends with `\\n\\n`).\n if (pendingFragments.length === 0) {\n const trailing = processLines(chunk)\n if (trailing !== '') {\n pendingFragments.push(trailing)\n pendingFragmentsLength = trailing.length\n }\n checkBufferSize()\n return\n }\n\n // We have a buffered prefix. If this chunk also has no terminator, append\n // to the buffer without concatenating — that's the O(N²) trap we're\n // avoiding (large single `data:` payload split across many tiny chunks).\n if (chunk.indexOf('\\n') === -1 && chunk.indexOf('\\r') === -1) {\n pendingFragments.push(chunk)\n pendingFragmentsLength += chunk.length\n checkBufferSize()\n return\n }\n\n // Terminator arrived. Join the accumulated fragments + this chunk once,\n // process, and buffer any new trailing partial line.\n pendingFragments.push(chunk)\n const input = pendingFragments.join('')\n pendingFragments.length = 0\n pendingFragmentsLength = 0\n const trailing = processLines(input)\n if (trailing !== '') {\n pendingFragments.push(trailing)\n pendingFragmentsLength = trailing.length\n }\n checkBufferSize()\n }\n\n function checkBufferSize() {\n if (maxBufferSize === undefined) return\n if (pendingFragmentsLength + data.length <= maxBufferSize) return\n\n terminated = true\n pendingFragments.length = 0\n pendingFragmentsLength = 0\n id = undefined\n data = ''\n dataLines = 0\n eventType = undefined\n onError(\n new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {\n type: 'max-buffer-size-exceeded',\n }),\n )\n }\n\n /**\n * Splits `chunk` into SSE lines and dispatches each to the appropriate handler.\n * Returns any trailing bytes that did not terminate with a line break, so the\n * caller can prepend them to the next chunk.\n *\n * The SSE spec permits three line terminators: `\\n`, `\\r`, and `\\r\\n`. Real-world\n * streams almost always use plain `\\n`, so we take a fast path when no `\\r` is\n * present in the chunk. The slow path is spec-correct but does more work per line.\n */\n function processLines(chunk: string): string {\n let searchIndex = 0\n\n // Fast path: LF-only chunk (the common case for typical SSE servers).\n // We can scan forward with a single `indexOf('\\n')` per line and inline\n // the hot-path branches for `data:` and `event:` without the CR bookkeeping\n // the slow path needs.\n if (chunk.indexOf('\\r') === -1) {\n let lfIndex = chunk.indexOf('\\n', searchIndex)\n while (lfIndex !== -1) {\n // Blank line: end-of-event marker. Dispatch the accumulated event (if any)\n // and reset the buffered fields. This is hoisted out of `parseLine` because\n // it's the single most common line shape after `data:` lines.\n if (searchIndex === lfIndex) {\n if (dataLines > 0) {\n onEvent({id, event: eventType, data})\n }\n id = undefined\n data = ''\n dataLines = 0\n eventType = undefined\n searchIndex = lfIndex + 1\n lfIndex = chunk.indexOf('\\n', searchIndex)\n continue\n }\n const firstCharCode = chunk.charCodeAt(searchIndex)\n if (isDataPrefix(chunk, searchIndex, firstCharCode)) {\n // `data:` line — append the value to the event's data buffer.\n // 'data:'.length === 5, 'data: '.length === 6\n const valueStart =\n chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5\n const value = chunk.slice(valueStart, lfIndex)\n // Fast path within a fast path: if this is the first data line AND the\n // next char is another LF (i.e. `data:foo\\n\\n`), dispatch immediately\n // without ever writing to the `data` buffer. This is the shape of a\n // typical single-line SSE event (ChatGPT-style streams, etc.) and is\n // hot enough to be worth the duplication.\n if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {\n onEvent({id, event: eventType, data: value})\n id = undefined\n data = ''\n eventType = undefined\n searchIndex = lfIndex + 2\n lfIndex = chunk.indexOf('\\n', searchIndex)\n continue\n }\n // Multi-line data: concatenate with newline separator per spec.\n data = dataLines === 0 ? value : `${data}\\n${value}`\n dataLines++\n } else if (isEventPrefix(chunk, searchIndex, firstCharCode)) {\n // `event:` line — set the event type for the next dispatch. Per spec,\n // an empty value resets `event type` to its default (undefined here).\n // 'event:'.length === 6, 'event: '.length === 7\n eventType =\n chunk.slice(\n chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6,\n lfIndex,\n ) || undefined\n } else {\n // Everything else: `id:`, `retry:`, comment lines (`:` prefix), unknown\n // fields, or malformed lines. These are rarer and go through the full\n // per-line parser, which handles the SSE field grammar in detail.\n parseLine(chunk, searchIndex, lfIndex)\n }\n searchIndex = lfIndex + 1\n lfIndex = chunk.indexOf('\\n', searchIndex)\n }\n return chunk.slice(searchIndex)\n }\n\n // Slow path: the chunk contains at least one `\\r`, so lines may be terminated\n // by `\\r`, `\\n`, or `\\r\\n`. We locate the next terminator by looking at both\n // the nearest `\\r` and `\\n` and picking whichever comes first.\n while (searchIndex < chunk.length) {\n const crIndex = chunk.indexOf('\\r', searchIndex)\n const lfIndex = chunk.indexOf('\\n', searchIndex)\n\n let lineEnd = -1\n if (crIndex !== -1 && lfIndex !== -1) {\n lineEnd = crIndex < lfIndex ? crIndex : lfIndex\n } else if (crIndex !== -1) {\n // A trailing `\\r` at the very end of the chunk is ambiguous: it could be\n // a bare-CR terminator, or the first half of a `\\r\\n` whose `\\n` arrives\n // in the next chunk. Defer until we see more input.\n if (crIndex === chunk.length - 1) {\n lineEnd = -1\n } else {\n lineEnd = crIndex\n }\n } else if (lfIndex !== -1) {\n lineEnd = lfIndex\n }\n\n if (lineEnd === -1) {\n break\n }\n\n parseLine(chunk, searchIndex, lineEnd)\n searchIndex = lineEnd + 1\n // If we just consumed a `\\r` and the next char is `\\n`, skip it so the\n // pair is treated as a single terminator rather than an empty line.\n if (chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF) {\n searchIndex++\n }\n }\n\n return chunk.slice(searchIndex)\n }\n\n function parseLine(chunk: string, start: number, end: number) {\n if (start === end) {\n dispatchEvent()\n return\n }\n\n const firstCharCode = chunk.charCodeAt(start)\n\n if (isDataPrefix(chunk, start, firstCharCode)) {\n // 'data:'.length === 5, 'data: '.length === 6\n const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5\n const value = chunk.slice(valueStart, end)\n data = dataLines === 0 ? value : `${data}\\n${value}`\n dataLines++\n return\n }\n\n if (isEventPrefix(chunk, start, firstCharCode)) {\n // 'event:'.length === 6, 'event: '.length === 7\n eventType =\n chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || undefined\n return\n }\n\n // Fast path for \"id:\" — 'i' = 105, 'd' = 100, ':' = 58\n if (\n firstCharCode === 105 &&\n chunk.charCodeAt(start + 1) === 100 &&\n chunk.charCodeAt(start + 2) === 58\n ) {\n // 'id:'.length === 3, 'id: '.length === 4\n const value = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end)\n // If the field value does not contain U+0000 NULL, then set the `ID` buffer to\n // the field value. Otherwise, ignore the field.\n if (!value.includes('\\0')) id = value\n return\n }\n\n // Comment line — ':' = 58\n if (firstCharCode === 58) {\n if (onComment) {\n const line = chunk.slice(start, end)\n // skip ':' (+1), or ': ' (+2) when a space follows\n onComment(line.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1))\n }\n return\n }\n\n const line = chunk.slice(start, end)\n const fieldSeparatorIndex = line.indexOf(':')\n if (fieldSeparatorIndex === -1) {\n processField(line, '', line)\n return\n }\n\n const field = line.slice(0, fieldSeparatorIndex)\n // skip ':' (+1), or ': ' (+2) when a space follows\n const offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1\n const value = line.slice(fieldSeparatorIndex + offset)\n processField(field, value, line)\n }\n\n function processField(field: string, value: string, line: string) {\n // Field names must be compared literally, with no case folding performed.\n switch (field) {\n case 'event':\n // Set the `event type` buffer to field value\n eventType = value || undefined\n break\n case 'data':\n data = dataLines === 0 ? value : `${data}\\n${value}`\n dataLines++\n break\n case 'id':\n // If the field value does not contain U+0000 NULL, then set the `ID` buffer to\n // the field value. Otherwise, ignore the field.\n if (!value.includes('\\0')) id = value\n break\n case 'retry':\n // If the field value consists of only ASCII digits, then interpret the field value as an\n // integer in base ten, and set the event stream's reconnection time to that integer.\n // Otherwise, ignore the field.\n if (/^\\d+$/.test(value)) {\n onRetry(parseInt(value, 10))\n } else {\n onError(\n new ParseError(`Invalid \\`retry\\` value: \"${value}\"`, {\n type: 'invalid-retry',\n value,\n line,\n }),\n )\n }\n break\n default:\n // Otherwise, the field is ignored.\n onError(\n new ParseError(\n `Unknown field \"${field.length > 20 ? `${field.slice(0, 20)}…` : field}\"`,\n {type: 'unknown-field', field, value, line},\n ),\n )\n break\n }\n }\n\n function dispatchEvent() {\n if (dataLines > 0) {\n onEvent({\n id,\n event: eventType,\n data,\n })\n }\n\n id = undefined\n data = ''\n dataLines = 0\n eventType = undefined\n }\n\n function reset(options: {consume?: boolean} = {}) {\n if (options.consume && pendingFragments.length > 0) {\n const incompleteLine = pendingFragments.join('')\n parseLine(incompleteLine, 0, incompleteLine.length)\n }\n\n isFirstChunk = true\n id = undefined\n data = ''\n dataLines = 0\n eventType = undefined\n pendingFragments.length = 0\n pendingFragmentsLength = 0\n terminated = false\n }\n\n return {feed, reset}\n}\n\n/**\n * Checks if `chunk` starts with the literal `data:` at index `i`.\n *\n * Equivalent to `chunk.startsWith('data:', i)`, but benchmarks show this\n * hand-unrolled char-code comparison is ~20% faster on common event types.\n * The caller passes `firstCharCode` (the code at `i`) so it can be reused\n * across prefix checks.\n *\n * ASCII: 'd' = 100, 'a' = 97, 't' = 116, 'a' = 97, ':' = 58\n */\nfunction isDataPrefix(chunk: string, i: number, firstCharCode: number): boolean {\n return (\n firstCharCode === 100 &&\n chunk.charCodeAt(i + 1) === 97 &&\n chunk.charCodeAt(i + 2) === 116 &&\n chunk.charCodeAt(i + 3) === 97 &&\n chunk.charCodeAt(i + 4) === 58\n )\n}\n\n/**\n * Checks if `chunk` starts with the literal `event:` at index `i`.\n *\n * See {@link isDataPrefix} for why this is hand-unrolled rather than using\n * `String.prototype.startsWith`.\n *\n * ASCII: 'e' = 101, 'v' = 118, 'e' = 101, 'n' = 110, 't' = 116, ':' = 58\n */\nfunction isEventPrefix(chunk: string, i: number, firstCharCode: number): boolean {\n return (\n firstCharCode === 101 &&\n chunk.charCodeAt(i + 1) === 118 &&\n chunk.charCodeAt(i + 2) === 101 &&\n chunk.charCodeAt(i + 3) === 110 &&\n chunk.charCodeAt(i + 4) === 116 &&\n chunk.charCodeAt(i + 5) === 58\n )\n}\n"],"names":["trailing","value","line"],"mappings":"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"}
@@ -99,7 +99,7 @@ ${value2}`, dataLines++;
99
99
  }
100
100
  if (firstCharCode === 105 && chunk.charCodeAt(start + 1) === 100 && chunk.charCodeAt(start + 2) === 58) {
101
101
  const value2 = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);
102
- id = value2.includes("\0") ? void 0 : value2;
102
+ value2.includes("\0") || (id = value2);
103
103
  return;
104
104
  }
105
105
  if (firstCharCode === 58) {
@@ -127,7 +127,7 @@ ${value2}`, dataLines++;
127
127
  ${value}`, dataLines++;
128
128
  break;
129
129
  case "id":
130
- id = value.includes("\0") ? void 0 : value;
130
+ value.includes("\0") || (id = value);
131
131
  break;
132
132
  case "retry":
133
133
  /^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(