@bleedingdev/modern-js-bff-effect 3.9.0-ultramodern.10 → 3.9.0-ultramodern.12
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/handler/batch-handler.js +2 -3
- package/dist/cjs/effect/handler/batch.js +0 -9
- package/dist/cjs/effect-client/cross-project.js +5 -8
- package/dist/esm/data-platform/batch/options.mjs +10 -0
- package/dist/esm/data-platform/batch/queue.mjs +48 -83
- package/dist/esm/effect/handler/batch-handler.mjs +3 -4
- package/dist/esm/effect/handler/batch.mjs +1 -7
- package/dist/esm/effect-client/cross-project.mjs +6 -9
- 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/handler/batch-handler.mjs +3 -4
- package/dist/esm-node/effect/handler/batch.mjs +1 -7
- package/dist/esm-node/effect-client/cross-project.mjs +6 -9
- 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/handler/batch.d.ts +0 -1
- package/package.json +5 -5
- package/src/data-platform/batch/options.ts +20 -0
- package/src/data-platform/batch/queue.ts +61 -89
- package/src/effect/handler/batch-handler.ts +6 -12
- package/src/effect/handler/batch.ts +0 -10
- package/src/effect-client/cross-project.ts +9 -14
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
DataTransportRequestInfo,
|
|
9
9
|
} from '../types';
|
|
10
10
|
import { DEFAULT_DATA_BATCH_HEADER } from '../types';
|
|
11
|
+
import { normalizeBatchLimits } from './options';
|
|
11
12
|
import { decodeBatchBody, isNullBodyStatus } from './protocol';
|
|
12
13
|
import {
|
|
13
14
|
createBatchId,
|
|
@@ -37,6 +38,9 @@ type NormalizedBatchTransportRequest = {
|
|
|
37
38
|
endpoint: string;
|
|
38
39
|
method: string;
|
|
39
40
|
headers: Record<string, string>;
|
|
41
|
+
itemHeaders: Record<string, string>;
|
|
42
|
+
bucketKey: string;
|
|
43
|
+
itemPath: string;
|
|
40
44
|
credentials: RequestCredentials;
|
|
41
45
|
body: BodyInit | null;
|
|
42
46
|
hasOpaqueSourceBody: boolean;
|
|
@@ -76,7 +80,6 @@ type QueuedBatchRequest = {
|
|
|
76
80
|
|
|
77
81
|
type BatchBucket = {
|
|
78
82
|
items: QueuedBatchRequest[];
|
|
79
|
-
bytes: number;
|
|
80
83
|
batchId: string;
|
|
81
84
|
sentAt: number;
|
|
82
85
|
timer: ReturnType<typeof setTimeout> | null;
|
|
@@ -185,7 +188,6 @@ export class BatchBucketRegistry {
|
|
|
185
188
|
|
|
186
189
|
const next: BatchBucket = {
|
|
187
190
|
items: [],
|
|
188
|
-
bytes: 0,
|
|
189
191
|
batchId: createBatchId(),
|
|
190
192
|
sentAt: Date.now(),
|
|
191
193
|
timer: null,
|
|
@@ -215,15 +217,9 @@ export function createBatchTransportQueue({
|
|
|
215
217
|
bucketRegistry,
|
|
216
218
|
}: CreateBatchTransportQueueOptions) {
|
|
217
219
|
const flushIntervalMs = Math.max(0, options.flushIntervalMs ?? 8);
|
|
218
|
-
const maxBatchSize
|
|
219
|
-
|
|
220
|
+
const { maxBatchSize, maxBatchBytes, allowedMethods } =
|
|
221
|
+
normalizeBatchLimits(options);
|
|
220
222
|
const requestTimeoutMs = options.requestTimeoutMs;
|
|
221
|
-
const allowedMethods = new Set(
|
|
222
|
-
(options.allowedMethods && options.allowedMethods.length > 0
|
|
223
|
-
? options.allowedMethods
|
|
224
|
-
: ['GET']
|
|
225
|
-
).map(method => method.toUpperCase()),
|
|
226
|
-
);
|
|
227
223
|
const onEvent = options.onEvent;
|
|
228
224
|
|
|
229
225
|
const buckets = bucketRegistry ?? new BatchBucketRegistry();
|
|
@@ -284,7 +280,6 @@ export function createBatchTransportQueue({
|
|
|
284
280
|
return;
|
|
285
281
|
}
|
|
286
282
|
bucket.items.splice(index, 1);
|
|
287
|
-
bucket.bytes = measurePayload(bucket, bucket.items);
|
|
288
283
|
if (bucket.items.length === 0 && bucket.timer) {
|
|
289
284
|
clearTimeout(bucket.timer);
|
|
290
285
|
bucket.timer = null;
|
|
@@ -347,11 +342,11 @@ export function createBatchTransportQueue({
|
|
|
347
342
|
return runSingle(request);
|
|
348
343
|
};
|
|
349
344
|
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
345
|
+
const finishFlush = (bucketKey: string, bucket: BatchBucket) => {
|
|
346
|
+
bucket.flushing = false;
|
|
347
|
+
if (bucket.items.length > 0 && !bucket.timer) void flushBucket(bucketKey);
|
|
348
|
+
buckets.releaseIfIdle(bucketKey, bucket);
|
|
349
|
+
};
|
|
355
350
|
|
|
356
351
|
const flushBucket = async (bucketKey: string) => {
|
|
357
352
|
const bucket = buckets.get(bucketKey);
|
|
@@ -379,7 +374,6 @@ export function createBatchTransportQueue({
|
|
|
379
374
|
sentAt: bucket.sentAt,
|
|
380
375
|
};
|
|
381
376
|
bucket.items = [];
|
|
382
|
-
bucket.bytes = 0;
|
|
383
377
|
rotatePayloadIdentity(bucket);
|
|
384
378
|
const firstItem = items[0];
|
|
385
379
|
if (!firstItem) {
|
|
@@ -396,17 +390,28 @@ export function createBatchTransportQueue({
|
|
|
396
390
|
size: items.length,
|
|
397
391
|
reason: disabledEndpoints.has(endpoint) ? 'batch-disabled' : undefined,
|
|
398
392
|
});
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
393
|
+
try {
|
|
394
|
+
await settleRequests(items, runSingle);
|
|
395
|
+
} finally {
|
|
396
|
+
finishFlush(bucketKey, bucket);
|
|
403
397
|
}
|
|
404
|
-
buckets.releaseIfIdle(bucketKey, bucket);
|
|
405
398
|
return;
|
|
406
399
|
}
|
|
407
400
|
|
|
408
401
|
const payload = createPayload(payloadIdentity, items);
|
|
409
402
|
const batchId = payload.batchId;
|
|
403
|
+
const settleAmbiguousBatchRequests = (reason: string) => {
|
|
404
|
+
emitDataBatchTransportEvent(onEvent, {
|
|
405
|
+
type: 'fallback',
|
|
406
|
+
endpoint,
|
|
407
|
+
batchId,
|
|
408
|
+
size: items.length,
|
|
409
|
+
reason,
|
|
410
|
+
});
|
|
411
|
+
return settleRequests(items, request =>
|
|
412
|
+
runAfterAmbiguousBatch(request, reason),
|
|
413
|
+
);
|
|
414
|
+
};
|
|
410
415
|
|
|
411
416
|
emitDataBatchTransportEvent(onEvent, {
|
|
412
417
|
type: 'flush',
|
|
@@ -473,20 +478,17 @@ export function createBatchTransportQueue({
|
|
|
473
478
|
batchId,
|
|
474
479
|
reason: `batch-endpoint-unavailable-${String(response.status)}`,
|
|
475
480
|
});
|
|
481
|
+
await settleRequests(items, request =>
|
|
482
|
+
runAfterAmbiguousBatch(
|
|
483
|
+
request,
|
|
484
|
+
`batch-response-${String(response.status)}`,
|
|
485
|
+
),
|
|
486
|
+
);
|
|
476
487
|
} else {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
batchId,
|
|
481
|
-
size: items.length,
|
|
482
|
-
reason: `batch-response-${String(response.status)}`,
|
|
483
|
-
});
|
|
488
|
+
await settleAmbiguousBatchRequests(
|
|
489
|
+
`batch-response-${String(response.status)}`,
|
|
490
|
+
);
|
|
484
491
|
}
|
|
485
|
-
await settleAmbiguousBatchRequests(
|
|
486
|
-
items,
|
|
487
|
-
`batch-response-${String(response.status)}`,
|
|
488
|
-
);
|
|
489
|
-
bucket.flushing = false;
|
|
490
492
|
return;
|
|
491
493
|
}
|
|
492
494
|
|
|
@@ -494,28 +496,12 @@ export function createBatchTransportQueue({
|
|
|
494
496
|
response.json(),
|
|
495
497
|
);
|
|
496
498
|
if (!isBatchResponsePayload(result)) {
|
|
497
|
-
|
|
498
|
-
type: 'fallback',
|
|
499
|
-
endpoint,
|
|
500
|
-
batchId,
|
|
501
|
-
size: items.length,
|
|
502
|
-
reason: 'invalid-batch-response',
|
|
503
|
-
});
|
|
504
|
-
await settleAmbiguousBatchRequests(items, 'invalid-batch-response');
|
|
505
|
-
bucket.flushing = false;
|
|
499
|
+
await settleAmbiguousBatchRequests('invalid-batch-response');
|
|
506
500
|
return;
|
|
507
501
|
}
|
|
508
502
|
|
|
509
503
|
if (result.batchId !== batchId) {
|
|
510
|
-
|
|
511
|
-
type: 'fallback',
|
|
512
|
-
endpoint,
|
|
513
|
-
batchId,
|
|
514
|
-
size: items.length,
|
|
515
|
-
reason: 'mismatched-batch-id',
|
|
516
|
-
});
|
|
517
|
-
await settleAmbiguousBatchRequests(items, 'mismatched-batch-id');
|
|
518
|
-
bucket.flushing = false;
|
|
504
|
+
await settleAmbiguousBatchRequests('mismatched-batch-id');
|
|
519
505
|
return;
|
|
520
506
|
}
|
|
521
507
|
|
|
@@ -555,23 +541,12 @@ export function createBatchTransportQueue({
|
|
|
555
541
|
}
|
|
556
542
|
return;
|
|
557
543
|
}
|
|
558
|
-
|
|
559
|
-
type: 'fallback',
|
|
560
|
-
endpoint,
|
|
561
|
-
batchId,
|
|
562
|
-
size: items.length,
|
|
563
|
-
reason: 'batch-transport-error',
|
|
564
|
-
});
|
|
565
|
-
await settleAmbiguousBatchRequests(items, 'batch-transport-error');
|
|
544
|
+
await settleAmbiguousBatchRequests('batch-transport-error');
|
|
566
545
|
} finally {
|
|
567
546
|
for (const item of items) {
|
|
568
547
|
item.abortBatchIfUnused = undefined;
|
|
569
548
|
}
|
|
570
|
-
bucket
|
|
571
|
-
if (bucket.items.length > 0 && !bucket.timer) {
|
|
572
|
-
void flushBucket(bucketKey);
|
|
573
|
-
}
|
|
574
|
-
buckets.releaseIfIdle(bucketKey, bucket);
|
|
549
|
+
finishFlush(bucketKey, bucket);
|
|
575
550
|
}
|
|
576
551
|
};
|
|
577
552
|
|
|
@@ -617,7 +592,18 @@ export function createBatchTransportQueue({
|
|
|
617
592
|
options.endpoint,
|
|
618
593
|
).toString();
|
|
619
594
|
|
|
595
|
+
const itemHeaders = { ...headers };
|
|
596
|
+
delete itemHeaders.authorization;
|
|
597
|
+
delete itemHeaders.cookie;
|
|
620
598
|
return {
|
|
599
|
+
itemHeaders,
|
|
600
|
+
itemPath: `${requestUrl.pathname}${requestUrl.search}`,
|
|
601
|
+
bucketKey: stableStringify({
|
|
602
|
+
endpoint,
|
|
603
|
+
authorization: headers.authorization ?? null,
|
|
604
|
+
cookie: headers.cookie ?? null,
|
|
605
|
+
credentials,
|
|
606
|
+
}),
|
|
621
607
|
requestUrl,
|
|
622
608
|
endpoint,
|
|
623
609
|
method,
|
|
@@ -660,20 +646,11 @@ export function createBatchTransportQueue({
|
|
|
660
646
|
return undefined;
|
|
661
647
|
}
|
|
662
648
|
|
|
663
|
-
const itemHeaders = { ...request.headers };
|
|
664
|
-
delete itemHeaders.authorization;
|
|
665
|
-
delete itemHeaders.cookie;
|
|
666
|
-
const bucketKey = stableStringify({
|
|
667
|
-
endpoint: request.endpoint,
|
|
668
|
-
authorization: request.headers.authorization ?? null,
|
|
669
|
-
cookie: request.headers.cookie ?? null,
|
|
670
|
-
credentials: request.credentials,
|
|
671
|
-
});
|
|
672
649
|
return stableStringify({
|
|
673
|
-
bucketKey,
|
|
674
|
-
path:
|
|
650
|
+
bucketKey: request.bucketKey,
|
|
651
|
+
path: request.itemPath,
|
|
675
652
|
method: request.method,
|
|
676
|
-
headers: itemHeaders,
|
|
653
|
+
headers: request.itemHeaders,
|
|
677
654
|
body: null,
|
|
678
655
|
});
|
|
679
656
|
};
|
|
@@ -704,9 +681,7 @@ export function createBatchTransportQueue({
|
|
|
704
681
|
.slice(2, 8)}`;
|
|
705
682
|
nextItemId += 1;
|
|
706
683
|
|
|
707
|
-
const itemHeaders = { ...request.
|
|
708
|
-
delete itemHeaders.authorization;
|
|
709
|
-
delete itemHeaders.cookie;
|
|
684
|
+
const itemHeaders = { ...request.itemHeaders };
|
|
710
685
|
if (
|
|
711
686
|
preparedBody.body &&
|
|
712
687
|
preparedBody.inferredContentType &&
|
|
@@ -716,18 +691,13 @@ export function createBatchTransportQueue({
|
|
|
716
691
|
}
|
|
717
692
|
const item: DataBatchRequestItem = {
|
|
718
693
|
id: itemId,
|
|
719
|
-
path:
|
|
694
|
+
path: request.itemPath,
|
|
720
695
|
method: request.method,
|
|
721
696
|
headers: itemHeaders,
|
|
722
697
|
...(preparedBody.body ? { body: preparedBody.body } : {}),
|
|
723
698
|
};
|
|
724
699
|
|
|
725
|
-
const bucketKey =
|
|
726
|
-
endpoint: request.endpoint,
|
|
727
|
-
authorization: request.headers.authorization ?? null,
|
|
728
|
-
cookie: request.headers.cookie ?? null,
|
|
729
|
-
credentials: request.credentials,
|
|
730
|
-
});
|
|
700
|
+
const { bucketKey } = request;
|
|
731
701
|
|
|
732
702
|
const key = stableStringify({
|
|
733
703
|
bucketKey,
|
|
@@ -824,14 +794,16 @@ export function createBatchTransportQueue({
|
|
|
824
794
|
}
|
|
825
795
|
|
|
826
796
|
bucket.items.push(queued);
|
|
827
|
-
bucket.bytes = measurePayload(bucket, bucket.items);
|
|
828
797
|
emitDataBatchTransportEvent(onEvent, {
|
|
829
798
|
type: 'enqueue',
|
|
830
799
|
endpoint: request.endpoint,
|
|
831
800
|
size: bucket.items.length,
|
|
832
801
|
});
|
|
833
802
|
|
|
834
|
-
if (
|
|
803
|
+
if (
|
|
804
|
+
bucket.items.length >= maxBatchSize ||
|
|
805
|
+
measurePayload(bucket, bucket.items) >= maxBatchBytes
|
|
806
|
+
) {
|
|
835
807
|
void flushBucket(bucketKey);
|
|
836
808
|
return queued.promise;
|
|
837
809
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
isPlainObject,
|
|
9
9
|
normalizeMethod as normalizeItemMethod,
|
|
10
10
|
} from '../../data-platform';
|
|
11
|
+
import { normalizeBatchLimits } from '../../data-platform/batch/options';
|
|
11
12
|
import {
|
|
12
13
|
decodeBatchBody,
|
|
13
14
|
isBatchBody,
|
|
@@ -18,7 +19,6 @@ import {
|
|
|
18
19
|
createBatchValidationResponse,
|
|
19
20
|
isBatchRequestPayload,
|
|
20
21
|
mapWithConcurrency,
|
|
21
|
-
normalizeBatchAllowedMethods,
|
|
22
22
|
normalizeBatchPath,
|
|
23
23
|
promiseWithTimeout,
|
|
24
24
|
toBatchItemError,
|
|
@@ -129,14 +129,11 @@ export function createDataPlatformBatchRequestHandler<TContext>(options: {
|
|
|
129
129
|
const dataPlatformBatchOptions = options.dataPlatform?.batch;
|
|
130
130
|
const batchEnabled = dataPlatformBatchOptions?.enabled !== false;
|
|
131
131
|
const batchPath = normalizeBatchPath(dataPlatformBatchOptions?.endpoint);
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
1024,
|
|
138
|
-
dataPlatformBatchOptions?.maxBatchBytes ?? 64 * 1024,
|
|
139
|
-
);
|
|
132
|
+
const {
|
|
133
|
+
maxBatchSize: batchMaxSize,
|
|
134
|
+
maxBatchBytes: batchMaxBytes,
|
|
135
|
+
allowedMethods: batchAllowedMethods,
|
|
136
|
+
} = normalizeBatchLimits(dataPlatformBatchOptions);
|
|
140
137
|
const batchConcurrency = Math.max(
|
|
141
138
|
1,
|
|
142
139
|
dataPlatformBatchOptions?.maxConcurrency ?? 4,
|
|
@@ -145,9 +142,6 @@ export function createDataPlatformBatchRequestHandler<TContext>(options: {
|
|
|
145
142
|
0,
|
|
146
143
|
dataPlatformBatchOptions?.requestTimeoutMs ?? 10_000,
|
|
147
144
|
);
|
|
148
|
-
const batchAllowedMethods = normalizeBatchAllowedMethods(
|
|
149
|
-
dataPlatformBatchOptions?.allowedMethods,
|
|
150
|
-
);
|
|
151
145
|
const envelopeHeader =
|
|
152
146
|
options.dataPlatform?.envelopeHeader || DEFAULT_DATA_ENVELOPE_HEADER;
|
|
153
147
|
const normalizedEnvelopeHeader = envelopeHeader.toLowerCase();
|
|
@@ -24,16 +24,6 @@ export function normalizeBatchPath(pathname: string | undefined) {
|
|
|
24
24
|
return pathname as `/${string}`;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function normalizeBatchAllowedMethods(
|
|
28
|
-
allowedMethods: string[] | undefined,
|
|
29
|
-
) {
|
|
30
|
-
const source =
|
|
31
|
-
Array.isArray(allowedMethods) && allowedMethods.length > 0
|
|
32
|
-
? allowedMethods
|
|
33
|
-
: ['GET'];
|
|
34
|
-
return new Set(source.map(method => method.toUpperCase()));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
27
|
type ParsedBatchRequestPayload = Omit<DataBatchRequestPayload, 'items'> & {
|
|
38
28
|
items: unknown[];
|
|
39
29
|
};
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
BFF_ENVELOPE_HEADER,
|
|
3
3
|
BFF_OPERATION_CONTEXT_DETAIL_HEADER,
|
|
4
4
|
BFF_OPERATION_CONTEXT_HEADER,
|
|
5
|
+
digestOperationContract,
|
|
5
6
|
resolveCrossProjectRequestObservation,
|
|
6
7
|
} from '@modern-js/server-runtime-extensions/bff-policy';
|
|
7
8
|
import * as Effect from 'effect/Effect';
|
|
@@ -66,22 +67,16 @@ export function withCrossProjectPolicy<
|
|
|
66
67
|
);
|
|
67
68
|
}
|
|
68
69
|
return Effect.gen(function* () {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
globalThis.crypto.subtle.digest(
|
|
78
|
-
'SHA-256',
|
|
79
|
-
new TextEncoder().encode(input),
|
|
70
|
+
const schemaHash = yield* Effect.promise(() =>
|
|
71
|
+
digestOperationContract(
|
|
72
|
+
{
|
|
73
|
+
httpMethod: contract.method,
|
|
74
|
+
name: contract.name,
|
|
75
|
+
routePath: contract.routePath,
|
|
76
|
+
},
|
|
77
|
+
options.requestId,
|
|
80
78
|
),
|
|
81
79
|
);
|
|
82
|
-
const schemaHash = Array.from(new Uint8Array(digest), value =>
|
|
83
|
-
value.toString(16).padStart(2, '0'),
|
|
84
|
-
).join('');
|
|
85
80
|
return request.pipe(
|
|
86
81
|
HttpClientRequest.setHeader(
|
|
87
82
|
BFF_ENVELOPE_HEADER,
|