@noodleseed/one 0.22.0 → 0.23.0

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 (197) hide show
  1. package/dist/commands/author-loop.d.ts.map +1 -1
  2. package/dist/commands/author-loop.js +2 -2
  3. package/dist/commands/author-loop.js.map +1 -1
  4. package/dist/commands/catalog-dispatch.d.ts.map +1 -1
  5. package/dist/commands/catalog-dispatch.js +21 -2
  6. package/dist/commands/catalog-dispatch.js.map +1 -1
  7. package/dist/commands/deploy-ops.d.ts.map +1 -1
  8. package/dist/commands/deploy-ops.js +28 -11
  9. package/dist/commands/deploy-ops.js.map +1 -1
  10. package/dist/first-run.d.ts +11 -0
  11. package/dist/first-run.d.ts.map +1 -1
  12. package/dist/first-run.js +19 -4
  13. package/dist/first-run.js.map +1 -1
  14. package/dist/project-scaffold-templates.d.ts +11 -0
  15. package/dist/project-scaffold-templates.d.ts.map +1 -0
  16. package/dist/project-scaffold-templates.js +359 -0
  17. package/dist/project-scaffold-templates.js.map +1 -0
  18. package/dist/project.d.ts.map +1 -1
  19. package/dist/project.js +1 -323
  20. package/dist/project.js.map +1 -1
  21. package/dist/react.d.ts +2 -2
  22. package/dist/react.d.ts.map +1 -1
  23. package/dist/react.js +1 -1
  24. package/dist/react.js.map +1 -1
  25. package/dist/validate.d.ts +12 -0
  26. package/dist/validate.d.ts.map +1 -1
  27. package/dist/validate.js +11 -0
  28. package/dist/validate.js.map +1 -1
  29. package/node_modules/@noodle-borg/agent-kit/dist/skill-content.d.ts.map +1 -1
  30. package/node_modules/@noodle-borg/agent-kit/dist/skill-content.js +6 -9
  31. package/node_modules/@noodle-borg/agent-kit/dist/skill-content.js.map +1 -1
  32. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  33. package/node_modules/@noodle-borg/authoring/dist/index.d.ts +3 -3
  34. package/node_modules/@noodle-borg/authoring/dist/index.d.ts.map +1 -1
  35. package/node_modules/@noodle-borg/authoring/dist/index.js +1 -1
  36. package/node_modules/@noodle-borg/authoring/dist/index.js.map +1 -1
  37. package/node_modules/@noodle-borg/authoring/dist/react/components.d.ts +302 -7
  38. package/node_modules/@noodle-borg/authoring/dist/react/components.d.ts.map +1 -1
  39. package/node_modules/@noodle-borg/authoring/dist/react/components.js +39 -7
  40. package/node_modules/@noodle-borg/authoring/dist/react/components.js.map +1 -1
  41. package/node_modules/@noodle-borg/authoring/dist/react/index.d.ts +4 -2
  42. package/node_modules/@noodle-borg/authoring/dist/react/index.d.ts.map +1 -1
  43. package/node_modules/@noodle-borg/authoring/dist/react/index.js +2 -1
  44. package/node_modules/@noodle-borg/authoring/dist/react/index.js.map +1 -1
  45. package/node_modules/@noodle-borg/authoring/dist/react/semantic-components.d.ts +2618 -0
  46. package/node_modules/@noodle-borg/authoring/dist/react/semantic-components.d.ts.map +1 -0
  47. package/node_modules/@noodle-borg/authoring/dist/react/semantic-components.js +110 -0
  48. package/node_modules/@noodle-borg/authoring/dist/react/semantic-components.js.map +1 -0
  49. package/node_modules/@noodle-borg/authoring/dist/react.d.ts +2 -2
  50. package/node_modules/@noodle-borg/authoring/dist/react.d.ts.map +1 -1
  51. package/node_modules/@noodle-borg/authoring/dist/react.js +1 -1
  52. package/node_modules/@noodle-borg/authoring/dist/react.js.map +1 -1
  53. package/node_modules/@noodle-borg/authoring/dist/recording.d.ts +1 -1
  54. package/node_modules/@noodle-borg/authoring/dist/recording.d.ts.map +1 -1
  55. package/node_modules/@noodle-borg/authoring/dist/recording.js +14 -1
  56. package/node_modules/@noodle-borg/authoring/dist/recording.js.map +1 -1
  57. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +25 -2
  58. package/node_modules/@noodle-borg/authoring/dist/server.d.ts.map +1 -1
  59. package/node_modules/@noodle-borg/authoring/dist/server.js +7 -3
  60. package/node_modules/@noodle-borg/authoring/dist/server.js.map +1 -1
  61. package/node_modules/@noodle-borg/compiler/dist/compile.d.ts.map +1 -1
  62. package/node_modules/@noodle-borg/compiler/dist/compile.js +20 -8
  63. package/node_modules/@noodle-borg/compiler/dist/compile.js.map +1 -1
  64. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +2 -0
  65. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts.map +1 -1
  66. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +6 -0
  67. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js.map +1 -1
  68. package/node_modules/@noodle-borg/connector-http/package.json +1 -1
  69. package/node_modules/@noodle-borg/protocol/dist/mapping.d.ts.map +1 -1
  70. package/node_modules/@noodle-borg/protocol/dist/mapping.js +8 -0
  71. package/node_modules/@noodle-borg/protocol/dist/mapping.js.map +1 -1
  72. package/node_modules/hono/dist/adapter/aws-lambda/handler.js +15 -21
  73. package/node_modules/hono/dist/adapter/bun/websocket.js +3 -1
  74. package/node_modules/hono/dist/adapter/lambda-edge/handler.js +8 -2
  75. package/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +15 -21
  76. package/node_modules/hono/dist/cjs/adapter/bun/websocket.js +3 -1
  77. package/node_modules/hono/dist/cjs/adapter/lambda-edge/handler.js +8 -2
  78. package/node_modules/hono/dist/cjs/helper/css/common.js +3 -1
  79. package/node_modules/hono/dist/cjs/helper/css/index.js +9 -1
  80. package/node_modules/hono/dist/cjs/jsx/base.js +8 -14
  81. package/node_modules/hono/dist/cjs/jsx/components.js +41 -21
  82. package/node_modules/hono/dist/cjs/jsx/context.js +131 -5
  83. package/node_modules/hono/dist/cjs/jsx/streaming.js +9 -7
  84. package/node_modules/hono/dist/cjs/middleware/cors/index.js +2 -5
  85. package/node_modules/hono/dist/cjs/middleware/serve-static/index.js +2 -2
  86. package/node_modules/hono/dist/cjs/utils/body.js +12 -4
  87. package/node_modules/hono/dist/cjs/utils/buffer.js +2 -1
  88. package/node_modules/hono/dist/cjs/validator/validator.js +3 -3
  89. package/node_modules/hono/dist/helper/css/common.js +3 -1
  90. package/node_modules/hono/dist/helper/css/index.js +9 -1
  91. package/node_modules/hono/dist/jsx/base.js +15 -15
  92. package/node_modules/hono/dist/jsx/components.js +42 -22
  93. package/node_modules/hono/dist/jsx/context.js +129 -5
  94. package/node_modules/hono/dist/jsx/streaming.js +10 -8
  95. package/node_modules/hono/dist/middleware/cors/index.js +2 -5
  96. package/node_modules/hono/dist/middleware/serve-static/index.js +2 -2
  97. package/node_modules/hono/dist/types/adapter/aws-lambda/handler.d.ts +1 -1
  98. package/node_modules/hono/dist/types/jsx/base.d.ts +1 -3
  99. package/node_modules/hono/dist/types/jsx/context.d.ts +39 -0
  100. package/node_modules/hono/dist/types/middleware/context-storage/index.d.ts +2 -2
  101. package/node_modules/hono/dist/types/utils/body.d.ts +1 -1
  102. package/node_modules/hono/dist/utils/body.js +12 -4
  103. package/node_modules/hono/dist/utils/buffer.js +2 -1
  104. package/node_modules/hono/dist/validator/validator.js +3 -3
  105. package/node_modules/hono/package.json +9 -10
  106. package/node_modules/tailwindcss/dist/chunk-HMCCH6MG.mjs +1 -0
  107. package/node_modules/tailwindcss/dist/default-theme.mjs +1 -1
  108. package/node_modules/tailwindcss/dist/lib.js +13 -13
  109. package/node_modules/tailwindcss/dist/lib.mjs +18 -18
  110. package/node_modules/tailwindcss/package.json +2 -2
  111. package/node_modules/undici/README.md +3 -2
  112. package/node_modules/undici/docs/docs/api/Agent.md +248 -44
  113. package/node_modules/undici/docs/docs/api/BalancedPool.md +246 -48
  114. package/node_modules/undici/docs/docs/api/CacheStorage.md +171 -13
  115. package/node_modules/undici/docs/docs/api/CacheStore.md +294 -98
  116. package/node_modules/undici/docs/docs/api/Client.md +365 -178
  117. package/node_modules/undici/docs/docs/api/ClientStats.md +80 -14
  118. package/node_modules/undici/docs/docs/api/Connector.md +118 -36
  119. package/node_modules/undici/docs/docs/api/ContentType.md +65 -25
  120. package/node_modules/undici/docs/docs/api/Cookies.md +125 -47
  121. package/node_modules/undici/docs/docs/api/Debug.md +34 -18
  122. package/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +364 -164
  123. package/node_modules/undici/docs/docs/api/Dispatcher.md +479 -1093
  124. package/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +101 -87
  125. package/node_modules/undici/docs/docs/api/Errors.md +503 -40
  126. package/node_modules/undici/docs/docs/api/EventSource.md +294 -32
  127. package/node_modules/undici/docs/docs/api/Fetch.md +680 -31
  128. package/node_modules/undici/docs/docs/api/GlobalInstallation.md +78 -98
  129. package/node_modules/undici/docs/docs/api/H2CClient.md +298 -176
  130. package/node_modules/undici/docs/docs/api/Interceptors.md +355 -0
  131. package/node_modules/undici/docs/docs/api/MockAgent.md +314 -322
  132. package/node_modules/undici/docs/docs/api/MockCallHistory.md +272 -98
  133. package/node_modules/undici/docs/docs/api/MockCallHistoryLog.md +189 -26
  134. package/node_modules/undici/docs/docs/api/MockClient.md +151 -33
  135. package/node_modules/undici/docs/docs/api/MockErrors.md +56 -5
  136. package/node_modules/undici/docs/docs/api/MockPool.md +299 -263
  137. package/node_modules/undici/docs/docs/api/Pool.md +235 -43
  138. package/node_modules/undici/docs/docs/api/PoolStats.md +119 -13
  139. package/node_modules/undici/docs/docs/api/ProxyAgent.md +179 -160
  140. package/node_modules/undici/docs/docs/api/RedirectHandler.md +238 -63
  141. package/node_modules/undici/docs/docs/api/RetryAgent.md +113 -26
  142. package/node_modules/undici/docs/docs/api/RetryHandler.md +161 -77
  143. package/node_modules/undici/docs/docs/api/RoundRobinPool.md +166 -72
  144. package/node_modules/undici/docs/docs/api/SnapshotAgent.md +264 -542
  145. package/node_modules/undici/docs/docs/api/Socks5ProxyAgent.md +162 -186
  146. package/node_modules/undici/docs/docs/api/Util.md +68 -11
  147. package/node_modules/undici/docs/docs/api/WebSocket.md +475 -80
  148. package/node_modules/undici/docs/docs/api/api-lifecycle.md +100 -32
  149. package/node_modules/undici/docs/docs/best-practices/client-certificate.md +2 -0
  150. package/node_modules/undici/docs/docs/best-practices/crawling.md +3 -1
  151. package/node_modules/undici/docs/docs/best-practices/migrating-from-v7-to-v8.md +6 -4
  152. package/node_modules/undici/docs/docs/best-practices/mocking-request.md +9 -7
  153. package/node_modules/undici/docs/docs/best-practices/proxy.md +3 -1
  154. package/node_modules/undici/docs/docs/best-practices/undici-vs-builtin-fetch.md +10 -8
  155. package/node_modules/undici/docs/docs/best-practices/writing-tests.md +2 -0
  156. package/node_modules/undici/docs/docs/{GettingStarted.md → getting-started.md} +22 -19
  157. package/node_modules/undici/docs/docs/index.md +779 -0
  158. package/node_modules/undici/docs/docs/site.json +125 -0
  159. package/node_modules/undici/docs/docs/type-map.json +79 -0
  160. package/node_modules/undici/lib/api/api-request.js +7 -1
  161. package/node_modules/undici/lib/api/readable.js +47 -2
  162. package/node_modules/undici/lib/core/errors.js +20 -0
  163. package/node_modules/undici/lib/core/request.js +1 -1
  164. package/node_modules/undici/lib/core/util.js +24 -1
  165. package/node_modules/undici/lib/dispatcher/client-h1.js +92 -1
  166. package/node_modules/undici/lib/dispatcher/client-h2.js +322 -118
  167. package/node_modules/undici/lib/dispatcher/client.js +6 -2
  168. package/node_modules/undici/lib/dispatcher/dispatcher-base.js +1 -0
  169. package/node_modules/undici/lib/dispatcher/proxy-agent.js +42 -7
  170. package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +4 -2
  171. package/node_modules/undici/lib/handler/redirect-handler.js +1 -0
  172. package/node_modules/undici/lib/handler/retry-handler.js +57 -19
  173. package/node_modules/undici/lib/util/cache.js +8 -2
  174. package/node_modules/undici/lib/web/cookies/parse.js +20 -27
  175. package/node_modules/undici/lib/web/cookies/util.js +1 -1
  176. package/node_modules/undici/lib/web/eventsource/eventsource.js +7 -18
  177. package/node_modules/undici/lib/web/eventsource/util.js +32 -1
  178. package/node_modules/undici/lib/web/fetch/body.js +43 -0
  179. package/node_modules/undici/lib/web/fetch/constants.js +1 -1
  180. package/node_modules/undici/lib/web/fetch/index.js +11 -2
  181. package/node_modules/undici/lib/web/fetch/request.js +1 -0
  182. package/node_modules/undici/lib/web/fetch/util.js +4 -1
  183. package/node_modules/undici/lib/web/websocket/receiver.js +20 -3
  184. package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +8 -1
  185. package/node_modules/undici/lib/web/websocket/websocket.js +3 -1
  186. package/node_modules/undici/package.json +1 -1
  187. package/node_modules/undici/types/client.d.ts +6 -1
  188. package/node_modules/undici/types/cookies.d.ts +1 -1
  189. package/node_modules/undici/types/errors.d.ts +10 -0
  190. package/node_modules/undici/types/fetch.d.ts +1 -0
  191. package/node_modules/undici/types/handlers.d.ts +2 -0
  192. package/node_modules/undici/types/proxy-agent.d.ts +7 -0
  193. package/package.json +4 -4
  194. package/react/styles.css +510 -5
  195. package/react/styles.source.css +850 -0
  196. package/node_modules/hono/dist/tsconfig.build.tsbuildinfo +0 -1
  197. package/node_modules/tailwindcss/dist/chunk-XVOTX5KS.mjs +0 -1
@@ -1,116 +1,200 @@
1
- # Class: RetryHandler
1
+ # RetryHandler
2
2
 
3
- Extends: `undici.DispatcherHandlers`
3
+ <!--introduced_in=v5.28.0-->
4
+ <!--type=module-->
5
+ <!-- source_link=lib/handler/retry-handler.js -->
4
6
 
5
- A handler class that implements the retry logic for a request.
7
+ > Stability: 2 - Stable
6
8
 
7
- ## `new RetryHandler(opts, { dispatch, handler })`
9
+ A [`Dispatcher`][] handler that automatically retries a request when it fails
10
+ with a recoverable network error or an eligible HTTP status code. It wraps an
11
+ inner handler and re-dispatches the request, applying an exponential backoff and
12
+ honouring the `Retry-After` response header. When a response is partially
13
+ consumed before the failure, the handler resumes the download with a `Range`
14
+ request guarded by the original `ETag`.
8
15
 
9
- Arguments:
16
+ The handler is most often used indirectly through [`RetryAgent`][], but it can
17
+ also be supplied directly to [`dispatcher.dispatch()`][] for fine-grained
18
+ control over the retry behaviour.
10
19
 
11
- - **opts** `Dispatch.DispatchOptions & { retryOptions?: RetryOptions }` (required) - An intersection of `Dispatcher.DispatchOptions` and an optional `RetryOptions` object.
12
- - **{ dispatch, handler }** `RetryHandlers` (required) - Object containing the `dispatch` to be used on every retry, and `handler` for handling the `dispatch` lifecycle.
13
-
14
- Returns: `retryHandler`
15
-
16
- ### Parameter: `Dispatch.DispatchOptions & RetryOptions`
17
-
18
- Extends: [`Dispatch.DispatchOptions`](/docs/docs/api/Dispatcher.md#parameter-dispatchoptions).
19
-
20
- #### `RetryOptions`
20
+ ```mjs
21
+ import { RetryHandler } from 'undici'
22
+ ```
21
23
 
22
- - **throwOnError** `boolean` (optional) - Disable to prevent throwing error on last retry attept, useful if you need the body on errors from server or if you have custom error handler.
23
- - **retry** `(err: Error, context: RetryContext, callback: (err?: Error | null) => void) => void` (optional) - Function to be called after every retry. It should pass error if no more retries should be performed.
24
- - **maxRetries** `number` (optional) - Maximum number of retries. Default: `5`
25
- - **maxTimeout** `number` (optional) - Maximum number of milliseconds to wait before retrying. Default: `30000` (30 seconds)
26
- - **minTimeout** `number` (optional) - Minimum number of milliseconds to wait before retrying. Default: `500` (half a second)
27
- - **timeoutFactor** `number` (optional) - Factor to multiply the timeout by for each retry attempt. Default: `2`
28
- - **retryAfter** `boolean` (optional) - It enables automatic retry after the `Retry-After` header is received. Default: `true`
29
- - **methods** `string[]` (optional) - Array of HTTP methods to retry. Default: `['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE']`
30
- - **statusCodes** `number[]` (optional) - Array of HTTP status codes to retry. Default: `[429, 500, 502, 503, 504]`
31
- - **errorCodes** `string[]` (optional) - Array of Error codes to retry. Default: `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN', 'ENETUNREACH', 'EHOSTDOWN', 'EHOSTUNREACH', 'EPIPE', 'UND_ERR_SOCKET']`
24
+ ## Class: `RetryHandler`
32
25
 
33
- **`RetryContext`**
26
+ <!-- YAML
27
+ added: v5.28.0
28
+ -->
34
29
 
35
- - `state`: `RetryState` - Current retry state. It can be mutated.
36
- - `opts`: `Dispatch.DispatchOptions & RetryOptions` - Options passed to the retry handler.
30
+ * Extends: {DispatchHandler}
37
31
 
38
- **`RetryState`**
32
+ Implements the [`DispatchHandler`][] interface. An instance is consumed by a
33
+ single dispatch call and forwards the dispatch lifecycle to the inner `handler`,
34
+ re-issuing the request through the supplied `dispatch` function whenever a retry
35
+ is warranted.
39
36
 
40
- It represents the retry state for a given request.
37
+ > **Note:** The `RetryHandler` does not retry over stateful bodies (for example
38
+ > streams or `AsyncIterable`), because once consumed they cannot be replayed. In
39
+ > these situations the body is identified as stateful and the request is rejected
40
+ > with the `UND_ERR_REQ_RETRY` error instead of being retried.
41
41
 
42
- - `counter`: `number` - Current retry attempt.
42
+ ### `new RetryHandler(options, retryHandlers)`
43
43
 
44
- ### Parameter `RetryHandlers`
44
+ <!-- YAML
45
+ added: v5.28.0
46
+ changes:
47
+ - version: v7.0.0
48
+ pr-url: https://github.com/nodejs/undici/pull/3883
49
+ description: Reimplemented on top of the new dispatch lifecycle hooks.
50
+ -->
45
51
 
46
- - **dispatch** `(options: Dispatch.DispatchOptions, handlers: Dispatch.DispatchHandler) => Promise<Dispatch.DispatchResponse>` (required) - Dispatch function to be called after every retry.
47
- - **handler** Extends [`Dispatch.DispatchHandler`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler) (required) - Handler function to be called after the request is successful or the retries are exhausted.
52
+ * `options` {DispatchOptions} The dispatch options for the request, extended with
53
+ an optional `retryOptions` field. Type is
54
+ `DispatchOptions & { retryOptions?: RetryOptions }`.
55
+ * `retryOptions` {RetryOptions} (optional) Configuration controlling when and
56
+ how the request is retried. See [`RetryOptions`](#retryoptions).
57
+ * `retryHandlers` {RetryHandlers} The handlers used to drive the retry loop. See
58
+ [`RetryHandlers`](#parameter-retryhandlers).
59
+ * Returns: {RetryHandler}
48
60
 
49
- >__Note__: The `RetryHandler` does not retry over stateful bodies (e.g. streams, AsyncIterable) as those, once consumed, are left in a state that cannot be reutilized. For these situations the `RetryHandler` will identify
50
- >the body as stateful and will not retry the request rejecting with the error `UND_ERR_REQ_RETRY`.
61
+ #### `RetryOptions`
51
62
 
52
- Examples:
63
+ * `throwOnError` {boolean} When `true`, an error is thrown on the last retry
64
+ attempt and propagated to the inner handler; when `false`, the failing response
65
+ is passed through instead, which is useful when the error body is needed or a
66
+ custom error handler is in place. **Default:** `true`.
67
+ * `retry` {Function} Callback invoked on every retry iteration to decide whether
68
+ another attempt should be made. It receives the error, the retry context, and a
69
+ callback. Call the callback with an `Error` to stop retrying, or with `null` to
70
+ schedule another attempt. **Default:** the built-in retry strategy described
71
+ below.
72
+ * `err` {Error} The error that triggered the retry.
73
+ * `context` {RetryContext} The current retry context. See
74
+ [`RetryContext`](#retrycontext).
75
+ * `callback` {Function} Signals the outcome of this iteration.
76
+ * `result` {Error|null} (optional) An `Error` to abort retrying, or `null` to
77
+ retry.
78
+ * `maxRetries` {number} Maximum number of retries allowed. **Default:** `5`.
79
+ * `maxTimeout` {number} Maximum number of milliseconds to wait between retries.
80
+ **Default:** `30000` (30 seconds).
81
+ * `minTimeout` {number} Initial number of milliseconds to wait before the first
82
+ retry. **Default:** `500` (half a second).
83
+ * `timeoutFactor` {number} Multiplier applied to the timeout between successive
84
+ retries to produce an exponential backoff. **Default:** `2`.
85
+ * `retryAfter` {boolean} When `true`, the delay before the next retry is inferred
86
+ from the `Retry-After` response header when present. **Default:** `true`.
87
+ * `methods` {string[]} HTTP methods that are eligible for retrying. **Default:**
88
+ `['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE']`.
89
+ * `statusCodes` {number[]} HTTP status codes that trigger a retry. **Default:**
90
+ `[500, 502, 503, 504, 429]`.
91
+ * `errorCodes` {string[]} Network error codes that trigger a retry. **Default:**
92
+ `['ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN', 'ENETUNREACH', 'EHOSTDOWN', 'EHOSTUNREACH', 'EPIPE', 'UND_ERR_SOCKET']`.
93
+
94
+ The default `retry` strategy computes the delay before the next attempt as
95
+ `minTimeout * timeoutFactor ** (counter - 1)`, capped at `maxTimeout`. When a
96
+ `Retry-After` header is present it takes precedence (interpreted as seconds, or as
97
+ an HTTP date), still capped at `maxTimeout`. The default strategy stops retrying
98
+ once `counter` exceeds `maxRetries`, when the error code is not in `errorCodes`,
99
+ when the method is not in `methods`, or when the response status code is not in
100
+ `statusCodes`.
101
+
102
+ #### `RetryContext`
103
+
104
+ * `state` {RetryState} The current retry state. See [`RetryState`](#retrystate).
105
+ * `opts` {DispatchOptions} The dispatch options passed to the handler, including
106
+ the resolved `retryOptions`. Type is
107
+ `DispatchOptions & { retryOptions?: RetryOptions }`.
108
+
109
+ The context object passed as the second argument to the `retry` callback.
110
+
111
+ #### `RetryState`
112
+
113
+ * `counter` {number} The current retry attempt, starting at `1` for the first
114
+ retry.
115
+
116
+ Represents the retry state for a given request.
117
+
118
+ #### Parameter: `RetryHandlers`
119
+
120
+ * `dispatch` {Function} The dispatch function called to (re-)issue the request on
121
+ every attempt. Type is `(options, handler) => boolean`.
122
+ * `handler` {DispatchHandler} The inner handler invoked once the request succeeds
123
+ or the retries are exhausted.
124
+
125
+ ### Examples
126
+
127
+ ```mjs
128
+ import { Client, RetryHandler } from 'undici'
129
+
130
+ const client = new Client(`http://localhost:${server.address().port}`)
131
+ const chunks = []
53
132
 
54
- ```js
55
- const client = new Client(`http://localhost:${server.address().port}`);
56
- const chunks = [];
57
133
  const handler = new RetryHandler(
58
134
  {
59
135
  ...dispatchOptions,
60
136
  retryOptions: {
61
- // custom retry function
62
- retry: function (err, state, callback) {
63
- counter++;
64
-
65
- if (err.code && err.code === "UND_ERR_DESTROYED") {
66
- callback(err);
67
- return;
137
+ // Custom retry decision function.
138
+ retry (err, { state, opts }, callback) {
139
+ if (err.code === 'UND_ERR_DESTROYED') {
140
+ callback(err)
141
+ return
68
142
  }
69
143
 
70
144
  if (err.statusCode === 206) {
71
- callback(err);
72
- return;
145
+ callback(err)
146
+ return
73
147
  }
74
148
 
75
- setTimeout(() => callback(null), 1000);
76
- },
77
- },
149
+ setTimeout(() => callback(null), 1000)
150
+ }
151
+ }
78
152
  },
79
153
  {
80
- dispatch: (...args) => {
81
- return client.dispatch(...args);
154
+ dispatch (...args) {
155
+ return client.dispatch(...args)
82
156
  },
83
157
  handler: {
84
- onRequestStart() {},
85
- onBodySent(chunk) {},
86
- onResponseStart(_controller, status, headers) {
87
- // do something with headers
158
+ onRequestStart () {},
159
+ onResponseStart (controller, status, headers) {
160
+ // Do something with the response headers.
88
161
  },
89
- onResponseData(_controller, chunk) {
90
- chunks.push(chunk);
162
+ onResponseData (controller, chunk) {
163
+ chunks.push(chunk)
91
164
  },
92
- onResponseEnd() {},
93
- onResponseError(_controller, err) {
94
- // handle error properly
95
- },
96
- },
165
+ onResponseEnd () {},
166
+ onResponseError (controller, err) {
167
+ // Handle the error.
168
+ }
169
+ }
97
170
  }
98
- );
171
+ )
172
+
173
+ client.dispatch(dispatchOptions, handler)
99
174
  ```
100
175
 
101
- #### Example - Basic RetryHandler with defaults
176
+ A minimal handler that relies entirely on the default retry options:
177
+
178
+ ```mjs
179
+ import { Client, RetryHandler } from 'undici'
180
+
181
+ const client = new Client(`http://localhost:${server.address().port}`)
102
182
 
103
- ```js
104
- const client = new Client(`http://localhost:${server.address().port}`);
105
183
  const handler = new RetryHandler(dispatchOptions, {
106
184
  dispatch: client.dispatch.bind(client),
107
185
  handler: {
108
- onRequestStart() {},
109
- onBodySent(chunk) {},
110
- onResponseStart(_controller, status, headers) {},
111
- onResponseData(_controller, chunk) {},
112
- onResponseEnd() {},
113
- onResponseError(_controller, err) {},
114
- },
115
- });
186
+ onRequestStart () {},
187
+ onResponseStart (controller, status, headers) {},
188
+ onResponseData (controller, chunk) {},
189
+ onResponseEnd () {},
190
+ onResponseError (controller, err) {}
191
+ }
192
+ })
193
+
194
+ client.dispatch(dispatchOptions, handler)
116
195
  ```
196
+
197
+ [`Dispatcher`]: Dispatcher.md#class-dispatcher
198
+ [`DispatchHandler`]: Dispatcher.md#parameter-dispatchhandler
199
+ [`RetryAgent`]: RetryAgent.md#class-retryagent
200
+ [`dispatcher.dispatch()`]: Dispatcher.md#dispatcherdispatchoptions-handler
@@ -1,131 +1,213 @@
1
- # Class: RoundRobinPool
1
+ # RoundRobinPool
2
2
 
3
- Extends: `undici.Dispatcher`
3
+ <!--introduced_in=v7.17.0-->
4
+ <!--type=module-->
5
+ <!-- source_link=lib/dispatcher/round-robin-pool.js -->
4
6
 
5
- A pool of [Client](/docs/docs/api/Client.md) instances connected to the same upstream target with round-robin client selection.
7
+ > Stability: 2 - Stable
6
8
 
7
- Unlike [`Pool`](/docs/docs/api/Pool.md), which always selects the first available client, `RoundRobinPool` cycles through clients in a round-robin fashion. This ensures even distribution of requests across all connections, which is particularly useful when the upstream target is behind a load balancer that round-robins TCP connections across multiple backend servers (e.g., Kubernetes Services).
9
+ A pool of [`Client`][] instances connected to the same upstream target that
10
+ selects clients in a round-robin fashion.
8
11
 
9
- Requests are not guaranteed to be dispatched in order of invocation.
12
+ Unlike [`Pool`][], which always reuses the first available client,
13
+ `RoundRobinPool` cycles through its clients so that requests are distributed
14
+ evenly across every open connection. This is useful when the upstream target is
15
+ fronted by a load balancer that distributes TCP connections across multiple
16
+ backend servers (for example, a Kubernetes Service): each connection is pinned
17
+ to a backend by the load balancer, and `RoundRobinPool` spreads requests across
18
+ those connections so every backend receives a comparable share of traffic.
10
19
 
11
- ## `new RoundRobinPool(url[, options])`
20
+ Requests are not guaranteed to be dispatched in the order they were invoked.
12
21
 
13
- Arguments:
22
+ ```mjs
23
+ import { RoundRobinPool } from 'undici'
24
+
25
+ const pool = new RoundRobinPool('http://localhost:3000', { connections: 10 })
26
+ ```
27
+
28
+ `RoundRobinPool` distributes HTTP requests evenly across TCP connections, not
29
+ across backend servers directly. Even backend distribution therefore depends on
30
+ the load balancer assigning different connections to different backends (for
31
+ example, round-robin, random, or least-connections without client affinity). If
32
+ the load balancer pins all connections from one source to the same backend (for
33
+ example, source-IP affinity or sticky sessions), consider [`BalancedPool`][]
34
+ with the individual backend addresses instead.
35
+
36
+ ## Class: `RoundRobinPool`
37
+
38
+ <!-- YAML
39
+ added: v7.17.0
40
+ -->
41
+
42
+ * Extends: {Dispatcher}
43
+
44
+ ### `new RoundRobinPool(url[, options])`
14
45
 
15
- * **url** `URL | string` - It should only include the **protocol, hostname, and port**.
16
- * **options** `RoundRobinPoolOptions` (optional)
46
+ <!-- YAML
47
+ added: v7.17.0
48
+ -->
17
49
 
18
- ### Parameter: `RoundRobinPoolOptions`
50
+ * `url` {URL|string} The upstream target. It should only include the
51
+ **protocol, hostname, and port**.
52
+ * `options` {RoundRobinPoolOptions} (optional)
19
53
 
20
- Extends: [`ClientOptions`](/docs/docs/api/Client.md#parameter-clientoptions)
54
+ #### Parameter: `RoundRobinPoolOptions`
21
55
 
22
- * **factory** `(origin: URL, opts: Object) => Dispatcher` - Default: `(origin, opts) => new Client(origin, opts)`
23
- * **connections** `number | null` (optional) - Default: `null` - The number of `Client` instances to create. When set to `null`, the `RoundRobinPool` instance will create an unlimited amount of `Client` instances.
24
- * **clientTtl** `number | null` (optional) - Default: `null` - The amount of time before a `Client` instance is removed from the `RoundRobinPool` and closed. When set to `null`, `Client` instances will not be removed or closed based on age.
56
+ Extends: {ClientOptions}
25
57
 
26
- ## Use Case
58
+ * `factory` {Function} A function used to create the underlying {Client}
59
+ instances. **Default:** `(origin, opts) => new Client(origin, opts)`.
60
+ * `origin` {URL}
61
+ * `opts` {Object}
62
+ * Returns: {Dispatcher}
63
+ * `connections` {number|null} (optional) The maximum number of {Client}
64
+ instances to create. When set to `null`, the `RoundRobinPool` instance creates
65
+ an unlimited number of {Client} instances. **Default:** `null`.
66
+ * `clientTtl` {number|null} (optional) The amount of time, in milliseconds,
67
+ before a {Client} instance is removed from the `RoundRobinPool` and closed.
68
+ When set to `null`, {Client} instances are not removed or closed based on age.
69
+ **Default:** `null`.
27
70
 
28
- `RoundRobinPool` is designed for scenarios where:
71
+ `RoundRobinPool` inherits all [`Client`][] options. A [`Client`][] instance is
72
+ created lazily on the first dispatch and additional instances are created on
73
+ demand, up to `connections`, when every existing client is busy.
29
74
 
30
- 1. You connect to a single origin (e.g., `http://my-service.namespace.svc`)
31
- 2. That origin is backed by a load balancer distributing TCP connections across multiple servers
32
- 3. You want requests evenly distributed across all backend servers
75
+ ### `roundRobinPool.closed`
33
76
 
34
- **Example**: In Kubernetes, when using a Service DNS name with multiple Pod replicas, kube-proxy load balances TCP connections. `RoundRobinPool` ensures each connection (and thus each Pod) receives an equal share of requests.
77
+ <!-- YAML
78
+ added: v7.17.0
79
+ -->
35
80
 
36
- ### Important: Backend Distribution Considerations
81
+ * Type: {boolean}
37
82
 
38
- `RoundRobinPool` distributes **HTTP requests** evenly across **TCP connections**. Whether this translates to even backend server distribution depends on the load balancer's behavior:
83
+ `true` after `roundRobinPool.close()` has been called.
39
84
 
40
- **✓ Works when the load balancer**:
41
- - Assigns different backends to different TCP connections from the same client
42
- - Uses algorithms like: round-robin, random, least-connections (without client affinity)
43
- - Example: Default Kubernetes Services without `sessionAffinity`
85
+ ### `roundRobinPool.destroyed`
44
86
 
45
- **✗ Does NOT work when**:
46
- - Load balancer has client/source IP affinity (all connections from one IP → same backend)
47
- - Load balancer uses source-IP-hash or sticky sessions
87
+ <!-- YAML
88
+ added: v7.17.0
89
+ -->
48
90
 
49
- **How it works:**
50
- 1. `RoundRobinPool` creates N TCP connections to the load balancer endpoint
51
- 2. Load balancer assigns each TCP connection to a backend (per its algorithm)
52
- 3. `RoundRobinPool` cycles HTTP requests across those N connections
53
- 4. Result: Requests distributed proportionally to how the LB distributed the connections
91
+ * Type: {boolean}
54
92
 
55
- If the load balancer assigns all connections to the same backend (e.g., due to session affinity), `RoundRobinPool` cannot overcome this. In such cases, consider using [`BalancedPool`](/docs/docs/api/BalancedPool.md) with direct backend addresses (e.g., individual pod IPs) instead of a load-balanced endpoint.
93
+ `true` after `roundRobinPool.destroy()` has been called, or after
94
+ `roundRobinPool.close()` has been called and the pool shutdown has completed.
56
95
 
57
- ## Instance Properties
96
+ ### `roundRobinPool.stats`
58
97
 
59
- ### `RoundRobinPool.closed`
98
+ <!-- YAML
99
+ added: v7.17.0
100
+ -->
60
101
 
61
- Implements [Client.closed](/docs/docs/api/Client.md#clientclosed)
102
+ * Type: {PoolStats}
62
103
 
63
- ### `RoundRobinPool.destroyed`
104
+ Aggregate connection statistics for the pool. See [`PoolStats`][].
64
105
 
65
- Implements [Client.destroyed](/docs/docs/api/Client.md#clientdestroyed)
106
+ ### `roundRobinPool.close([callback])`
66
107
 
67
- ### `RoundRobinPool.stats`
108
+ <!-- YAML
109
+ added: v7.17.0
110
+ -->
68
111
 
69
- Returns [`PoolStats`](/docs/docs/api/PoolStats.md) instance for this pool.
112
+ Closes the pool and gracefully waits for enqueued requests to complete before
113
+ resolving. Implements [`dispatcher.close([callback])`][].
70
114
 
71
- ## Instance Methods
115
+ ### `roundRobinPool.destroy([error[, callback]])`
72
116
 
73
- ### `RoundRobinPool.close([callback])`
117
+ <!-- YAML
118
+ added: v7.17.0
119
+ -->
74
120
 
75
- Implements [`Dispatcher.close([callback])`](/docs/docs/api/Dispatcher.md#dispatcherclosecallback-promise).
121
+ Destroys the pool abruptly. All pending and running requests are aborted with
122
+ the given `error`. Implements [`dispatcher.destroy([error[, callback]])`][].
76
123
 
77
- ### `RoundRobinPool.destroy([error, callback])`
124
+ ### `roundRobinPool.connect(options[, callback])`
78
125
 
79
- Implements [`Dispatcher.destroy([error, callback])`](/docs/docs/api/Dispatcher.md#dispatcherdestroyerror-callback-promise).
126
+ <!-- YAML
127
+ added: v7.17.0
128
+ -->
80
129
 
81
- ### `RoundRobinPool.connect(options[, callback])`
130
+ Starts two-way communications with the requested resource using
131
+ [HTTP CONNECT][]. See [`dispatcher.connect(options[, callback])`][].
82
132
 
83
- See [`Dispatcher.connect(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherconnectoptions-callback).
133
+ ### `roundRobinPool.dispatch(options, handler)`
84
134
 
85
- ### `RoundRobinPool.dispatch(options, handler)`
135
+ <!-- YAML
136
+ added: v7.17.0
137
+ -->
86
138
 
87
- Implements [`Dispatcher.dispatch(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherdispatchoptions-handler).
139
+ Dispatches a request through the next client selected in round-robin order.
140
+ Implements [`dispatcher.dispatch(options, handler)`][].
88
141
 
89
- ### `RoundRobinPool.pipeline(options, handler)`
142
+ ### `roundRobinPool.pipeline(options, handler)`
90
143
 
91
- See [`Dispatcher.pipeline(options, handler)`](/docs/docs/api/Dispatcher.md#dispatcherpipelineoptions-handler).
144
+ <!-- YAML
145
+ added: v7.17.0
146
+ -->
92
147
 
93
- ### `RoundRobinPool.request(options[, callback])`
148
+ For easy use with [`stream.pipeline`][]. See
149
+ [`dispatcher.pipeline(options, handler)`][].
94
150
 
95
- See [`Dispatcher.request(options [, callback])`](/docs/docs/api/Dispatcher.md#dispatcherrequestoptions-callback).
151
+ ### `roundRobinPool.request(options[, callback])`
96
152
 
97
- ### `RoundRobinPool.stream(options, factory[, callback])`
153
+ <!-- YAML
154
+ added: v7.17.0
155
+ -->
98
156
 
99
- See [`Dispatcher.stream(options, factory[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherstreamoptions-factory-callback).
157
+ Performs an HTTP request. See [`dispatcher.request(options[, callback])`][].
100
158
 
101
- ### `RoundRobinPool.upgrade(options[, callback])`
159
+ ### `roundRobinPool.stream(options, factory[, callback])`
102
160
 
103
- See [`Dispatcher.upgrade(options[, callback])`](/docs/docs/api/Dispatcher.md#dispatcherupgradeoptions-callback).
161
+ <!-- YAML
162
+ added: v7.17.0
163
+ -->
104
164
 
105
- ## Instance Events
165
+ A faster version of [`roundRobinPool.request()`][]. See
166
+ [`dispatcher.stream(options, factory[, callback])`][].
167
+
168
+ ### `roundRobinPool.upgrade(options[, callback])`
169
+
170
+ <!-- YAML
171
+ added: v7.17.0
172
+ -->
173
+
174
+ Upgrades a connection to a different protocol. See
175
+ [`dispatcher.upgrade(options[, callback])`][].
106
176
 
107
177
  ### Event: `'connect'`
108
178
 
109
- See [Dispatcher Event: `'connect'`](/docs/docs/api/Dispatcher.md#event-connect).
179
+ <!-- YAML
180
+ added: v7.17.0
181
+ -->
182
+
183
+ See [Dispatcher Event: `'connect'`][].
110
184
 
111
185
  ### Event: `'disconnect'`
112
186
 
113
- See [Dispatcher Event: `'disconnect'`](/docs/docs/api/Dispatcher.md#event-disconnect).
187
+ <!-- YAML
188
+ added: v7.17.0
189
+ -->
190
+
191
+ See [Dispatcher Event: `'disconnect'`][].
114
192
 
115
193
  ### Event: `'drain'`
116
194
 
117
- See [Dispatcher Event: `'drain'`](/docs/docs/api/Dispatcher.md#event-drain).
195
+ <!-- YAML
196
+ added: v7.17.0
197
+ -->
198
+
199
+ See [Dispatcher Event: `'drain'`][].
118
200
 
119
201
  ## Example
120
202
 
121
- ```javascript
203
+ ```mjs
122
204
  import { RoundRobinPool } from 'undici'
123
205
 
124
- const pool = new RoundRobinPool('http://my-service.default.svc.cluster.local', {
206
+ const pool = new RoundRobinPool('http://localhost:3000', {
125
207
  connections: 10
126
208
  })
127
209
 
128
- // Requests will be distributed evenly across all 10 connections
210
+ // Requests are distributed evenly across all 10 connections.
129
211
  for (let i = 0; i < 100; i++) {
130
212
  const { body } = await pool.request({
131
213
  path: '/api/data',
@@ -137,9 +219,21 @@ for (let i = 0; i < 100; i++) {
137
219
  await pool.close()
138
220
  ```
139
221
 
140
- ## See Also
141
-
142
- - [Pool](/docs/docs/api/Pool.md) - Connection pool without round-robin
143
- - [BalancedPool](/docs/docs/api/BalancedPool.md) - Load balancing across multiple origins
144
- - [Issue #3648](https://github.com/nodejs/undici/issues/3648) - Original issue describing uneven distribution
145
-
222
+ [HTTP CONNECT]: https://developer.mozilla.org/docs/Web/HTTP/Methods/CONNECT
223
+ [`BalancedPool`]: BalancedPool.md#class-balancedpool
224
+ [`Client`]: Client.md#class-client
225
+ [`Pool`]: Pool.md#class-pool
226
+ [`PoolStats`]: PoolStats.md#class-poolstats
227
+ [`dispatcher.close([callback])`]: Dispatcher.md#dispatcherclosecallback-promise
228
+ [`dispatcher.connect(options[, callback])`]: Dispatcher.md#dispatcherconnectoptions-callback
229
+ [`dispatcher.destroy([error[, callback]])`]: Dispatcher.md#dispatcherdestroyerror-callback-promise
230
+ [`dispatcher.dispatch(options, handler)`]: Dispatcher.md#dispatcherdispatchoptions-handler
231
+ [`dispatcher.pipeline(options, handler)`]: Dispatcher.md#dispatcherpipelineoptions-handler
232
+ [`dispatcher.request(options[, callback])`]: Dispatcher.md#dispatcherrequestoptions-callback
233
+ [`dispatcher.stream(options, factory[, callback])`]: Dispatcher.md#dispatcherstreamoptions-factory-callback
234
+ [`dispatcher.upgrade(options[, callback])`]: Dispatcher.md#dispatcherupgradeoptions-callback
235
+ [`roundRobinPool.request()`]: #roundrobinpoolrequestoptions-callback
236
+ [`stream.pipeline`]: https://nodejs.org/api/stream.html#streampipelinesource-transforms-destination-callback
237
+ [Dispatcher Event: `'connect'`]: Dispatcher.md#event-connect
238
+ [Dispatcher Event: `'disconnect'`]: Dispatcher.md#event-disconnect
239
+ [Dispatcher Event: `'drain'`]: Dispatcher.md#event-drain