@bleedingdev/modern-js-bff-effect 3.9.0-ultramodern.11 → 3.9.0-ultramodern.13

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 (51) hide show
  1. package/dist/cjs/data-platform/batch/options.js +48 -0
  2. package/dist/cjs/data-platform/batch/queue.js +48 -83
  3. package/dist/cjs/effect/entry-shape.js +4 -1
  4. package/dist/cjs/effect/handler/batch-handler.js +2 -3
  5. package/dist/cjs/effect/handler/batch.js +0 -9
  6. package/dist/cjs/effect/module.js +4 -0
  7. package/dist/cjs/effect/operation-context.js +1 -1
  8. package/dist/cjs/effect/rpcSerialization.js +0 -2
  9. package/dist/cjs/effect-client/cross-project.js +5 -8
  10. package/dist/cjs/microvertical-api.js +1 -1
  11. package/dist/esm/data-platform/batch/options.mjs +10 -0
  12. package/dist/esm/data-platform/batch/queue.mjs +48 -83
  13. package/dist/esm/effect/entry-shape-registry.mjs +2 -2
  14. package/dist/esm/effect/handler/batch-handler.mjs +3 -4
  15. package/dist/esm/effect/handler/batch.mjs +1 -7
  16. package/dist/esm/effect/module.mjs +4 -0
  17. package/dist/esm/effect/operation-context.mjs +1 -1
  18. package/dist/esm/effect/rpcSerialization.mjs +0 -2
  19. package/dist/esm/effect-client/cross-project.mjs +6 -9
  20. package/dist/esm/microvertical-api.mjs +1 -1
  21. package/dist/esm-node/data-platform/batch/options.mjs +11 -0
  22. package/dist/esm-node/data-platform/batch/queue.mjs +48 -83
  23. package/dist/esm-node/effect/entry-shape.mjs +4 -1
  24. package/dist/esm-node/effect/handler/batch-handler.mjs +3 -4
  25. package/dist/esm-node/effect/handler/batch.mjs +1 -7
  26. package/dist/esm-node/effect/module.mjs +4 -0
  27. package/dist/esm-node/effect/operation-context.mjs +1 -1
  28. package/dist/esm-node/effect/rpcSerialization.mjs +0 -2
  29. package/dist/esm-node/effect-client/cross-project.mjs +6 -9
  30. package/dist/esm-node/microvertical-api.mjs +1 -1
  31. package/dist/types/data-platform/batch/options.d.ts +7 -0
  32. package/dist/types/data-platform/batch/queue.d.ts +0 -1
  33. package/dist/types/effect/entry-shape.d.ts +2 -0
  34. package/dist/types/effect/handler/batch.d.ts +0 -1
  35. package/dist/types/effect/handler/types.d.ts +1 -1
  36. package/dist/types/effect/module.d.ts +2 -1
  37. package/dist/types/effect-client/index.d.ts +3 -3
  38. package/dist/types/microvertical-api.d.ts +4 -4
  39. package/package.json +12 -12
  40. package/src/data-platform/batch/options.ts +20 -0
  41. package/src/data-platform/batch/queue.ts +61 -89
  42. package/src/effect/entry-shape.ts +12 -0
  43. package/src/effect/handler/batch-handler.ts +6 -12
  44. package/src/effect/handler/batch.ts +0 -10
  45. package/src/effect/handler/types.ts +1 -2
  46. package/src/effect/module.ts +7 -0
  47. package/src/effect/operation-context.ts +1 -1
  48. package/src/effect/rpcSerialization.ts +0 -2
  49. package/src/effect-client/cross-project.ts +9 -14
  50. package/src/effect-client/index.ts +4 -4
  51. package/src/microvertical-api.ts +1 -1
@@ -1,5 +1,6 @@
1
1
  import { stableStringify } from "../codec.mjs";
2
2
  import { DEFAULT_DATA_BATCH_HEADER } from "../types.mjs";
3
+ import { normalizeBatchLimits } from "./options.mjs";
3
4
  import { decodeBatchBody, isNullBodyStatus } from "./protocol.mjs";
4
5
  import { createBatchId, measureTextBytes, normalizeMethod, shouldBatchRequest, toHeaderRecord, toRequestBody } from "./request.mjs";
5
6
  import { isBatchResponsePayload, parseResponseLikeCreateRequest } from "./response.mjs";
@@ -78,7 +79,6 @@ class BatchBucketRegistry {
78
79
  if (existing) return existing;
79
80
  const next = {
80
81
  items: [],
81
- bytes: 0,
82
82
  batchId: createBatchId(),
83
83
  sentAt: Date.now(),
84
84
  timer: null,
@@ -97,12 +97,8 @@ class BatchBucketRegistry {
97
97
  }
98
98
  function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
99
99
  const flushIntervalMs = Math.max(0, options.flushIntervalMs ?? 8);
100
- const maxBatchSize = Math.max(1, options.maxBatchSize ?? 16);
101
- const maxBatchBytes = Math.max(1024, options.maxBatchBytes ?? 65536);
100
+ const { maxBatchSize, maxBatchBytes, allowedMethods } = normalizeBatchLimits(options);
102
101
  const requestTimeoutMs = options.requestTimeoutMs;
103
- const allowedMethods = new Set((options.allowedMethods && options.allowedMethods.length > 0 ? options.allowedMethods : [
104
- 'GET'
105
- ]).map((method)=>method.toUpperCase()));
106
102
  const onEvent = options.onEvent;
107
103
  const buckets = bucketRegistry ?? new BatchBucketRegistry();
108
104
  const pendingByKey = new Map();
@@ -135,7 +131,6 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
135
131
  const index = bucket.items.indexOf(request);
136
132
  if (-1 === index) return;
137
133
  bucket.items.splice(index, 1);
138
- bucket.bytes = measurePayload(bucket, bucket.items);
139
134
  if (0 === bucket.items.length && bucket.timer) {
140
135
  clearTimeout(bucket.timer);
141
136
  bucket.timer = null;
@@ -177,7 +172,11 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
177
172
  if (!SAFE_REPLAY_METHODS.has(request.item.method)) throw new Error(`Batch result is unknown for ${request.item.method}; automatic replay is disabled (${reason}).`);
178
173
  return runSingle(request);
179
174
  };
180
- const settleAmbiguousBatchRequests = (items, reason)=>settleRequests(items, (request)=>runAfterAmbiguousBatch(request, reason));
175
+ const finishFlush = (bucketKey, bucket)=>{
176
+ bucket.flushing = false;
177
+ if (bucket.items.length > 0 && !bucket.timer) flushBucket(bucketKey);
178
+ buckets.releaseIfIdle(bucketKey, bucket);
179
+ };
181
180
  const flushBucket = async (bucketKey)=>{
182
181
  const bucket = buckets.get(bucketKey);
183
182
  if (!bucket || bucket.flushing) return;
@@ -194,7 +193,6 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
194
193
  sentAt: bucket.sentAt
195
194
  };
196
195
  bucket.items = [];
197
- bucket.bytes = 0;
198
196
  rotatePayloadIdentity(bucket);
199
197
  const firstItem = items[0];
200
198
  if (!firstItem) {
@@ -210,14 +208,25 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
210
208
  size: items.length,
211
209
  reason: disabledEndpoints.has(endpoint) ? 'batch-disabled' : void 0
212
210
  });
213
- await settleRequests(items, runSingle);
214
- bucket.flushing = false;
215
- if (bucket.items.length > 0 && !bucket.timer) flushBucket(bucketKey);
216
- buckets.releaseIfIdle(bucketKey, bucket);
211
+ try {
212
+ await settleRequests(items, runSingle);
213
+ } finally{
214
+ finishFlush(bucketKey, bucket);
215
+ }
217
216
  return;
218
217
  }
219
218
  const payload = createPayload(payloadIdentity, items);
220
219
  const batchId = payload.batchId;
220
+ const settleAmbiguousBatchRequests = (reason)=>{
221
+ emitDataBatchTransportEvent(onEvent, {
222
+ type: 'fallback',
223
+ endpoint,
224
+ batchId,
225
+ size: items.length,
226
+ reason
227
+ });
228
+ return settleRequests(items, (request)=>runAfterAmbiguousBatch(request, reason));
229
+ };
221
230
  emitDataBatchTransportEvent(onEvent, {
222
231
  type: 'flush',
223
232
  endpoint,
@@ -268,42 +277,13 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
268
277
  batchId,
269
278
  reason: `batch-endpoint-unavailable-${String(response.status)}`
270
279
  });
271
- } else emitDataBatchTransportEvent(onEvent, {
272
- type: 'fallback',
273
- endpoint,
274
- batchId,
275
- size: items.length,
276
- reason: `batch-response-${String(response.status)}`
277
- });
278
- await settleAmbiguousBatchRequests(items, `batch-response-${String(response.status)}`);
279
- bucket.flushing = false;
280
+ await settleRequests(items, (request)=>runAfterAmbiguousBatch(request, `batch-response-${String(response.status)}`));
281
+ } else await settleAmbiguousBatchRequests(`batch-response-${String(response.status)}`);
280
282
  return;
281
283
  }
282
284
  const result = await runWithSignal(controller.signal, async ()=>response.json());
283
- if (!isBatchResponsePayload(result)) {
284
- emitDataBatchTransportEvent(onEvent, {
285
- type: 'fallback',
286
- endpoint,
287
- batchId,
288
- size: items.length,
289
- reason: 'invalid-batch-response'
290
- });
291
- await settleAmbiguousBatchRequests(items, 'invalid-batch-response');
292
- bucket.flushing = false;
293
- return;
294
- }
295
- if (result.batchId !== batchId) {
296
- emitDataBatchTransportEvent(onEvent, {
297
- type: 'fallback',
298
- endpoint,
299
- batchId,
300
- size: items.length,
301
- reason: 'mismatched-batch-id'
302
- });
303
- await settleAmbiguousBatchRequests(items, 'mismatched-batch-id');
304
- bucket.flushing = false;
305
- return;
306
- }
285
+ if (!isBatchResponsePayload(result)) return void await settleAmbiguousBatchRequests('invalid-batch-response');
286
+ if (result.batchId !== batchId) return void await settleAmbiguousBatchRequests('mismatched-batch-id');
307
287
  const itemMap = new Map();
308
288
  for (const item of result.items)itemMap.set(item.id, item);
309
289
  await settleRequests(items, async (request)=>{
@@ -327,19 +307,10 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
327
307
  });
328
308
  return;
329
309
  }
330
- emitDataBatchTransportEvent(onEvent, {
331
- type: 'fallback',
332
- endpoint,
333
- batchId,
334
- size: items.length,
335
- reason: 'batch-transport-error'
336
- });
337
- await settleAmbiguousBatchRequests(items, 'batch-transport-error');
310
+ await settleAmbiguousBatchRequests('batch-transport-error');
338
311
  } finally{
339
312
  for (const item of items)item.abortBatchIfUnused = void 0;
340
- bucket.flushing = false;
341
- if (bucket.items.length > 0 && !bucket.timer) flushBucket(bucketKey);
342
- buckets.releaseIfIdle(bucketKey, bucket);
313
+ finishFlush(bucketKey, bucket);
343
314
  }
344
315
  };
345
316
  const pendingReadPromises = new Map();
@@ -363,7 +334,20 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
363
334
  const sourceSignal = initDefinesSignal ? initSignal ?? void 0 : sourceRequest?.signal;
364
335
  const requestUrl = toAbsoluteUrl(input);
365
336
  const endpoint = normalizeBatchEndpoint(requestUrl, options.endpoint).toString();
337
+ const itemHeaders = {
338
+ ...headers
339
+ };
340
+ delete itemHeaders.authorization;
341
+ delete itemHeaders.cookie;
366
342
  return {
343
+ itemHeaders,
344
+ itemPath: `${requestUrl.pathname}${requestUrl.search}`,
345
+ bucketKey: stableStringify({
346
+ endpoint,
347
+ authorization: headers.authorization ?? null,
348
+ cookie: headers.cookie ?? null,
349
+ credentials
350
+ }),
367
351
  requestUrl,
368
352
  endpoint,
369
353
  method,
@@ -396,22 +380,11 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
396
380
  batchEndpoint: request.endpoint,
397
381
  requestUrl: request.requestUrl
398
382
  })) return;
399
- const itemHeaders = {
400
- ...request.headers
401
- };
402
- delete itemHeaders.authorization;
403
- delete itemHeaders.cookie;
404
- const bucketKey = stableStringify({
405
- endpoint: request.endpoint,
406
- authorization: request.headers.authorization ?? null,
407
- cookie: request.headers.cookie ?? null,
408
- credentials: request.credentials
409
- });
410
383
  return stableStringify({
411
- bucketKey,
412
- path: `${request.requestUrl.pathname}${request.requestUrl.search}`,
384
+ bucketKey: request.bucketKey,
385
+ path: request.itemPath,
413
386
  method: request.method,
414
- headers: itemHeaders,
387
+ headers: request.itemHeaders,
415
388
  body: null
416
389
  });
417
390
  };
@@ -429,26 +402,19 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
429
402
  const itemId = `${Date.now().toString(36)}_${nextItemId.toString(36)}_${Math.random().toString(16).slice(2, 8)}`;
430
403
  nextItemId += 1;
431
404
  const itemHeaders = {
432
- ...request.headers
405
+ ...request.itemHeaders
433
406
  };
434
- delete itemHeaders.authorization;
435
- delete itemHeaders.cookie;
436
407
  if (preparedBody.body && preparedBody.inferredContentType && !itemHeaders['content-type']) itemHeaders['content-type'] = preparedBody.inferredContentType;
437
408
  const item = {
438
409
  id: itemId,
439
- path: `${request.requestUrl.pathname}${request.requestUrl.search}`,
410
+ path: request.itemPath,
440
411
  method: request.method,
441
412
  headers: itemHeaders,
442
413
  ...preparedBody.body ? {
443
414
  body: preparedBody.body
444
415
  } : {}
445
416
  };
446
- const bucketKey = stableStringify({
447
- endpoint: request.endpoint,
448
- authorization: request.headers.authorization ?? null,
449
- cookie: request.headers.cookie ?? null,
450
- credentials: request.credentials
451
- });
417
+ const { bucketKey } = request;
452
418
  const key = stableStringify({
453
419
  bucketKey,
454
420
  path: item.path,
@@ -542,13 +508,12 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
542
508
  }
543
509
  }
544
510
  bucket.items.push(queued);
545
- bucket.bytes = measurePayload(bucket, bucket.items);
546
511
  emitDataBatchTransportEvent(onEvent, {
547
512
  type: 'enqueue',
548
513
  endpoint: request.endpoint,
549
514
  size: bucket.items.length
550
515
  });
551
- if (bucket.items.length >= maxBatchSize || bucket.bytes >= maxBatchBytes) {
516
+ if (bucket.items.length >= maxBatchSize || measurePayload(bucket, bucket.items) >= maxBatchBytes) {
552
517
  flushBucket(bucketKey);
553
518
  return queued.promise;
554
519
  }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  import { __webpack_require__ } from "../rslib-runtime~1.mjs";
3
3
  __webpack_require__.add({
4
- 1970 (module) {
4
+ Oz (module) {
5
5
  const validatorAwareHandlerFactories = new WeakSet();
6
6
  module.exports = {
7
7
  register (factory) {
@@ -14,4 +14,4 @@ __webpack_require__.add({
14
14
  };
15
15
  }
16
16
  });
17
- __webpack_require__("1970");
17
+ __webpack_require__("Oz");
@@ -1,6 +1,7 @@
1
1
  import { DEFAULT_DATA_BATCH_HEADER, DEFAULT_DATA_ENVELOPE_HEADER, decodeRequestEnvelopeHeader, isPlainObject, normalizeMethod } from "../../data-platform/index.mjs";
2
+ import { normalizeBatchLimits } from "../../data-platform/batch/options.mjs";
2
3
  import { decodeBatchBody, isBatchBody, toBatchResponseItem } from "../../data-platform/batch/protocol.mjs";
3
- import { BatchItemTimeoutError, createBatchValidationResponse, isBatchRequestPayload, mapWithConcurrency, normalizeBatchAllowedMethods, normalizeBatchPath, promiseWithTimeout, toBatchItemError } from "./batch.mjs";
4
+ import { BatchItemTimeoutError, createBatchValidationResponse, isBatchRequestPayload, mapWithConcurrency, normalizeBatchPath, promiseWithTimeout, toBatchItemError } from "./batch.mjs";
4
5
  import { getMountedPrefixFromContext, removeMountedPrefixFromBatchPath } from "./routing.mjs";
5
6
  import * as __rspack_external_effect_Effect_41060786 from "effect/Effect";
6
7
  const HOP_BY_HOP_BATCH_ITEM_HEADERS = new Set([
@@ -91,11 +92,9 @@ function createDataPlatformBatchRequestHandler(options) {
91
92
  const dataPlatformBatchOptions = options.dataPlatform?.batch;
92
93
  const batchEnabled = dataPlatformBatchOptions?.enabled !== false;
93
94
  const batchPath = normalizeBatchPath(dataPlatformBatchOptions?.endpoint);
94
- const batchMaxSize = Math.max(1, dataPlatformBatchOptions?.maxBatchSize ?? 16);
95
- const batchMaxBytes = Math.max(1024, dataPlatformBatchOptions?.maxBatchBytes ?? 65536);
95
+ const { maxBatchSize: batchMaxSize, maxBatchBytes: batchMaxBytes, allowedMethods: batchAllowedMethods } = normalizeBatchLimits(dataPlatformBatchOptions);
96
96
  const batchConcurrency = Math.max(1, dataPlatformBatchOptions?.maxConcurrency ?? 4);
97
97
  const batchItemTimeoutMs = Math.max(0, dataPlatformBatchOptions?.requestTimeoutMs ?? 10000);
98
- const batchAllowedMethods = normalizeBatchAllowedMethods(dataPlatformBatchOptions?.allowedMethods);
99
98
  const envelopeHeader = options.dataPlatform?.envelopeHeader || DEFAULT_DATA_ENVELOPE_HEADER;
100
99
  const normalizedEnvelopeHeader = envelopeHeader.toLowerCase();
101
100
  const handle = async (request, context)=>{
@@ -6,12 +6,6 @@ function normalizeBatchPath(pathname) {
6
6
  if (!pathname.startsWith('/')) return `/${pathname}`;
7
7
  return pathname;
8
8
  }
9
- function normalizeBatchAllowedMethods(allowedMethods) {
10
- const source = Array.isArray(allowedMethods) && allowedMethods.length > 0 ? allowedMethods : [
11
- 'GET'
12
- ];
13
- return new Set(source.map((method)=>method.toUpperCase()));
14
- }
15
9
  function isBatchRequestPayload(value) {
16
10
  return isPlainObject(value) && 2 === value.protocolVersion && 'string' == typeof value.batchId && 'number' == typeof value.sentAt && Array.isArray(value.items);
17
11
  }
@@ -109,4 +103,4 @@ async function mapWithConcurrency(items, concurrency, mapper) {
109
103
  return output;
110
104
  }
111
105
  export { toHeaderRecord } from "../../headers.mjs";
112
- export { BatchItemTimeoutError, createBatchValidationResponse, isBatchRequestPayload, mapWithConcurrency, normalizeBatchAllowedMethods, normalizeBatchPath, prepareJsonRequestBody, promiseWithTimeout, toBatchItemError };
106
+ export { BatchItemTimeoutError, createBatchValidationResponse, isBatchRequestPayload, mapWithConcurrency, normalizeBatchPath, prepareJsonRequestBody, promiseWithTimeout, toBatchItemError };
@@ -42,6 +42,10 @@ function resolveClassifiedEffectBffModuleHandler(facts, options = {}) {
42
42
  if (facts.unsupportedShape) {
43
43
  if (rejectUnsupportedEffectModuleShape(options, facts.unsupportedShape)) return null;
44
44
  }
45
+ if (void 0 !== facts.module.rpc && !facts.createHandlerValidatorAware) {
46
+ rejectUnsupportedEffectModuleShape(options, 'unbranded RPC runtime');
47
+ return null;
48
+ }
45
49
  if ('function' == typeof facts.createHandler) {
46
50
  if (facts.createHandlerValidatorAware) {
47
51
  const factory = facts.createHandler;
@@ -54,7 +54,7 @@ const createEffectOperationContext = ({ request, path, method })=>{
54
54
  } : {},
55
55
  routePath: servicePath,
56
56
  method: (method || request.method || 'GET').toUpperCase(),
57
- source: 'effect-adapter',
57
+ source: 'effect-server',
58
58
  ...path && path !== servicePath ? {
59
59
  attributes: {
60
60
  mountedPath: path
@@ -7,8 +7,6 @@ function getRpcSerializationLayer(serialization) {
7
7
  return RpcSerialization.layerJsonRpc();
8
8
  case 'ndJsonRpc':
9
9
  return RpcSerialization.layerNdJsonRpc();
10
- case 'msgPack':
11
- return RpcSerialization.layerMsgPack;
12
10
  default:
13
11
  return RpcSerialization.layerJsonRpc();
14
12
  }
@@ -1,4 +1,4 @@
1
- import { BFF_ENVELOPE_HEADER, BFF_OPERATION_CONTEXT_DETAIL_HEADER, BFF_OPERATION_CONTEXT_HEADER, resolveCrossProjectRequestObservation } from "@modern-js/server-runtime-extensions/bff-policy";
1
+ import { BFF_ENVELOPE_HEADER, BFF_OPERATION_CONTEXT_DETAIL_HEADER, BFF_OPERATION_CONTEXT_HEADER, digestOperationContract, resolveCrossProjectRequestObservation } from "@modern-js/server-runtime-extensions/bff-policy";
2
2
  import { HttpClient, HttpClientRequest } from "effect/unstable/http";
3
3
  import { HttpApi } from "effect/unstable/httpapi";
4
4
  import * as __rspack_external_effect_Effect_41060786 from "effect/Effect";
@@ -30,14 +30,11 @@ function withCrossProjectPolicy(api, options, client) {
30
30
  const contract = observed && contracts[`${observed.method}:${observed.routePath}`];
31
31
  if (void 0 === contract) return __rspack_external_effect_Effect_41060786.die(new Error('[BFF][Effect] Request has no matching shared operation contract.'));
32
32
  return __rspack_external_effect_Effect_41060786.gen(function*() {
33
- const input = encodeJson({
34
- httpMethod: contract.method,
35
- name: contract.name,
36
- requestId: options.requestId,
37
- routePath: contract.routePath
38
- });
39
- const digest = yield* __rspack_external_effect_Effect_41060786.promise(()=>globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(input)));
40
- const schemaHash = Array.from(new Uint8Array(digest), (value)=>value.toString(16).padStart(2, '0')).join('');
33
+ const schemaHash = yield* __rspack_external_effect_Effect_41060786.promise(()=>digestOperationContract({
34
+ httpMethod: contract.method,
35
+ name: contract.name,
36
+ routePath: contract.routePath
37
+ }, options.requestId));
41
38
  return request.pipe(HttpClientRequest.setHeader(BFF_ENVELOPE_HEADER, encodeJson({
42
39
  requestId: options.requestId
43
40
  })), HttpClientRequest.setHeader(BFF_OPERATION_CONTEXT_HEADER, contract.operationId), HttpClientRequest.setHeader(BFF_OPERATION_CONTEXT_DETAIL_HEADER, encodeJson({
@@ -29,7 +29,7 @@ const MicroVerticalOperationSourceSchema = Schema.Literals([
29
29
  'client',
30
30
  'server',
31
31
  'generated-client',
32
- 'effect-adapter',
32
+ 'effect-server',
33
33
  'data-platform',
34
34
  'unknown'
35
35
  ]);
@@ -0,0 +1,11 @@
1
+ import "node:module";
2
+ function normalizeBatchLimits(options = {}) {
3
+ return {
4
+ maxBatchSize: Math.max(1, options.maxBatchSize ?? 16),
5
+ maxBatchBytes: Math.max(1024, options.maxBatchBytes ?? 65536),
6
+ allowedMethods: new Set((void 0 !== options.allowedMethods && options.allowedMethods.length > 0 ? options.allowedMethods : [
7
+ 'GET'
8
+ ]).map((method)=>method.toUpperCase()))
9
+ };
10
+ }
11
+ export { normalizeBatchLimits };
@@ -1,6 +1,7 @@
1
1
  import "node:module";
2
2
  import { stableStringify } from "../codec.mjs";
3
3
  import { DEFAULT_DATA_BATCH_HEADER } from "../types.mjs";
4
+ import { normalizeBatchLimits } from "./options.mjs";
4
5
  import { decodeBatchBody, isNullBodyStatus } from "./protocol.mjs";
5
6
  import { createBatchId, measureTextBytes, normalizeMethod, shouldBatchRequest, toHeaderRecord, toRequestBody } from "./request.mjs";
6
7
  import { isBatchResponsePayload, parseResponseLikeCreateRequest } from "./response.mjs";
@@ -79,7 +80,6 @@ class BatchBucketRegistry {
79
80
  if (existing) return existing;
80
81
  const next = {
81
82
  items: [],
82
- bytes: 0,
83
83
  batchId: createBatchId(),
84
84
  sentAt: Date.now(),
85
85
  timer: null,
@@ -98,12 +98,8 @@ class BatchBucketRegistry {
98
98
  }
99
99
  function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
100
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);
101
+ const { maxBatchSize, maxBatchBytes, allowedMethods } = normalizeBatchLimits(options);
103
102
  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
103
  const onEvent = options.onEvent;
108
104
  const buckets = bucketRegistry ?? new BatchBucketRegistry();
109
105
  const pendingByKey = new Map();
@@ -136,7 +132,6 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
136
132
  const index = bucket.items.indexOf(request);
137
133
  if (-1 === index) return;
138
134
  bucket.items.splice(index, 1);
139
- bucket.bytes = measurePayload(bucket, bucket.items);
140
135
  if (0 === bucket.items.length && bucket.timer) {
141
136
  clearTimeout(bucket.timer);
142
137
  bucket.timer = null;
@@ -178,7 +173,11 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
178
173
  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
174
  return runSingle(request);
180
175
  };
181
- const settleAmbiguousBatchRequests = (items, reason)=>settleRequests(items, (request)=>runAfterAmbiguousBatch(request, reason));
176
+ const finishFlush = (bucketKey, bucket)=>{
177
+ bucket.flushing = false;
178
+ if (bucket.items.length > 0 && !bucket.timer) flushBucket(bucketKey);
179
+ buckets.releaseIfIdle(bucketKey, bucket);
180
+ };
182
181
  const flushBucket = async (bucketKey)=>{
183
182
  const bucket = buckets.get(bucketKey);
184
183
  if (!bucket || bucket.flushing) return;
@@ -195,7 +194,6 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
195
194
  sentAt: bucket.sentAt
196
195
  };
197
196
  bucket.items = [];
198
- bucket.bytes = 0;
199
197
  rotatePayloadIdentity(bucket);
200
198
  const firstItem = items[0];
201
199
  if (!firstItem) {
@@ -211,14 +209,25 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
211
209
  size: items.length,
212
210
  reason: disabledEndpoints.has(endpoint) ? 'batch-disabled' : void 0
213
211
  });
214
- await settleRequests(items, runSingle);
215
- bucket.flushing = false;
216
- if (bucket.items.length > 0 && !bucket.timer) flushBucket(bucketKey);
217
- buckets.releaseIfIdle(bucketKey, bucket);
212
+ try {
213
+ await settleRequests(items, runSingle);
214
+ } finally{
215
+ finishFlush(bucketKey, bucket);
216
+ }
218
217
  return;
219
218
  }
220
219
  const payload = createPayload(payloadIdentity, items);
221
220
  const batchId = payload.batchId;
221
+ const settleAmbiguousBatchRequests = (reason)=>{
222
+ emitDataBatchTransportEvent(onEvent, {
223
+ type: 'fallback',
224
+ endpoint,
225
+ batchId,
226
+ size: items.length,
227
+ reason
228
+ });
229
+ return settleRequests(items, (request)=>runAfterAmbiguousBatch(request, reason));
230
+ };
222
231
  emitDataBatchTransportEvent(onEvent, {
223
232
  type: 'flush',
224
233
  endpoint,
@@ -269,42 +278,13 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
269
278
  batchId,
270
279
  reason: `batch-endpoint-unavailable-${String(response.status)}`
271
280
  });
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
+ await settleRequests(items, (request)=>runAfterAmbiguousBatch(request, `batch-response-${String(response.status)}`));
282
+ } else await settleAmbiguousBatchRequests(`batch-response-${String(response.status)}`);
281
283
  return;
282
284
  }
283
285
  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
- }
286
+ if (!isBatchResponsePayload(result)) return void await settleAmbiguousBatchRequests('invalid-batch-response');
287
+ if (result.batchId !== batchId) return void await settleAmbiguousBatchRequests('mismatched-batch-id');
308
288
  const itemMap = new Map();
309
289
  for (const item of result.items)itemMap.set(item.id, item);
310
290
  await settleRequests(items, async (request)=>{
@@ -328,19 +308,10 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
328
308
  });
329
309
  return;
330
310
  }
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');
311
+ await settleAmbiguousBatchRequests('batch-transport-error');
339
312
  } finally{
340
313
  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);
314
+ finishFlush(bucketKey, bucket);
344
315
  }
345
316
  };
346
317
  const pendingReadPromises = new Map();
@@ -364,7 +335,20 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
364
335
  const sourceSignal = initDefinesSignal ? initSignal ?? void 0 : sourceRequest?.signal;
365
336
  const requestUrl = toAbsoluteUrl(input);
366
337
  const endpoint = normalizeBatchEndpoint(requestUrl, options.endpoint).toString();
338
+ const itemHeaders = {
339
+ ...headers
340
+ };
341
+ delete itemHeaders.authorization;
342
+ delete itemHeaders.cookie;
367
343
  return {
344
+ itemHeaders,
345
+ itemPath: `${requestUrl.pathname}${requestUrl.search}`,
346
+ bucketKey: stableStringify({
347
+ endpoint,
348
+ authorization: headers.authorization ?? null,
349
+ cookie: headers.cookie ?? null,
350
+ credentials
351
+ }),
368
352
  requestUrl,
369
353
  endpoint,
370
354
  method,
@@ -397,22 +381,11 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
397
381
  batchEndpoint: request.endpoint,
398
382
  requestUrl: request.requestUrl
399
383
  })) 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
384
  return stableStringify({
412
- bucketKey,
413
- path: `${request.requestUrl.pathname}${request.requestUrl.search}`,
385
+ bucketKey: request.bucketKey,
386
+ path: request.itemPath,
414
387
  method: request.method,
415
- headers: itemHeaders,
388
+ headers: request.itemHeaders,
416
389
  body: null
417
390
  });
418
391
  };
@@ -430,26 +403,19 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
430
403
  const itemId = `${Date.now().toString(36)}_${nextItemId.toString(36)}_${Math.random().toString(16).slice(2, 8)}`;
431
404
  nextItemId += 1;
432
405
  const itemHeaders = {
433
- ...request.headers
406
+ ...request.itemHeaders
434
407
  };
435
- delete itemHeaders.authorization;
436
- delete itemHeaders.cookie;
437
408
  if (preparedBody.body && preparedBody.inferredContentType && !itemHeaders['content-type']) itemHeaders['content-type'] = preparedBody.inferredContentType;
438
409
  const item = {
439
410
  id: itemId,
440
- path: `${request.requestUrl.pathname}${request.requestUrl.search}`,
411
+ path: request.itemPath,
441
412
  method: request.method,
442
413
  headers: itemHeaders,
443
414
  ...preparedBody.body ? {
444
415
  body: preparedBody.body
445
416
  } : {}
446
417
  };
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
- });
418
+ const { bucketKey } = request;
453
419
  const key = stableStringify({
454
420
  bucketKey,
455
421
  path: item.path,
@@ -543,13 +509,12 @@ function createBatchTransportQueue({ options, baseFetch, bucketRegistry }) {
543
509
  }
544
510
  }
545
511
  bucket.items.push(queued);
546
- bucket.bytes = measurePayload(bucket, bucket.items);
547
512
  emitDataBatchTransportEvent(onEvent, {
548
513
  type: 'enqueue',
549
514
  endpoint: request.endpoint,
550
515
  size: bucket.items.length
551
516
  });
552
- if (bucket.items.length >= maxBatchSize || bucket.bytes >= maxBatchBytes) {
517
+ if (bucket.items.length >= maxBatchSize || measurePayload(bucket, bucket.items) >= maxBatchBytes) {
553
518
  flushBucket(bucketKey);
554
519
  return queued.promise;
555
520
  }
@@ -13,7 +13,10 @@ function createLocalValidatorAwareHandlerFactoryRegistry() {
13
13
  }
14
14
  function loadNodeValidatorAwareHandlerFactoryRegistry() {
15
15
  const moduleUrl = import.meta.url;
16
- if ('string' != typeof moduleUrl || !moduleUrl.startsWith('file:')) return createLocalValidatorAwareHandlerFactoryRegistry();
16
+ if ('string' != typeof moduleUrl || !moduleUrl.startsWith('file:')) {
17
+ if ("u" > typeof __modernjs_backend_private_capability__) return __modernjs_backend_private_capability__;
18
+ return createLocalValidatorAwareHandlerFactoryRegistry();
19
+ }
17
20
  const moduleBuiltin = process.getBuiltinModule('node:module');
18
21
  try {
19
22
  return moduleBuiltin.createRequire(moduleUrl)("#effect-entry-shape-registry");