@bleedingdev/modern-js-bff-effect 3.8.3-ultramodern.2

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 (230) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/dist/cjs/data-platform/batch/protocol.js +130 -0
  4. package/dist/cjs/data-platform/batch/queue.js +623 -0
  5. package/dist/cjs/data-platform/batch/request.js +121 -0
  6. package/dist/cjs/data-platform/batch/response.js +65 -0
  7. package/dist/cjs/data-platform/batch/telemetry.js +71 -0
  8. package/dist/cjs/data-platform/batch/url.js +69 -0
  9. package/dist/cjs/data-platform/batch-transport.js +59 -0
  10. package/dist/cjs/data-platform/codec.js +155 -0
  11. package/dist/cjs/data-platform/index.js +124 -0
  12. package/dist/cjs/data-platform/invalidation.js +61 -0
  13. package/dist/cjs/data-platform/trace.js +73 -0
  14. package/dist/cjs/data-platform/types.js +48 -0
  15. package/dist/cjs/data-platform/validation.js +178 -0
  16. package/dist/cjs/effect/context.js +59 -0
  17. package/dist/cjs/effect/dispatch.js +101 -0
  18. package/dist/cjs/effect/edge-context.js +61 -0
  19. package/dist/cjs/effect/edge-dispatcher.js +354 -0
  20. package/dist/cjs/effect/edge.js +62 -0
  21. package/dist/cjs/effect/endpoint-contracts.js +121 -0
  22. package/dist/cjs/effect/entry-shape-registry.js +31 -0
  23. package/dist/cjs/effect/entry-shape.js +113 -0
  24. package/dist/cjs/effect/handler/batch-handler.js +270 -0
  25. package/dist/cjs/effect/handler/batch.js +170 -0
  26. package/dist/cjs/effect/handler/definition.js +99 -0
  27. package/dist/cjs/effect/handler/envelope.js +104 -0
  28. package/dist/cjs/effect/handler/http.js +107 -0
  29. package/dist/cjs/effect/handler/openapi.js +58 -0
  30. package/dist/cjs/effect/handler/routing.js +100 -0
  31. package/dist/cjs/effect/handler/rpc.js +63 -0
  32. package/dist/cjs/effect/handler/types.js +43 -0
  33. package/dist/cjs/effect/handler.js +173 -0
  34. package/dist/cjs/effect/index.js +137 -0
  35. package/dist/cjs/effect/module.js +118 -0
  36. package/dist/cjs/effect/operation-context.js +116 -0
  37. package/dist/cjs/effect/rpcSerialization.js +54 -0
  38. package/dist/cjs/effect-client/batch.js +73 -0
  39. package/dist/cjs/effect-client/envelope.js +145 -0
  40. package/dist/cjs/effect-client/guards.js +44 -0
  41. package/dist/cjs/effect-client/index.js +208 -0
  42. package/dist/cjs/effect-client/request-context.js +64 -0
  43. package/dist/cjs/effect-client/request.js +66 -0
  44. package/dist/cjs/effect-client/runtime.js +100 -0
  45. package/dist/cjs/effect-client/types.js +18 -0
  46. package/dist/cjs/headers.js +46 -0
  47. package/dist/cjs/index.js +83 -0
  48. package/dist/esm/data-platform/batch/protocol.mjs +71 -0
  49. package/dist/esm/data-platform/batch/queue.mjs +582 -0
  50. package/dist/esm/data-platform/batch/request.mjs +68 -0
  51. package/dist/esm/data-platform/batch/response.mjs +24 -0
  52. package/dist/esm/data-platform/batch/telemetry.mjs +27 -0
  53. package/dist/esm/data-platform/batch/url.mjs +25 -0
  54. package/dist/esm/data-platform/batch-transport.mjs +12 -0
  55. package/dist/esm/data-platform/codec.mjs +90 -0
  56. package/dist/esm/data-platform/index.mjs +7 -0
  57. package/dist/esm/data-platform/invalidation.mjs +20 -0
  58. package/dist/esm/data-platform/trace.mjs +29 -0
  59. package/dist/esm/data-platform/types.mjs +4 -0
  60. package/dist/esm/data-platform/validation.mjs +128 -0
  61. package/dist/esm/effect/context.mjs +12 -0
  62. package/dist/esm/effect/dispatch.mjs +63 -0
  63. package/dist/esm/effect/edge-context.mjs +14 -0
  64. package/dist/esm/effect/edge-dispatcher.mjs +115 -0
  65. package/dist/esm/effect/edge.mjs +1 -0
  66. package/dist/esm/effect/endpoint-contracts.mjs +68 -0
  67. package/dist/esm/effect/entry-shape-registry.mjs +17 -0
  68. package/dist/esm/effect/entry-shape.mjs +51 -0
  69. package/dist/esm/effect/handler/batch-handler.mjs +232 -0
  70. package/dist/esm/effect/handler/batch.mjs +105 -0
  71. package/dist/esm/effect/handler/definition.mjs +58 -0
  72. package/dist/esm/effect/handler/envelope.mjs +63 -0
  73. package/dist/esm/effect/handler/http.mjs +69 -0
  74. package/dist/esm/effect/handler/openapi.mjs +20 -0
  75. package/dist/esm/effect/handler/routing.mjs +50 -0
  76. package/dist/esm/effect/handler/rpc.mjs +22 -0
  77. package/dist/esm/effect/handler/types.mjs +5 -0
  78. package/dist/esm/effect/handler.mjs +13 -0
  79. package/dist/esm/effect/index.mjs +7 -0
  80. package/dist/esm/effect/module.mjs +80 -0
  81. package/dist/esm/effect/operation-context.mjs +75 -0
  82. package/dist/esm/effect/rpcSerialization.mjs +16 -0
  83. package/dist/esm/effect-client/batch.mjs +35 -0
  84. package/dist/esm/effect-client/envelope.mjs +104 -0
  85. package/dist/esm/effect-client/guards.mjs +3 -0
  86. package/dist/esm/effect-client/index.mjs +110 -0
  87. package/dist/esm/effect-client/request-context.mjs +23 -0
  88. package/dist/esm/effect-client/request.mjs +28 -0
  89. package/dist/esm/effect-client/runtime.mjs +62 -0
  90. package/dist/esm/effect-client/types.mjs +0 -0
  91. package/dist/esm/headers.mjs +8 -0
  92. package/dist/esm/index.mjs +4 -0
  93. package/dist/esm/rslib-runtime.mjs +18 -0
  94. package/dist/esm-node/data-platform/batch/protocol.mjs +72 -0
  95. package/dist/esm-node/data-platform/batch/queue.mjs +583 -0
  96. package/dist/esm-node/data-platform/batch/request.mjs +69 -0
  97. package/dist/esm-node/data-platform/batch/response.mjs +25 -0
  98. package/dist/esm-node/data-platform/batch/telemetry.mjs +28 -0
  99. package/dist/esm-node/data-platform/batch/url.mjs +26 -0
  100. package/dist/esm-node/data-platform/batch-transport.mjs +13 -0
  101. package/dist/esm-node/data-platform/codec.mjs +91 -0
  102. package/dist/esm-node/data-platform/index.mjs +8 -0
  103. package/dist/esm-node/data-platform/invalidation.mjs +21 -0
  104. package/dist/esm-node/data-platform/trace.mjs +30 -0
  105. package/dist/esm-node/data-platform/types.mjs +5 -0
  106. package/dist/esm-node/data-platform/validation.mjs +129 -0
  107. package/dist/esm-node/effect/context.mjs +13 -0
  108. package/dist/esm-node/effect/dispatch.mjs +64 -0
  109. package/dist/esm-node/effect/edge-context.mjs +15 -0
  110. package/dist/esm-node/effect/edge-dispatcher.mjs +116 -0
  111. package/dist/esm-node/effect/edge.mjs +2 -0
  112. package/dist/esm-node/effect/endpoint-contracts.mjs +69 -0
  113. package/dist/esm-node/effect/entry-shape-registry.mjs +18 -0
  114. package/dist/esm-node/effect/entry-shape.mjs +63 -0
  115. package/dist/esm-node/effect/handler/batch-handler.mjs +233 -0
  116. package/dist/esm-node/effect/handler/batch.mjs +106 -0
  117. package/dist/esm-node/effect/handler/definition.mjs +59 -0
  118. package/dist/esm-node/effect/handler/envelope.mjs +64 -0
  119. package/dist/esm-node/effect/handler/http.mjs +70 -0
  120. package/dist/esm-node/effect/handler/openapi.mjs +21 -0
  121. package/dist/esm-node/effect/handler/routing.mjs +51 -0
  122. package/dist/esm-node/effect/handler/rpc.mjs +23 -0
  123. package/dist/esm-node/effect/handler/types.mjs +6 -0
  124. package/dist/esm-node/effect/handler.mjs +14 -0
  125. package/dist/esm-node/effect/index.mjs +8 -0
  126. package/dist/esm-node/effect/module.mjs +81 -0
  127. package/dist/esm-node/effect/operation-context.mjs +76 -0
  128. package/dist/esm-node/effect/rpcSerialization.mjs +17 -0
  129. package/dist/esm-node/effect-client/batch.mjs +36 -0
  130. package/dist/esm-node/effect-client/envelope.mjs +105 -0
  131. package/dist/esm-node/effect-client/guards.mjs +4 -0
  132. package/dist/esm-node/effect-client/index.mjs +111 -0
  133. package/dist/esm-node/effect-client/request-context.mjs +24 -0
  134. package/dist/esm-node/effect-client/request.mjs +29 -0
  135. package/dist/esm-node/effect-client/runtime.mjs +63 -0
  136. package/dist/esm-node/effect-client/types.mjs +1 -0
  137. package/dist/esm-node/headers.mjs +9 -0
  138. package/dist/esm-node/index.mjs +5 -0
  139. package/dist/esm-node/rslib-runtime.mjs +19 -0
  140. package/dist/types/data-platform/batch/protocol.d.ts +9 -0
  141. package/dist/types/data-platform/batch/queue.d.ts +47 -0
  142. package/dist/types/data-platform/batch/request.d.ts +19 -0
  143. package/dist/types/data-platform/batch/response.d.ts +3 -0
  144. package/dist/types/data-platform/batch/telemetry.d.ts +4 -0
  145. package/dist/types/data-platform/batch/url.d.ts +4 -0
  146. package/dist/types/data-platform/batch-transport.d.ts +3 -0
  147. package/dist/types/data-platform/codec.d.ts +17 -0
  148. package/dist/types/data-platform/index.d.ts +8 -0
  149. package/dist/types/data-platform/invalidation.d.ts +8 -0
  150. package/dist/types/data-platform/trace.d.ts +4 -0
  151. package/dist/types/data-platform/types.d.ts +151 -0
  152. package/dist/types/data-platform/validation.d.ts +29 -0
  153. package/dist/types/effect/context.d.ts +5 -0
  154. package/dist/types/effect/dispatch.d.ts +13 -0
  155. package/dist/types/effect/edge-context.d.ts +5 -0
  156. package/dist/types/effect/edge-dispatcher.d.ts +37 -0
  157. package/dist/types/effect/edge.d.ts +1 -0
  158. package/dist/types/effect/endpoint-contracts.d.ts +67 -0
  159. package/dist/types/effect/entry-shape.d.ts +32 -0
  160. package/dist/types/effect/handler/batch-handler.d.ts +11 -0
  161. package/dist/types/effect/handler/batch.d.ts +15 -0
  162. package/dist/types/effect/handler/definition.d.ts +7 -0
  163. package/dist/types/effect/handler/envelope.d.ts +13 -0
  164. package/dist/types/effect/handler/http.d.ts +15 -0
  165. package/dist/types/effect/handler/openapi.d.ts +4 -0
  166. package/dist/types/effect/handler/routing.d.ts +5 -0
  167. package/dist/types/effect/handler/rpc.d.ts +7 -0
  168. package/dist/types/effect/handler/types.d.ts +153 -0
  169. package/dist/types/effect/handler.d.ts +13 -0
  170. package/dist/types/effect/index.d.ts +6 -0
  171. package/dist/types/effect/module.d.ts +36 -0
  172. package/dist/types/effect/operation-context.d.ts +15 -0
  173. package/dist/types/effect/rpcSerialization.d.ts +3 -0
  174. package/dist/types/effect-client/batch.d.ts +2 -0
  175. package/dist/types/effect-client/envelope.d.ts +15 -0
  176. package/dist/types/effect-client/guards.d.ts +2 -0
  177. package/dist/types/effect-client/index.d.ts +58 -0
  178. package/dist/types/effect-client/request-context.d.ts +3 -0
  179. package/dist/types/effect-client/request.d.ts +1 -0
  180. package/dist/types/effect-client/runtime.d.ts +5 -0
  181. package/dist/types/effect-client/types.d.ts +68 -0
  182. package/dist/types/headers.d.ts +1 -0
  183. package/dist/types/index.d.ts +3 -0
  184. package/package.json +164 -0
  185. package/src/data-platform/batch/protocol.ts +112 -0
  186. package/src/data-platform/batch/queue.ts +876 -0
  187. package/src/data-platform/batch/request.ts +130 -0
  188. package/src/data-platform/batch/response.ts +74 -0
  189. package/src/data-platform/batch/telemetry.ts +47 -0
  190. package/src/data-platform/batch/url.ts +54 -0
  191. package/src/data-platform/batch-transport.ts +22 -0
  192. package/src/data-platform/codec.ts +145 -0
  193. package/src/data-platform/index.ts +63 -0
  194. package/src/data-platform/invalidation.ts +57 -0
  195. package/src/data-platform/trace.ts +49 -0
  196. package/src/data-platform/types.ts +186 -0
  197. package/src/data-platform/validation.ts +303 -0
  198. package/src/effect/context.ts +37 -0
  199. package/src/effect/dispatch.ts +132 -0
  200. package/src/effect/edge-context.ts +51 -0
  201. package/src/effect/edge-dispatcher.ts +310 -0
  202. package/src/effect/edge.ts +1 -0
  203. package/src/effect/endpoint-contracts.ts +191 -0
  204. package/src/effect/entry-shape-registry.cjs +16 -0
  205. package/src/effect/entry-shape-registry.d.cts +6 -0
  206. package/src/effect/entry-shape.ts +155 -0
  207. package/src/effect/handler/batch-handler.ts +475 -0
  208. package/src/effect/handler/batch.ts +182 -0
  209. package/src/effect/handler/definition.ts +121 -0
  210. package/src/effect/handler/envelope.ts +133 -0
  211. package/src/effect/handler/http.ts +133 -0
  212. package/src/effect/handler/openapi.ts +45 -0
  213. package/src/effect/handler/routing.ts +91 -0
  214. package/src/effect/handler/rpc.ts +45 -0
  215. package/src/effect/handler/types.ts +255 -0
  216. package/src/effect/handler.ts +33 -0
  217. package/src/effect/index.ts +22 -0
  218. package/src/effect/module.ts +186 -0
  219. package/src/effect/operation-context.ts +144 -0
  220. package/src/effect/rpcSerialization.ts +20 -0
  221. package/src/effect-client/batch.ts +50 -0
  222. package/src/effect-client/envelope.ts +238 -0
  223. package/src/effect-client/guards.ts +6 -0
  224. package/src/effect-client/index.ts +325 -0
  225. package/src/effect-client/request-context.ts +54 -0
  226. package/src/effect-client/request.ts +51 -0
  227. package/src/effect-client/runtime.ts +122 -0
  228. package/src/effect-client/types.ts +84 -0
  229. package/src/headers.ts +7 -0
  230. package/src/index.ts +3 -0
@@ -0,0 +1,72 @@
1
+ import "node:module";
2
+ import { isPlainObject } from "../codec.mjs";
3
+ const BASE64_CHUNK_SIZE = 0x8000;
4
+ const CANONICAL_BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
5
+ const RESPONSE_BLOCKED_HEADERS = new Set([
6
+ 'connection',
7
+ 'content-length',
8
+ 'keep-alive',
9
+ 'proxy-authenticate',
10
+ 'proxy-authorization',
11
+ 'proxy-connection',
12
+ 'set-cookie',
13
+ 'te',
14
+ 'trailer',
15
+ 'transfer-encoding',
16
+ 'upgrade'
17
+ ]);
18
+ const NULL_BODY_STATUSES = new Set([
19
+ 204,
20
+ 205,
21
+ 304
22
+ ]);
23
+ function encodeBatchBody(bytes) {
24
+ let binary = '';
25
+ for(let offset = 0; offset < bytes.byteLength; offset += BASE64_CHUNK_SIZE)binary += String.fromCharCode(...bytes.subarray(offset, offset + BASE64_CHUNK_SIZE));
26
+ return {
27
+ encoding: 'base64',
28
+ data: btoa(binary)
29
+ };
30
+ }
31
+ function encodeBatchText(value) {
32
+ return encodeBatchBody(new TextEncoder().encode(value));
33
+ }
34
+ function decodeBatchBody(body) {
35
+ const binary = atob(body.data);
36
+ const bytes = new Uint8Array(binary.length);
37
+ for(let index = 0; index < binary.length; index += 1)bytes[index] = binary.charCodeAt(index);
38
+ return bytes;
39
+ }
40
+ function isBatchBody(value) {
41
+ return isPlainObject(value) && 'base64' === value.encoding && 'string' == typeof value.data && CANONICAL_BASE64.test(value.data) && btoa(atob(value.data)) === value.data;
42
+ }
43
+ function isBatchHeaderList(value) {
44
+ return Array.isArray(value) && value.every((entry)=>Array.isArray(entry) && 2 === entry.length && 'string' == typeof entry[0] && 'string' == typeof entry[1]);
45
+ }
46
+ function isNullBodyStatus(status) {
47
+ return NULL_BODY_STATUSES.has(status);
48
+ }
49
+ function toBatchResponseHeaders(headers) {
50
+ const connectionHeaders = new Set((headers.get('connection') || '').split(',').map((header)=>header.trim().toLowerCase()).filter(Boolean));
51
+ const output = [];
52
+ headers.forEach((value, name)=>{
53
+ const normalizedName = name.toLowerCase();
54
+ if (!RESPONSE_BLOCKED_HEADERS.has(normalizedName) && !connectionHeaders.has(normalizedName)) output.push([
55
+ normalizedName,
56
+ value
57
+ ]);
58
+ });
59
+ return output;
60
+ }
61
+ async function toBatchResponseItem(id, response, omitBody = false) {
62
+ const body = omitBody || null === response.body ? void 0 : encodeBatchBody(new Uint8Array(await response.arrayBuffer()));
63
+ return {
64
+ id,
65
+ status: response.status,
66
+ headers: toBatchResponseHeaders(response.headers),
67
+ ...body ? {
68
+ body
69
+ } : {}
70
+ };
71
+ }
72
+ export { decodeBatchBody, encodeBatchBody, encodeBatchText, isBatchBody, isBatchHeaderList, isNullBodyStatus, toBatchResponseHeaders, toBatchResponseItem };
@@ -0,0 +1,583 @@
1
+ import "node:module";
2
+ import { stableStringify } from "../codec.mjs";
3
+ import { DEFAULT_DATA_BATCH_HEADER } from "../types.mjs";
4
+ import { decodeBatchBody, isNullBodyStatus } from "./protocol.mjs";
5
+ import { createBatchId, measureTextBytes, normalizeMethod, shouldBatchRequest, toHeaderRecord, toRequestBody } from "./request.mjs";
6
+ import { isBatchResponsePayload, parseResponseLikeCreateRequest } from "./response.mjs";
7
+ import { emitDataBatchTransportEvent } from "./telemetry.mjs";
8
+ import { normalizeBatchEndpoint, toAbsoluteUrl } from "./url.mjs";
9
+ const SAFE_REPLAY_METHODS = new Set([
10
+ 'GET',
11
+ 'HEAD'
12
+ ]);
13
+ const abortReason = (signal)=>signal.reason ?? new DOMException('The request was aborted', 'AbortError');
14
+ const createRequestLifetime = (sourceSignal, timeoutMs)=>{
15
+ const controller = new AbortController();
16
+ let timedOut = false;
17
+ let timeoutHandle = null;
18
+ const abortFromSource = ()=>{
19
+ if (sourceSignal) controller.abort(abortReason(sourceSignal));
20
+ };
21
+ if (sourceSignal?.aborted) abortFromSource();
22
+ else if (sourceSignal) sourceSignal.addEventListener('abort', abortFromSource, {
23
+ once: true
24
+ });
25
+ if (!controller.signal.aborted && timeoutMs && timeoutMs > 0) timeoutHandle = setTimeout(()=>{
26
+ timedOut = true;
27
+ controller.abort(new DOMException('The batched request timed out', 'TimeoutError'));
28
+ }, timeoutMs);
29
+ return {
30
+ signal: controller.signal,
31
+ get timedOut () {
32
+ return timedOut;
33
+ },
34
+ dispose () {
35
+ if (null !== timeoutHandle) {
36
+ clearTimeout(timeoutHandle);
37
+ timeoutHandle = null;
38
+ }
39
+ sourceSignal?.removeEventListener('abort', abortFromSource);
40
+ }
41
+ };
42
+ };
43
+ const runWithSignal = (signal, operation)=>{
44
+ if (signal.aborted) return Promise.reject(abortReason(signal));
45
+ return new Promise((resolve, reject)=>{
46
+ const onAbort = ()=>{
47
+ signal.removeEventListener('abort', onAbort);
48
+ reject(abortReason(signal));
49
+ };
50
+ signal.addEventListener('abort', onAbort, {
51
+ once: true
52
+ });
53
+ let pending;
54
+ try {
55
+ pending = operation();
56
+ } catch (error) {
57
+ signal.removeEventListener('abort', onAbort);
58
+ reject(error);
59
+ return;
60
+ }
61
+ pending.then((value)=>{
62
+ signal.removeEventListener('abort', onAbort);
63
+ resolve(value);
64
+ }, (error)=>{
65
+ signal.removeEventListener('abort', onAbort);
66
+ reject(error);
67
+ });
68
+ });
69
+ };
70
+ class BatchBucketRegistry {
71
+ get size() {
72
+ return this.buckets.size;
73
+ }
74
+ get(bucketKey) {
75
+ return this.buckets.get(bucketKey);
76
+ }
77
+ ensure(bucketKey) {
78
+ const existing = this.buckets.get(bucketKey);
79
+ if (existing) return existing;
80
+ const next = {
81
+ items: [],
82
+ bytes: 0,
83
+ batchId: createBatchId(),
84
+ sentAt: Date.now(),
85
+ timer: null,
86
+ flushing: false
87
+ };
88
+ this.buckets.set(bucketKey, next);
89
+ return next;
90
+ }
91
+ releaseIfIdle(bucketKey, bucket) {
92
+ if (this.buckets.get(bucketKey) !== bucket || bucket.flushing || null !== bucket.timer || bucket.items.length > 0) return false;
93
+ return this.buckets.delete(bucketKey);
94
+ }
95
+ constructor(){
96
+ this.buckets = new Map();
97
+ }
98
+ }
99
+ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
100
+ const flushIntervalMs = Math.max(0, options.flushIntervalMs ?? 8);
101
+ const maxBatchSize = Math.max(1, options.maxBatchSize ?? 16);
102
+ const maxBatchBytes = Math.max(1024, options.maxBatchBytes ?? 65536);
103
+ const requestTimeoutMs = options.requestTimeoutMs;
104
+ const allowedMethods = new Set((options.allowedMethods && options.allowedMethods.length > 0 ? options.allowedMethods : [
105
+ 'GET'
106
+ ]).map((method)=>method.toUpperCase()));
107
+ const onEvent = options.onEvent;
108
+ const buckets = bucketRegistry ?? new BatchBucketRegistry();
109
+ const pendingByKey = new Map();
110
+ const disabledEndpoints = new Set();
111
+ let nextItemId = 0;
112
+ const createPayload = (bucket, items)=>({
113
+ protocolVersion: 2,
114
+ batchId: bucket.batchId,
115
+ sentAt: bucket.sentAt,
116
+ items: items.map((item)=>item.item)
117
+ });
118
+ const measurePayload = (bucket, items)=>measureTextBytes(JSON.stringify(createPayload(bucket, items)));
119
+ const rotatePayloadIdentity = (bucket)=>{
120
+ bucket.batchId = createBatchId();
121
+ bucket.sentAt = Date.now();
122
+ };
123
+ const settleRequest = (request, settlement)=>{
124
+ if ('settled' === request.phase) return false;
125
+ request.phase = 'settled';
126
+ request.lifetime.signal.removeEventListener('abort', request.abortListener);
127
+ request.lifetime.dispose();
128
+ if (pendingByKey.get(request.key) === request) pendingByKey.delete(request.key);
129
+ if ('resolve' === settlement.type) request.resolve(settlement.value);
130
+ else request.reject(settlement.reason);
131
+ return true;
132
+ };
133
+ const removeQueuedRequest = (request)=>{
134
+ const bucket = buckets.get(request.bucketKey);
135
+ if (!bucket) return;
136
+ const index = bucket.items.indexOf(request);
137
+ if (-1 === index) return;
138
+ bucket.items.splice(index, 1);
139
+ bucket.bytes = measurePayload(bucket, bucket.items);
140
+ if (0 === bucket.items.length && bucket.timer) {
141
+ clearTimeout(bucket.timer);
142
+ bucket.timer = null;
143
+ }
144
+ buckets.releaseIfIdle(request.bucketKey, bucket);
145
+ };
146
+ const abortQueuedRequest = (request)=>{
147
+ if ('settled' === request.phase) return;
148
+ if ('queued' === request.phase) removeQueuedRequest(request);
149
+ const abortBatchIfUnused = request.abortBatchIfUnused;
150
+ settleRequest(request, {
151
+ type: 'reject',
152
+ reason: abortReason(request.lifetime.signal)
153
+ });
154
+ abortBatchIfUnused?.();
155
+ };
156
+ const runSingle = (request)=>runWithSignal(request.lifetime.signal, async ()=>{
157
+ const response = await baseFetch(request.requestInput, request.requestInit);
158
+ return parseResponseLikeCreateRequest(response);
159
+ });
160
+ const settleRequests = async (items, runner)=>{
161
+ await Promise.all(items.map(async (item)=>{
162
+ if ('settled' === item.phase) return;
163
+ try {
164
+ const value = await runner(item);
165
+ settleRequest(item, {
166
+ type: 'resolve',
167
+ value
168
+ });
169
+ } catch (error) {
170
+ settleRequest(item, {
171
+ type: 'reject',
172
+ reason: error
173
+ });
174
+ }
175
+ }));
176
+ };
177
+ const runAfterAmbiguousBatch = (request, reason)=>{
178
+ if (!SAFE_REPLAY_METHODS.has(request.item.method)) throw new Error(`Batch result is unknown for ${request.item.method}; automatic replay is disabled (${reason}).`);
179
+ return runSingle(request);
180
+ };
181
+ const settleAmbiguousBatchRequests = (items, reason)=>settleRequests(items, (request)=>runAfterAmbiguousBatch(request, reason));
182
+ const flushBucket = async (bucketKey)=>{
183
+ const bucket = buckets.get(bucketKey);
184
+ if (!bucket || bucket.flushing) return;
185
+ if (bucket.timer) {
186
+ clearTimeout(bucket.timer);
187
+ bucket.timer = null;
188
+ }
189
+ if (0 === bucket.items.length) return void buckets.releaseIfIdle(bucketKey, bucket);
190
+ bucket.flushing = true;
191
+ const items = bucket.items;
192
+ for (const item of items)item.phase = 'flushing';
193
+ const payloadIdentity = {
194
+ batchId: bucket.batchId,
195
+ sentAt: bucket.sentAt
196
+ };
197
+ bucket.items = [];
198
+ bucket.bytes = 0;
199
+ rotatePayloadIdentity(bucket);
200
+ const firstItem = items[0];
201
+ if (!firstItem) {
202
+ bucket.flushing = false;
203
+ buckets.releaseIfIdle(bucketKey, bucket);
204
+ return;
205
+ }
206
+ const endpoint = firstItem.endpoint;
207
+ if (1 === items.length || disabledEndpoints.has(endpoint)) {
208
+ emitDataBatchTransportEvent(onEvent, {
209
+ type: disabledEndpoints.has(endpoint) ? 'fallback' : 'flush',
210
+ endpoint,
211
+ size: items.length,
212
+ reason: disabledEndpoints.has(endpoint) ? 'batch-disabled' : void 0
213
+ });
214
+ await settleRequests(items, runSingle);
215
+ bucket.flushing = false;
216
+ if (bucket.items.length > 0 && !bucket.timer) flushBucket(bucketKey);
217
+ buckets.releaseIfIdle(bucketKey, bucket);
218
+ return;
219
+ }
220
+ const payload = createPayload(payloadIdentity, items);
221
+ const batchId = payload.batchId;
222
+ emitDataBatchTransportEvent(onEvent, {
223
+ type: 'flush',
224
+ endpoint,
225
+ batchId,
226
+ size: items.length
227
+ });
228
+ const payloadJson = JSON.stringify(payload);
229
+ const traceparent = items.find((item)=>'string' == typeof item.item.headers?.traceparent)?.item.headers?.traceparent || void 0;
230
+ const authorization = firstItem.authorization;
231
+ const cookie = firstItem.cookie;
232
+ const requestInit = {
233
+ method: 'POST',
234
+ credentials: firstItem.credentials,
235
+ headers: {
236
+ accept: 'application/json, */*;q=0.8',
237
+ 'content-type': 'application/json; charset=utf-8',
238
+ [DEFAULT_DATA_BATCH_HEADER]: '2',
239
+ ...traceparent ? {
240
+ traceparent
241
+ } : {},
242
+ ...'string' == typeof authorization ? {
243
+ authorization
244
+ } : {},
245
+ ...'string' == typeof cookie ? {
246
+ cookie
247
+ } : {}
248
+ },
249
+ body: payloadJson
250
+ };
251
+ const controller = new AbortController();
252
+ let abortedBecauseUnused = false;
253
+ const abortBatchIfUnused = ()=>{
254
+ if (!controller.signal.aborted && items.every((item)=>'settled' === item.phase)) {
255
+ abortedBecauseUnused = true;
256
+ controller.abort(items.find((item)=>item.lifetime.timedOut) ? new DOMException('The batch timed out', 'TimeoutError') : new DOMException('Every batched request was aborted', 'AbortError'));
257
+ }
258
+ };
259
+ for (const item of items)item.abortBatchIfUnused = abortBatchIfUnused;
260
+ requestInit.signal = controller.signal;
261
+ try {
262
+ const response = await runWithSignal(controller.signal, ()=>baseFetch(endpoint, requestInit));
263
+ if (!response.ok) {
264
+ if (404 === response.status || 405 === response.status) {
265
+ disabledEndpoints.add(endpoint);
266
+ emitDataBatchTransportEvent(onEvent, {
267
+ type: 'disable',
268
+ endpoint,
269
+ batchId,
270
+ reason: `batch-endpoint-unavailable-${String(response.status)}`
271
+ });
272
+ } else emitDataBatchTransportEvent(onEvent, {
273
+ type: 'fallback',
274
+ endpoint,
275
+ batchId,
276
+ size: items.length,
277
+ reason: `batch-response-${String(response.status)}`
278
+ });
279
+ await settleAmbiguousBatchRequests(items, `batch-response-${String(response.status)}`);
280
+ bucket.flushing = false;
281
+ return;
282
+ }
283
+ const result = await runWithSignal(controller.signal, async ()=>response.json());
284
+ if (!isBatchResponsePayload(result)) {
285
+ emitDataBatchTransportEvent(onEvent, {
286
+ type: 'fallback',
287
+ endpoint,
288
+ batchId,
289
+ size: items.length,
290
+ reason: 'invalid-batch-response'
291
+ });
292
+ await settleAmbiguousBatchRequests(items, 'invalid-batch-response');
293
+ bucket.flushing = false;
294
+ return;
295
+ }
296
+ if (result.batchId !== batchId) {
297
+ emitDataBatchTransportEvent(onEvent, {
298
+ type: 'fallback',
299
+ endpoint,
300
+ batchId,
301
+ size: items.length,
302
+ reason: 'mismatched-batch-id'
303
+ });
304
+ await settleAmbiguousBatchRequests(items, 'mismatched-batch-id');
305
+ bucket.flushing = false;
306
+ return;
307
+ }
308
+ const itemMap = new Map();
309
+ for (const item of result.items)itemMap.set(item.id, item);
310
+ await settleRequests(items, async (request)=>{
311
+ const resultItem = itemMap.get(request.item.id);
312
+ if (!resultItem) return runAfterAmbiguousBatch(request, 'missing-batch-result');
313
+ const reconstructedBody = resultItem.body ? decodeBatchBody(resultItem.body) : null;
314
+ const reconstructedResponse = new Response(isNullBodyStatus(resultItem.status) ? null : reconstructedBody, {
315
+ status: resultItem.status,
316
+ headers: resultItem.headers
317
+ });
318
+ return parseResponseLikeCreateRequest(reconstructedResponse);
319
+ });
320
+ } catch {
321
+ if (abortedBecauseUnused) {
322
+ if (items.some((item)=>item.lifetime.timedOut)) emitDataBatchTransportEvent(onEvent, {
323
+ type: 'fallback',
324
+ endpoint,
325
+ batchId,
326
+ size: items.length,
327
+ reason: 'batch-timeout'
328
+ });
329
+ return;
330
+ }
331
+ emitDataBatchTransportEvent(onEvent, {
332
+ type: 'fallback',
333
+ endpoint,
334
+ batchId,
335
+ size: items.length,
336
+ reason: 'batch-transport-error'
337
+ });
338
+ await settleAmbiguousBatchRequests(items, 'batch-transport-error');
339
+ } finally{
340
+ for (const item of items)item.abortBatchIfUnused = void 0;
341
+ bucket.flushing = false;
342
+ if (bucket.items.length > 0 && !bucket.timer) flushBucket(bucketKey);
343
+ buckets.releaseIfIdle(bucketKey, bucket);
344
+ }
345
+ };
346
+ const pendingReadPromises = new Map();
347
+ const normalizeRequest = (input, init)=>{
348
+ const sourceRequest = "u" > typeof Request && input instanceof Request ? input : void 0;
349
+ const initDefinesSignal = void 0 !== init && 'signal' in init;
350
+ const initDefinesBody = void 0 !== init && 'body' in init;
351
+ const initSnapshot = init ? {
352
+ ...init
353
+ } : void 0;
354
+ const getInitValue = (key)=>{
355
+ if (!init) return;
356
+ return Object.prototype.hasOwnProperty.call(initSnapshot, key) ? initSnapshot?.[key] : init[key];
357
+ };
358
+ const method = normalizeMethod(getInitValue('method') ?? sourceRequest?.method);
359
+ const headers = toHeaderRecord(getInitValue('headers') ?? sourceRequest?.headers);
360
+ const credentials = getInitValue('credentials') ?? sourceRequest?.credentials ?? 'same-origin';
361
+ const initBody = getInitValue('body');
362
+ const body = initBody ?? null;
363
+ const initSignal = initDefinesSignal ? getInitValue('signal') : void 0;
364
+ const sourceSignal = initDefinesSignal ? initSignal ?? void 0 : sourceRequest?.signal;
365
+ const requestUrl = toAbsoluteUrl(input);
366
+ const endpoint = normalizeBatchEndpoint(requestUrl, options.endpoint).toString();
367
+ return {
368
+ requestUrl,
369
+ endpoint,
370
+ method,
371
+ headers,
372
+ credentials,
373
+ body,
374
+ hasOpaqueSourceBody: void 0 === initBody && void 0 !== sourceRequest && null !== sourceRequest.body,
375
+ requestInput: sourceRequest ?? requestUrl.toString(),
376
+ requestInit: {
377
+ ...initSnapshot,
378
+ method,
379
+ headers,
380
+ credentials,
381
+ ...initDefinesBody ? {
382
+ body: initBody
383
+ } : {},
384
+ ...initDefinesSignal ? {
385
+ signal: initSignal
386
+ } : {}
387
+ },
388
+ sourceSignal
389
+ };
390
+ };
391
+ const getPendingReadKey = (request)=>{
392
+ if (!SAFE_REPLAY_METHODS.has(request.method) || void 0 !== request.sourceSignal || null !== request.body || request.hasOpaqueSourceBody || disabledEndpoints.has(request.endpoint) || !shouldBatchRequest({
393
+ method: request.method,
394
+ body: void 0,
395
+ headers: request.headers,
396
+ allowedMethods,
397
+ batchEndpoint: request.endpoint,
398
+ requestUrl: request.requestUrl
399
+ })) return;
400
+ const itemHeaders = {
401
+ ...request.headers
402
+ };
403
+ delete itemHeaders.authorization;
404
+ delete itemHeaders.cookie;
405
+ const bucketKey = stableStringify({
406
+ endpoint: request.endpoint,
407
+ authorization: request.headers.authorization ?? null,
408
+ cookie: request.headers.cookie ?? null,
409
+ credentials: request.credentials
410
+ });
411
+ return stableStringify({
412
+ bucketKey,
413
+ path: `${request.requestUrl.pathname}${request.requestUrl.search}`,
414
+ method: request.method,
415
+ headers: itemHeaders,
416
+ body: null
417
+ });
418
+ };
419
+ const enqueue = async (request)=>{
420
+ const preparedBody = await toRequestBody(request.body);
421
+ const hasOpaqueBody = !preparedBody.batchable || request.hasOpaqueSourceBody;
422
+ if (disabledEndpoints.has(request.endpoint) || !shouldBatchRequest({
423
+ method: request.method,
424
+ body: hasOpaqueBody ? '[opaque request body]' : void 0,
425
+ headers: request.headers,
426
+ allowedMethods,
427
+ batchEndpoint: request.endpoint,
428
+ requestUrl: request.requestUrl
429
+ })) return baseFetch(request.requestInput, request.requestInit).then(parseResponseLikeCreateRequest);
430
+ const itemId = `${Date.now().toString(36)}_${nextItemId.toString(36)}_${Math.random().toString(16).slice(2, 8)}`;
431
+ nextItemId += 1;
432
+ const itemHeaders = {
433
+ ...request.headers
434
+ };
435
+ delete itemHeaders.authorization;
436
+ delete itemHeaders.cookie;
437
+ if (preparedBody.body && preparedBody.inferredContentType && !itemHeaders['content-type']) itemHeaders['content-type'] = preparedBody.inferredContentType;
438
+ const item = {
439
+ id: itemId,
440
+ path: `${request.requestUrl.pathname}${request.requestUrl.search}`,
441
+ method: request.method,
442
+ headers: itemHeaders,
443
+ ...preparedBody.body ? {
444
+ body: preparedBody.body
445
+ } : {}
446
+ };
447
+ const bucketKey = stableStringify({
448
+ endpoint: request.endpoint,
449
+ authorization: request.headers.authorization ?? null,
450
+ cookie: request.headers.cookie ?? null,
451
+ credentials: request.credentials
452
+ });
453
+ const key = stableStringify({
454
+ bucketKey,
455
+ path: item.path,
456
+ method: item.method,
457
+ headers: item.headers,
458
+ body: item.body ?? null
459
+ });
460
+ const canSharePending = SAFE_REPLAY_METHODS.has(request.method) && void 0 === request.sourceSignal;
461
+ const existing = canSharePending ? pendingByKey.get(key) : void 0;
462
+ if (existing) return existing.promise;
463
+ const lifetime = createRequestLifetime(request.sourceSignal, requestTimeoutMs);
464
+ request.requestInit.signal = lifetime.signal;
465
+ const deferred = Promise.withResolvers();
466
+ const queued = {
467
+ key,
468
+ bucketKey,
469
+ endpoint: request.endpoint,
470
+ requestInput: request.requestInput,
471
+ requestInit: request.requestInit,
472
+ authorization: request.headers.authorization,
473
+ cookie: request.headers.cookie,
474
+ credentials: request.credentials,
475
+ item,
476
+ lifetime,
477
+ phase: 'queued',
478
+ promise: deferred.promise,
479
+ abortListener: ()=>abortQueuedRequest(queued),
480
+ resolve: deferred.resolve,
481
+ reject: deferred.reject
482
+ };
483
+ lifetime.signal.addEventListener('abort', queued.abortListener, {
484
+ once: true
485
+ });
486
+ if (lifetime.signal.aborted) {
487
+ abortQueuedRequest(queued);
488
+ return queued.promise;
489
+ }
490
+ if (canSharePending) pendingByKey.set(key, queued);
491
+ const bucket = buckets.ensure(bucketKey);
492
+ const singletonBytes = measurePayload(bucket, [
493
+ queued
494
+ ]);
495
+ if (singletonBytes > maxBatchBytes) {
496
+ emitDataBatchTransportEvent(onEvent, {
497
+ type: 'fallback',
498
+ endpoint: request.endpoint,
499
+ size: 1,
500
+ reason: 'batch-item-too-large'
501
+ });
502
+ queued.phase = 'flushing';
503
+ settleRequests([
504
+ queued
505
+ ], runSingle);
506
+ buckets.releaseIfIdle(bucketKey, bucket);
507
+ return queued.promise;
508
+ }
509
+ const candidateItems = [
510
+ ...bucket.items,
511
+ queued
512
+ ];
513
+ const candidateBytes = measurePayload(bucket, candidateItems);
514
+ if (bucket.items.length > 0 && candidateBytes > maxBatchBytes) {
515
+ if (bucket.flushing) {
516
+ emitDataBatchTransportEvent(onEvent, {
517
+ type: 'fallback',
518
+ endpoint: request.endpoint,
519
+ size: 1,
520
+ reason: 'batch-capacity-during-flush'
521
+ });
522
+ queued.phase = 'flushing';
523
+ settleRequests([
524
+ queued
525
+ ], runSingle);
526
+ return queued.promise;
527
+ }
528
+ flushBucket(bucketKey);
529
+ if (measurePayload(bucket, [
530
+ queued
531
+ ]) > maxBatchBytes) {
532
+ emitDataBatchTransportEvent(onEvent, {
533
+ type: 'fallback',
534
+ endpoint: request.endpoint,
535
+ size: 1,
536
+ reason: 'batch-item-too-large'
537
+ });
538
+ queued.phase = 'flushing';
539
+ settleRequests([
540
+ queued
541
+ ], runSingle);
542
+ return queued.promise;
543
+ }
544
+ }
545
+ bucket.items.push(queued);
546
+ bucket.bytes = measurePayload(bucket, bucket.items);
547
+ emitDataBatchTransportEvent(onEvent, {
548
+ type: 'enqueue',
549
+ endpoint: request.endpoint,
550
+ size: bucket.items.length
551
+ });
552
+ if (bucket.items.length >= maxBatchSize || bucket.bytes >= maxBatchBytes) {
553
+ flushBucket(bucketKey);
554
+ return queued.promise;
555
+ }
556
+ if (!bucket.timer) bucket.timer = setTimeout(()=>{
557
+ bucket.timer = null;
558
+ flushBucket(bucketKey);
559
+ }, flushIntervalMs);
560
+ return queued.promise;
561
+ };
562
+ return (input, init)=>{
563
+ let request;
564
+ try {
565
+ request = normalizeRequest(input, init);
566
+ } catch (error) {
567
+ return Promise.reject(error);
568
+ }
569
+ const pendingReadKey = getPendingReadKey(request);
570
+ const existing = pendingReadKey ? pendingReadPromises.get(pendingReadKey) : void 0;
571
+ if (existing) return existing;
572
+ const pending = enqueue(request);
573
+ if (pendingReadKey) {
574
+ pendingReadPromises.set(pendingReadKey, pending);
575
+ const clearPendingRead = ()=>{
576
+ if (pendingReadPromises.get(pendingReadKey) === pending) pendingReadPromises.delete(pendingReadKey);
577
+ };
578
+ pending.then(clearPendingRead, clearPendingRead);
579
+ }
580
+ return pending;
581
+ };
582
+ }
583
+ export { BatchBucketRegistry, createBatchTransportQueue };