@bleedingdev/modern-js-bff-effect 3.8.3-ultramodern.2
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/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/cjs/data-platform/batch/protocol.js +130 -0
- package/dist/cjs/data-platform/batch/queue.js +623 -0
- package/dist/cjs/data-platform/batch/request.js +121 -0
- package/dist/cjs/data-platform/batch/response.js +65 -0
- package/dist/cjs/data-platform/batch/telemetry.js +71 -0
- package/dist/cjs/data-platform/batch/url.js +69 -0
- package/dist/cjs/data-platform/batch-transport.js +59 -0
- package/dist/cjs/data-platform/codec.js +155 -0
- package/dist/cjs/data-platform/index.js +124 -0
- package/dist/cjs/data-platform/invalidation.js +61 -0
- package/dist/cjs/data-platform/trace.js +73 -0
- package/dist/cjs/data-platform/types.js +48 -0
- package/dist/cjs/data-platform/validation.js +178 -0
- package/dist/cjs/effect/context.js +59 -0
- package/dist/cjs/effect/dispatch.js +101 -0
- package/dist/cjs/effect/edge-context.js +61 -0
- package/dist/cjs/effect/edge-dispatcher.js +354 -0
- package/dist/cjs/effect/edge.js +62 -0
- package/dist/cjs/effect/endpoint-contracts.js +121 -0
- package/dist/cjs/effect/entry-shape-registry.js +31 -0
- package/dist/cjs/effect/entry-shape.js +113 -0
- package/dist/cjs/effect/handler/batch-handler.js +270 -0
- package/dist/cjs/effect/handler/batch.js +170 -0
- package/dist/cjs/effect/handler/definition.js +99 -0
- package/dist/cjs/effect/handler/envelope.js +104 -0
- package/dist/cjs/effect/handler/http.js +107 -0
- package/dist/cjs/effect/handler/openapi.js +58 -0
- package/dist/cjs/effect/handler/routing.js +100 -0
- package/dist/cjs/effect/handler/rpc.js +63 -0
- package/dist/cjs/effect/handler/types.js +43 -0
- package/dist/cjs/effect/handler.js +173 -0
- package/dist/cjs/effect/index.js +137 -0
- package/dist/cjs/effect/module.js +118 -0
- package/dist/cjs/effect/operation-context.js +116 -0
- package/dist/cjs/effect/rpcSerialization.js +54 -0
- package/dist/cjs/effect-client/batch.js +73 -0
- package/dist/cjs/effect-client/envelope.js +145 -0
- package/dist/cjs/effect-client/guards.js +44 -0
- package/dist/cjs/effect-client/index.js +208 -0
- package/dist/cjs/effect-client/request-context.js +64 -0
- package/dist/cjs/effect-client/request.js +66 -0
- package/dist/cjs/effect-client/runtime.js +100 -0
- package/dist/cjs/effect-client/types.js +18 -0
- package/dist/cjs/headers.js +46 -0
- package/dist/cjs/index.js +83 -0
- package/dist/esm/data-platform/batch/protocol.mjs +71 -0
- package/dist/esm/data-platform/batch/queue.mjs +582 -0
- package/dist/esm/data-platform/batch/request.mjs +68 -0
- package/dist/esm/data-platform/batch/response.mjs +24 -0
- package/dist/esm/data-platform/batch/telemetry.mjs +27 -0
- package/dist/esm/data-platform/batch/url.mjs +25 -0
- package/dist/esm/data-platform/batch-transport.mjs +12 -0
- package/dist/esm/data-platform/codec.mjs +90 -0
- package/dist/esm/data-platform/index.mjs +7 -0
- package/dist/esm/data-platform/invalidation.mjs +20 -0
- package/dist/esm/data-platform/trace.mjs +29 -0
- package/dist/esm/data-platform/types.mjs +4 -0
- package/dist/esm/data-platform/validation.mjs +128 -0
- package/dist/esm/effect/context.mjs +12 -0
- package/dist/esm/effect/dispatch.mjs +63 -0
- package/dist/esm/effect/edge-context.mjs +14 -0
- package/dist/esm/effect/edge-dispatcher.mjs +115 -0
- package/dist/esm/effect/edge.mjs +1 -0
- package/dist/esm/effect/endpoint-contracts.mjs +68 -0
- package/dist/esm/effect/entry-shape-registry.mjs +17 -0
- package/dist/esm/effect/entry-shape.mjs +51 -0
- package/dist/esm/effect/handler/batch-handler.mjs +232 -0
- package/dist/esm/effect/handler/batch.mjs +105 -0
- package/dist/esm/effect/handler/definition.mjs +58 -0
- package/dist/esm/effect/handler/envelope.mjs +63 -0
- package/dist/esm/effect/handler/http.mjs +69 -0
- package/dist/esm/effect/handler/openapi.mjs +20 -0
- package/dist/esm/effect/handler/routing.mjs +50 -0
- package/dist/esm/effect/handler/rpc.mjs +22 -0
- package/dist/esm/effect/handler/types.mjs +5 -0
- package/dist/esm/effect/handler.mjs +13 -0
- package/dist/esm/effect/index.mjs +7 -0
- package/dist/esm/effect/module.mjs +80 -0
- package/dist/esm/effect/operation-context.mjs +75 -0
- package/dist/esm/effect/rpcSerialization.mjs +16 -0
- package/dist/esm/effect-client/batch.mjs +35 -0
- package/dist/esm/effect-client/envelope.mjs +104 -0
- package/dist/esm/effect-client/guards.mjs +3 -0
- package/dist/esm/effect-client/index.mjs +110 -0
- package/dist/esm/effect-client/request-context.mjs +23 -0
- package/dist/esm/effect-client/request.mjs +28 -0
- package/dist/esm/effect-client/runtime.mjs +62 -0
- package/dist/esm/effect-client/types.mjs +0 -0
- package/dist/esm/headers.mjs +8 -0
- package/dist/esm/index.mjs +4 -0
- package/dist/esm/rslib-runtime.mjs +18 -0
- package/dist/esm-node/data-platform/batch/protocol.mjs +72 -0
- package/dist/esm-node/data-platform/batch/queue.mjs +583 -0
- package/dist/esm-node/data-platform/batch/request.mjs +69 -0
- package/dist/esm-node/data-platform/batch/response.mjs +25 -0
- package/dist/esm-node/data-platform/batch/telemetry.mjs +28 -0
- package/dist/esm-node/data-platform/batch/url.mjs +26 -0
- package/dist/esm-node/data-platform/batch-transport.mjs +13 -0
- package/dist/esm-node/data-platform/codec.mjs +91 -0
- package/dist/esm-node/data-platform/index.mjs +8 -0
- package/dist/esm-node/data-platform/invalidation.mjs +21 -0
- package/dist/esm-node/data-platform/trace.mjs +30 -0
- package/dist/esm-node/data-platform/types.mjs +5 -0
- package/dist/esm-node/data-platform/validation.mjs +129 -0
- package/dist/esm-node/effect/context.mjs +13 -0
- package/dist/esm-node/effect/dispatch.mjs +64 -0
- package/dist/esm-node/effect/edge-context.mjs +15 -0
- package/dist/esm-node/effect/edge-dispatcher.mjs +116 -0
- package/dist/esm-node/effect/edge.mjs +2 -0
- package/dist/esm-node/effect/endpoint-contracts.mjs +69 -0
- package/dist/esm-node/effect/entry-shape-registry.mjs +18 -0
- package/dist/esm-node/effect/entry-shape.mjs +63 -0
- package/dist/esm-node/effect/handler/batch-handler.mjs +233 -0
- package/dist/esm-node/effect/handler/batch.mjs +106 -0
- package/dist/esm-node/effect/handler/definition.mjs +59 -0
- package/dist/esm-node/effect/handler/envelope.mjs +64 -0
- package/dist/esm-node/effect/handler/http.mjs +70 -0
- package/dist/esm-node/effect/handler/openapi.mjs +21 -0
- package/dist/esm-node/effect/handler/routing.mjs +51 -0
- package/dist/esm-node/effect/handler/rpc.mjs +23 -0
- package/dist/esm-node/effect/handler/types.mjs +6 -0
- package/dist/esm-node/effect/handler.mjs +14 -0
- package/dist/esm-node/effect/index.mjs +8 -0
- package/dist/esm-node/effect/module.mjs +81 -0
- package/dist/esm-node/effect/operation-context.mjs +76 -0
- package/dist/esm-node/effect/rpcSerialization.mjs +17 -0
- package/dist/esm-node/effect-client/batch.mjs +36 -0
- package/dist/esm-node/effect-client/envelope.mjs +105 -0
- package/dist/esm-node/effect-client/guards.mjs +4 -0
- package/dist/esm-node/effect-client/index.mjs +111 -0
- package/dist/esm-node/effect-client/request-context.mjs +24 -0
- package/dist/esm-node/effect-client/request.mjs +29 -0
- package/dist/esm-node/effect-client/runtime.mjs +63 -0
- package/dist/esm-node/effect-client/types.mjs +1 -0
- package/dist/esm-node/headers.mjs +9 -0
- package/dist/esm-node/index.mjs +5 -0
- package/dist/esm-node/rslib-runtime.mjs +19 -0
- package/dist/types/data-platform/batch/protocol.d.ts +9 -0
- package/dist/types/data-platform/batch/queue.d.ts +47 -0
- package/dist/types/data-platform/batch/request.d.ts +19 -0
- package/dist/types/data-platform/batch/response.d.ts +3 -0
- package/dist/types/data-platform/batch/telemetry.d.ts +4 -0
- package/dist/types/data-platform/batch/url.d.ts +4 -0
- package/dist/types/data-platform/batch-transport.d.ts +3 -0
- package/dist/types/data-platform/codec.d.ts +17 -0
- package/dist/types/data-platform/index.d.ts +8 -0
- package/dist/types/data-platform/invalidation.d.ts +8 -0
- package/dist/types/data-platform/trace.d.ts +4 -0
- package/dist/types/data-platform/types.d.ts +151 -0
- package/dist/types/data-platform/validation.d.ts +29 -0
- package/dist/types/effect/context.d.ts +5 -0
- package/dist/types/effect/dispatch.d.ts +13 -0
- package/dist/types/effect/edge-context.d.ts +5 -0
- package/dist/types/effect/edge-dispatcher.d.ts +37 -0
- package/dist/types/effect/edge.d.ts +1 -0
- package/dist/types/effect/endpoint-contracts.d.ts +67 -0
- package/dist/types/effect/entry-shape.d.ts +32 -0
- package/dist/types/effect/handler/batch-handler.d.ts +11 -0
- package/dist/types/effect/handler/batch.d.ts +15 -0
- package/dist/types/effect/handler/definition.d.ts +7 -0
- package/dist/types/effect/handler/envelope.d.ts +13 -0
- package/dist/types/effect/handler/http.d.ts +15 -0
- package/dist/types/effect/handler/openapi.d.ts +4 -0
- package/dist/types/effect/handler/routing.d.ts +5 -0
- package/dist/types/effect/handler/rpc.d.ts +7 -0
- package/dist/types/effect/handler/types.d.ts +153 -0
- package/dist/types/effect/handler.d.ts +13 -0
- package/dist/types/effect/index.d.ts +6 -0
- package/dist/types/effect/module.d.ts +36 -0
- package/dist/types/effect/operation-context.d.ts +15 -0
- package/dist/types/effect/rpcSerialization.d.ts +3 -0
- package/dist/types/effect-client/batch.d.ts +2 -0
- package/dist/types/effect-client/envelope.d.ts +15 -0
- package/dist/types/effect-client/guards.d.ts +2 -0
- package/dist/types/effect-client/index.d.ts +58 -0
- package/dist/types/effect-client/request-context.d.ts +3 -0
- package/dist/types/effect-client/request.d.ts +1 -0
- package/dist/types/effect-client/runtime.d.ts +5 -0
- package/dist/types/effect-client/types.d.ts +68 -0
- package/dist/types/headers.d.ts +1 -0
- package/dist/types/index.d.ts +3 -0
- package/package.json +164 -0
- package/src/data-platform/batch/protocol.ts +112 -0
- package/src/data-platform/batch/queue.ts +876 -0
- package/src/data-platform/batch/request.ts +130 -0
- package/src/data-platform/batch/response.ts +74 -0
- package/src/data-platform/batch/telemetry.ts +47 -0
- package/src/data-platform/batch/url.ts +54 -0
- package/src/data-platform/batch-transport.ts +22 -0
- package/src/data-platform/codec.ts +145 -0
- package/src/data-platform/index.ts +63 -0
- package/src/data-platform/invalidation.ts +57 -0
- package/src/data-platform/trace.ts +49 -0
- package/src/data-platform/types.ts +186 -0
- package/src/data-platform/validation.ts +303 -0
- package/src/effect/context.ts +37 -0
- package/src/effect/dispatch.ts +132 -0
- package/src/effect/edge-context.ts +51 -0
- package/src/effect/edge-dispatcher.ts +310 -0
- package/src/effect/edge.ts +1 -0
- package/src/effect/endpoint-contracts.ts +191 -0
- package/src/effect/entry-shape-registry.cjs +16 -0
- package/src/effect/entry-shape-registry.d.cts +6 -0
- package/src/effect/entry-shape.ts +155 -0
- package/src/effect/handler/batch-handler.ts +475 -0
- package/src/effect/handler/batch.ts +182 -0
- package/src/effect/handler/definition.ts +121 -0
- package/src/effect/handler/envelope.ts +133 -0
- package/src/effect/handler/http.ts +133 -0
- package/src/effect/handler/openapi.ts +45 -0
- package/src/effect/handler/routing.ts +91 -0
- package/src/effect/handler/rpc.ts +45 -0
- package/src/effect/handler/types.ts +255 -0
- package/src/effect/handler.ts +33 -0
- package/src/effect/index.ts +22 -0
- package/src/effect/module.ts +186 -0
- package/src/effect/operation-context.ts +144 -0
- package/src/effect/rpcSerialization.ts +20 -0
- package/src/effect-client/batch.ts +50 -0
- package/src/effect-client/envelope.ts +238 -0
- package/src/effect-client/guards.ts +6 -0
- package/src/effect-client/index.ts +325 -0
- package/src/effect-client/request-context.ts +54 -0
- package/src/effect-client/request.ts +51 -0
- package/src/effect-client/runtime.ts +122 -0
- package/src/effect-client/types.ts +84 -0
- package/src/headers.ts +7 -0
- package/src/index.ts +3 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { trace } from "@opentelemetry/api";
|
|
2
|
+
const DATA_BATCH_TRANSPORT_OTEL_EVENT = 'modernjs.data.batch';
|
|
3
|
+
function createDataBatchTransportTelemetryAttributes(event) {
|
|
4
|
+
return {
|
|
5
|
+
'modernjs.data.batch.type': event.type,
|
|
6
|
+
'modernjs.data.batch.endpoint': event.endpoint,
|
|
7
|
+
'modernjs.data.batch.degraded': 'fallback' === event.type || 'disable' === event.type,
|
|
8
|
+
...event.batchId ? {
|
|
9
|
+
'modernjs.data.batch.id': event.batchId
|
|
10
|
+
} : {},
|
|
11
|
+
...'number' == typeof event.size ? {
|
|
12
|
+
'modernjs.data.batch.size': event.size
|
|
13
|
+
} : {},
|
|
14
|
+
...event.reason ? {
|
|
15
|
+
'modernjs.data.batch.reason': event.reason
|
|
16
|
+
} : {}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function emitDataBatchTransportEvent(onEvent, event) {
|
|
20
|
+
try {
|
|
21
|
+
onEvent?.(event);
|
|
22
|
+
} catch {}
|
|
23
|
+
try {
|
|
24
|
+
trace.getActiveSpan()?.addEvent(DATA_BATCH_TRANSPORT_OTEL_EVENT, createDataBatchTransportTelemetryAttributes(event));
|
|
25
|
+
} catch {}
|
|
26
|
+
}
|
|
27
|
+
export { DATA_BATCH_TRANSPORT_OTEL_EVENT, createDataBatchTransportTelemetryAttributes, emitDataBatchTransportEvent };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { DEFAULT_DATA_BATCH_ENDPOINT } from "../types.mjs";
|
|
2
|
+
function resolveRuntimeOrigin() {
|
|
3
|
+
if ("u" > typeof window && window.location && 'string' == typeof window.location.origin && window.location.origin) return window.location.origin;
|
|
4
|
+
if ("u" > typeof globalThis && globalThis.location && 'string' == typeof globalThis.location?.origin) return globalThis.location.origin;
|
|
5
|
+
return 'http://localhost';
|
|
6
|
+
}
|
|
7
|
+
function toAbsoluteUrl(input) {
|
|
8
|
+
if (input instanceof URL) return input;
|
|
9
|
+
if ("u" > typeof Request && input instanceof Request) return new URL(input.url);
|
|
10
|
+
const value = String(input);
|
|
11
|
+
try {
|
|
12
|
+
return new URL(value);
|
|
13
|
+
} catch {
|
|
14
|
+
return new URL(value, resolveRuntimeOrigin());
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function normalizeBatchEndpoint(requestUrl, endpoint) {
|
|
18
|
+
const value = endpoint || DEFAULT_DATA_BATCH_ENDPOINT;
|
|
19
|
+
try {
|
|
20
|
+
return new URL(value);
|
|
21
|
+
} catch {
|
|
22
|
+
return new URL(value, requestUrl.origin);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export { normalizeBatchEndpoint, resolveRuntimeOrigin, toAbsoluteUrl };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createBatchTransportQueue } from "./batch/queue.mjs";
|
|
2
|
+
function createDataBatchTransport(options = {}) {
|
|
3
|
+
const fallbackFetch = 'function' == typeof fetch ? fetch.bind(globalThis) : void 0;
|
|
4
|
+
const baseFetch = options.fetch || fallbackFetch;
|
|
5
|
+
if (!baseFetch) throw new Error('createDataBatchTransport requires a fetch implementation');
|
|
6
|
+
return createBatchTransportQueue({
|
|
7
|
+
options,
|
|
8
|
+
baseFetch
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export { DATA_BATCH_TRANSPORT_OTEL_EVENT, createDataBatchTransportTelemetryAttributes, emitDataBatchTransportEvent } from "./batch/telemetry.mjs";
|
|
12
|
+
export { createDataBatchTransport };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
function isPlainObject(value) {
|
|
2
|
+
if ('object' != typeof value || null === value || Array.isArray(value)) return false;
|
|
3
|
+
const proto = Object.getPrototypeOf(value);
|
|
4
|
+
return proto === Object.prototype || null === proto;
|
|
5
|
+
}
|
|
6
|
+
function canonicalize(value) {
|
|
7
|
+
if (Array.isArray(value)) return value.map((item)=>canonicalize(item));
|
|
8
|
+
if (isPlainObject(value)) return Object.keys(value).sort().reduce((acc, key)=>{
|
|
9
|
+
acc[key] = canonicalize(value[key]);
|
|
10
|
+
return acc;
|
|
11
|
+
}, {});
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function stableStringify(value) {
|
|
15
|
+
return JSON.stringify(canonicalize(value));
|
|
16
|
+
}
|
|
17
|
+
function encodeRequestEnvelopeHeader(envelope) {
|
|
18
|
+
return encodeURIComponent(stableStringify(envelope));
|
|
19
|
+
}
|
|
20
|
+
function isRequestEnvelopeShape(value) {
|
|
21
|
+
if (!isPlainObject(value)) return false;
|
|
22
|
+
return 1 === value.protocolVersion && 'string' == typeof value.operationId && 'string' == typeof value.appNamespace && 'string' == typeof value.origin && 'string' == typeof value.scopeKey && 'string' == typeof value.inputHash && 'number' == typeof value.timestamp;
|
|
23
|
+
}
|
|
24
|
+
function decodeRequestEnvelopeHeader(value) {
|
|
25
|
+
try {
|
|
26
|
+
const decoded = decodeURIComponent(value);
|
|
27
|
+
const parsed = JSON.parse(decoded);
|
|
28
|
+
if (!isRequestEnvelopeShape(parsed)) return null;
|
|
29
|
+
return parsed;
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function hashString(value) {
|
|
35
|
+
let hash = 0x811c9dc5;
|
|
36
|
+
for(let index = 0; index < value.length; index++){
|
|
37
|
+
hash ^= value.charCodeAt(index);
|
|
38
|
+
hash = Math.imul(hash, 0x01000193);
|
|
39
|
+
}
|
|
40
|
+
return (hash >>> 0).toString(16).padStart(8, '0');
|
|
41
|
+
}
|
|
42
|
+
function sanitizeSegment(segment) {
|
|
43
|
+
const normalized = segment.trim().replace(/[^a-zA-Z0-9_-]+/g, '_');
|
|
44
|
+
return normalized.length > 0 ? normalized : 'unknown';
|
|
45
|
+
}
|
|
46
|
+
function normalizeOrigin(origin) {
|
|
47
|
+
const normalized = origin.trim();
|
|
48
|
+
try {
|
|
49
|
+
const url = new URL(normalized);
|
|
50
|
+
return `${url.protocol}//${url.host}`.toLowerCase();
|
|
51
|
+
} catch {
|
|
52
|
+
return normalized.toLowerCase();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function normalizeOperationDescriptor(input) {
|
|
56
|
+
return {
|
|
57
|
+
appNamespace: sanitizeSegment(input.appNamespace),
|
|
58
|
+
apiId: sanitizeSegment(input.apiId),
|
|
59
|
+
group: sanitizeSegment(input.group),
|
|
60
|
+
endpoint: sanitizeSegment(input.endpoint),
|
|
61
|
+
schemaHash: input.schemaHash || null,
|
|
62
|
+
version: input.version ?? 1
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function createOperationId(input) {
|
|
66
|
+
const descriptor = normalizeOperationDescriptor(input);
|
|
67
|
+
const readable = `${descriptor.appNamespace}.${descriptor.apiId}.${descriptor.group}.${descriptor.endpoint}.v${String(descriptor.version)}`;
|
|
68
|
+
return `${readable}:${hashString(stableStringify(descriptor))}`;
|
|
69
|
+
}
|
|
70
|
+
function buildScopeKey(scope) {
|
|
71
|
+
const canonical = {
|
|
72
|
+
appNamespace: sanitizeSegment(scope.appNamespace),
|
|
73
|
+
origin: normalizeOrigin(scope.origin),
|
|
74
|
+
tenantId: scope.tenantId ?? null,
|
|
75
|
+
userId: scope.userId ?? null,
|
|
76
|
+
sessionId: scope.sessionId ?? null
|
|
77
|
+
};
|
|
78
|
+
return `${canonical.appNamespace}:${hashString(stableStringify(canonical))}`;
|
|
79
|
+
}
|
|
80
|
+
function buildQueryKey(input) {
|
|
81
|
+
const canonical = {
|
|
82
|
+
operationId: input.operationId,
|
|
83
|
+
scopeKey: input.scopeKey,
|
|
84
|
+
requestMode: input.requestMode ?? 'cache-first',
|
|
85
|
+
requestInput: input.requestInput ?? null,
|
|
86
|
+
selectionPlan: input.selectionPlan ?? null
|
|
87
|
+
};
|
|
88
|
+
return `${input.operationId}:${hashString(stableStringify(canonical))}`;
|
|
89
|
+
}
|
|
90
|
+
export { buildQueryKey, buildScopeKey, createOperationId, decodeRequestEnvelopeHeader, encodeRequestEnvelopeHeader, hashString, isPlainObject, normalizeOrigin, sanitizeSegment, stableStringify };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { measureTextBytes, normalizeMethod } from "./batch/request.mjs";
|
|
2
|
+
export { DATA_BATCH_TRANSPORT_OTEL_EVENT, createDataBatchTransport, createDataBatchTransportTelemetryAttributes, emitDataBatchTransportEvent } from "./batch-transport.mjs";
|
|
3
|
+
export { buildQueryKey, buildScopeKey, createOperationId, decodeRequestEnvelopeHeader, encodeRequestEnvelopeHeader, isPlainObject, normalizeOrigin, stableStringify } from "./codec.mjs";
|
|
4
|
+
export { createInvalidationEvent, shouldApplyInvalidation } from "./invalidation.mjs";
|
|
5
|
+
export { deriveChildTraceContext, formatTraceparentHeader, parseTraceparentHeader } from "./trace.mjs";
|
|
6
|
+
export { DEFAULT_DATA_BATCH_ENDPOINT, DEFAULT_DATA_BATCH_HEADER, DEFAULT_DATA_ENVELOPE_HEADER } from "./types.mjs";
|
|
7
|
+
export { createHydrationEnvelope, createRequestEnvelope, validateHydrationEnvelope, validateRequestEnvelope, validateSelectionPlan } from "./validation.mjs";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { buildScopeKey, createOperationId, sanitizeSegment } from "./codec.mjs";
|
|
2
|
+
function createInvalidationEvent(input) {
|
|
3
|
+
return {
|
|
4
|
+
sourceNamespace: sanitizeSegment(input.sourceOperation.appNamespace),
|
|
5
|
+
sourceOperationId: createOperationId(input.sourceOperation),
|
|
6
|
+
scopeKey: buildScopeKey(input.sourceScope),
|
|
7
|
+
targetNamespaces: input.targetNamespaces?.map((namespace)=>sanitizeSegment(namespace)),
|
|
8
|
+
targetOperationIds: input.targetOperations?.map((operation)=>createOperationId(operation))
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function shouldApplyInvalidation(event, subscriber) {
|
|
12
|
+
if (subscriber.scopeKey && subscriber.scopeKey !== event.scopeKey) return false;
|
|
13
|
+
const sameNamespace = subscriber.namespace === event.sourceNamespace;
|
|
14
|
+
const allowedCrossNamespace = true === subscriber.acceptCrossNamespace && event.targetNamespaces?.includes(subscriber.namespace) === true;
|
|
15
|
+
if (!sameNamespace && !allowedCrossNamespace) return false;
|
|
16
|
+
if (!subscriber.operationIds || 0 === subscriber.operationIds.length) return true;
|
|
17
|
+
if (!event.targetOperationIds || 0 === event.targetOperationIds.length) return true;
|
|
18
|
+
return subscriber.operationIds.some((operationId)=>event.targetOperationIds?.includes(operationId));
|
|
19
|
+
}
|
|
20
|
+
export { createInvalidationEvent, shouldApplyInvalidation };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { parseTraceparent } from "@modern-js/create-request";
|
|
2
|
+
function isAllZeroHex(value) {
|
|
3
|
+
return /^0+$/.test(value);
|
|
4
|
+
}
|
|
5
|
+
function isValidHex(value, length) {
|
|
6
|
+
return value.length === length && /^[0-9a-f]+$/.test(value);
|
|
7
|
+
}
|
|
8
|
+
function parseTraceparentHeader(header) {
|
|
9
|
+
return parseTraceparent(header) ?? null;
|
|
10
|
+
}
|
|
11
|
+
function formatTraceparentHeader(trace) {
|
|
12
|
+
const traceId = trace.traceId.toLowerCase();
|
|
13
|
+
const spanId = trace.spanId.toLowerCase();
|
|
14
|
+
if (!isValidHex(traceId, 32) || !isValidHex(spanId, 16)) throw new Error('Invalid trace context: traceId/spanId format mismatch');
|
|
15
|
+
if (isAllZeroHex(traceId) || isAllZeroHex(spanId)) throw new Error('Invalid trace context: traceId/spanId cannot be zero');
|
|
16
|
+
const flags = trace.sampled ? '01' : '00';
|
|
17
|
+
return `00-${traceId}-${spanId}-${flags}`;
|
|
18
|
+
}
|
|
19
|
+
function deriveChildTraceContext(parent, childSpanId) {
|
|
20
|
+
const normalizedSpanId = childSpanId.toLowerCase();
|
|
21
|
+
if (!isValidHex(normalizedSpanId, 16) || isAllZeroHex(normalizedSpanId)) throw new Error('Invalid child span id');
|
|
22
|
+
return {
|
|
23
|
+
traceId: parent.traceId.toLowerCase(),
|
|
24
|
+
spanId: normalizedSpanId,
|
|
25
|
+
sampled: parent.sampled,
|
|
26
|
+
parentSpanId: parent.spanId.toLowerCase()
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export { deriveChildTraceContext, formatTraceparentHeader, parseTraceparentHeader };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const DEFAULT_DATA_ENVELOPE_HEADER = 'x-modernjs-data-envelope';
|
|
2
|
+
const DEFAULT_DATA_BATCH_ENDPOINT = '/_data/batch';
|
|
3
|
+
const DEFAULT_DATA_BATCH_HEADER = 'x-modernjs-data-batch';
|
|
4
|
+
export { DEFAULT_DATA_BATCH_ENDPOINT, DEFAULT_DATA_BATCH_HEADER, DEFAULT_DATA_ENVELOPE_HEADER };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { buildScopeKey, createOperationId, hashString, isPlainObject, normalizeOrigin, sanitizeSegment, stableStringify } from "./codec.mjs";
|
|
2
|
+
import { formatTraceparentHeader, parseTraceparentHeader } from "./trace.mjs";
|
|
3
|
+
function validateSelectionPlan(plan, options = {}) {
|
|
4
|
+
const maxDepthLimit = options.maxDepth ?? 8;
|
|
5
|
+
const maxFieldsLimit = options.maxFields ?? 256;
|
|
6
|
+
const allowedLeafPaths = options.allowedLeafPaths ? new Set(options.allowedLeafPaths) : null;
|
|
7
|
+
const errors = [];
|
|
8
|
+
let maxDepth = 0;
|
|
9
|
+
let fieldCount = 0;
|
|
10
|
+
const walk = (node, path)=>{
|
|
11
|
+
if (!isPlainObject(node)) return void errors.push(`Selection node at "${path.join('.') || '<root>'}" must be an object`);
|
|
12
|
+
const keys = Object.keys(node);
|
|
13
|
+
if (0 === keys.length) return void errors.push(`Selection node at "${path.join('.') || '<root>'}" cannot be empty`);
|
|
14
|
+
for (const key of keys){
|
|
15
|
+
fieldCount += 1;
|
|
16
|
+
if (fieldCount > maxFieldsLimit) return void errors.push(`Selection has too many fields: ${String(fieldCount)} > ${String(maxFieldsLimit)}`);
|
|
17
|
+
const nextPath = [
|
|
18
|
+
...path,
|
|
19
|
+
key
|
|
20
|
+
];
|
|
21
|
+
const depth = nextPath.length;
|
|
22
|
+
if (depth > maxDepth) maxDepth = depth;
|
|
23
|
+
if (depth > maxDepthLimit) return void errors.push(`Selection exceeds maxDepth at "${nextPath.join('.')}" (${String(depth)} > ${String(maxDepthLimit)})`);
|
|
24
|
+
const value = node[key];
|
|
25
|
+
if (true === value) {
|
|
26
|
+
if (allowedLeafPaths && !allowedLeafPaths.has(nextPath.join('.'))) errors.push(`Unknown selected field "${nextPath.join('.')}"`);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!isPlainObject(value)) {
|
|
30
|
+
errors.push(`Invalid selection value at "${nextPath.join('.')}"; expected true or nested object`);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
walk(value, nextPath);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
walk(plan, []);
|
|
37
|
+
return {
|
|
38
|
+
ok: 0 === errors.length,
|
|
39
|
+
errors,
|
|
40
|
+
stats: {
|
|
41
|
+
maxDepth,
|
|
42
|
+
fieldCount
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function createRequestEnvelope(input) {
|
|
47
|
+
if (input.requireTraceContext && !input.traceContext) throw new Error('Trace context is required for this request envelope');
|
|
48
|
+
const traceparent = input.traceContext ? formatTraceparentHeader(input.traceContext) : void 0;
|
|
49
|
+
const envelope = {
|
|
50
|
+
protocolVersion: input.protocolVersion ?? 1,
|
|
51
|
+
operationId: createOperationId(input.operation),
|
|
52
|
+
appNamespace: sanitizeSegment(input.scope.appNamespace),
|
|
53
|
+
origin: normalizeOrigin(input.scope.origin),
|
|
54
|
+
requestMode: input.requestMode ?? 'cache-first',
|
|
55
|
+
mutationMode: input.mutationMode,
|
|
56
|
+
scopeKey: buildScopeKey(input.scope),
|
|
57
|
+
input: input.requestInput,
|
|
58
|
+
inputHash: hashString(stableStringify(input.requestInput ?? null)),
|
|
59
|
+
selectionPlan: input.selectionPlan,
|
|
60
|
+
selectionHash: input.selectionPlan ? hashString(stableStringify(input.selectionPlan)) : void 0,
|
|
61
|
+
traceparent,
|
|
62
|
+
timestamp: input.timestamp ?? Date.now()
|
|
63
|
+
};
|
|
64
|
+
return envelope;
|
|
65
|
+
}
|
|
66
|
+
function validateRequestEnvelope(envelope, options = {}) {
|
|
67
|
+
const errors = [];
|
|
68
|
+
if (void 0 !== options.expectedProtocolVersion && envelope.protocolVersion !== options.expectedProtocolVersion) errors.push(`Protocol mismatch: expected ${String(options.expectedProtocolVersion)} but received ${String(envelope.protocolVersion)}`);
|
|
69
|
+
if (options.expectedNamespace && envelope.appNamespace !== options.expectedNamespace) errors.push(`Namespace mismatch: expected ${options.expectedNamespace} but received ${envelope.appNamespace}`);
|
|
70
|
+
if (options.expectedOrigin) {
|
|
71
|
+
const expectedOrigin = normalizeOrigin(options.expectedOrigin);
|
|
72
|
+
if (envelope.origin !== expectedOrigin) errors.push(`Origin mismatch: expected ${expectedOrigin} but received ${envelope.origin}`);
|
|
73
|
+
}
|
|
74
|
+
if (!envelope.operationId) errors.push('Missing operationId');
|
|
75
|
+
if (!envelope.scopeKey) errors.push('Missing scopeKey');
|
|
76
|
+
if (options.requireTraceContext && !envelope.traceparent) errors.push('Missing trace context');
|
|
77
|
+
if (envelope.traceparent && !parseTraceparentHeader(envelope.traceparent)) errors.push('Invalid traceparent header');
|
|
78
|
+
const computedInputHash = hashString(stableStringify(envelope.input ?? null));
|
|
79
|
+
if (computedInputHash !== envelope.inputHash) errors.push('Input hash mismatch');
|
|
80
|
+
if (envelope.selectionPlan) {
|
|
81
|
+
const computedSelectionHash = hashString(stableStringify(envelope.selectionPlan));
|
|
82
|
+
if (computedSelectionHash !== envelope.selectionHash) errors.push('Selection hash mismatch');
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
ok: 0 === errors.length,
|
|
86
|
+
errors
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function createHydrationEnvelope(input) {
|
|
90
|
+
const envelopeWithoutChecksum = {
|
|
91
|
+
protocolVersion: input.protocolVersion ?? 1,
|
|
92
|
+
runtimeVersion: input.runtimeVersion,
|
|
93
|
+
appNamespace: sanitizeSegment(input.scope.appNamespace),
|
|
94
|
+
origin: normalizeOrigin(input.scope.origin),
|
|
95
|
+
createdAt: input.createdAt ?? Date.now(),
|
|
96
|
+
payload: input.payload
|
|
97
|
+
};
|
|
98
|
+
const checksum = hashString(stableStringify(envelopeWithoutChecksum));
|
|
99
|
+
return {
|
|
100
|
+
...envelopeWithoutChecksum,
|
|
101
|
+
checksum
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function validateHydrationEnvelope(envelope, options = {}) {
|
|
105
|
+
const errors = [];
|
|
106
|
+
if (void 0 !== options.expectedProtocolVersion && envelope.protocolVersion !== options.expectedProtocolVersion) errors.push(`Protocol mismatch: expected ${String(options.expectedProtocolVersion)} but received ${String(envelope.protocolVersion)}`);
|
|
107
|
+
if (options.expectedNamespace && envelope.appNamespace !== options.expectedNamespace) errors.push(`Namespace mismatch: expected ${options.expectedNamespace} but received ${envelope.appNamespace}`);
|
|
108
|
+
if (options.expectedOrigin) {
|
|
109
|
+
const expectedOrigin = normalizeOrigin(options.expectedOrigin);
|
|
110
|
+
if (envelope.origin !== expectedOrigin) errors.push(`Origin mismatch: expected ${expectedOrigin} but received ${envelope.origin}`);
|
|
111
|
+
}
|
|
112
|
+
if (options.expectedRuntimeVersion && envelope.runtimeVersion !== options.expectedRuntimeVersion) errors.push(`Runtime version mismatch: expected ${options.expectedRuntimeVersion} but received ${envelope.runtimeVersion}`);
|
|
113
|
+
const checksumBase = {
|
|
114
|
+
protocolVersion: envelope.protocolVersion,
|
|
115
|
+
runtimeVersion: envelope.runtimeVersion,
|
|
116
|
+
appNamespace: envelope.appNamespace,
|
|
117
|
+
origin: envelope.origin,
|
|
118
|
+
createdAt: envelope.createdAt,
|
|
119
|
+
payload: envelope.payload
|
|
120
|
+
};
|
|
121
|
+
const checksum = hashString(stableStringify(checksumBase));
|
|
122
|
+
if (checksum !== envelope.checksum) errors.push('Hydration checksum mismatch');
|
|
123
|
+
return {
|
|
124
|
+
ok: 0 === errors.length,
|
|
125
|
+
errors
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
export { createHydrationEnvelope, createRequestEnvelope, validateHydrationEnvelope, validateRequestEnvelope, validateSelectionPlan };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { createEffectOperationContext, kEffectContextStorage } from "./operation-context.mjs";
|
|
3
|
+
const globalStore = globalThis;
|
|
4
|
+
const effectContextStorage = globalStore[kEffectContextStorage] ?? (globalStore[kEffectContextStorage] = new AsyncLocalStorage());
|
|
5
|
+
const runWithEffectContext = (context, cb)=>effectContextStorage.run(context, cb);
|
|
6
|
+
const useEffectContext = ()=>{
|
|
7
|
+
const context = effectContextStorage.getStore();
|
|
8
|
+
if (!context) throw new Error("Can't call useEffectContext out of Effect runtime scope");
|
|
9
|
+
return context;
|
|
10
|
+
};
|
|
11
|
+
const useOperationContext = ()=>useEffectContext().operationContext;
|
|
12
|
+
export { createEffectOperationContext, runWithEffectContext, useEffectContext, useOperationContext };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createSafeFailureResponse } from "@modern-js/runtime-extensions/safe-failure";
|
|
2
|
+
import { createEffectOperationContext } from "./operation-context.mjs";
|
|
3
|
+
function normalizeEffectBffPrefix(prefix) {
|
|
4
|
+
if (!prefix || '/' === prefix) return '';
|
|
5
|
+
return prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
|
|
6
|
+
}
|
|
7
|
+
function removeEffectBffPrefixFromPath(pathname, prefix) {
|
|
8
|
+
const normalized = normalizeEffectBffPrefix(prefix);
|
|
9
|
+
if (!normalized || pathname !== normalized && !pathname.startsWith(`${normalized}/`)) return pathname;
|
|
10
|
+
const sliced = pathname.slice(normalized.length);
|
|
11
|
+
return sliced.startsWith('/') ? sliced : `/${sliced}`;
|
|
12
|
+
}
|
|
13
|
+
function matchesEffectBffPrefix(pathname, prefix) {
|
|
14
|
+
const normalized = normalizeEffectBffPrefix(prefix);
|
|
15
|
+
return !normalized || pathname === normalized || pathname.startsWith(`${normalized}/`);
|
|
16
|
+
}
|
|
17
|
+
function createEffectBffMountedRequest(req, prefix) {
|
|
18
|
+
const url = new URL(req.url);
|
|
19
|
+
const nextPath = removeEffectBffPrefixFromPath(url.pathname, prefix);
|
|
20
|
+
if (nextPath !== url.pathname) {
|
|
21
|
+
url.pathname = nextPath;
|
|
22
|
+
return new Request(url, req);
|
|
23
|
+
}
|
|
24
|
+
return req;
|
|
25
|
+
}
|
|
26
|
+
function createEffectBffDispatchContext(originalRequest, mountedRequest, options) {
|
|
27
|
+
const path = options.path ?? new URL(originalRequest.url).pathname;
|
|
28
|
+
const method = options.method ?? originalRequest.method;
|
|
29
|
+
const env = options.env ?? {};
|
|
30
|
+
return {
|
|
31
|
+
request: mountedRequest,
|
|
32
|
+
env,
|
|
33
|
+
path,
|
|
34
|
+
method,
|
|
35
|
+
operationContext: createEffectOperationContext({
|
|
36
|
+
request: mountedRequest,
|
|
37
|
+
env,
|
|
38
|
+
path,
|
|
39
|
+
method
|
|
40
|
+
})
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function dispatchEffectBffRequestWithContext(handler, request, options) {
|
|
44
|
+
const requestPath = new URL(request.url).pathname;
|
|
45
|
+
if (!matchesEffectBffPrefix(requestPath, options.prefix)) return new Response('', {
|
|
46
|
+
status: 404
|
|
47
|
+
});
|
|
48
|
+
const mountedRequest = createEffectBffMountedRequest(request, options.prefix);
|
|
49
|
+
const effectContext = createEffectBffDispatchContext(request, mountedRequest, options);
|
|
50
|
+
try {
|
|
51
|
+
const response = await options.runWithEffectContext(effectContext, ()=>handler(mountedRequest, effectContext));
|
|
52
|
+
if (!(response instanceof Response)) throw new Error('[BFF][Effect] Effect handler must return Response instance.');
|
|
53
|
+
return new Response(response.body, response);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (error instanceof Response) return new Response(error.body, error);
|
|
56
|
+
if (options.onError) {
|
|
57
|
+
const errorResponse = await options.onError(error, effectContext);
|
|
58
|
+
if (errorResponse instanceof Response) return errorResponse;
|
|
59
|
+
}
|
|
60
|
+
return createSafeFailureResponse(error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export { dispatchEffectBffRequestWithContext };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createEffectOperationContext, kEffectContextStorage } from "./operation-context.mjs";
|
|
2
|
+
const globalStore = globalThis;
|
|
3
|
+
const asyncHooks = globalStore.process?.getBuiltinModule?.('node:async_hooks');
|
|
4
|
+
const AsyncLocalStorage = asyncHooks?.AsyncLocalStorage;
|
|
5
|
+
if ('function' != typeof AsyncLocalStorage) throw new Error('[BFF][Effect] The edge runtime must provide AsyncLocalStorage. Enable Node.js compatibility or the nodejs_als compatibility flag.');
|
|
6
|
+
const effectContextStorage = globalStore[kEffectContextStorage] ?? (globalStore[kEffectContextStorage] = new AsyncLocalStorage());
|
|
7
|
+
const runWithEffectContext = (context, cb)=>effectContextStorage.run(context, cb);
|
|
8
|
+
const useEffectContext = ()=>{
|
|
9
|
+
const context = effectContextStorage.getStore();
|
|
10
|
+
if (!context) throw new Error("Can't call useEffectContext out of Effect runtime scope");
|
|
11
|
+
return context;
|
|
12
|
+
};
|
|
13
|
+
const useOperationContext = ()=>useEffectContext().operationContext;
|
|
14
|
+
export { createEffectOperationContext, runWithEffectContext, useEffectContext, useOperationContext };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { evaluateCrossProjectPolicy, resolveCrossProjectRequestObservation } from "@modern-js/bff-core/security/cross-project-policy";
|
|
2
|
+
import { toHeaderRecord } from "../headers.mjs";
|
|
3
|
+
import { dispatchEffectBffRequestWithContext } from "./dispatch.mjs";
|
|
4
|
+
import { runWithEffectContext, useEffectContext, useOperationContext } from "./edge-context.mjs";
|
|
5
|
+
import { resolveEffectBffModuleHandler } from "./module.mjs";
|
|
6
|
+
import * as __rspack_external_effect_Config_76cfa59d from "effect/Config";
|
|
7
|
+
import * as __rspack_external_effect_Effect_41060786 from "effect/Effect";
|
|
8
|
+
import * as __rspack_external_effect_Layer_69c46453 from "effect/Layer";
|
|
9
|
+
import * as __rspack_external_effect_Option_0498e63a from "effect/Option";
|
|
10
|
+
import * as __rspack_external_effect_Schema_6065f72d from "effect/Schema";
|
|
11
|
+
const NORMALIZED_POLICY_BOOLEAN_FIELDS = [
|
|
12
|
+
'enabled',
|
|
13
|
+
'requireEnvelope',
|
|
14
|
+
'requireOperationContext',
|
|
15
|
+
'requireOperationContextDetails',
|
|
16
|
+
'requireOperationSchemaHash',
|
|
17
|
+
'requireOperationVersion',
|
|
18
|
+
'allowUnknownOperations'
|
|
19
|
+
];
|
|
20
|
+
function assertNormalizedCrossProjectPolicy(policy) {
|
|
21
|
+
if (void 0 === policy) return;
|
|
22
|
+
if (!policy || 'object' != typeof policy || Array.isArray(policy)) throw new Error('[BFF][Effect] Edge cross-project policy must be a normalized object.');
|
|
23
|
+
for (const field of NORMALIZED_POLICY_BOOLEAN_FIELDS)if ('boolean' != typeof policy[field]) throw new Error(`[BFF][Effect] Edge cross-project policy requires boolean ${field}.`);
|
|
24
|
+
if (!policy.expectedOperationContracts || 'object' != typeof policy.expectedOperationContracts || Array.isArray(policy.expectedOperationContracts)) throw new Error('[BFF][Effect] Edge cross-project policy requires expectedOperationContracts object.');
|
|
25
|
+
}
|
|
26
|
+
function restoreEffectBffMountPath(request, prefix) {
|
|
27
|
+
const pathname = new URL(request.url).pathname;
|
|
28
|
+
if (!prefix || '/' === prefix) return pathname;
|
|
29
|
+
const normalizedPrefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
|
|
30
|
+
return '/' === pathname ? normalizedPrefix : `${normalizedPrefix}${pathname}`;
|
|
31
|
+
}
|
|
32
|
+
async function dispatchEffectBffRequest(handler, request, options = {}) {
|
|
33
|
+
return dispatchEffectBffRequestWithContext(handler, request, {
|
|
34
|
+
...options,
|
|
35
|
+
runWithEffectContext: runWithEffectContext
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
async function createEffectBffEdgeDispatcher(options) {
|
|
39
|
+
const crossProjectPolicy = options.crossProjectPolicy;
|
|
40
|
+
assertNormalizedCrossProjectPolicy(crossProjectPolicy);
|
|
41
|
+
const loaded = await resolveEffectBffModuleHandler(options.module, {
|
|
42
|
+
openapi: options.openapi,
|
|
43
|
+
dataPlatform: options.dataPlatform,
|
|
44
|
+
validateRequest: crossProjectPolicy?.enabled === true ? (request)=>{
|
|
45
|
+
const pathname = new URL(request.url).pathname;
|
|
46
|
+
const observedRequest = resolveCrossProjectRequestObservation({
|
|
47
|
+
method: request.method,
|
|
48
|
+
pathname
|
|
49
|
+
}, crossProjectPolicy) ?? {
|
|
50
|
+
method: request.method,
|
|
51
|
+
routePath: restoreEffectBffMountPath(request, options.prefix)
|
|
52
|
+
};
|
|
53
|
+
const violation = evaluateCrossProjectPolicy(toHeaderRecord(request.headers), crossProjectPolicy, observedRequest);
|
|
54
|
+
if (!violation) return null;
|
|
55
|
+
return new Response(JSON.stringify({
|
|
56
|
+
code: violation.code,
|
|
57
|
+
reason: violation.reason,
|
|
58
|
+
message: violation.message
|
|
59
|
+
}), {
|
|
60
|
+
status: violation.status,
|
|
61
|
+
headers: {
|
|
62
|
+
'content-type': 'application/json; charset=utf-8'
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
} : void 0,
|
|
66
|
+
onWarning: options.onWarning
|
|
67
|
+
});
|
|
68
|
+
if (!loaded) throw new Error('[BFF][Effect] Invalid Effect edge module. Export defineEffectBff(...) or a { api, layer } HttpApi module.');
|
|
69
|
+
const retiredError = new Error('[BFF][Effect] Edge dispatcher is disposing or has been disposed.');
|
|
70
|
+
let acceptingRequests = true;
|
|
71
|
+
let activeDispatches = 0;
|
|
72
|
+
let resolveDrain;
|
|
73
|
+
let disposePromise;
|
|
74
|
+
return {
|
|
75
|
+
dispatch: (request, dispatchOptions = {})=>{
|
|
76
|
+
if (!acceptingRequests) return Promise.reject(retiredError);
|
|
77
|
+
activeDispatches += 1;
|
|
78
|
+
return dispatchEffectBffRequest(loaded.handler, request, {
|
|
79
|
+
...dispatchOptions,
|
|
80
|
+
prefix: options.prefix,
|
|
81
|
+
onError: options.onError
|
|
82
|
+
}).finally(()=>{
|
|
83
|
+
activeDispatches -= 1;
|
|
84
|
+
if (0 === activeDispatches) {
|
|
85
|
+
resolveDrain?.();
|
|
86
|
+
resolveDrain = void 0;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
dispose: ()=>{
|
|
91
|
+
disposePromise ??= (async ()=>{
|
|
92
|
+
acceptingRequests = false;
|
|
93
|
+
if (activeDispatches > 0) await new Promise((resolve)=>{
|
|
94
|
+
resolveDrain = resolve;
|
|
95
|
+
});
|
|
96
|
+
await loaded.dispose?.();
|
|
97
|
+
})();
|
|
98
|
+
return disposePromise;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const createEffectBffTestHandler = createEffectBffEdgeHandler;
|
|
103
|
+
async function createEffectBffEdgeHandler(options) {
|
|
104
|
+
const dispatcher = await createEffectBffEdgeDispatcher(options);
|
|
105
|
+
return {
|
|
106
|
+
handler: dispatcher.dispatch,
|
|
107
|
+
dispose: dispatcher.dispose
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export { Cookies, Etag, FetchHttpClient, FindMyWay, Headers, HttpBody, HttpClient, HttpClientError, HttpClientRequest, HttpClientResponse, HttpEffect, HttpIncomingMessage, HttpMethod, HttpMiddleware, HttpPlatform, HttpRouter, HttpServer, HttpServerError, HttpServerRequest, HttpServerRespondable, HttpServerResponse, HttpStaticServer, HttpStatus, HttpTraceContext, Multipart, MultipartParser, Template, Url, UrlParams } from "effect/unstable/http";
|
|
111
|
+
export { HttpApi, HttpApiBuilder, HttpApiClient, HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiMiddleware, HttpApiSchema, HttpApiSecurity, HttpApiTest, OpenApi } from "effect/unstable/httpapi";
|
|
112
|
+
export { Rpc, RpcClient, RpcClientError, RpcGroup, RpcMessage, RpcMiddleware, RpcSchema, RpcSerialization, RpcServer, RpcTest, RpcWorker, Utils } from "effect/unstable/rpc";
|
|
113
|
+
export { createHttpApiHandler, defineEffectBff, defineEffectRpcBff } from "./handler.mjs";
|
|
114
|
+
export { createEffectOperationContext } from "./operation-context.mjs";
|
|
115
|
+
export { __rspack_external_effect_Config_76cfa59d as Config, __rspack_external_effect_Effect_41060786 as Effect, __rspack_external_effect_Layer_69c46453 as Layer, __rspack_external_effect_Option_0498e63a as Option, __rspack_external_effect_Schema_6065f72d as Schema, createEffectBffEdgeDispatcher, createEffectBffEdgeHandler, createEffectBffTestHandler, dispatchEffectBffRequest, runWithEffectContext, useEffectContext, useOperationContext };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./edge-dispatcher.mjs";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { createOperationContractHash } from "@modern-js/bff-core";
|
|
2
|
+
import { classifyEffectBffEntryModule, isValidatorAwareHandlerFactory } from "./entry-shape.mjs";
|
|
3
|
+
function resolveEffectEndpointName(endpoint) {
|
|
4
|
+
if ('string' == typeof endpoint.identifier && endpoint.identifier) return endpoint.identifier;
|
|
5
|
+
if ('string' == typeof endpoint.name && endpoint.name) return endpoint.name;
|
|
6
|
+
return '';
|
|
7
|
+
}
|
|
8
|
+
function ensureLeadingSlash(pathname) {
|
|
9
|
+
return pathname.startsWith('/') ? pathname : `/${pathname}`;
|
|
10
|
+
}
|
|
11
|
+
function normalizeEffectPrefix(prefix) {
|
|
12
|
+
if ('/' === prefix) return '';
|
|
13
|
+
return ensureLeadingSlash(prefix || '/api');
|
|
14
|
+
}
|
|
15
|
+
function getEffectRoutePath(prefix, endpointPath) {
|
|
16
|
+
const normalizedPrefix = normalizeEffectPrefix(prefix);
|
|
17
|
+
const normalizedEndpointPath = ensureLeadingSlash(endpointPath);
|
|
18
|
+
const finalEndpointPath = '/' === normalizedEndpointPath ? '' : endpointPath;
|
|
19
|
+
if (!normalizedPrefix && !finalEndpointPath) return '/';
|
|
20
|
+
return `${normalizedPrefix}${finalEndpointPath || ''}`;
|
|
21
|
+
}
|
|
22
|
+
function resolveEffectApiId(api) {
|
|
23
|
+
const fallback = 'EffectHttpApi';
|
|
24
|
+
if ('identifier' in api && 'string' == typeof api.identifier && api.identifier) return api.identifier;
|
|
25
|
+
return fallback;
|
|
26
|
+
}
|
|
27
|
+
function collectEffectEndpoints(reflect, api, prefix) {
|
|
28
|
+
const endpoints = [];
|
|
29
|
+
const apiId = resolveEffectApiId(api);
|
|
30
|
+
reflect(api, {
|
|
31
|
+
onGroup: ()=>{},
|
|
32
|
+
onEndpoint: ({ group, endpoint })=>{
|
|
33
|
+
endpoints.push({
|
|
34
|
+
apiId,
|
|
35
|
+
groupName: String(group.identifier),
|
|
36
|
+
endpointName: resolveEffectEndpointName(endpoint),
|
|
37
|
+
method: String(endpoint.method).toUpperCase(),
|
|
38
|
+
routePath: getEffectRoutePath(prefix, String(endpoint.path))
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
return endpoints.sort((a, b)=>{
|
|
43
|
+
if (a.groupName === b.groupName) return a.endpointName.localeCompare(b.endpointName);
|
|
44
|
+
return a.groupName.localeCompare(b.groupName);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function toOperationContractSources(endpoints) {
|
|
48
|
+
return endpoints.map(createEffectOperationContractSource);
|
|
49
|
+
}
|
|
50
|
+
function createEffectOperationContractSource(endpoint) {
|
|
51
|
+
return {
|
|
52
|
+
name: endpoint.endpointName,
|
|
53
|
+
httpMethod: endpoint.method,
|
|
54
|
+
routePath: endpoint.routePath
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function extractHttpApiFromModule(mod, isHttpApi) {
|
|
58
|
+
const facts = classifyEffectBffEntryModule(mod, {
|
|
59
|
+
isRequestHandler: (value)=>'function' == typeof value,
|
|
60
|
+
isValidatorAwareHandlerFactory: isValidatorAwareHandlerFactory,
|
|
61
|
+
isHttpApi
|
|
62
|
+
});
|
|
63
|
+
return !facts?.legacyShape && facts?.api !== void 0 && facts.hasRuntimeLayer && (void 0 === facts.createHandler || facts.createHandlerValidatorAware) ? facts.api : null;
|
|
64
|
+
}
|
|
65
|
+
function createEffectEndpointContractHash(endpoint, requestId) {
|
|
66
|
+
return createOperationContractHash(createEffectOperationContractSource(endpoint), requestId);
|
|
67
|
+
}
|
|
68
|
+
export { collectEffectEndpoints, createEffectEndpointContractHash, ensureLeadingSlash, extractHttpApiFromModule, normalizeEffectPrefix, toOperationContractSources };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
import { __webpack_require__ } from "../rslib-runtime.mjs";
|
|
3
|
+
__webpack_require__.add({
|
|
4
|
+
1970 (module) {
|
|
5
|
+
const validatorAwareHandlerFactories = new WeakSet();
|
|
6
|
+
module.exports = {
|
|
7
|
+
register (factory) {
|
|
8
|
+
validatorAwareHandlerFactories.add(factory);
|
|
9
|
+
return factory;
|
|
10
|
+
},
|
|
11
|
+
is (factory) {
|
|
12
|
+
return 'function' == typeof factory && validatorAwareHandlerFactories.has(factory);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
__webpack_require__("1970");
|