@bleedingdev/modern-js-bff-effect 3.9.0-ultramodern.2 → 3.9.0-ultramodern.21
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/README.md +20 -4
- package/dist/cjs/assembly.js +50 -0
- package/dist/cjs/{effect-client/guards.js → data-platform/batch/options.js} +13 -9
- package/dist/cjs/data-platform/batch/queue.js +48 -83
- package/dist/cjs/data-platform/trace.js +2 -2
- package/dist/cjs/effect/context.js +28 -4
- package/dist/cjs/effect/edge-dispatcher.js +12 -12
- package/dist/cjs/effect/edge.js +36 -3
- package/dist/cjs/effect/endpoint-contracts.js +3 -4
- package/dist/cjs/effect/entry-shape.js +40 -12
- package/dist/cjs/effect/generated-entry.js +57 -0
- package/dist/cjs/effect/handler/batch-handler.js +7 -7
- package/dist/cjs/effect/handler/batch.js +8 -10
- package/dist/cjs/effect/handler/definition.js +1 -21
- package/dist/cjs/effect/index.js +8 -8
- package/dist/cjs/effect/module.js +8 -4
- package/dist/cjs/effect/operation-context.js +10 -14
- package/dist/cjs/effect/rpcSerialization.js +0 -2
- package/dist/cjs/effect-client/cross-project.js +89 -0
- package/dist/cjs/effect-client/index.js +5 -3
- package/dist/cjs/microvertical-api.js +120 -0
- package/dist/esm/assembly.mjs +12 -0
- package/dist/esm/data-platform/batch/options.mjs +10 -0
- package/dist/esm/data-platform/batch/queue.mjs +48 -83
- package/dist/esm/data-platform/trace.mjs +1 -1
- package/dist/esm/effect/context.mjs +21 -6
- package/dist/esm/effect/edge-dispatcher.mjs +6 -6
- package/dist/esm/effect/edge.mjs +3 -0
- package/dist/esm/effect/endpoint-contracts.mjs +2 -3
- package/dist/esm/effect/entry-shape-registry.mjs +2 -2
- package/dist/esm/effect/entry-shape.mjs +29 -6
- package/dist/esm/effect/generated-entry.mjs +19 -0
- package/dist/esm/effect/handler/batch-handler.mjs +8 -8
- package/dist/esm/effect/handler/batch.mjs +9 -8
- package/dist/esm/effect/handler/definition.mjs +1 -21
- package/dist/esm/effect/index.mjs +1 -1
- package/dist/esm/effect/module.mjs +8 -4
- package/dist/esm/effect/operation-context.mjs +3 -4
- package/dist/esm/effect/rpcSerialization.mjs +0 -2
- package/dist/esm/effect-client/cross-project.mjs +51 -0
- package/dist/esm/effect-client/index.mjs +4 -2
- package/dist/esm/microvertical-api.mjs +67 -0
- package/dist/esm-node/assembly.mjs +13 -0
- 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/data-platform/trace.mjs +1 -1
- package/dist/esm-node/effect/context.mjs +21 -6
- package/dist/esm-node/effect/edge-dispatcher.mjs +6 -6
- package/dist/esm-node/effect/edge.mjs +3 -0
- package/dist/esm-node/effect/endpoint-contracts.mjs +2 -3
- package/dist/esm-node/effect/entry-shape.mjs +29 -6
- package/dist/esm-node/effect/generated-entry.mjs +20 -0
- package/dist/esm-node/effect/handler/batch-handler.mjs +8 -8
- package/dist/esm-node/effect/handler/batch.mjs +9 -8
- package/dist/esm-node/effect/handler/definition.mjs +1 -21
- package/dist/esm-node/effect/index.mjs +1 -1
- package/dist/esm-node/effect/module.mjs +8 -4
- package/dist/esm-node/effect/operation-context.mjs +3 -4
- package/dist/esm-node/effect/rpcSerialization.mjs +0 -2
- package/dist/esm-node/effect-client/cross-project.mjs +52 -0
- package/dist/esm-node/effect-client/index.mjs +4 -2
- package/dist/esm-node/microvertical-api.mjs +68 -0
- package/dist/types/assembly.d.ts +10 -0
- 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/context.d.ts +15 -2
- package/dist/types/effect/edge-dispatcher.d.ts +3 -3
- package/dist/types/effect/edge.d.ts +3 -0
- package/dist/types/effect/endpoint-contracts.d.ts +2 -20
- package/dist/types/effect/entry-shape.d.ts +16 -2
- package/dist/types/effect/generated-entry.d.ts +7 -0
- package/dist/types/effect/handler/batch.d.ts +7 -2
- package/dist/types/effect/handler/types.d.ts +2 -17
- package/dist/types/effect/handler.d.ts +1 -1
- package/dist/types/effect/module.d.ts +2 -1
- package/dist/types/effect/operation-context.d.ts +1 -2
- package/dist/types/effect-client/cross-project.d.ts +10 -0
- package/dist/types/effect-client/index.d.ts +7 -4
- package/dist/types/microvertical-api.d.ts +65 -0
- package/package.json +47 -42
- package/src/assembly.ts +43 -0
- package/src/data-platform/batch/options.ts +20 -0
- package/src/data-platform/batch/queue.ts +63 -92
- package/src/data-platform/trace.ts +1 -1
- package/src/effect/context.ts +64 -12
- package/src/effect/edge-dispatcher.ts +14 -11
- package/src/effect/edge.ts +6 -0
- package/src/effect/endpoint-contracts.ts +3 -24
- package/src/effect/entry-shape.ts +105 -10
- package/src/effect/generated-entry.ts +36 -0
- package/src/effect/handler/batch-handler.ts +27 -28
- package/src/effect/handler/batch.ts +9 -13
- package/src/effect/handler/definition.ts +0 -45
- package/src/effect/handler/http.ts +2 -2
- package/src/effect/handler/types.ts +3 -32
- package/src/effect/handler.ts +0 -1
- package/src/effect/index.ts +1 -1
- package/src/effect/module.ts +11 -4
- package/src/effect/operation-context.ts +2 -6
- package/src/effect/rpcSerialization.ts +0 -2
- package/src/effect-client/cross-project.ts +104 -0
- package/src/effect-client/index.ts +17 -7
- package/src/microvertical-api.ts +102 -0
- package/dist/cjs/effect/edge-context.js +0 -61
- package/dist/cjs/effect-client/batch.js +0 -73
- package/dist/cjs/effect-client/envelope.js +0 -145
- package/dist/cjs/effect-client/request-context.js +0 -64
- package/dist/cjs/effect-client/request.js +0 -66
- package/dist/cjs/effect-client/runtime.js +0 -100
- package/dist/cjs/effect-client/types.js +0 -18
- package/dist/esm/effect/edge-context.mjs +0 -14
- package/dist/esm/effect-client/batch.mjs +0 -35
- package/dist/esm/effect-client/envelope.mjs +0 -104
- package/dist/esm/effect-client/guards.mjs +0 -3
- package/dist/esm/effect-client/request-context.mjs +0 -23
- package/dist/esm/effect-client/request.mjs +0 -28
- package/dist/esm/effect-client/runtime.mjs +0 -62
- package/dist/esm/effect-client/types.mjs +0 -0
- package/dist/esm-node/effect/edge-context.mjs +0 -15
- package/dist/esm-node/effect-client/batch.mjs +0 -36
- package/dist/esm-node/effect-client/envelope.mjs +0 -105
- package/dist/esm-node/effect-client/guards.mjs +0 -4
- package/dist/esm-node/effect-client/request-context.mjs +0 -24
- package/dist/esm-node/effect-client/request.mjs +0 -29
- package/dist/esm-node/effect-client/runtime.mjs +0 -63
- package/dist/esm-node/effect-client/types.mjs +0 -1
- package/dist/types/effect/edge-context.d.ts +0 -5
- package/dist/types/effect-client/batch.d.ts +0 -2
- package/dist/types/effect-client/envelope.d.ts +0 -15
- package/dist/types/effect-client/guards.d.ts +0 -2
- package/dist/types/effect-client/request-context.d.ts +0 -3
- package/dist/types/effect-client/request.d.ts +0 -1
- package/dist/types/effect-client/runtime.d.ts +0 -5
- package/dist/types/effect-client/types.d.ts +0 -68
- package/src/effect/edge-context.ts +0 -51
- package/src/effect-client/batch.ts +0 -50
- package/src/effect-client/envelope.ts +0 -238
- package/src/effect-client/guards.ts +0 -6
- package/src/effect-client/request-context.ts +0 -54
- package/src/effect-client/request.ts +0 -51
- package/src/effect-client/runtime.ts +0 -122
- package/src/effect-client/types.ts +0 -84
|
@@ -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;
|
|
@@ -307,15 +302,14 @@ export function createBatchTransportQueue({
|
|
|
307
302
|
abortBatchIfUnused?.();
|
|
308
303
|
};
|
|
309
304
|
|
|
310
|
-
const runSingle = (request: QueuedBatchRequest) =>
|
|
311
|
-
|
|
305
|
+
const runSingle = (request: QueuedBatchRequest) =>
|
|
306
|
+
runWithSignal(request.lifetime.signal, async () => {
|
|
312
307
|
const response = await baseFetch(
|
|
313
308
|
request.requestInput,
|
|
314
309
|
request.requestInit,
|
|
315
310
|
);
|
|
316
311
|
return parseResponseLikeCreateRequest(response);
|
|
317
312
|
});
|
|
318
|
-
};
|
|
319
313
|
|
|
320
314
|
const settleRequests = async (
|
|
321
315
|
items: QueuedBatchRequest[],
|
|
@@ -348,11 +342,11 @@ export function createBatchTransportQueue({
|
|
|
348
342
|
return runSingle(request);
|
|
349
343
|
};
|
|
350
344
|
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
+
};
|
|
356
350
|
|
|
357
351
|
const flushBucket = async (bucketKey: string) => {
|
|
358
352
|
const bucket = buckets.get(bucketKey);
|
|
@@ -380,7 +374,6 @@ export function createBatchTransportQueue({
|
|
|
380
374
|
sentAt: bucket.sentAt,
|
|
381
375
|
};
|
|
382
376
|
bucket.items = [];
|
|
383
|
-
bucket.bytes = 0;
|
|
384
377
|
rotatePayloadIdentity(bucket);
|
|
385
378
|
const firstItem = items[0];
|
|
386
379
|
if (!firstItem) {
|
|
@@ -397,17 +390,28 @@ export function createBatchTransportQueue({
|
|
|
397
390
|
size: items.length,
|
|
398
391
|
reason: disabledEndpoints.has(endpoint) ? 'batch-disabled' : undefined,
|
|
399
392
|
});
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
393
|
+
try {
|
|
394
|
+
await settleRequests(items, runSingle);
|
|
395
|
+
} finally {
|
|
396
|
+
finishFlush(bucketKey, bucket);
|
|
404
397
|
}
|
|
405
|
-
buckets.releaseIfIdle(bucketKey, bucket);
|
|
406
398
|
return;
|
|
407
399
|
}
|
|
408
400
|
|
|
409
401
|
const payload = createPayload(payloadIdentity, items);
|
|
410
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
|
+
};
|
|
411
415
|
|
|
412
416
|
emitDataBatchTransportEvent(onEvent, {
|
|
413
417
|
type: 'flush',
|
|
@@ -474,20 +478,17 @@ export function createBatchTransportQueue({
|
|
|
474
478
|
batchId,
|
|
475
479
|
reason: `batch-endpoint-unavailable-${String(response.status)}`,
|
|
476
480
|
});
|
|
481
|
+
await settleRequests(items, request =>
|
|
482
|
+
runAfterAmbiguousBatch(
|
|
483
|
+
request,
|
|
484
|
+
`batch-response-${String(response.status)}`,
|
|
485
|
+
),
|
|
486
|
+
);
|
|
477
487
|
} else {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
batchId,
|
|
482
|
-
size: items.length,
|
|
483
|
-
reason: `batch-response-${String(response.status)}`,
|
|
484
|
-
});
|
|
488
|
+
await settleAmbiguousBatchRequests(
|
|
489
|
+
`batch-response-${String(response.status)}`,
|
|
490
|
+
);
|
|
485
491
|
}
|
|
486
|
-
await settleAmbiguousBatchRequests(
|
|
487
|
-
items,
|
|
488
|
-
`batch-response-${String(response.status)}`,
|
|
489
|
-
);
|
|
490
|
-
bucket.flushing = false;
|
|
491
492
|
return;
|
|
492
493
|
}
|
|
493
494
|
|
|
@@ -495,28 +496,12 @@ export function createBatchTransportQueue({
|
|
|
495
496
|
response.json(),
|
|
496
497
|
);
|
|
497
498
|
if (!isBatchResponsePayload(result)) {
|
|
498
|
-
|
|
499
|
-
type: 'fallback',
|
|
500
|
-
endpoint,
|
|
501
|
-
batchId,
|
|
502
|
-
size: items.length,
|
|
503
|
-
reason: 'invalid-batch-response',
|
|
504
|
-
});
|
|
505
|
-
await settleAmbiguousBatchRequests(items, 'invalid-batch-response');
|
|
506
|
-
bucket.flushing = false;
|
|
499
|
+
await settleAmbiguousBatchRequests('invalid-batch-response');
|
|
507
500
|
return;
|
|
508
501
|
}
|
|
509
502
|
|
|
510
503
|
if (result.batchId !== batchId) {
|
|
511
|
-
|
|
512
|
-
type: 'fallback',
|
|
513
|
-
endpoint,
|
|
514
|
-
batchId,
|
|
515
|
-
size: items.length,
|
|
516
|
-
reason: 'mismatched-batch-id',
|
|
517
|
-
});
|
|
518
|
-
await settleAmbiguousBatchRequests(items, 'mismatched-batch-id');
|
|
519
|
-
bucket.flushing = false;
|
|
504
|
+
await settleAmbiguousBatchRequests('mismatched-batch-id');
|
|
520
505
|
return;
|
|
521
506
|
}
|
|
522
507
|
|
|
@@ -556,23 +541,12 @@ export function createBatchTransportQueue({
|
|
|
556
541
|
}
|
|
557
542
|
return;
|
|
558
543
|
}
|
|
559
|
-
|
|
560
|
-
type: 'fallback',
|
|
561
|
-
endpoint,
|
|
562
|
-
batchId,
|
|
563
|
-
size: items.length,
|
|
564
|
-
reason: 'batch-transport-error',
|
|
565
|
-
});
|
|
566
|
-
await settleAmbiguousBatchRequests(items, 'batch-transport-error');
|
|
544
|
+
await settleAmbiguousBatchRequests('batch-transport-error');
|
|
567
545
|
} finally {
|
|
568
546
|
for (const item of items) {
|
|
569
547
|
item.abortBatchIfUnused = undefined;
|
|
570
548
|
}
|
|
571
|
-
bucket
|
|
572
|
-
if (bucket.items.length > 0 && !bucket.timer) {
|
|
573
|
-
void flushBucket(bucketKey);
|
|
574
|
-
}
|
|
575
|
-
buckets.releaseIfIdle(bucketKey, bucket);
|
|
549
|
+
finishFlush(bucketKey, bucket);
|
|
576
550
|
}
|
|
577
551
|
};
|
|
578
552
|
|
|
@@ -618,7 +592,18 @@ export function createBatchTransportQueue({
|
|
|
618
592
|
options.endpoint,
|
|
619
593
|
).toString();
|
|
620
594
|
|
|
595
|
+
const itemHeaders = { ...headers };
|
|
596
|
+
delete itemHeaders.authorization;
|
|
597
|
+
delete itemHeaders.cookie;
|
|
621
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
|
+
}),
|
|
622
607
|
requestUrl,
|
|
623
608
|
endpoint,
|
|
624
609
|
method,
|
|
@@ -661,20 +646,11 @@ export function createBatchTransportQueue({
|
|
|
661
646
|
return undefined;
|
|
662
647
|
}
|
|
663
648
|
|
|
664
|
-
const itemHeaders = { ...request.headers };
|
|
665
|
-
delete itemHeaders.authorization;
|
|
666
|
-
delete itemHeaders.cookie;
|
|
667
|
-
const bucketKey = stableStringify({
|
|
668
|
-
endpoint: request.endpoint,
|
|
669
|
-
authorization: request.headers.authorization ?? null,
|
|
670
|
-
cookie: request.headers.cookie ?? null,
|
|
671
|
-
credentials: request.credentials,
|
|
672
|
-
});
|
|
673
649
|
return stableStringify({
|
|
674
|
-
bucketKey,
|
|
675
|
-
path:
|
|
650
|
+
bucketKey: request.bucketKey,
|
|
651
|
+
path: request.itemPath,
|
|
676
652
|
method: request.method,
|
|
677
|
-
headers: itemHeaders,
|
|
653
|
+
headers: request.itemHeaders,
|
|
678
654
|
body: null,
|
|
679
655
|
});
|
|
680
656
|
};
|
|
@@ -705,9 +681,7 @@ export function createBatchTransportQueue({
|
|
|
705
681
|
.slice(2, 8)}`;
|
|
706
682
|
nextItemId += 1;
|
|
707
683
|
|
|
708
|
-
const itemHeaders = { ...request.
|
|
709
|
-
delete itemHeaders.authorization;
|
|
710
|
-
delete itemHeaders.cookie;
|
|
684
|
+
const itemHeaders = { ...request.itemHeaders };
|
|
711
685
|
if (
|
|
712
686
|
preparedBody.body &&
|
|
713
687
|
preparedBody.inferredContentType &&
|
|
@@ -717,18 +691,13 @@ export function createBatchTransportQueue({
|
|
|
717
691
|
}
|
|
718
692
|
const item: DataBatchRequestItem = {
|
|
719
693
|
id: itemId,
|
|
720
|
-
path:
|
|
694
|
+
path: request.itemPath,
|
|
721
695
|
method: request.method,
|
|
722
696
|
headers: itemHeaders,
|
|
723
697
|
...(preparedBody.body ? { body: preparedBody.body } : {}),
|
|
724
698
|
};
|
|
725
699
|
|
|
726
|
-
const bucketKey =
|
|
727
|
-
endpoint: request.endpoint,
|
|
728
|
-
authorization: request.headers.authorization ?? null,
|
|
729
|
-
cookie: request.headers.cookie ?? null,
|
|
730
|
-
credentials: request.credentials,
|
|
731
|
-
});
|
|
700
|
+
const { bucketKey } = request;
|
|
732
701
|
|
|
733
702
|
const key = stableStringify({
|
|
734
703
|
bucketKey,
|
|
@@ -825,14 +794,16 @@ export function createBatchTransportQueue({
|
|
|
825
794
|
}
|
|
826
795
|
|
|
827
796
|
bucket.items.push(queued);
|
|
828
|
-
bucket.bytes = measurePayload(bucket, bucket.items);
|
|
829
797
|
emitDataBatchTransportEvent(onEvent, {
|
|
830
798
|
type: 'enqueue',
|
|
831
799
|
endpoint: request.endpoint,
|
|
832
800
|
size: bucket.items.length,
|
|
833
801
|
});
|
|
834
802
|
|
|
835
|
-
if (
|
|
803
|
+
if (
|
|
804
|
+
bucket.items.length >= maxBatchSize ||
|
|
805
|
+
measurePayload(bucket, bucket.items) >= maxBatchBytes
|
|
806
|
+
) {
|
|
836
807
|
void flushBucket(bucketKey);
|
|
837
808
|
return queued.promise;
|
|
838
809
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @effect-diagnostics asyncFunction:off globalDate:off globalRandom:off globalTimers:off newPromise:off strictBooleanExpressions:off
|
|
2
|
-
import { parseTraceparent } from '@modern-js/
|
|
2
|
+
import { parseTraceparent } from '@modern-js/runtime-extensions/request-context';
|
|
3
3
|
|
|
4
4
|
import type { TraceContext } from './types';
|
|
5
5
|
|
package/src/effect/context.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
// @effect-diagnostics strictBooleanExpressions:off
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
type EffectContext,
|
|
5
|
-
type EffectContextStorage,
|
|
6
|
-
kEffectContextStorage,
|
|
7
|
-
} from './operation-context';
|
|
2
|
+
import type { EffectContext, EffectContextStorage } from './operation-context';
|
|
8
3
|
|
|
9
4
|
export {
|
|
10
5
|
type CreateEffectOperationContextOptions,
|
|
@@ -12,21 +7,51 @@ export {
|
|
|
12
7
|
type EffectContext,
|
|
13
8
|
} from './operation-context';
|
|
14
9
|
|
|
10
|
+
type EffectContextStorageConstructor = new () => EffectContextStorage;
|
|
11
|
+
|
|
12
|
+
// One storage for every entry: each imports this module as
|
|
13
|
+
// `@modern-js/bff-effect/context`, the request that Module Federation shares
|
|
14
|
+
// and that the BFF bundle keeps external, so the server and its lambdas load
|
|
15
|
+
// the same instance. A backend federation container bundles its own copy and
|
|
16
|
+
// adopts the host's storage in init() instead.
|
|
15
17
|
const globalStore = globalThis as typeof globalThis & {
|
|
16
|
-
|
|
18
|
+
process?: {
|
|
19
|
+
getBuiltinModule?: (id: string) => unknown;
|
|
20
|
+
};
|
|
17
21
|
};
|
|
18
22
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
const asyncHooks = globalStore.process?.getBuiltinModule?.(
|
|
24
|
+
'node:async_hooks',
|
|
25
|
+
) as { AsyncLocalStorage?: EffectContextStorageConstructor } | undefined;
|
|
26
|
+
const AsyncLocalStorage = asyncHooks?.AsyncLocalStorage;
|
|
27
|
+
if (typeof AsyncLocalStorage !== 'function') {
|
|
28
|
+
throw new Error(
|
|
29
|
+
'[BFF][Effect] The edge runtime must provide AsyncLocalStorage. Enable Node.js compatibility or the nodejs_als compatibility flag.',
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Module Federation share through which a backend federation host hands its
|
|
35
|
+
* request storage to the containers it initializes.
|
|
36
|
+
*/
|
|
37
|
+
export const EFFECT_BFF_CONTEXT_STORAGE_SHARE =
|
|
38
|
+
'@modern-js/bff-effect/context-storage';
|
|
39
|
+
|
|
40
|
+
let effectContextStorage: EffectContextStorage = new AsyncLocalStorage();
|
|
41
|
+
let effectContextStorageUsed = false;
|
|
42
|
+
|
|
43
|
+
const usedEffectContextStorage = () => {
|
|
44
|
+
effectContextStorageUsed = true;
|
|
45
|
+
return effectContextStorage;
|
|
46
|
+
};
|
|
22
47
|
|
|
23
48
|
export const runWithEffectContext = <T>(
|
|
24
49
|
context: EffectContext,
|
|
25
50
|
cb: () => T,
|
|
26
|
-
): T =>
|
|
51
|
+
): T => usedEffectContextStorage().run(context, cb);
|
|
27
52
|
|
|
28
53
|
export const useEffectContext = (): EffectContext => {
|
|
29
|
-
const context =
|
|
54
|
+
const context = usedEffectContextStorage().getStore();
|
|
30
55
|
if (!context) {
|
|
31
56
|
throw new Error(`Can't call useEffectContext out of Effect runtime scope`);
|
|
32
57
|
}
|
|
@@ -34,4 +59,31 @@ export const useEffectContext = (): EffectContext => {
|
|
|
34
59
|
return context;
|
|
35
60
|
};
|
|
36
61
|
|
|
62
|
+
/** @internal The storage a backend federation host shares with containers. */
|
|
63
|
+
export const getEffectContextStorage = (): EffectContextStorage =>
|
|
64
|
+
effectContextStorage;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @internal Called through `adoptEffectBffShareScope()` from a backend
|
|
68
|
+
* federation container's `init()`, so its endpoints read the context the
|
|
69
|
+
* host dispatcher enters.
|
|
70
|
+
*/
|
|
71
|
+
export function adoptEffectContextStorage(storage: unknown): void {
|
|
72
|
+
const candidate = storage as Partial<EffectContextStorage> | undefined;
|
|
73
|
+
if (
|
|
74
|
+
typeof candidate?.run !== 'function' ||
|
|
75
|
+
typeof candidate.getStore !== 'function'
|
|
76
|
+
) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`[BFF][Effect] Share ${EFFECT_BFF_CONTEXT_STORAGE_SHARE} must provide an AsyncLocalStorage.`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (effectContextStorageUsed && candidate !== effectContextStorage) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
'[BFF][Effect] A backend federation container received the host request storage after an Effect API already used its own. Evaluate exposes only after container init().',
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
effectContextStorage = candidate as EffectContextStorage;
|
|
87
|
+
}
|
|
88
|
+
|
|
37
89
|
export const useOperationContext = () => useEffectContext().operationContext;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
// @effect-diagnostics anyUnknownInErrorContext:off asyncFunction:off strictBooleanExpressions:off
|
|
2
|
+
|
|
3
|
+
import { runWithEffectContext } from '@modern-js/bff-effect/context';
|
|
2
4
|
import {
|
|
3
5
|
evaluateCrossProjectPolicy,
|
|
4
6
|
type NormalizedCrossProjectPolicy,
|
|
5
7
|
resolveCrossProjectRequestObservation,
|
|
6
|
-
} from '@modern-js/
|
|
8
|
+
} from '@modern-js/server-runtime-extensions/bff-policy';
|
|
9
|
+
import * as Effect from 'effect/Effect';
|
|
7
10
|
import { toHeaderRecord } from '../headers';
|
|
8
11
|
import {
|
|
9
12
|
type DispatchEffectBffRequestOptions,
|
|
10
13
|
dispatchEffectBffRequestWithContext,
|
|
11
14
|
} from './dispatch';
|
|
12
|
-
import { runWithEffectContext } from './edge-context';
|
|
13
15
|
import type {
|
|
14
16
|
EffectBffOpenApiConfig,
|
|
15
17
|
EffectDataPlatformValidationOptions,
|
|
@@ -21,6 +23,11 @@ import {
|
|
|
21
23
|
} from './module';
|
|
22
24
|
import type { EffectContext } from './operation-context';
|
|
23
25
|
|
|
26
|
+
export {
|
|
27
|
+
runWithEffectContext,
|
|
28
|
+
useEffectContext,
|
|
29
|
+
useOperationContext,
|
|
30
|
+
} from '@modern-js/bff-effect/context';
|
|
24
31
|
export * as Config from 'effect/Config';
|
|
25
32
|
export * as Effect from 'effect/Effect';
|
|
26
33
|
export * as Layer from 'effect/Layer';
|
|
@@ -84,14 +91,8 @@ export {
|
|
|
84
91
|
RpcWorker,
|
|
85
92
|
Utils,
|
|
86
93
|
} from 'effect/unstable/rpc';
|
|
87
|
-
export {
|
|
88
|
-
runWithEffectContext,
|
|
89
|
-
useEffectContext,
|
|
90
|
-
useOperationContext,
|
|
91
|
-
} from './edge-context';
|
|
92
94
|
export type {
|
|
93
95
|
EffectApiClientFromApi,
|
|
94
|
-
EffectApiPromiseClientFromApi,
|
|
95
96
|
EffectBffDefinition,
|
|
96
97
|
EffectBffHandlerFactory,
|
|
97
98
|
EffectBffOpenApiConfig,
|
|
@@ -285,9 +286,11 @@ export async function createEffectBffEdgeDispatcher(
|
|
|
285
286
|
disposePromise ??= (async () => {
|
|
286
287
|
acceptingRequests = false;
|
|
287
288
|
if (activeDispatches > 0) {
|
|
288
|
-
await
|
|
289
|
-
|
|
290
|
-
|
|
289
|
+
await Effect.runPromise(
|
|
290
|
+
Effect.callback<void>(resume => {
|
|
291
|
+
resolveDrain = () => resume(Effect.void);
|
|
292
|
+
}),
|
|
293
|
+
);
|
|
291
294
|
}
|
|
292
295
|
await loaded.dispose?.();
|
|
293
296
|
})();
|
package/src/effect/edge.ts
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
+
export { EFFECT_BFF_CONTEXT_STORAGE_SHARE } from '@modern-js/bff-effect/context';
|
|
1
2
|
export * from './edge-dispatcher';
|
|
3
|
+
export {
|
|
4
|
+
adoptEffectBffShareScope,
|
|
5
|
+
EFFECT_BFF_HANDLER_FACTORY_REGISTRY_SHARE,
|
|
6
|
+
} from './entry-shape';
|
|
7
|
+
export { createEffectBffEdgeDispatcherFactory } from './generated-entry';
|
|
@@ -1,21 +1,9 @@
|
|
|
1
1
|
// @effect-diagnostics asyncFunction:off strictBooleanExpressions:off
|
|
2
|
-
/**
|
|
3
|
-
* Shared Effect HttpApi endpoint reflection used by BOTH sides of the
|
|
4
|
-
* cross-project contract:
|
|
5
|
-
*
|
|
6
|
-
* - the `@modern-js/plugin-bff-extensions/client-generator` package stamps
|
|
7
|
-
* each generated operation with a per-endpoint contract hash;
|
|
8
|
-
* - the `@modern-js/plugin-bff-extensions/effect-adapter` package derives the
|
|
9
|
-
* expected operation-contract map for the cross-project policy from the
|
|
10
|
-
* same endpoints.
|
|
11
|
-
*
|
|
12
|
-
* Keeping route-path normalization and the endpoint -> contract mapping in
|
|
13
|
-
* one module is what guarantees the two hashes agree.
|
|
14
|
-
*/
|
|
2
|
+
/** Shared endpoint normalization and identity hashing for server policy metadata. */
|
|
15
3
|
import {
|
|
16
4
|
createOperationContractHash,
|
|
17
5
|
type OperationContractSource,
|
|
18
|
-
} from '@modern-js/bff-
|
|
6
|
+
} from '@modern-js/server-runtime-extensions/bff-policy/node';
|
|
19
7
|
import {
|
|
20
8
|
classifyEffectBffEntryModule,
|
|
21
9
|
isValidatorAwareHandlerFactory,
|
|
@@ -38,13 +26,7 @@ type HttpApiGroupLike = {
|
|
|
38
26
|
};
|
|
39
27
|
|
|
40
28
|
type HttpApiEndpointLike = {
|
|
41
|
-
/**
|
|
42
|
-
* effect 4.0.0-beta.98 renamed the endpoint `name` property to `identifier`,
|
|
43
|
-
* matching `HttpApiGroup`. `name` is kept as a fallback so reflection over an
|
|
44
|
-
* older Effect build does not silently degrade to the class name.
|
|
45
|
-
*/
|
|
46
29
|
identifier?: unknown;
|
|
47
|
-
name?: unknown;
|
|
48
30
|
method?: unknown;
|
|
49
31
|
path?: unknown;
|
|
50
32
|
};
|
|
@@ -53,9 +35,6 @@ function resolveEffectEndpointName(endpoint: HttpApiEndpointLike): string {
|
|
|
53
35
|
if (typeof endpoint.identifier === 'string' && endpoint.identifier) {
|
|
54
36
|
return endpoint.identifier;
|
|
55
37
|
}
|
|
56
|
-
if (typeof endpoint.name === 'string' && endpoint.name) {
|
|
57
|
-
return endpoint.name;
|
|
58
|
-
}
|
|
59
38
|
return '';
|
|
60
39
|
}
|
|
61
40
|
|
|
@@ -168,7 +147,7 @@ export async function extractHttpApiFromModule(
|
|
|
168
147
|
isValidatorAwareHandlerFactory,
|
|
169
148
|
isHttpApi,
|
|
170
149
|
});
|
|
171
|
-
return facts?.
|
|
150
|
+
return facts?.unsupportedShape ||
|
|
172
151
|
facts?.api === undefined ||
|
|
173
152
|
!facts.hasRuntimeLayer ||
|
|
174
153
|
(facts.createHandler !== undefined && !facts.createHandlerValidatorAware)
|