@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.
- package/dist/cjs/data-platform/batch/options.js +48 -0
- package/dist/cjs/data-platform/batch/queue.js +48 -83
- package/dist/cjs/effect/entry-shape.js +4 -1
- package/dist/cjs/effect/handler/batch-handler.js +2 -3
- package/dist/cjs/effect/handler/batch.js +0 -9
- package/dist/cjs/effect/module.js +4 -0
- package/dist/cjs/effect/operation-context.js +1 -1
- package/dist/cjs/effect/rpcSerialization.js +0 -2
- package/dist/cjs/effect-client/cross-project.js +5 -8
- package/dist/cjs/microvertical-api.js +1 -1
- package/dist/esm/data-platform/batch/options.mjs +10 -0
- package/dist/esm/data-platform/batch/queue.mjs +48 -83
- package/dist/esm/effect/entry-shape-registry.mjs +2 -2
- package/dist/esm/effect/handler/batch-handler.mjs +3 -4
- package/dist/esm/effect/handler/batch.mjs +1 -7
- package/dist/esm/effect/module.mjs +4 -0
- package/dist/esm/effect/operation-context.mjs +1 -1
- package/dist/esm/effect/rpcSerialization.mjs +0 -2
- package/dist/esm/effect-client/cross-project.mjs +6 -9
- package/dist/esm/microvertical-api.mjs +1 -1
- package/dist/esm-node/data-platform/batch/options.mjs +11 -0
- package/dist/esm-node/data-platform/batch/queue.mjs +48 -83
- package/dist/esm-node/effect/entry-shape.mjs +4 -1
- package/dist/esm-node/effect/handler/batch-handler.mjs +3 -4
- package/dist/esm-node/effect/handler/batch.mjs +1 -7
- package/dist/esm-node/effect/module.mjs +4 -0
- package/dist/esm-node/effect/operation-context.mjs +1 -1
- package/dist/esm-node/effect/rpcSerialization.mjs +0 -2
- package/dist/esm-node/effect-client/cross-project.mjs +6 -9
- package/dist/esm-node/microvertical-api.mjs +1 -1
- package/dist/types/data-platform/batch/options.d.ts +7 -0
- package/dist/types/data-platform/batch/queue.d.ts +0 -1
- package/dist/types/effect/entry-shape.d.ts +2 -0
- package/dist/types/effect/handler/batch.d.ts +0 -1
- package/dist/types/effect/handler/types.d.ts +1 -1
- package/dist/types/effect/module.d.ts +2 -1
- package/dist/types/effect-client/index.d.ts +3 -3
- package/dist/types/microvertical-api.d.ts +4 -4
- package/package.json +12 -12
- package/src/data-platform/batch/options.ts +20 -0
- package/src/data-platform/batch/queue.ts +61 -89
- package/src/effect/entry-shape.ts +12 -0
- package/src/effect/handler/batch-handler.ts +6 -12
- package/src/effect/handler/batch.ts +0 -10
- package/src/effect/handler/types.ts +1 -2
- package/src/effect/module.ts +7 -0
- package/src/effect/operation-context.ts +1 -1
- package/src/effect/rpcSerialization.ts +0 -2
- package/src/effect-client/cross-project.ts +9 -14
- package/src/effect-client/index.ts +4 -4
- 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 =
|
|
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
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
272
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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.
|
|
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:
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
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__("
|
|
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,
|
|
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
|
|
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,
|
|
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-
|
|
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
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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({
|
|
@@ -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 =
|
|
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
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
273
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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.
|
|
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:
|
|
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 =
|
|
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.
|
|
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:'))
|
|
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");
|