@bleedingdev/modern-js-bff-effect 3.9.0-ultramodern.3 → 3.9.0-ultramodern.5

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 (60) hide show
  1. package/dist/cjs/assembly.js +50 -0
  2. package/dist/cjs/data-platform/trace.js +2 -2
  3. package/dist/cjs/effect/edge-dispatcher.js +7 -7
  4. package/dist/cjs/effect/edge.js +15 -2
  5. package/dist/cjs/effect/endpoint-contracts.js +2 -2
  6. package/dist/cjs/effect/generated-entry.js +57 -0
  7. package/dist/cjs/effect/handler/batch-handler.js +5 -4
  8. package/dist/cjs/effect/handler/batch.js +8 -1
  9. package/dist/cjs/effect/operation-context.js +7 -7
  10. package/dist/cjs/effect-client/index.js +2 -2
  11. package/dist/cjs/microvertical-api.js +120 -0
  12. package/dist/esm/assembly.mjs +12 -0
  13. package/dist/esm/data-platform/trace.mjs +1 -1
  14. package/dist/esm/effect/edge-dispatcher.mjs +5 -5
  15. package/dist/esm/effect/edge.mjs +1 -0
  16. package/dist/esm/effect/endpoint-contracts.mjs +1 -1
  17. package/dist/esm/effect/generated-entry.mjs +19 -0
  18. package/dist/esm/effect/handler/batch-handler.mjs +5 -4
  19. package/dist/esm/effect/handler/batch.mjs +8 -1
  20. package/dist/esm/effect/operation-context.mjs +1 -1
  21. package/dist/esm/effect-client/index.mjs +1 -1
  22. package/dist/esm/microvertical-api.mjs +67 -0
  23. package/dist/esm-node/assembly.mjs +13 -0
  24. package/dist/esm-node/data-platform/trace.mjs +1 -1
  25. package/dist/esm-node/effect/edge-dispatcher.mjs +5 -5
  26. package/dist/esm-node/effect/edge.mjs +1 -0
  27. package/dist/esm-node/effect/endpoint-contracts.mjs +1 -1
  28. package/dist/esm-node/effect/generated-entry.mjs +20 -0
  29. package/dist/esm-node/effect/handler/batch-handler.mjs +5 -4
  30. package/dist/esm-node/effect/handler/batch.mjs +8 -1
  31. package/dist/esm-node/effect/operation-context.mjs +1 -1
  32. package/dist/esm-node/effect-client/index.mjs +1 -1
  33. package/dist/esm-node/microvertical-api.mjs +68 -0
  34. package/dist/types/assembly.d.ts +10 -0
  35. package/dist/types/effect/context.d.ts +1 -1
  36. package/dist/types/effect/edge-context.d.ts +1 -1
  37. package/dist/types/effect/edge-dispatcher.d.ts +1 -1
  38. package/dist/types/effect/edge.d.ts +1 -0
  39. package/dist/types/effect/endpoint-contracts.d.ts +1 -1
  40. package/dist/types/effect/generated-entry.d.ts +7 -0
  41. package/dist/types/effect/handler/batch.d.ts +7 -1
  42. package/dist/types/effect/operation-context.d.ts +1 -1
  43. package/dist/types/effect-client/index.d.ts +1 -1
  44. package/dist/types/effect-client/types.d.ts +1 -1
  45. package/dist/types/microvertical-api.d.ts +65 -0
  46. package/package.json +30 -4
  47. package/src/assembly.ts +43 -0
  48. package/src/data-platform/batch/queue.ts +2 -3
  49. package/src/data-platform/trace.ts +1 -1
  50. package/src/effect/edge-dispatcher.ts +7 -4
  51. package/src/effect/edge.ts +1 -0
  52. package/src/effect/endpoint-contracts.ts +1 -1
  53. package/src/effect/entry-shape.ts +4 -1
  54. package/src/effect/generated-entry.ts +36 -0
  55. package/src/effect/handler/batch-handler.ts +21 -16
  56. package/src/effect/handler/batch.ts +9 -1
  57. package/src/effect/operation-context.ts +1 -1
  58. package/src/effect-client/index.ts +1 -1
  59. package/src/effect-client/types.ts +1 -1
  60. package/src/microvertical-api.ts +102 -0
@@ -3,7 +3,8 @@ import {
3
3
  evaluateCrossProjectPolicy,
4
4
  type NormalizedCrossProjectPolicy,
5
5
  resolveCrossProjectRequestObservation,
6
- } from '@modern-js/bff-core/security/cross-project-policy';
6
+ } from '@modern-js/server-runtime-extensions/bff-policy';
7
+ import * as Effect from 'effect/Effect';
7
8
  import { toHeaderRecord } from '../headers';
8
9
  import {
9
10
  type DispatchEffectBffRequestOptions,
@@ -285,9 +286,11 @@ export async function createEffectBffEdgeDispatcher(
285
286
  disposePromise ??= (async () => {
286
287
  acceptingRequests = false;
287
288
  if (activeDispatches > 0) {
288
- await new Promise<void>(resolve => {
289
- resolveDrain = resolve;
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
  })();
@@ -1 +1,2 @@
1
1
  export * from './edge-dispatcher';
2
+ export { createEffectBffEdgeDispatcherFactory } from './generated-entry';
@@ -15,7 +15,7 @@
15
15
  import {
16
16
  createOperationContractHash,
17
17
  type OperationContractSource,
18
- } from '@modern-js/bff-core';
18
+ } from '@modern-js/server-runtime-extensions/bff-policy/node';
19
19
  import {
20
20
  classifyEffectBffEntryModule,
21
21
  isValidatorAwareHandlerFactory,
@@ -42,8 +42,11 @@ function loadNodeValidatorAwareHandlerFactoryRegistry(): ValidatorAwareHandlerFa
42
42
  }
43
43
  }
44
44
 
45
+ declare const __MODERN_EFFECT_NODE_RUNTIME__: boolean;
46
+
45
47
  const validatorAwareHandlerFactoryRegistry =
46
- process.env.MODERN_EFFECT_NODE_RUNTIME === 'true'
48
+ typeof __MODERN_EFFECT_NODE_RUNTIME__ !== 'undefined' &&
49
+ __MODERN_EFFECT_NODE_RUNTIME__
47
50
  ? loadNodeValidatorAwareHandlerFactoryRegistry()
48
51
  : createLocalValidatorAwareHandlerFactoryRegistry();
49
52
 
@@ -0,0 +1,36 @@
1
+ import {
2
+ createEffectBffEdgeDispatcher,
3
+ type EffectBffEdgeHandlerOptions,
4
+ } from './edge-dispatcher';
5
+
6
+ type OperationContracts = NonNullable<
7
+ EffectBffEdgeHandlerOptions['crossProjectPolicy']
8
+ >['expectedOperationContracts'];
9
+
10
+ export function createEffectBffEdgeDispatcherFactory(
11
+ module: EffectBffEdgeHandlerOptions['module'],
12
+ generatedOperationContracts: OperationContracts,
13
+ ) {
14
+ return (options?: Omit<EffectBffEdgeHandlerOptions, 'module'>) => {
15
+ const policy = options?.crossProjectPolicy;
16
+ const crossProjectPolicy =
17
+ policy === undefined ||
18
+ policy === null ||
19
+ policy.expectedOperationContracts === null ||
20
+ typeof policy.expectedOperationContracts !== 'object' ||
21
+ Array.isArray(policy.expectedOperationContracts)
22
+ ? policy
23
+ : {
24
+ ...policy,
25
+ expectedOperationContracts: {
26
+ ...policy.expectedOperationContracts,
27
+ ...generatedOperationContracts,
28
+ },
29
+ };
30
+ return createEffectBffEdgeDispatcher({
31
+ ...options,
32
+ crossProjectPolicy,
33
+ module,
34
+ });
35
+ };
36
+ }
@@ -1,4 +1,5 @@
1
1
  // @effect-diagnostics anyUnknownInErrorContext:off asyncFunction:off globalDate:off globalTimers:off newPromise:off strictBooleanExpressions:off
2
+ import * as Effect from 'effect/Effect';
2
3
  import {
3
4
  type DataBatchResponsePayload,
4
5
  DEFAULT_DATA_BATCH_HEADER,
@@ -417,14 +418,16 @@ export function createDataPlatformBatchRequestHandler<TContext>(options: {
417
418
  );
418
419
  } catch (error) {
419
420
  if (error instanceof BatchItemTimeoutError) {
420
- console.error({
421
- event: 'bff.batch.item.timeout',
422
- batchId: payload.batchId,
423
- itemId,
424
- method: itemMethod,
425
- path: itemPathname,
426
- errorName: error.name,
427
- });
421
+ Effect.runSync(
422
+ Effect.logError({
423
+ event: 'bff.batch.item.timeout',
424
+ batchId: payload.batchId,
425
+ itemId,
426
+ method: itemMethod,
427
+ path: itemPathname,
428
+ errorName: error.name,
429
+ }),
430
+ );
428
431
  return toBatchItemError(
429
432
  itemId,
430
433
  504,
@@ -435,14 +438,16 @@ export function createDataPlatformBatchRequestHandler<TContext>(options: {
435
438
  return toBatchResponseItem(itemId, error, itemMethod === 'HEAD');
436
439
  }
437
440
 
438
- console.error({
439
- event: 'bff.batch.item.failure',
440
- batchId: payload.batchId,
441
- itemId,
442
- method: itemMethod,
443
- path: itemPathname,
444
- errorName: error instanceof Error ? error.name : 'UnknownError',
445
- });
441
+ Effect.runSync(
442
+ Effect.logError({
443
+ event: 'bff.batch.item.failure',
444
+ batchId: payload.batchId,
445
+ itemId,
446
+ method: itemMethod,
447
+ path: itemPathname,
448
+ errorName: error instanceof Error ? error.name : 'UnknownError',
449
+ }),
450
+ );
446
451
  return toBatchItemError(itemId, 500, 'Internal Server Error');
447
452
  } finally {
448
453
  request.signal.removeEventListener('abort', abortFromBatchRequest);
@@ -1,5 +1,6 @@
1
1
  // @effect-diagnostics anyUnknownInErrorContext:off asyncFunction:off globalDate:off globalTimers:off newPromise:off strictBooleanExpressions:off
2
2
 
3
+ import * as Data from 'effect/Data';
3
4
  import {
4
5
  type DataBatchRequestPayload,
5
6
  type DataBatchResponseItem,
@@ -117,7 +118,14 @@ export function toBatchItemError(
117
118
  };
118
119
  }
119
120
 
120
- export class BatchItemTimeoutError extends Error {}
121
+ export class BatchItemTimeoutError extends Data.TaggedError(
122
+ 'BatchItemTimeoutError',
123
+ )<{ readonly message: string }> {
124
+ constructor(message: string) {
125
+ super({ message });
126
+ this.name = 'Error';
127
+ }
128
+ }
121
129
 
122
130
  export function promiseWithTimeout<T>(
123
131
  effect: Promise<T>,
@@ -6,7 +6,7 @@ import {
6
6
  BFF_TRACEPARENT_HEADER,
7
7
  type OperationContext,
8
8
  parseTraceparent,
9
- } from '@modern-js/create-request';
9
+ } from '@modern-js/runtime-extensions/request-context';
10
10
 
11
11
  export type EffectContext = {
12
12
  request: Request;
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  createRequestContextHeaders,
4
4
  type RequestContextInput,
5
- } from '@modern-js/create-request';
5
+ } from '@modern-js/runtime-extensions/request-context';
6
6
  import * as Data from 'effect/Data';
7
7
  import * as Effect from 'effect/Effect';
8
8
  import * as Exit from 'effect/Exit';
@@ -59,7 +59,7 @@ export type EffectRequestContext = {
59
59
  headers: Record<string, string>;
60
60
  } & Record<string, unknown>;
61
61
 
62
- /** Structural slice of `@modern-js/create-request` the runtime relies on. */
62
+ /** Structural slice of `@modern-js/runtime-extensions/request-policy` the runtime relies on. */
63
63
  export type EffectRequestRuntime = {
64
64
  createRequest: (options: {
65
65
  path: string;
@@ -0,0 +1,102 @@
1
+ import { Schema } from 'effect';
2
+
3
+ const MicroVerticalAppIdSchema = Schema.String.pipe(
4
+ Schema.brand('MicroVerticalAppId'),
5
+ Schema.decodeTo(Schema.String),
6
+ );
7
+ const MicroVerticalUnitIdSchema = Schema.String.pipe(
8
+ Schema.brand('MicroVerticalUnitId'),
9
+ Schema.decodeTo(Schema.String),
10
+ );
11
+ const MicroVerticalOperationIdSchema = Schema.String.pipe(
12
+ Schema.brand('MicroVerticalOperationId'),
13
+ Schema.decodeTo(Schema.String),
14
+ );
15
+ const MicroVerticalTraceIdSchema = Schema.String.pipe(
16
+ Schema.brand('MicroVerticalTraceId'),
17
+ Schema.decodeTo(Schema.String),
18
+ );
19
+
20
+ export const MicroVerticalBuildMarkerSchema = Schema.Struct({
21
+ appId: MicroVerticalAppIdSchema,
22
+ build: Schema.String,
23
+ buildMarker: Schema.String,
24
+ deployProfile: Schema.String,
25
+ packageName: Schema.String,
26
+ sourceRevision: Schema.String,
27
+ surface: Schema.String,
28
+ unitId: MicroVerticalUnitIdSchema,
29
+ version: Schema.String,
30
+ });
31
+ export type MicroVerticalBuildMarker =
32
+ typeof MicroVerticalBuildMarkerSchema.Type;
33
+
34
+ export const MicroVerticalReadinessSchema = Schema.Struct({
35
+ checks: Schema.Struct({
36
+ api: Schema.Literal('ready'),
37
+ moduleFederation: Schema.Literal('ready'),
38
+ ssr: Schema.Literal('ready'),
39
+ translations: Schema.Literal('ready'),
40
+ }),
41
+ marker: MicroVerticalBuildMarkerSchema,
42
+ status: Schema.Literal('ready'),
43
+ versionSkew: Schema.Literal('none'),
44
+ });
45
+ export type MicroVerticalReadiness = typeof MicroVerticalReadinessSchema.Type;
46
+
47
+ export const MicroVerticalOperationSourceSchema = Schema.Literals([
48
+ 'client',
49
+ 'server',
50
+ 'generated-client',
51
+ 'effect-adapter',
52
+ 'data-platform',
53
+ 'unknown',
54
+ ]);
55
+ export type MicroVerticalOperationSource =
56
+ typeof MicroVerticalOperationSourceSchema.Type;
57
+
58
+ export const MicroVerticalOperationContextSchema = Schema.Struct({
59
+ method: Schema.String,
60
+ operationId: MicroVerticalOperationIdSchema,
61
+ routePath: Schema.String,
62
+ source: MicroVerticalOperationSourceSchema,
63
+ traceId: Schema.optionalKey(MicroVerticalTraceIdSchema),
64
+ });
65
+ export type MicroVerticalOperationContext =
66
+ typeof MicroVerticalOperationContextSchema.Type;
67
+
68
+ export const createMicroVerticalOperationContext = <
69
+ const Method extends string,
70
+ const OperationId extends string,
71
+ const RoutePath extends string,
72
+ >(input: {
73
+ readonly method: Method;
74
+ readonly operationId: OperationId;
75
+ readonly routePath: RoutePath;
76
+ // eslint-disable-next-line effect-native/no-threaded-correlation-parameter -- This pure browser-safe contract deliberately carries optional trace metadata; it does not perform ambient request work.
77
+ readonly traceId?: string;
78
+ }): Readonly<typeof input & { source: 'generated-client' }> => {
79
+ const context = {
80
+ method: input.method,
81
+ operationId: input.operationId,
82
+ routePath: input.routePath,
83
+ source: 'generated-client' as const,
84
+ };
85
+ return input.traceId === undefined
86
+ ? context
87
+ : { ...context, traceId: input.traceId };
88
+ };
89
+
90
+ export const microVerticalOperationAttributes = (
91
+ operationContext: MicroVerticalOperationContext,
92
+ ) => {
93
+ const attributes = {
94
+ 'modernjs.operation.id': operationContext.operationId,
95
+ 'modernjs.operation.method': operationContext.method,
96
+ 'modernjs.operation.route': operationContext.routePath,
97
+ 'modernjs.operation.source': operationContext.source,
98
+ };
99
+ return operationContext.traceId === undefined
100
+ ? attributes
101
+ : { ...attributes, 'modernjs.trace.id': operationContext.traceId };
102
+ };