@bleedingdev/modern-js-bff-effect 3.9.0-ultramodern.8 → 3.9.3-ultramodern.1
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/assembly.js +2 -2
- package/dist/cjs/data-platform/batch/options.js +48 -0
- package/dist/cjs/data-platform/batch/queue.js +48 -83
- package/dist/cjs/effect/context.js +28 -4
- package/dist/cjs/effect/edge-dispatcher.js +19 -19
- package/dist/cjs/effect/edge.js +26 -6
- package/dist/cjs/effect/entry-shape.js +38 -10
- package/dist/cjs/effect/handler/batch-handler.js +2 -3
- package/dist/cjs/effect/handler/batch.js +0 -9
- package/dist/cjs/effect/handler/http.js +1 -1
- package/dist/cjs/effect/handler/openapi.js +3 -3
- package/dist/cjs/effect/handler/rpc.js +2 -2
- package/dist/cjs/effect/handler.js +26 -26
- package/dist/cjs/effect/index.js +8 -8
- package/dist/cjs/effect/module.js +6 -2
- package/dist/cjs/effect/operation-context.js +3 -7
- package/dist/cjs/effect/rpcSerialization.js +1 -3
- package/dist/cjs/effect-client/cross-project.js +8 -11
- package/dist/cjs/effect-client/index.js +10 -10
- package/dist/cjs/microvertical-api.js +1 -1
- package/dist/esm/assembly.mjs +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/context.mjs +21 -6
- package/dist/esm/effect/edge-dispatcher.mjs +5 -5
- package/dist/esm/effect/edge.mjs +2 -0
- package/dist/esm/effect/entry-shape-registry.mjs +2 -2
- package/dist/esm/effect/entry-shape.mjs +27 -4
- package/dist/esm/effect/handler/batch-handler.mjs +3 -4
- package/dist/esm/effect/handler/batch.mjs +1 -7
- package/dist/esm/effect/handler/http.mjs +1 -1
- package/dist/esm/effect/handler/openapi.mjs +2 -2
- package/dist/esm/effect/handler/rpc.mjs +2 -2
- package/dist/esm/effect/handler.mjs +6 -6
- package/dist/esm/effect/index.mjs +1 -1
- package/dist/esm/effect/module.mjs +5 -1
- package/dist/esm/effect/operation-context.mjs +2 -3
- package/dist/esm/effect/rpcSerialization.mjs +1 -3
- package/dist/esm/effect-client/cross-project.mjs +8 -11
- package/dist/esm/effect-client/index.mjs +5 -5
- package/dist/esm/microvertical-api.mjs +1 -1
- package/dist/esm-node/assembly.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/context.mjs +21 -6
- package/dist/esm-node/effect/edge-dispatcher.mjs +5 -5
- package/dist/esm-node/effect/edge.mjs +2 -0
- package/dist/esm-node/effect/entry-shape.mjs +27 -4
- 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/handler/http.mjs +1 -1
- package/dist/esm-node/effect/handler/openapi.mjs +2 -2
- package/dist/esm-node/effect/handler/rpc.mjs +2 -2
- package/dist/esm-node/effect/handler.mjs +6 -6
- package/dist/esm-node/effect/index.mjs +1 -1
- package/dist/esm-node/effect/module.mjs +5 -1
- package/dist/esm-node/effect/operation-context.mjs +2 -3
- package/dist/esm-node/effect/rpcSerialization.mjs +1 -3
- package/dist/esm-node/effect-client/cross-project.mjs +8 -11
- package/dist/esm-node/effect-client/index.mjs +5 -5
- package/dist/esm-node/microvertical-api.mjs +1 -1
- package/dist/types/assembly.d.ts +2 -2
- 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 +14 -1
- package/dist/types/effect/edge-dispatcher.d.ts +4 -4
- package/dist/types/effect/edge.d.ts +2 -0
- package/dist/types/effect/entry-shape.d.ts +14 -0
- package/dist/types/effect/handler/batch.d.ts +0 -1
- package/dist/types/effect/handler/definition.d.ts +2 -2
- package/dist/types/effect/handler/http.d.ts +2 -2
- package/dist/types/effect/handler/openapi.d.ts +2 -2
- package/dist/types/effect/handler/rpc.d.ts +1 -1
- package/dist/types/effect/handler/types.d.ts +5 -5
- package/dist/types/effect/handler.d.ts +5 -5
- package/dist/types/effect/module.d.ts +2 -1
- package/dist/types/effect/operation-context.d.ts +0 -1
- package/dist/types/effect/rpcSerialization.d.ts +1 -1
- package/dist/types/effect-client/cross-project.d.ts +2 -2
- package/dist/types/effect-client/index.d.ts +6 -6
- package/dist/types/microvertical-api.d.ts +4 -4
- package/package.json +39 -45
- package/src/assembly.ts +3 -2
- package/src/data-platform/batch/options.ts +20 -0
- package/src/data-platform/batch/queue.ts +61 -89
- package/src/effect/context.ts +64 -12
- package/src/effect/edge-dispatcher.ts +13 -12
- package/src/effect/edge.ts +5 -0
- package/src/effect/entry-shape.ts +97 -7
- package/src/effect/handler/batch-handler.ts +6 -12
- package/src/effect/handler/batch.ts +0 -12
- package/src/effect/handler/definition.ts +2 -3
- package/src/effect/handler/http.ts +4 -3
- package/src/effect/handler/openapi.ts +2 -2
- package/src/effect/handler/rpc.ts +3 -2
- package/src/effect/handler/types.ts +6 -12
- package/src/effect/handler.ts +5 -5
- package/src/effect/index.ts +1 -1
- package/src/effect/module.ts +8 -1
- package/src/effect/operation-context.ts +1 -5
- package/src/effect/rpcSerialization.ts +1 -3
- package/src/effect-client/cross-project.ts +11 -16
- package/src/effect-client/index.ts +11 -15
- package/src/microvertical-api.ts +1 -1
- package/dist/cjs/effect/edge-context.js +0 -61
- package/dist/esm/effect/edge-context.mjs +0 -14
- package/dist/esm-node/effect/edge-context.mjs +0 -15
- package/dist/types/effect/edge-context.d.ts +0 -5
- package/src/effect/edge-context.ts +0 -51
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { type RequestContextInput } from '@modern-js/runtime-extensions/request-context';
|
|
2
2
|
import * as Effect from 'effect/Effect';
|
|
3
|
+
import { HttpClient } from 'effect/http';
|
|
4
|
+
import { HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from 'effect/http-api';
|
|
3
5
|
import * as Layer from 'effect/Layer';
|
|
4
|
-
import {
|
|
5
|
-
import { HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from 'effect/unstable/httpapi';
|
|
6
|
-
import { Rpc, RpcClient, RpcGroup, RpcSchema, RpcSerialization } from 'effect/unstable/rpc';
|
|
6
|
+
import { Rpc, RpcClient, RpcClientError, RpcGroup, RpcSchema, RpcSerialization } from 'effect/rpc';
|
|
7
7
|
import { type EffectCrossProjectClientOptions } from './cross-project.js';
|
|
8
8
|
export * as Effect from 'effect/Effect';
|
|
9
|
+
export * as HttpClientError from 'effect/http/HttpClientError';
|
|
9
10
|
export * as Layer from 'effect/Layer';
|
|
10
11
|
export * as Schema from 'effect/Schema';
|
|
11
|
-
export * as HttpClientError from 'effect/unstable/http/HttpClientError';
|
|
12
12
|
export type { EffectCrossProjectClientOptions } from './cross-project.js';
|
|
13
13
|
export { HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup, HttpApiSchema, Rpc, RpcClient, RpcGroup, RpcSchema, RpcSerialization, };
|
|
14
14
|
export type EffectHttpApiClientOptions = {
|
|
@@ -30,7 +30,7 @@ export type EffectViewSelection<T> = {
|
|
|
30
30
|
export type EffectViewData<T, Selection> = Selection extends true ? T : Selection extends Record<string, unknown> ? {
|
|
31
31
|
[K in keyof Selection & keyof T]: EffectMaskedValue<T[K], Selection[K]>;
|
|
32
32
|
} : T;
|
|
33
|
-
export type EffectRpcSerialization = 'json' | 'ndjson' | 'jsonRpc' | 'ndJsonRpc'
|
|
33
|
+
export type EffectRpcSerialization = 'json' | 'ndjson' | 'jsonRpc' | 'ndJsonRpc';
|
|
34
34
|
declare const EffectRpcClientError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
35
35
|
readonly _tag: "EffectRpcClientError";
|
|
36
36
|
} & Readonly<A>;
|
|
@@ -50,7 +50,7 @@ export type EffectRpcClientOptions<Rpcs extends Rpc.Any, Flatten extends boolean
|
|
|
50
50
|
flatten?: Flatten;
|
|
51
51
|
serialization?: EffectRpcSerialization;
|
|
52
52
|
};
|
|
53
|
-
export type EffectRpcClientHandle<Rpcs extends Rpc.Any, Flatten extends boolean = false> = (Flatten extends true ? RpcClient.RpcClient.Flat<Rpcs,
|
|
53
|
+
export type EffectRpcClientHandle<Rpcs extends Rpc.Any, Flatten extends boolean = false> = (Flatten extends true ? RpcClient.RpcClient.Flat<Rpcs, RpcClientError.RpcClientError> : RpcClient.RpcClient<Rpcs, RpcClientError.RpcClientError>) & {
|
|
54
54
|
dispose: () => Promise<void>;
|
|
55
55
|
};
|
|
56
56
|
export declare function view<T>(): <const Selection extends EffectViewSelection<T>>(selection: Selection) => Selection;
|
|
@@ -33,13 +33,13 @@ export declare const MicroVerticalReadinessSchema: Schema.Struct<{
|
|
|
33
33
|
readonly versionSkew: Schema.Literal<"none">;
|
|
34
34
|
}>;
|
|
35
35
|
export type MicroVerticalReadiness = typeof MicroVerticalReadinessSchema.Type;
|
|
36
|
-
export declare const MicroVerticalOperationSourceSchema: Schema.Literals<readonly ["client", "server", "generated-client", "effect-
|
|
36
|
+
export declare const MicroVerticalOperationSourceSchema: Schema.Literals<readonly ["client", "server", "generated-client", "effect-server", "data-platform", "unknown"]>;
|
|
37
37
|
export type MicroVerticalOperationSource = typeof MicroVerticalOperationSourceSchema.Type;
|
|
38
38
|
export declare const MicroVerticalOperationContextSchema: Schema.Struct<{
|
|
39
39
|
readonly method: Schema.String;
|
|
40
40
|
readonly operationId: Schema.compose<Schema.String, Schema.brand<Schema.String, "MicroVerticalOperationId">>;
|
|
41
41
|
readonly routePath: Schema.String;
|
|
42
|
-
readonly source: Schema.Literals<readonly ["client", "server", "generated-client", "effect-
|
|
42
|
+
readonly source: Schema.Literals<readonly ["client", "server", "generated-client", "effect-server", "data-platform", "unknown"]>;
|
|
43
43
|
readonly traceId: Schema.optionalKey<Schema.compose<Schema.String, Schema.brand<Schema.String, "MicroVerticalTraceId">>>;
|
|
44
44
|
}>;
|
|
45
45
|
export type MicroVerticalOperationContext = typeof MicroVerticalOperationContextSchema.Type;
|
|
@@ -55,11 +55,11 @@ export declare const microVerticalOperationAttributes: (operationContext: MicroV
|
|
|
55
55
|
'modernjs.operation.id': string;
|
|
56
56
|
'modernjs.operation.method': string;
|
|
57
57
|
'modernjs.operation.route': string;
|
|
58
|
-
'modernjs.operation.source': "client" | "data-platform" | "effect-
|
|
58
|
+
'modernjs.operation.source': "client" | "data-platform" | "effect-server" | "generated-client" | "server" | "unknown";
|
|
59
59
|
} | {
|
|
60
60
|
'modernjs.operation.id': string;
|
|
61
61
|
'modernjs.operation.method': string;
|
|
62
62
|
'modernjs.operation.route': string;
|
|
63
|
-
'modernjs.operation.source': "client" | "data-platform" | "effect-
|
|
63
|
+
'modernjs.operation.source': "client" | "data-platform" | "effect-server" | "generated-client" | "server" | "unknown";
|
|
64
64
|
'modernjs.trace.id': string;
|
|
65
65
|
};
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bleedingdev/modern-js-bff-effect",
|
|
3
3
|
"description": "Fork-owned Effect runtime, client, edge dispatcher, and data-platform primitives for UltraModern.js BFFs.",
|
|
4
|
-
"homepage": "https://github.com/
|
|
4
|
+
"homepage": "https://github.com/ultramodernjs/ultramodern.js#readme",
|
|
5
5
|
"bugs": {
|
|
6
|
-
"url": "https://github.com/
|
|
6
|
+
"url": "https://github.com/ultramodernjs/ultramodern.js/issues"
|
|
7
7
|
},
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/
|
|
10
|
+
"url": "git+https://github.com/ultramodernjs/ultramodern.js.git",
|
|
11
11
|
"directory": "packages/server/bff-effect"
|
|
12
12
|
},
|
|
13
13
|
"license": "MIT",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"modern.js",
|
|
19
19
|
"ultramodern.js"
|
|
20
20
|
],
|
|
21
|
-
"version": "3.9.
|
|
21
|
+
"version": "3.9.3-ultramodern.1",
|
|
22
22
|
"files": [
|
|
23
23
|
"dist",
|
|
24
24
|
"src"
|
|
@@ -35,62 +35,49 @@
|
|
|
35
35
|
"./package.json": "./package.json",
|
|
36
36
|
".": {
|
|
37
37
|
"types": "./dist/types/index.d.ts",
|
|
38
|
-
"node":
|
|
39
|
-
|
|
40
|
-
"require": "./dist/cjs/index.js"
|
|
41
|
-
},
|
|
38
|
+
"node": "./dist/esm-node/index.mjs",
|
|
39
|
+
"import": "./dist/esm/index.mjs",
|
|
42
40
|
"default": "./dist/cjs/index.js"
|
|
43
41
|
},
|
|
44
42
|
"./effect": {
|
|
45
43
|
"types": "./dist/types/effect/index.d.ts",
|
|
46
|
-
"node":
|
|
47
|
-
|
|
48
|
-
"require": "./dist/cjs/effect/index.js"
|
|
49
|
-
},
|
|
44
|
+
"node": "./dist/esm-node/effect/index.mjs",
|
|
45
|
+
"import": "./dist/esm/effect/index.mjs",
|
|
50
46
|
"default": "./dist/cjs/effect/index.js"
|
|
51
47
|
},
|
|
48
|
+
"./context": {
|
|
49
|
+
"types": "./dist/types/effect/context.d.ts",
|
|
50
|
+
"node": "./dist/esm-node/effect/context.mjs",
|
|
51
|
+
"import": "./dist/esm/effect/context.mjs",
|
|
52
|
+
"default": "./dist/cjs/effect/context.js"
|
|
53
|
+
},
|
|
52
54
|
"./effect-edge": {
|
|
53
55
|
"types": "./dist/types/effect/edge.d.ts",
|
|
54
|
-
"node":
|
|
55
|
-
"import": "./dist/esm-node/effect/edge.mjs",
|
|
56
|
-
"require": "./dist/cjs/effect/edge.js"
|
|
57
|
-
},
|
|
56
|
+
"node": "./dist/esm-node/effect/edge.mjs",
|
|
58
57
|
"import": "./dist/esm/effect/edge.mjs",
|
|
59
58
|
"default": "./dist/esm/effect/edge.mjs"
|
|
60
59
|
},
|
|
61
60
|
"./effect-client": {
|
|
62
61
|
"types": "./dist/types/effect-client/index.d.ts",
|
|
63
|
-
"node":
|
|
64
|
-
"import": "./dist/esm-node/effect-client/index.mjs",
|
|
65
|
-
"require": "./dist/cjs/effect-client/index.js"
|
|
66
|
-
},
|
|
62
|
+
"node": "./dist/esm-node/effect-client/index.mjs",
|
|
67
63
|
"import": "./dist/esm/effect-client/index.mjs",
|
|
68
64
|
"default": "./dist/esm/effect-client/index.mjs"
|
|
69
65
|
},
|
|
70
66
|
"./microvertical-api": {
|
|
71
67
|
"types": "./dist/types/microvertical-api.d.ts",
|
|
72
|
-
"node":
|
|
73
|
-
"import": "./dist/esm-node/microvertical-api.mjs",
|
|
74
|
-
"require": "./dist/cjs/microvertical-api.js"
|
|
75
|
-
},
|
|
68
|
+
"node": "./dist/esm-node/microvertical-api.mjs",
|
|
76
69
|
"import": "./dist/esm/microvertical-api.mjs",
|
|
77
70
|
"default": "./dist/esm/microvertical-api.mjs"
|
|
78
71
|
},
|
|
79
72
|
"./data-platform": {
|
|
80
73
|
"types": "./dist/types/data-platform/index.d.ts",
|
|
81
|
-
"node":
|
|
82
|
-
"import": "./dist/esm-node/data-platform/index.mjs",
|
|
83
|
-
"require": "./dist/cjs/data-platform/index.js"
|
|
84
|
-
},
|
|
74
|
+
"node": "./dist/esm-node/data-platform/index.mjs",
|
|
85
75
|
"import": "./dist/esm/data-platform/index.mjs",
|
|
86
76
|
"default": "./dist/esm/data-platform/index.mjs"
|
|
87
77
|
},
|
|
88
78
|
"./assembly": {
|
|
89
79
|
"types": "./dist/types/assembly.d.ts",
|
|
90
|
-
"node":
|
|
91
|
-
"import": "./dist/esm-node/assembly.mjs",
|
|
92
|
-
"require": "./dist/cjs/assembly.js"
|
|
93
|
-
},
|
|
80
|
+
"node": "./dist/esm-node/assembly.mjs",
|
|
94
81
|
"default": "./dist/esm/assembly.mjs"
|
|
95
82
|
}
|
|
96
83
|
},
|
|
@@ -104,6 +91,10 @@
|
|
|
104
91
|
"./dist/types/effect/index.d.ts",
|
|
105
92
|
"./src/effect/index.ts"
|
|
106
93
|
],
|
|
94
|
+
"context": [
|
|
95
|
+
"./dist/types/effect/context.d.ts",
|
|
96
|
+
"./src/effect/context.ts"
|
|
97
|
+
],
|
|
107
98
|
"effect-edge": [
|
|
108
99
|
"./dist/types/effect/edge.d.ts",
|
|
109
100
|
"./src/effect/edge.ts"
|
|
@@ -127,13 +118,13 @@
|
|
|
127
118
|
}
|
|
128
119
|
},
|
|
129
120
|
"engines": {
|
|
130
|
-
"node": ">=26.
|
|
121
|
+
"node": ">=26.10.0"
|
|
131
122
|
},
|
|
132
123
|
"dependencies": {
|
|
133
|
-
"@modern-js/bff-core": "npm:@bleedingdev/modern-js-bff-core@3.9.
|
|
134
|
-
"@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.9.
|
|
135
|
-
"@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.
|
|
136
|
-
"@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.
|
|
124
|
+
"@modern-js/bff-core": "npm:@bleedingdev/modern-js-bff-core@3.9.3-ultramodern.1",
|
|
125
|
+
"@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.9.3-ultramodern.1",
|
|
126
|
+
"@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.3-ultramodern.1",
|
|
127
|
+
"@modern-js/server-runtime-extensions": "npm:@bleedingdev/modern-js-server-runtime-extensions@3.9.3-ultramodern.1",
|
|
137
128
|
"@opentelemetry/api": "1.9.1",
|
|
138
129
|
"@opentelemetry/api-logs": "0.222.0",
|
|
139
130
|
"@opentelemetry/resources": "2.11.0",
|
|
@@ -145,8 +136,8 @@
|
|
|
145
136
|
"@opentelemetry/semantic-conventions": "1.43.0"
|
|
146
137
|
},
|
|
147
138
|
"peerDependencies": {
|
|
148
|
-
"@effect/opentelemetry": "4.0.0
|
|
149
|
-
"effect": "4.0.0
|
|
139
|
+
"@effect/opentelemetry": "4.0.0",
|
|
140
|
+
"effect": "4.0.0"
|
|
150
141
|
},
|
|
151
142
|
"peerDependenciesMeta": {
|
|
152
143
|
"@effect/opentelemetry": {
|
|
@@ -157,13 +148,13 @@
|
|
|
157
148
|
}
|
|
158
149
|
},
|
|
159
150
|
"devDependencies": {
|
|
160
|
-
"@effect/opentelemetry": "4.0.0
|
|
161
|
-
"@rslib/core": "1.0.
|
|
151
|
+
"@effect/opentelemetry": "4.0.0",
|
|
152
|
+
"@rslib/core": "1.0.3",
|
|
162
153
|
"@scripts/rstest-config": "2.66.0",
|
|
163
|
-
"@types/node": "^26.
|
|
164
|
-
"@types/react": "^19.
|
|
165
|
-
"
|
|
166
|
-
"
|
|
154
|
+
"@types/node": "^26.6.3",
|
|
155
|
+
"@types/react": "^19.3.0",
|
|
156
|
+
"effect": "4.0.0",
|
|
157
|
+
"esbuild": "^0.28.2"
|
|
167
158
|
},
|
|
168
159
|
"sideEffects": false,
|
|
169
160
|
"publishConfig": {
|
|
@@ -173,5 +164,8 @@
|
|
|
173
164
|
"dev": "rslib build --watch",
|
|
174
165
|
"build": "rslib build",
|
|
175
166
|
"test": "rstest"
|
|
167
|
+
},
|
|
168
|
+
"ultramodern": {
|
|
169
|
+
"sourceName": "@modern-js/bff-effect"
|
|
176
170
|
}
|
|
177
171
|
}
|
package/src/assembly.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** Server-only assembly for the invariant tail of a strict Effect BFF runtime factory. */
|
|
2
|
+
|
|
3
|
+
import type { HttpApi, HttpApiGroup } from 'effect/http-api';
|
|
4
|
+
import { HttpApiBuilder } from 'effect/http-api';
|
|
2
5
|
import * as Layer from 'effect/Layer';
|
|
3
|
-
import type { HttpApi, HttpApiGroup } from 'effect/unstable/httpapi';
|
|
4
|
-
import { HttpApiBuilder } from 'effect/unstable/httpapi';
|
|
5
6
|
import { defineEffectBff } from './effect/handler/definition';
|
|
6
7
|
import type { EffectRuntimeRequirements } from './effect/handler/types';
|
|
7
8
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DataBatchTransportOptions } from '../types';
|
|
2
|
+
|
|
3
|
+
/** Wire limits and method eligibility are identical on both sides. */
|
|
4
|
+
export function normalizeBatchLimits(
|
|
5
|
+
options: Pick<
|
|
6
|
+
DataBatchTransportOptions,
|
|
7
|
+
'maxBatchSize' | 'maxBatchBytes' | 'allowedMethods'
|
|
8
|
+
> = {},
|
|
9
|
+
) {
|
|
10
|
+
return {
|
|
11
|
+
maxBatchSize: Math.max(1, options.maxBatchSize ?? 16),
|
|
12
|
+
maxBatchBytes: Math.max(1024, options.maxBatchBytes ?? 64 * 1024),
|
|
13
|
+
allowedMethods: new Set(
|
|
14
|
+
(options.allowedMethods !== undefined && options.allowedMethods.length > 0
|
|
15
|
+
? options.allowedMethods
|
|
16
|
+
: ['GET']
|
|
17
|
+
).map(method => method.toUpperCase()),
|
|
18
|
+
),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -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
|
}
|
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;
|