@bleedingdev/modern-js-plugin-bff 3.8.1-ultramodern.1 → 3.8.1-ultramodern.3
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/runtime/data-platform/index.js +10 -0
- package/dist/cjs/runtime/effect/handler/batch-handler.js +6 -6
- package/dist/cjs/runtime/effect/handler/batch.js +6 -42
- package/dist/cjs/runtime/effect/handler/routing.js +2 -2
- package/dist/cjs/utils/crossProjectServerPolicy.js +2 -8
- package/dist/cjs/utils/headers.js +46 -0
- package/dist/esm/runtime/data-platform/index.mjs +2 -1
- package/dist/esm/runtime/effect/handler/batch-handler.mjs +5 -5
- package/dist/esm/runtime/effect/handler/batch.mjs +5 -32
- package/dist/esm/runtime/effect/handler/routing.mjs +1 -1
- package/dist/esm/utils/crossProjectServerPolicy.mjs +1 -7
- package/dist/esm/utils/headers.mjs +8 -0
- package/dist/esm-node/runtime/data-platform/index.mjs +2 -1
- package/dist/esm-node/runtime/effect/handler/batch-handler.mjs +5 -5
- package/dist/esm-node/runtime/effect/handler/batch.mjs +5 -32
- package/dist/esm-node/runtime/effect/handler/routing.mjs +1 -1
- package/dist/esm-node/utils/crossProjectServerPolicy.mjs +1 -7
- package/dist/esm-node/utils/headers.mjs +9 -0
- package/dist/types/runtime/data-platform/index.d.ts +2 -1
- package/dist/types/runtime/effect/handler/batch.d.ts +1 -5
- package/dist/types/utils/headers.d.ts +1 -0
- package/package.json +12 -12
- package/docs/data-platform-architecture.md +0 -61
|
@@ -45,6 +45,9 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
45
45
|
emitDataBatchTransportEvent: ()=>external_batch_transport_js_namespaceObject.emitDataBatchTransportEvent,
|
|
46
46
|
encodeRequestEnvelopeHeader: ()=>external_codec_js_namespaceObject.encodeRequestEnvelopeHeader,
|
|
47
47
|
formatTraceparentHeader: ()=>external_trace_js_namespaceObject.formatTraceparentHeader,
|
|
48
|
+
isPlainObject: ()=>external_codec_js_namespaceObject.isPlainObject,
|
|
49
|
+
measureTextBytes: ()=>request_js_namespaceObject.measureTextBytes,
|
|
50
|
+
normalizeMethod: ()=>request_js_namespaceObject.normalizeMethod,
|
|
48
51
|
normalizeOrigin: ()=>external_codec_js_namespaceObject.normalizeOrigin,
|
|
49
52
|
parseTraceparentHeader: ()=>external_trace_js_namespaceObject.parseTraceparentHeader,
|
|
50
53
|
shouldApplyInvalidation: ()=>external_invalidation_js_namespaceObject.shouldApplyInvalidation,
|
|
@@ -53,6 +56,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
53
56
|
validateRequestEnvelope: ()=>external_validation_js_namespaceObject.validateRequestEnvelope,
|
|
54
57
|
validateSelectionPlan: ()=>external_validation_js_namespaceObject.validateSelectionPlan
|
|
55
58
|
});
|
|
59
|
+
const request_js_namespaceObject = require("./batch/request.js");
|
|
56
60
|
const external_batch_transport_js_namespaceObject = require("./batch-transport.js");
|
|
57
61
|
const external_codec_js_namespaceObject = require("./codec.js");
|
|
58
62
|
const external_invalidation_js_namespaceObject = require("./invalidation.js");
|
|
@@ -76,6 +80,9 @@ exports.deriveChildTraceContext = __webpack_exports__.deriveChildTraceContext;
|
|
|
76
80
|
exports.emitDataBatchTransportEvent = __webpack_exports__.emitDataBatchTransportEvent;
|
|
77
81
|
exports.encodeRequestEnvelopeHeader = __webpack_exports__.encodeRequestEnvelopeHeader;
|
|
78
82
|
exports.formatTraceparentHeader = __webpack_exports__.formatTraceparentHeader;
|
|
83
|
+
exports.isPlainObject = __webpack_exports__.isPlainObject;
|
|
84
|
+
exports.measureTextBytes = __webpack_exports__.measureTextBytes;
|
|
85
|
+
exports.normalizeMethod = __webpack_exports__.normalizeMethod;
|
|
79
86
|
exports.normalizeOrigin = __webpack_exports__.normalizeOrigin;
|
|
80
87
|
exports.parseTraceparentHeader = __webpack_exports__.parseTraceparentHeader;
|
|
81
88
|
exports.shouldApplyInvalidation = __webpack_exports__.shouldApplyInvalidation;
|
|
@@ -101,6 +108,9 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
101
108
|
"emitDataBatchTransportEvent",
|
|
102
109
|
"encodeRequestEnvelopeHeader",
|
|
103
110
|
"formatTraceparentHeader",
|
|
111
|
+
"isPlainObject",
|
|
112
|
+
"measureTextBytes",
|
|
113
|
+
"normalizeMethod",
|
|
104
114
|
"normalizeOrigin",
|
|
105
115
|
"parseTraceparentHeader",
|
|
106
116
|
"shouldApplyInvalidation",
|
|
@@ -46,12 +46,12 @@ function createDataPlatformBatchRequestHandler(options) {
|
|
|
46
46
|
const normalizedEnvelopeHeader = envelopeHeader.toLowerCase();
|
|
47
47
|
const handle = async (request, context)=>{
|
|
48
48
|
const mountedPrefix = (0, external_routing_js_namespaceObject.getMountedPrefixFromContext)(request, context);
|
|
49
|
-
const method = (0,
|
|
49
|
+
const method = (0, index_js_namespaceObject.normalizeMethod)(request.method);
|
|
50
50
|
if ('POST' !== method) return (0, external_batch_js_namespaceObject.createBatchValidationResponse)('Batch endpoint only supports POST requests', 405);
|
|
51
51
|
const contentType = request.headers.get('content-type') || '';
|
|
52
52
|
if (!contentType.includes('application/json')) return (0, external_batch_js_namespaceObject.createBatchValidationResponse)('Batch endpoint requires application/json content-type', 415);
|
|
53
53
|
const payloadText = await request.text();
|
|
54
|
-
if ((0,
|
|
54
|
+
if ((0, index_js_namespaceObject.measureTextBytes)(payloadText) > batchMaxBytes) return (0, external_batch_js_namespaceObject.createBatchValidationResponse)(`Batch payload exceeds max size (${String(batchMaxBytes)} bytes)`, 413);
|
|
55
55
|
let payload;
|
|
56
56
|
try {
|
|
57
57
|
payload = JSON.parse(payloadText);
|
|
@@ -63,20 +63,20 @@ function createDataPlatformBatchRequestHandler(options) {
|
|
|
63
63
|
if (payload.items.length > batchMaxSize) return (0, external_batch_js_namespaceObject.createBatchValidationResponse)(`Batch item count exceeds max size (${String(batchMaxSize)})`, 413);
|
|
64
64
|
const responseItems = await (0, external_batch_js_namespaceObject.mapWithConcurrency)(payload.items, batchConcurrency, async (rawItem, index)=>{
|
|
65
65
|
const fallbackId = `item_${String(index)}`;
|
|
66
|
-
const itemId = (0,
|
|
67
|
-
if (!(0,
|
|
66
|
+
const itemId = (0, index_js_namespaceObject.isPlainObject)(rawItem) && 'string' == typeof rawItem.id ? rawItem.id : fallbackId;
|
|
67
|
+
if (!(0, index_js_namespaceObject.isPlainObject)(rawItem)) return (0, external_batch_js_namespaceObject.toBatchItemError)(itemId, 400, 'Invalid batch item; expected object');
|
|
68
68
|
if ('string' != typeof rawItem.path || 0 === rawItem.path.length) return (0, external_batch_js_namespaceObject.toBatchItemError)(itemId, 400, 'Invalid batch item path');
|
|
69
69
|
if (!rawItem.path.startsWith('/')) return (0, external_batch_js_namespaceObject.toBatchItemError)(itemId, 400, 'Batch item path must start with "/"');
|
|
70
70
|
const normalizedItemPath = (0, external_routing_js_namespaceObject.removeMountedPrefixFromBatchPath)(rawItem.path, mountedPrefix);
|
|
71
71
|
const itemPathname = normalizedItemPath.split('?')[0] || normalizedItemPath;
|
|
72
72
|
if (itemPathname === batchPath || itemPathname.startsWith(`${batchPath}/`)) return (0, external_batch_js_namespaceObject.toBatchItemError)(itemId, 400, 'Batch item path cannot target batch endpoint');
|
|
73
|
-
const itemMethod = (0,
|
|
73
|
+
const itemMethod = (0, index_js_namespaceObject.normalizeMethod)('string' == typeof rawItem.method ? rawItem.method : void 0);
|
|
74
74
|
if (!batchAllowedMethods.has(itemMethod)) return (0, external_batch_js_namespaceObject.toBatchItemError)(itemId, 405, `Batch item method ${itemMethod} is not allowed`);
|
|
75
75
|
if (void 0 !== rawItem.body && null !== rawItem.body && 'string' != typeof rawItem.body) return (0, external_batch_js_namespaceObject.toBatchItemError)(itemId, 400, 'Batch item body must be a string when provided');
|
|
76
76
|
if (('GET' === itemMethod || 'HEAD' === itemMethod) && 'string' == typeof rawItem.body) return (0, external_batch_js_namespaceObject.toBatchItemError)(itemId, 400, `${itemMethod} batch item cannot include body`);
|
|
77
77
|
const normalizedHeaders = {};
|
|
78
78
|
if (void 0 !== rawItem.headers) {
|
|
79
|
-
if (!(0,
|
|
79
|
+
if (!(0, index_js_namespaceObject.isPlainObject)(rawItem.headers)) return (0, external_batch_js_namespaceObject.toBatchItemError)(itemId, 400, 'Batch item headers must be an object');
|
|
80
80
|
for (const [key, value] of Object.entries(rawItem.headers)){
|
|
81
81
|
if ('string' != typeof value) return (0, external_batch_js_namespaceObject.toBatchItemError)(itemId, 400, `Invalid header "${key}" for batch item`);
|
|
82
82
|
normalizedHeaders[key.toLowerCase()] = value;
|
|
@@ -30,41 +30,21 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
30
30
|
__webpack_require__.d(__webpack_exports__, {
|
|
31
31
|
createBatchValidationResponse: ()=>createBatchValidationResponse,
|
|
32
32
|
isBatchRequestPayload: ()=>isBatchRequestPayload,
|
|
33
|
-
isPlainObject: ()=>isPlainObject,
|
|
34
33
|
mapWithConcurrency: ()=>mapWithConcurrency,
|
|
35
34
|
normalizeBatchAllowedMethods: ()=>normalizeBatchAllowedMethods,
|
|
36
35
|
normalizeBatchPath: ()=>normalizeBatchPath,
|
|
37
|
-
normalizeItemMethod: ()=>normalizeItemMethod,
|
|
38
36
|
prepareJsonRequestBody: ()=>prepareJsonRequestBody,
|
|
39
37
|
promiseWithTimeout: ()=>promiseWithTimeout,
|
|
40
38
|
toBatchItemError: ()=>toBatchItemError,
|
|
41
|
-
toHeaderRecord: ()=>toHeaderRecord
|
|
42
|
-
toTextLength: ()=>toTextLength
|
|
39
|
+
toHeaderRecord: ()=>headers_js_namespaceObject.toHeaderRecord
|
|
43
40
|
});
|
|
44
41
|
const index_js_namespaceObject = require("../../data-platform/index.js");
|
|
42
|
+
const headers_js_namespaceObject = require("../../../utils/headers.js");
|
|
45
43
|
function normalizeBatchPath(pathname) {
|
|
46
44
|
if (!pathname || '/' === pathname) return index_js_namespaceObject.DEFAULT_DATA_BATCH_ENDPOINT;
|
|
47
45
|
if (!pathname.startsWith('/')) return `/${pathname}`;
|
|
48
46
|
return pathname;
|
|
49
47
|
}
|
|
50
|
-
function isPlainObject(value) {
|
|
51
|
-
return 'object' == typeof value && null !== value && !Array.isArray(value);
|
|
52
|
-
}
|
|
53
|
-
function toTextLength(value) {
|
|
54
|
-
if ("u" > typeof TextEncoder) return new TextEncoder().encode(value).length;
|
|
55
|
-
if ("u" > typeof Buffer) return Buffer.byteLength(value);
|
|
56
|
-
return value.length;
|
|
57
|
-
}
|
|
58
|
-
function toHeaderRecord(headers) {
|
|
59
|
-
const record = {};
|
|
60
|
-
headers.forEach((value, key)=>{
|
|
61
|
-
record[key] = value;
|
|
62
|
-
});
|
|
63
|
-
return record;
|
|
64
|
-
}
|
|
65
|
-
function normalizeItemMethod(method) {
|
|
66
|
-
return (method || 'GET').toUpperCase();
|
|
67
|
-
}
|
|
68
48
|
function normalizeBatchAllowedMethods(allowedMethods) {
|
|
69
49
|
const source = Array.isArray(allowedMethods) && allowedMethods.length > 0 ? allowedMethods : [
|
|
70
50
|
'GET'
|
|
@@ -72,7 +52,7 @@ function normalizeBatchAllowedMethods(allowedMethods) {
|
|
|
72
52
|
return new Set(source.map((method)=>method.toUpperCase()));
|
|
73
53
|
}
|
|
74
54
|
function isBatchRequestPayload(value) {
|
|
75
|
-
return isPlainObject(value) && 1 === value.protocolVersion && 'string' == typeof value.batchId && 'number' == typeof value.sentAt && Array.isArray(value.items);
|
|
55
|
+
return (0, index_js_namespaceObject.isPlainObject)(value) && 1 === value.protocolVersion && 'string' == typeof value.batchId && 'number' == typeof value.sentAt && Array.isArray(value.items);
|
|
76
56
|
}
|
|
77
57
|
function createBatchValidationResponse(message, status = 400) {
|
|
78
58
|
return new Response(JSON.stringify({
|
|
@@ -84,16 +64,6 @@ function createBatchValidationResponse(message, status = 400) {
|
|
|
84
64
|
}
|
|
85
65
|
});
|
|
86
66
|
}
|
|
87
|
-
function createJsonValidationResponse(message, status = 400) {
|
|
88
|
-
return new Response(JSON.stringify({
|
|
89
|
-
message
|
|
90
|
-
}), {
|
|
91
|
-
status,
|
|
92
|
-
headers: {
|
|
93
|
-
'content-type': 'application/json; charset=utf-8'
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
67
|
function cloneWithoutJsonBodyHeaders(request) {
|
|
98
68
|
const headers = new Headers(request.headers);
|
|
99
69
|
headers.delete('content-type');
|
|
@@ -105,7 +75,7 @@ function cloneWithoutJsonBodyHeaders(request) {
|
|
|
105
75
|
});
|
|
106
76
|
}
|
|
107
77
|
async function prepareJsonRequestBody(request) {
|
|
108
|
-
const method =
|
|
78
|
+
const method = (0, index_js_namespaceObject.normalizeMethod)(request.method);
|
|
109
79
|
if ('GET' === method || 'HEAD' === method) return request;
|
|
110
80
|
const contentType = (request.headers.get('content-type') || '').toLowerCase();
|
|
111
81
|
if (!contentType.includes('application/json')) return request;
|
|
@@ -115,7 +85,7 @@ async function prepareJsonRequestBody(request) {
|
|
|
115
85
|
if ('' === bodyText) return cloneWithoutJsonBodyHeaders(request);
|
|
116
86
|
JSON.parse(bodyText);
|
|
117
87
|
} catch {
|
|
118
|
-
return
|
|
88
|
+
return createBatchValidationResponse('Invalid JSON request body');
|
|
119
89
|
}
|
|
120
90
|
return request;
|
|
121
91
|
}
|
|
@@ -166,29 +136,23 @@ async function mapWithConcurrency(items, concurrency, mapper) {
|
|
|
166
136
|
}
|
|
167
137
|
exports.createBatchValidationResponse = __webpack_exports__.createBatchValidationResponse;
|
|
168
138
|
exports.isBatchRequestPayload = __webpack_exports__.isBatchRequestPayload;
|
|
169
|
-
exports.isPlainObject = __webpack_exports__.isPlainObject;
|
|
170
139
|
exports.mapWithConcurrency = __webpack_exports__.mapWithConcurrency;
|
|
171
140
|
exports.normalizeBatchAllowedMethods = __webpack_exports__.normalizeBatchAllowedMethods;
|
|
172
141
|
exports.normalizeBatchPath = __webpack_exports__.normalizeBatchPath;
|
|
173
|
-
exports.normalizeItemMethod = __webpack_exports__.normalizeItemMethod;
|
|
174
142
|
exports.prepareJsonRequestBody = __webpack_exports__.prepareJsonRequestBody;
|
|
175
143
|
exports.promiseWithTimeout = __webpack_exports__.promiseWithTimeout;
|
|
176
144
|
exports.toBatchItemError = __webpack_exports__.toBatchItemError;
|
|
177
145
|
exports.toHeaderRecord = __webpack_exports__.toHeaderRecord;
|
|
178
|
-
exports.toTextLength = __webpack_exports__.toTextLength;
|
|
179
146
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
180
147
|
"createBatchValidationResponse",
|
|
181
148
|
"isBatchRequestPayload",
|
|
182
|
-
"isPlainObject",
|
|
183
149
|
"mapWithConcurrency",
|
|
184
150
|
"normalizeBatchAllowedMethods",
|
|
185
151
|
"normalizeBatchPath",
|
|
186
|
-
"normalizeItemMethod",
|
|
187
152
|
"prepareJsonRequestBody",
|
|
188
153
|
"promiseWithTimeout",
|
|
189
154
|
"toBatchItemError",
|
|
190
|
-
"toHeaderRecord"
|
|
191
|
-
"toTextLength"
|
|
155
|
+
"toHeaderRecord"
|
|
192
156
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
193
157
|
Object.defineProperty(exports, '__esModule', {
|
|
194
158
|
value: true
|
|
@@ -34,7 +34,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
34
34
|
isRpcRequest: ()=>isRpcRequest,
|
|
35
35
|
removeMountedPrefixFromBatchPath: ()=>removeMountedPrefixFromBatchPath
|
|
36
36
|
});
|
|
37
|
-
const
|
|
37
|
+
const index_js_namespaceObject = require("../../data-platform/index.js");
|
|
38
38
|
function getRequestPathname(request) {
|
|
39
39
|
try {
|
|
40
40
|
return new URL(request.url).pathname;
|
|
@@ -47,7 +47,7 @@ function normalizeMountPrefix(prefix) {
|
|
|
47
47
|
return prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
|
|
48
48
|
}
|
|
49
49
|
function getMountedPrefixFromContext(request, context) {
|
|
50
|
-
if (!(0,
|
|
50
|
+
if (!(0, index_js_namespaceObject.isPlainObject)(context) || 'string' != typeof context.path) return '';
|
|
51
51
|
const contextPath = normalizeMountPrefix(context.path);
|
|
52
52
|
const requestPath = normalizeMountPrefix(getRequestPathname(request));
|
|
53
53
|
if (!contextPath || !requestPath || contextPath === requestPath || !contextPath.endsWith(requestPath)) return '';
|
|
@@ -44,6 +44,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
44
44
|
const bff_core_namespaceObject = require("@modern-js/bff-core");
|
|
45
45
|
const external_path_namespaceObject = require("path");
|
|
46
46
|
var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
|
|
47
|
+
const external_headers_js_namespaceObject = require("./headers.js");
|
|
47
48
|
const readNearestPackageVersion = (startDir)=>{
|
|
48
49
|
if (!startDir) return;
|
|
49
50
|
let current = external_path_default().resolve(startDir);
|
|
@@ -80,16 +81,9 @@ const checkCrossProjectPolicyResponse = (headers, policy)=>{
|
|
|
80
81
|
headers: DENIAL_HEADERS
|
|
81
82
|
});
|
|
82
83
|
};
|
|
83
|
-
const toHeaderRecord = (headers)=>{
|
|
84
|
-
const record = {};
|
|
85
|
-
headers.forEach((value, key)=>{
|
|
86
|
-
record[key] = value;
|
|
87
|
-
});
|
|
88
|
-
return record;
|
|
89
|
-
};
|
|
90
84
|
const checkCrossProjectPolicyForRequest = (request, policy)=>{
|
|
91
85
|
if (!policy?.enabled) return null;
|
|
92
|
-
return checkCrossProjectPolicyResponse(toHeaderRecord(request.headers), policy);
|
|
86
|
+
return checkCrossProjectPolicyResponse((0, external_headers_js_namespaceObject.toHeaderRecord)(request.headers), policy);
|
|
93
87
|
};
|
|
94
88
|
exports.checkCrossProjectPolicyForRequest = __webpack_exports__.checkCrossProjectPolicyForRequest;
|
|
95
89
|
exports.checkCrossProjectPolicyResponse = __webpack_exports__.checkCrossProjectPolicyResponse;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
15
|
+
(()=>{
|
|
16
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
+
})();
|
|
18
|
+
(()=>{
|
|
19
|
+
__webpack_require__.r = (exports1)=>{
|
|
20
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
21
|
+
value: 'Module'
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
24
|
+
value: true
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
var __webpack_exports__ = {};
|
|
29
|
+
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
function toHeaderRecord(headers) {
|
|
31
|
+
const record = {};
|
|
32
|
+
headers.forEach((value, key)=>{
|
|
33
|
+
record[key] = value;
|
|
34
|
+
});
|
|
35
|
+
return record;
|
|
36
|
+
}
|
|
37
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
38
|
+
toHeaderRecord: ()=>toHeaderRecord
|
|
39
|
+
});
|
|
40
|
+
exports.toHeaderRecord = __webpack_exports__.toHeaderRecord;
|
|
41
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
42
|
+
"toHeaderRecord"
|
|
43
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
44
|
+
Object.defineProperty(exports, '__esModule', {
|
|
45
|
+
value: true
|
|
46
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
export { measureTextBytes, normalizeMethod } from "./batch/request.mjs";
|
|
1
2
|
export { DATA_BATCH_TRANSPORT_OTEL_EVENT, createDataBatchTransport, createDataBatchTransportTelemetryAttributes, emitDataBatchTransportEvent } from "./batch-transport.mjs";
|
|
2
|
-
export { buildQueryKey, buildScopeKey, createOperationId, decodeRequestEnvelopeHeader, encodeRequestEnvelopeHeader, normalizeOrigin, stableStringify } from "./codec.mjs";
|
|
3
|
+
export { buildQueryKey, buildScopeKey, createOperationId, decodeRequestEnvelopeHeader, encodeRequestEnvelopeHeader, isPlainObject, normalizeOrigin, stableStringify } from "./codec.mjs";
|
|
3
4
|
export { createInvalidationEvent, shouldApplyInvalidation } from "./invalidation.mjs";
|
|
4
5
|
export { deriveChildTraceContext, formatTraceparentHeader, parseTraceparentHeader } from "./trace.mjs";
|
|
5
6
|
export { DEFAULT_DATA_BATCH_ENDPOINT, DEFAULT_DATA_BATCH_HEADER, DEFAULT_DATA_ENVELOPE_HEADER } from "./types.mjs";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DEFAULT_DATA_BATCH_HEADER, DEFAULT_DATA_ENVELOPE_HEADER, decodeRequestEnvelopeHeader } from "../../data-platform/index.mjs";
|
|
2
|
-
import { createBatchValidationResponse, isBatchRequestPayload,
|
|
1
|
+
import { DEFAULT_DATA_BATCH_HEADER, DEFAULT_DATA_ENVELOPE_HEADER, decodeRequestEnvelopeHeader, isPlainObject, measureTextBytes, normalizeMethod } from "../../data-platform/index.mjs";
|
|
2
|
+
import { createBatchValidationResponse, isBatchRequestPayload, mapWithConcurrency, normalizeBatchAllowedMethods, normalizeBatchPath, promiseWithTimeout, toBatchItemError, toHeaderRecord } from "./batch.mjs";
|
|
3
3
|
import { getMountedPrefixFromContext, removeMountedPrefixFromBatchPath } from "./routing.mjs";
|
|
4
4
|
function createDataPlatformBatchRequestHandler(options) {
|
|
5
5
|
const dataPlatformBatchOptions = options.dataPlatform?.batch;
|
|
@@ -14,12 +14,12 @@ function createDataPlatformBatchRequestHandler(options) {
|
|
|
14
14
|
const normalizedEnvelopeHeader = envelopeHeader.toLowerCase();
|
|
15
15
|
const handle = async (request, context)=>{
|
|
16
16
|
const mountedPrefix = getMountedPrefixFromContext(request, context);
|
|
17
|
-
const method =
|
|
17
|
+
const method = normalizeMethod(request.method);
|
|
18
18
|
if ('POST' !== method) return createBatchValidationResponse('Batch endpoint only supports POST requests', 405);
|
|
19
19
|
const contentType = request.headers.get('content-type') || '';
|
|
20
20
|
if (!contentType.includes('application/json')) return createBatchValidationResponse('Batch endpoint requires application/json content-type', 415);
|
|
21
21
|
const payloadText = await request.text();
|
|
22
|
-
if (
|
|
22
|
+
if (measureTextBytes(payloadText) > batchMaxBytes) return createBatchValidationResponse(`Batch payload exceeds max size (${String(batchMaxBytes)} bytes)`, 413);
|
|
23
23
|
let payload;
|
|
24
24
|
try {
|
|
25
25
|
payload = JSON.parse(payloadText);
|
|
@@ -38,7 +38,7 @@ function createDataPlatformBatchRequestHandler(options) {
|
|
|
38
38
|
const normalizedItemPath = removeMountedPrefixFromBatchPath(rawItem.path, mountedPrefix);
|
|
39
39
|
const itemPathname = normalizedItemPath.split('?')[0] || normalizedItemPath;
|
|
40
40
|
if (itemPathname === batchPath || itemPathname.startsWith(`${batchPath}/`)) return toBatchItemError(itemId, 400, 'Batch item path cannot target batch endpoint');
|
|
41
|
-
const itemMethod =
|
|
41
|
+
const itemMethod = normalizeMethod('string' == typeof rawItem.method ? rawItem.method : void 0);
|
|
42
42
|
if (!batchAllowedMethods.has(itemMethod)) return toBatchItemError(itemId, 405, `Batch item method ${itemMethod} is not allowed`);
|
|
43
43
|
if (void 0 !== rawItem.body && null !== rawItem.body && 'string' != typeof rawItem.body) return toBatchItemError(itemId, 400, 'Batch item body must be a string when provided');
|
|
44
44
|
if (('GET' === itemMethod || 'HEAD' === itemMethod) && 'string' == typeof rawItem.body) return toBatchItemError(itemId, 400, `${itemMethod} batch item cannot include body`);
|
|
@@ -1,27 +1,9 @@
|
|
|
1
|
-
import { DEFAULT_DATA_BATCH_ENDPOINT } from "../../data-platform/index.mjs";
|
|
1
|
+
import { DEFAULT_DATA_BATCH_ENDPOINT, isPlainObject, normalizeMethod } from "../../data-platform/index.mjs";
|
|
2
2
|
function normalizeBatchPath(pathname) {
|
|
3
3
|
if (!pathname || '/' === pathname) return DEFAULT_DATA_BATCH_ENDPOINT;
|
|
4
4
|
if (!pathname.startsWith('/')) return `/${pathname}`;
|
|
5
5
|
return pathname;
|
|
6
6
|
}
|
|
7
|
-
function isPlainObject(value) {
|
|
8
|
-
return 'object' == typeof value && null !== value && !Array.isArray(value);
|
|
9
|
-
}
|
|
10
|
-
function toTextLength(value) {
|
|
11
|
-
if ("u" > typeof TextEncoder) return new TextEncoder().encode(value).length;
|
|
12
|
-
if ("u" > typeof Buffer) return Buffer.byteLength(value);
|
|
13
|
-
return value.length;
|
|
14
|
-
}
|
|
15
|
-
function toHeaderRecord(headers) {
|
|
16
|
-
const record = {};
|
|
17
|
-
headers.forEach((value, key)=>{
|
|
18
|
-
record[key] = value;
|
|
19
|
-
});
|
|
20
|
-
return record;
|
|
21
|
-
}
|
|
22
|
-
function normalizeItemMethod(method) {
|
|
23
|
-
return (method || 'GET').toUpperCase();
|
|
24
|
-
}
|
|
25
7
|
function normalizeBatchAllowedMethods(allowedMethods) {
|
|
26
8
|
const source = Array.isArray(allowedMethods) && allowedMethods.length > 0 ? allowedMethods : [
|
|
27
9
|
'GET'
|
|
@@ -41,16 +23,6 @@ function createBatchValidationResponse(message, status = 400) {
|
|
|
41
23
|
}
|
|
42
24
|
});
|
|
43
25
|
}
|
|
44
|
-
function createJsonValidationResponse(message, status = 400) {
|
|
45
|
-
return new Response(JSON.stringify({
|
|
46
|
-
message
|
|
47
|
-
}), {
|
|
48
|
-
status,
|
|
49
|
-
headers: {
|
|
50
|
-
'content-type': 'application/json; charset=utf-8'
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
26
|
function cloneWithoutJsonBodyHeaders(request) {
|
|
55
27
|
const headers = new Headers(request.headers);
|
|
56
28
|
headers.delete('content-type');
|
|
@@ -62,7 +34,7 @@ function cloneWithoutJsonBodyHeaders(request) {
|
|
|
62
34
|
});
|
|
63
35
|
}
|
|
64
36
|
async function prepareJsonRequestBody(request) {
|
|
65
|
-
const method =
|
|
37
|
+
const method = normalizeMethod(request.method);
|
|
66
38
|
if ('GET' === method || 'HEAD' === method) return request;
|
|
67
39
|
const contentType = (request.headers.get('content-type') || '').toLowerCase();
|
|
68
40
|
if (!contentType.includes('application/json')) return request;
|
|
@@ -72,7 +44,7 @@ async function prepareJsonRequestBody(request) {
|
|
|
72
44
|
if ('' === bodyText) return cloneWithoutJsonBodyHeaders(request);
|
|
73
45
|
JSON.parse(bodyText);
|
|
74
46
|
} catch {
|
|
75
|
-
return
|
|
47
|
+
return createBatchValidationResponse('Invalid JSON request body');
|
|
76
48
|
}
|
|
77
49
|
return request;
|
|
78
50
|
}
|
|
@@ -121,4 +93,5 @@ async function mapWithConcurrency(items, concurrency, mapper) {
|
|
|
121
93
|
await Promise.all(workers);
|
|
122
94
|
return output;
|
|
123
95
|
}
|
|
124
|
-
export {
|
|
96
|
+
export { toHeaderRecord } from "../../../utils/headers.mjs";
|
|
97
|
+
export { createBatchValidationResponse, isBatchRequestPayload, mapWithConcurrency, normalizeBatchAllowedMethods, normalizeBatchPath, prepareJsonRequestBody, promiseWithTimeout, toBatchItemError };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { checkCrossProjectPolicy, deriveOperationVersion, resolveCrossProjectPolicy } from "@modern-js/bff-core";
|
|
2
2
|
import path from "path";
|
|
3
|
+
import { toHeaderRecord } from "./headers.mjs";
|
|
3
4
|
const readNearestPackageVersion = (startDir)=>{
|
|
4
5
|
if (!startDir) return;
|
|
5
6
|
let current = path.resolve(startDir);
|
|
@@ -36,13 +37,6 @@ const checkCrossProjectPolicyResponse = (headers, policy)=>{
|
|
|
36
37
|
headers: DENIAL_HEADERS
|
|
37
38
|
});
|
|
38
39
|
};
|
|
39
|
-
const toHeaderRecord = (headers)=>{
|
|
40
|
-
const record = {};
|
|
41
|
-
headers.forEach((value, key)=>{
|
|
42
|
-
record[key] = value;
|
|
43
|
-
});
|
|
44
|
-
return record;
|
|
45
|
-
};
|
|
46
40
|
const checkCrossProjectPolicyForRequest = (request, policy)=>{
|
|
47
41
|
if (!policy?.enabled) return null;
|
|
48
42
|
return checkCrossProjectPolicyResponse(toHeaderRecord(request.headers), policy);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
+
export { measureTextBytes, normalizeMethod } from "./batch/request.mjs";
|
|
2
3
|
export { DATA_BATCH_TRANSPORT_OTEL_EVENT, createDataBatchTransport, createDataBatchTransportTelemetryAttributes, emitDataBatchTransportEvent } from "./batch-transport.mjs";
|
|
3
|
-
export { buildQueryKey, buildScopeKey, createOperationId, decodeRequestEnvelopeHeader, encodeRequestEnvelopeHeader, normalizeOrigin, stableStringify } from "./codec.mjs";
|
|
4
|
+
export { buildQueryKey, buildScopeKey, createOperationId, decodeRequestEnvelopeHeader, encodeRequestEnvelopeHeader, isPlainObject, normalizeOrigin, stableStringify } from "./codec.mjs";
|
|
4
5
|
export { createInvalidationEvent, shouldApplyInvalidation } from "./invalidation.mjs";
|
|
5
6
|
export { deriveChildTraceContext, formatTraceparentHeader, parseTraceparentHeader } from "./trace.mjs";
|
|
6
7
|
export { DEFAULT_DATA_BATCH_ENDPOINT, DEFAULT_DATA_BATCH_HEADER, DEFAULT_DATA_ENVELOPE_HEADER } from "./types.mjs";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
import { DEFAULT_DATA_BATCH_HEADER, DEFAULT_DATA_ENVELOPE_HEADER, decodeRequestEnvelopeHeader } from "../../data-platform/index.mjs";
|
|
3
|
-
import { createBatchValidationResponse, isBatchRequestPayload,
|
|
2
|
+
import { DEFAULT_DATA_BATCH_HEADER, DEFAULT_DATA_ENVELOPE_HEADER, decodeRequestEnvelopeHeader, isPlainObject, measureTextBytes, normalizeMethod } from "../../data-platform/index.mjs";
|
|
3
|
+
import { createBatchValidationResponse, isBatchRequestPayload, mapWithConcurrency, normalizeBatchAllowedMethods, normalizeBatchPath, promiseWithTimeout, toBatchItemError, toHeaderRecord } from "./batch.mjs";
|
|
4
4
|
import { getMountedPrefixFromContext, removeMountedPrefixFromBatchPath } from "./routing.mjs";
|
|
5
5
|
function createDataPlatformBatchRequestHandler(options) {
|
|
6
6
|
const dataPlatformBatchOptions = options.dataPlatform?.batch;
|
|
@@ -15,12 +15,12 @@ function createDataPlatformBatchRequestHandler(options) {
|
|
|
15
15
|
const normalizedEnvelopeHeader = envelopeHeader.toLowerCase();
|
|
16
16
|
const handle = async (request, context)=>{
|
|
17
17
|
const mountedPrefix = getMountedPrefixFromContext(request, context);
|
|
18
|
-
const method =
|
|
18
|
+
const method = normalizeMethod(request.method);
|
|
19
19
|
if ('POST' !== method) return createBatchValidationResponse('Batch endpoint only supports POST requests', 405);
|
|
20
20
|
const contentType = request.headers.get('content-type') || '';
|
|
21
21
|
if (!contentType.includes('application/json')) return createBatchValidationResponse('Batch endpoint requires application/json content-type', 415);
|
|
22
22
|
const payloadText = await request.text();
|
|
23
|
-
if (
|
|
23
|
+
if (measureTextBytes(payloadText) > batchMaxBytes) return createBatchValidationResponse(`Batch payload exceeds max size (${String(batchMaxBytes)} bytes)`, 413);
|
|
24
24
|
let payload;
|
|
25
25
|
try {
|
|
26
26
|
payload = JSON.parse(payloadText);
|
|
@@ -39,7 +39,7 @@ function createDataPlatformBatchRequestHandler(options) {
|
|
|
39
39
|
const normalizedItemPath = removeMountedPrefixFromBatchPath(rawItem.path, mountedPrefix);
|
|
40
40
|
const itemPathname = normalizedItemPath.split('?')[0] || normalizedItemPath;
|
|
41
41
|
if (itemPathname === batchPath || itemPathname.startsWith(`${batchPath}/`)) return toBatchItemError(itemId, 400, 'Batch item path cannot target batch endpoint');
|
|
42
|
-
const itemMethod =
|
|
42
|
+
const itemMethod = normalizeMethod('string' == typeof rawItem.method ? rawItem.method : void 0);
|
|
43
43
|
if (!batchAllowedMethods.has(itemMethod)) return toBatchItemError(itemId, 405, `Batch item method ${itemMethod} is not allowed`);
|
|
44
44
|
if (void 0 !== rawItem.body && null !== rawItem.body && 'string' != typeof rawItem.body) return toBatchItemError(itemId, 400, 'Batch item body must be a string when provided');
|
|
45
45
|
if (('GET' === itemMethod || 'HEAD' === itemMethod) && 'string' == typeof rawItem.body) return toBatchItemError(itemId, 400, `${itemMethod} batch item cannot include body`);
|
|
@@ -1,28 +1,10 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
import { DEFAULT_DATA_BATCH_ENDPOINT } from "../../data-platform/index.mjs";
|
|
2
|
+
import { DEFAULT_DATA_BATCH_ENDPOINT, isPlainObject, normalizeMethod } from "../../data-platform/index.mjs";
|
|
3
3
|
function normalizeBatchPath(pathname) {
|
|
4
4
|
if (!pathname || '/' === pathname) return DEFAULT_DATA_BATCH_ENDPOINT;
|
|
5
5
|
if (!pathname.startsWith('/')) return `/${pathname}`;
|
|
6
6
|
return pathname;
|
|
7
7
|
}
|
|
8
|
-
function isPlainObject(value) {
|
|
9
|
-
return 'object' == typeof value && null !== value && !Array.isArray(value);
|
|
10
|
-
}
|
|
11
|
-
function toTextLength(value) {
|
|
12
|
-
if ("u" > typeof TextEncoder) return new TextEncoder().encode(value).length;
|
|
13
|
-
if ("u" > typeof Buffer) return Buffer.byteLength(value);
|
|
14
|
-
return value.length;
|
|
15
|
-
}
|
|
16
|
-
function toHeaderRecord(headers) {
|
|
17
|
-
const record = {};
|
|
18
|
-
headers.forEach((value, key)=>{
|
|
19
|
-
record[key] = value;
|
|
20
|
-
});
|
|
21
|
-
return record;
|
|
22
|
-
}
|
|
23
|
-
function normalizeItemMethod(method) {
|
|
24
|
-
return (method || 'GET').toUpperCase();
|
|
25
|
-
}
|
|
26
8
|
function normalizeBatchAllowedMethods(allowedMethods) {
|
|
27
9
|
const source = Array.isArray(allowedMethods) && allowedMethods.length > 0 ? allowedMethods : [
|
|
28
10
|
'GET'
|
|
@@ -42,16 +24,6 @@ function createBatchValidationResponse(message, status = 400) {
|
|
|
42
24
|
}
|
|
43
25
|
});
|
|
44
26
|
}
|
|
45
|
-
function createJsonValidationResponse(message, status = 400) {
|
|
46
|
-
return new Response(JSON.stringify({
|
|
47
|
-
message
|
|
48
|
-
}), {
|
|
49
|
-
status,
|
|
50
|
-
headers: {
|
|
51
|
-
'content-type': 'application/json; charset=utf-8'
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
27
|
function cloneWithoutJsonBodyHeaders(request) {
|
|
56
28
|
const headers = new Headers(request.headers);
|
|
57
29
|
headers.delete('content-type');
|
|
@@ -63,7 +35,7 @@ function cloneWithoutJsonBodyHeaders(request) {
|
|
|
63
35
|
});
|
|
64
36
|
}
|
|
65
37
|
async function prepareJsonRequestBody(request) {
|
|
66
|
-
const method =
|
|
38
|
+
const method = normalizeMethod(request.method);
|
|
67
39
|
if ('GET' === method || 'HEAD' === method) return request;
|
|
68
40
|
const contentType = (request.headers.get('content-type') || '').toLowerCase();
|
|
69
41
|
if (!contentType.includes('application/json')) return request;
|
|
@@ -73,7 +45,7 @@ async function prepareJsonRequestBody(request) {
|
|
|
73
45
|
if ('' === bodyText) return cloneWithoutJsonBodyHeaders(request);
|
|
74
46
|
JSON.parse(bodyText);
|
|
75
47
|
} catch {
|
|
76
|
-
return
|
|
48
|
+
return createBatchValidationResponse('Invalid JSON request body');
|
|
77
49
|
}
|
|
78
50
|
return request;
|
|
79
51
|
}
|
|
@@ -122,4 +94,5 @@ async function mapWithConcurrency(items, concurrency, mapper) {
|
|
|
122
94
|
await Promise.all(workers);
|
|
123
95
|
return output;
|
|
124
96
|
}
|
|
125
|
-
export {
|
|
97
|
+
export { toHeaderRecord } from "../../../utils/headers.mjs";
|
|
98
|
+
export { createBatchValidationResponse, isBatchRequestPayload, mapWithConcurrency, normalizeBatchAllowedMethods, normalizeBatchPath, prepareJsonRequestBody, promiseWithTimeout, toBatchItemError };
|
|
@@ -2,6 +2,7 @@ import __rslib_shim_module__ from "node:module";
|
|
|
2
2
|
const require = /*#__PURE__*/ __rslib_shim_module__.createRequire(/*#__PURE__*/ (()=>import.meta.url)());
|
|
3
3
|
import { checkCrossProjectPolicy, deriveOperationVersion, resolveCrossProjectPolicy } from "@modern-js/bff-core";
|
|
4
4
|
import path from "path";
|
|
5
|
+
import { toHeaderRecord } from "./headers.mjs";
|
|
5
6
|
const readNearestPackageVersion = (startDir)=>{
|
|
6
7
|
if (!startDir) return;
|
|
7
8
|
let current = path.resolve(startDir);
|
|
@@ -38,13 +39,6 @@ const checkCrossProjectPolicyResponse = (headers, policy)=>{
|
|
|
38
39
|
headers: DENIAL_HEADERS
|
|
39
40
|
});
|
|
40
41
|
};
|
|
41
|
-
const toHeaderRecord = (headers)=>{
|
|
42
|
-
const record = {};
|
|
43
|
-
headers.forEach((value, key)=>{
|
|
44
|
-
record[key] = value;
|
|
45
|
-
});
|
|
46
|
-
return record;
|
|
47
|
-
};
|
|
48
42
|
const checkCrossProjectPolicyForRequest = (request, policy)=>{
|
|
49
43
|
if (!policy?.enabled) return null;
|
|
50
44
|
return checkCrossProjectPolicyResponse(toHeaderRecord(request.headers), policy);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
export { measureTextBytes, normalizeMethod } from './batch/request';
|
|
1
2
|
export { createDataBatchTransport, createDataBatchTransportTelemetryAttributes, DATA_BATCH_TRANSPORT_OTEL_EVENT, emitDataBatchTransportEvent, } from './batch-transport';
|
|
2
|
-
export { buildQueryKey, buildScopeKey, createOperationId, decodeRequestEnvelopeHeader, encodeRequestEnvelopeHeader, normalizeOrigin, stableStringify, } from './codec';
|
|
3
|
+
export { buildQueryKey, buildScopeKey, createOperationId, decodeRequestEnvelopeHeader, encodeRequestEnvelopeHeader, isPlainObject, normalizeOrigin, stableStringify, } from './codec';
|
|
3
4
|
export { createInvalidationEvent, shouldApplyInvalidation, } from './invalidation';
|
|
4
5
|
export { deriveChildTraceContext, formatTraceparentHeader, parseTraceparentHeader, } from './trace';
|
|
5
6
|
export type { CacheScope, DataBatchRequestItem, DataBatchRequestPayload, DataBatchResponseItem, DataBatchResponsePayload, DataBatchTransportEvent, DataBatchTransportOptions, DataBatchTransportTelemetryAttributes, DataMutationMode, DataRequestMode, HydrationEnvelope, HydrationEnvelopeValidationOptions, InvalidationEvent, InvalidationSubscriber, OperationDescriptor, RequestEnvelope, RequestEnvelopeValidationOptions, SelectionPlan, SelectionPlanValidationOptions, SelectionPlanValidationResult, TraceContext, } from './types';
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { type DataBatchRequestPayload, type DataBatchResponseItem } from '../../data-platform';
|
|
2
|
+
export { toHeaderRecord } from '../../../utils/headers';
|
|
2
3
|
export declare function normalizeBatchPath(pathname: string | undefined): `/${string}`;
|
|
3
|
-
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
4
|
-
export declare function toTextLength(value: string): number;
|
|
5
|
-
export declare function toHeaderRecord(headers: Headers): Record<string, string>;
|
|
6
|
-
export declare function normalizeItemMethod(method: string | undefined): string;
|
|
7
4
|
export declare function normalizeBatchAllowedMethods(allowedMethods: string[] | undefined): Set<string>;
|
|
8
5
|
type ParsedBatchRequestPayload = Omit<DataBatchRequestPayload, 'items'> & {
|
|
9
6
|
items: unknown[];
|
|
@@ -14,4 +11,3 @@ export declare function prepareJsonRequestBody(request: Request): Promise<Reques
|
|
|
14
11
|
export declare function toBatchItemError(id: string, status: number, message: string): DataBatchResponseItem;
|
|
15
12
|
export declare function promiseWithTimeout<T>(effect: Promise<T>, timeoutMs: number): Promise<T>;
|
|
16
13
|
export declare function mapWithConcurrency<TItem, TResult>(items: readonly TItem[], concurrency: number, mapper: (item: TItem, index: number) => Promise<TResult>): Promise<TResult[]>;
|
|
17
|
-
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function toHeaderRecord(headers: Headers): Record<string, string>;
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"modern",
|
|
18
18
|
"modern.js"
|
|
19
19
|
],
|
|
20
|
-
"version": "3.8.1-ultramodern.
|
|
20
|
+
"version": "3.8.1-ultramodern.3",
|
|
21
21
|
"types": "./dist/types/cli.d.ts",
|
|
22
22
|
"main": "./dist/cjs/cli.js",
|
|
23
23
|
"exports": {
|
|
@@ -169,12 +169,12 @@
|
|
|
169
169
|
}
|
|
170
170
|
},
|
|
171
171
|
"dependencies": {
|
|
172
|
-
"@modern-js/bff-core": "npm:@bleedingdev/modern-js-bff-core@3.8.1-ultramodern.
|
|
173
|
-
"@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.8.1-ultramodern.
|
|
174
|
-
"@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.8.1-ultramodern.
|
|
175
|
-
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.8.1-ultramodern.
|
|
176
|
-
"@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.8.1-ultramodern.
|
|
177
|
-
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.8.1-ultramodern.
|
|
172
|
+
"@modern-js/bff-core": "npm:@bleedingdev/modern-js-bff-core@3.8.1-ultramodern.3",
|
|
173
|
+
"@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.8.1-ultramodern.3",
|
|
174
|
+
"@modern-js/create-request": "npm:@bleedingdev/modern-js-create-request@3.8.1-ultramodern.3",
|
|
175
|
+
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.8.1-ultramodern.3",
|
|
176
|
+
"@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.8.1-ultramodern.3",
|
|
177
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.8.1-ultramodern.3",
|
|
178
178
|
"@module-federation/runtime": "2.8.2",
|
|
179
179
|
"@opentelemetry/api": "1.9.1",
|
|
180
180
|
"@opentelemetry/api-logs": "0.220.0",
|
|
@@ -205,11 +205,11 @@
|
|
|
205
205
|
},
|
|
206
206
|
"devDependencies": {
|
|
207
207
|
"@effect/opentelemetry": "4.0.0-beta.107",
|
|
208
|
-
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.8.1-ultramodern.
|
|
209
|
-
"@modern-js/bff-runtime": "npm:@bleedingdev/modern-js-bff-runtime@3.8.1-ultramodern.
|
|
210
|
-
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.8.1-ultramodern.
|
|
211
|
-
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.8.1-ultramodern.
|
|
212
|
-
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.8.1-ultramodern.
|
|
208
|
+
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.8.1-ultramodern.3",
|
|
209
|
+
"@modern-js/bff-runtime": "npm:@bleedingdev/modern-js-bff-runtime@3.8.1-ultramodern.3",
|
|
210
|
+
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.8.1-ultramodern.3",
|
|
211
|
+
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.8.1-ultramodern.3",
|
|
212
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.8.1-ultramodern.3",
|
|
213
213
|
"@rsbuild/core": "2.1.5",
|
|
214
214
|
"@rslib/core": "0.23.2",
|
|
215
215
|
"@scripts/rstest-config": "2.66.0",
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# UltraModern Data Platform Architecture (MF-safe)
|
|
2
|
-
|
|
3
|
-
This document defines a clean, greenfield data architecture for Fate-style orchestration while preserving Module Federation and micro-frontend interoperability.
|
|
4
|
-
|
|
5
|
-
## Goals
|
|
6
|
-
|
|
7
|
-
- One canonical data contract for requests, views, actions, hydration, and invalidation.
|
|
8
|
-
- Deterministic behavior across SSR/CSR and host/remote applications.
|
|
9
|
-
- Strong tenant/app isolation by default, explicit cross-app communication only.
|
|
10
|
-
- Protocol-level observability and integrity checks.
|
|
11
|
-
|
|
12
|
-
## Core Contracts
|
|
13
|
-
|
|
14
|
-
Implemented in `src/runtime/data-platform/index.ts`:
|
|
15
|
-
|
|
16
|
-
- Operation identity: `createOperationId`
|
|
17
|
-
- Cache/request scoping: `buildScopeKey`, `buildQueryKey`
|
|
18
|
-
- Selection safety: `validateSelectionPlan`
|
|
19
|
-
- Distributed tracing: `parseTraceparentHeader`, `formatTraceparentHeader`, `deriveChildTraceContext`
|
|
20
|
-
- Request envelope: `createRequestEnvelope`, `validateRequestEnvelope`
|
|
21
|
-
- Hydration integrity: `createHydrationEnvelope`, `validateHydrationEnvelope`
|
|
22
|
-
- Invalidation routing: `createInvalidationEvent`, `shouldApplyInvalidation`
|
|
23
|
-
|
|
24
|
-
## Mitigations Applied
|
|
25
|
-
|
|
26
|
-
1. Operation ID collisions
|
|
27
|
-
- IDs include namespace + api/group/endpoint + version + schema hash contribution.
|
|
28
|
-
- IDs are deterministic and hashed from canonical payloads.
|
|
29
|
-
|
|
30
|
-
2. Cross-app cache contamination
|
|
31
|
-
- Scope key includes normalized origin + app namespace + identity dimensions.
|
|
32
|
-
- Query keys include scope key and selection/input fingerprints.
|
|
33
|
-
|
|
34
|
-
3. Invalid selection pushdown
|
|
35
|
-
- Selection plans are depth-limited, field-limited, and optional allow-list validated.
|
|
36
|
-
|
|
37
|
-
4. Trace discontinuity across host/remotes
|
|
38
|
-
- Trace context is first-class and validated through W3C traceparent format.
|
|
39
|
-
- Child spans preserve trace id and parent linkage.
|
|
40
|
-
|
|
41
|
-
5. Hydration poisoning/staleness
|
|
42
|
-
- Hydration payloads include checksum over canonical serialized payload + metadata.
|
|
43
|
-
- Runtime/namespace/origin/protocol checks are validated.
|
|
44
|
-
|
|
45
|
-
6. Unintended cross-namespace invalidation
|
|
46
|
-
- Default: no cross-namespace invalidation.
|
|
47
|
-
- Cross-app invalidation requires explicit target namespace + opt-in subscriber.
|
|
48
|
-
|
|
49
|
-
## Test Strategy
|
|
50
|
-
|
|
51
|
-
Contract and scenario coverage is in `tests/data-platform-contract.test.ts`:
|
|
52
|
-
|
|
53
|
-
- Deterministic and collision-resistant operation IDs.
|
|
54
|
-
- Scope/query key isolation across namespaces and normalized origins.
|
|
55
|
-
- Selection validation guardrails (depth, count, allow-list, shape).
|
|
56
|
-
- Request envelope integrity and required trace context checks.
|
|
57
|
-
- Hydration checksum tamper detection.
|
|
58
|
-
- Invalidation routing policy enforcement.
|
|
59
|
-
- Simulated host/remote mutation and trace propagation scenarios.
|
|
60
|
-
|
|
61
|
-
This suite is intended to be the non-negotiable compatibility gate for future data-runtime changes.
|