@bleedingdev/modern-js-bff-core 3.5.0-ultramodern.36 → 3.5.0-ultramodern.38
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/adapter-kit/parity.js +2 -1
- package/dist/cjs/index.js +29 -48
- package/dist/cjs/security/crossProjectPolicy.js +4 -4
- package/dist/esm/adapter-kit/parity.mjs +2 -1
- package/dist/esm/index.mjs +0 -1
- package/dist/esm/security/crossProjectPolicy.mjs +4 -4
- package/dist/esm/security/operationContracts.mjs +2 -2
- package/dist/esm-node/adapter-kit/parity.mjs +2 -1
- package/dist/esm-node/index.mjs +0 -1
- package/dist/esm-node/security/crossProjectPolicy.mjs +4 -4
- package/dist/types/adapter-kit/parity.d.ts +2 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/security/crossProjectPolicy.d.ts +16 -10
- package/package.json +9 -9
- package/dist/cjs/contracts/eventContracts.js +0 -72
- package/dist/esm/contracts/eventContracts.mjs +0 -28
- package/dist/esm-node/contracts/eventContracts.mjs +0 -29
- package/dist/types/contracts/eventContracts.d.ts +0 -22
|
@@ -137,7 +137,8 @@ const createParityBffConfig = ()=>({
|
|
|
137
137
|
enabled: true,
|
|
138
138
|
allowedNamespaces: [
|
|
139
139
|
shared_js_namespaceObject.PARITY_REQUEST_ID
|
|
140
|
-
]
|
|
140
|
+
],
|
|
141
|
+
allowClientAssertedNamespace: true
|
|
141
142
|
}
|
|
142
143
|
});
|
|
143
144
|
const getParityContracts = ()=>(0, operationContracts_js_namespaceObject.buildOperationContractMap)({
|
package/dist/cjs/index.js
CHANGED
|
@@ -9,9 +9,6 @@ var __webpack_modules__ = {
|
|
|
9
9
|
"./client" (module) {
|
|
10
10
|
module.exports = require("./client/index.js");
|
|
11
11
|
},
|
|
12
|
-
"./contracts/eventContracts" (module) {
|
|
13
|
-
module.exports = require("./contracts/eventContracts.js");
|
|
14
|
-
},
|
|
15
12
|
"./errors/http" (module) {
|
|
16
13
|
module.exports = require("./errors/http.js");
|
|
17
14
|
},
|
|
@@ -117,26 +114,10 @@ var __webpack_exports__ = {};
|
|
|
117
114
|
"Api"
|
|
118
115
|
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_client__rspack_import_2[__rspack_import_key];
|
|
119
116
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
120
|
-
var
|
|
121
|
-
var
|
|
122
|
-
for(const __rspack_import_key in _contracts_eventContracts__rspack_import_3)if ([
|
|
123
|
-
"HttpError",
|
|
124
|
-
"registerPaths",
|
|
125
|
-
"createStorage",
|
|
126
|
-
"isWithMetaHandler",
|
|
127
|
-
"default",
|
|
128
|
-
"HANDLER_WITH_META",
|
|
129
|
-
"ValidationError",
|
|
130
|
-
"getRelativeRuntimePath",
|
|
131
|
-
"INPUT_PARAMS_DECIDER",
|
|
132
|
-
"isInputParamsDeciderHandler",
|
|
133
|
-
"Api"
|
|
134
|
-
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_contracts_eventContracts__rspack_import_3[__rspack_import_key];
|
|
135
|
-
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
136
|
-
var _errors_http__rspack_import_4 = __webpack_require__("./errors/http");
|
|
137
|
-
var _operators_http__rspack_import_5 = __webpack_require__("./operators/http");
|
|
117
|
+
var _errors_http__rspack_import_3 = __webpack_require__("./errors/http");
|
|
118
|
+
var _operators_http__rspack_import_4 = __webpack_require__("./operators/http");
|
|
138
119
|
var __rspack_reexport = {};
|
|
139
|
-
for(const __rspack_import_key in
|
|
120
|
+
for(const __rspack_import_key in _operators_http__rspack_import_4)if ([
|
|
140
121
|
"HttpError",
|
|
141
122
|
"registerPaths",
|
|
142
123
|
"createStorage",
|
|
@@ -148,11 +129,11 @@ var __webpack_exports__ = {};
|
|
|
148
129
|
"INPUT_PARAMS_DECIDER",
|
|
149
130
|
"isInputParamsDeciderHandler",
|
|
150
131
|
"Api"
|
|
151
|
-
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>
|
|
132
|
+
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_operators_http__rspack_import_4[__rspack_import_key];
|
|
152
133
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
153
|
-
var
|
|
134
|
+
var _router__rspack_import_5 = __webpack_require__("./router");
|
|
154
135
|
var __rspack_reexport = {};
|
|
155
|
-
for(const __rspack_import_key in
|
|
136
|
+
for(const __rspack_import_key in _router__rspack_import_5)if ([
|
|
156
137
|
"HttpError",
|
|
157
138
|
"registerPaths",
|
|
158
139
|
"createStorage",
|
|
@@ -164,11 +145,11 @@ var __webpack_exports__ = {};
|
|
|
164
145
|
"INPUT_PARAMS_DECIDER",
|
|
165
146
|
"isInputParamsDeciderHandler",
|
|
166
147
|
"Api"
|
|
167
|
-
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>
|
|
148
|
+
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_router__rspack_import_5[__rspack_import_key];
|
|
168
149
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
169
|
-
var
|
|
150
|
+
var _security_crossProjectPolicy__rspack_import_6 = __webpack_require__("./security/crossProjectPolicy");
|
|
170
151
|
var __rspack_reexport = {};
|
|
171
|
-
for(const __rspack_import_key in
|
|
152
|
+
for(const __rspack_import_key in _security_crossProjectPolicy__rspack_import_6)if ([
|
|
172
153
|
"HttpError",
|
|
173
154
|
"registerPaths",
|
|
174
155
|
"createStorage",
|
|
@@ -180,11 +161,11 @@ var __webpack_exports__ = {};
|
|
|
180
161
|
"INPUT_PARAMS_DECIDER",
|
|
181
162
|
"isInputParamsDeciderHandler",
|
|
182
163
|
"Api"
|
|
183
|
-
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>
|
|
164
|
+
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_security_crossProjectPolicy__rspack_import_6[__rspack_import_key];
|
|
184
165
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
185
|
-
var
|
|
166
|
+
var _security_operationContracts__rspack_import_7 = __webpack_require__("./security/operationContracts");
|
|
186
167
|
var __rspack_reexport = {};
|
|
187
|
-
for(const __rspack_import_key in
|
|
168
|
+
for(const __rspack_import_key in _security_operationContracts__rspack_import_7)if ([
|
|
188
169
|
"HttpError",
|
|
189
170
|
"registerPaths",
|
|
190
171
|
"createStorage",
|
|
@@ -196,11 +177,11 @@ var __webpack_exports__ = {};
|
|
|
196
177
|
"INPUT_PARAMS_DECIDER",
|
|
197
178
|
"isInputParamsDeciderHandler",
|
|
198
179
|
"Api"
|
|
199
|
-
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>
|
|
180
|
+
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_security_operationContracts__rspack_import_7[__rspack_import_key];
|
|
200
181
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
201
|
-
var
|
|
182
|
+
var _security_resolveCrossProjectPolicy__rspack_import_8 = __webpack_require__("./security/resolveCrossProjectPolicy");
|
|
202
183
|
var __rspack_reexport = {};
|
|
203
|
-
for(const __rspack_import_key in
|
|
184
|
+
for(const __rspack_import_key in _security_resolveCrossProjectPolicy__rspack_import_8)if ([
|
|
204
185
|
"HttpError",
|
|
205
186
|
"registerPaths",
|
|
206
187
|
"createStorage",
|
|
@@ -212,11 +193,11 @@ var __webpack_exports__ = {};
|
|
|
212
193
|
"INPUT_PARAMS_DECIDER",
|
|
213
194
|
"isInputParamsDeciderHandler",
|
|
214
195
|
"Api"
|
|
215
|
-
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>
|
|
196
|
+
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_security_resolveCrossProjectPolicy__rspack_import_8[__rspack_import_key];
|
|
216
197
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
217
|
-
var
|
|
198
|
+
var _types__rspack_import_9 = __webpack_require__("./types");
|
|
218
199
|
var __rspack_reexport = {};
|
|
219
|
-
for(const __rspack_import_key in
|
|
200
|
+
for(const __rspack_import_key in _types__rspack_import_9)if ([
|
|
220
201
|
"HttpError",
|
|
221
202
|
"registerPaths",
|
|
222
203
|
"createStorage",
|
|
@@ -228,20 +209,20 @@ var __webpack_exports__ = {};
|
|
|
228
209
|
"INPUT_PARAMS_DECIDER",
|
|
229
210
|
"isInputParamsDeciderHandler",
|
|
230
211
|
"Api"
|
|
231
|
-
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>
|
|
212
|
+
].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_types__rspack_import_9[__rspack_import_key];
|
|
232
213
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
233
|
-
var
|
|
214
|
+
var _utils__rspack_import_10 = __webpack_require__("./utils?637c");
|
|
234
215
|
__webpack_require__.d(__webpack_exports__, {
|
|
235
216
|
Api: ()=>_api__rspack_import_1.Api,
|
|
236
|
-
HANDLER_WITH_META: ()=>
|
|
237
|
-
HttpError: ()=>
|
|
238
|
-
INPUT_PARAMS_DECIDER: ()=>
|
|
239
|
-
ValidationError: ()=>
|
|
240
|
-
createStorage: ()=>
|
|
241
|
-
getRelativeRuntimePath: ()=>
|
|
242
|
-
isInputParamsDeciderHandler: ()=>
|
|
243
|
-
isWithMetaHandler: ()=>
|
|
244
|
-
registerPaths: ()=>
|
|
217
|
+
HANDLER_WITH_META: ()=>_utils__rspack_import_10.HANDLER_WITH_META,
|
|
218
|
+
HttpError: ()=>_errors_http__rspack_import_3.HttpError,
|
|
219
|
+
INPUT_PARAMS_DECIDER: ()=>_utils__rspack_import_10.INPUT_PARAMS_DECIDER,
|
|
220
|
+
ValidationError: ()=>_errors_http__rspack_import_3.ValidationError,
|
|
221
|
+
createStorage: ()=>_utils__rspack_import_10.createStorage,
|
|
222
|
+
getRelativeRuntimePath: ()=>_utils__rspack_import_10.getRelativeRuntimePath,
|
|
223
|
+
isInputParamsDeciderHandler: ()=>_utils__rspack_import_10.isInputParamsDeciderHandler,
|
|
224
|
+
isWithMetaHandler: ()=>_utils__rspack_import_10.isWithMetaHandler,
|
|
225
|
+
registerPaths: ()=>_utils__rspack_import_10.registerPaths
|
|
245
226
|
});
|
|
246
227
|
})();
|
|
247
228
|
exports.Api = __webpack_exports__.Api;
|
|
@@ -31,7 +31,7 @@ const BFF_ENVELOPE_HEADER = 'x-modernjs-bff-envelope';
|
|
|
31
31
|
const BFF_OPERATION_CONTEXT_HEADER = 'x-operation-id';
|
|
32
32
|
const BFF_OPERATION_CONTEXT_DETAIL_HEADER = 'x-modernjs-bff-operation-context';
|
|
33
33
|
const DEFAULT_DENY_STATUS = 403;
|
|
34
|
-
const NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE = 'cross-project namespace allowlist requires verifyProducerIdentity
|
|
34
|
+
const NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE = 'cross-project namespace allowlist requires verifyProducerIdentity';
|
|
35
35
|
let hasWarnedAdvisoryNamespaceAllowlist = false;
|
|
36
36
|
const normalizeHeaderName = (headerName, fallback)=>(headerName || fallback).trim().toLowerCase();
|
|
37
37
|
const normalizeStatusCode = (statusCode)=>{
|
|
@@ -53,11 +53,10 @@ const createViolation = (reason, message, status)=>({
|
|
|
53
53
|
message,
|
|
54
54
|
status
|
|
55
55
|
});
|
|
56
|
-
const isProductionMode = ()=>"u" > typeof process && process.env?.NODE_ENV === 'production';
|
|
57
56
|
const warnAdvisoryNamespaceAllowlist = ()=>{
|
|
58
57
|
if (hasWarnedAdvisoryNamespaceAllowlist) return;
|
|
59
58
|
hasWarnedAdvisoryNamespaceAllowlist = true;
|
|
60
|
-
console.warn(`[Modern.js BFF] ${NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE}.
|
|
59
|
+
console.warn(`[Modern.js BFF] ${NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE}. allowClientAssertedNamespace=true will continue to evaluate allowlist against client-asserted requestId namespaces only.`);
|
|
61
60
|
};
|
|
62
61
|
const createCrossProjectPolicyEvaluationState = (headers, policy)=>({
|
|
63
62
|
headers,
|
|
@@ -69,6 +68,7 @@ const createCrossProjectPolicyEvaluationState = (headers, policy)=>({
|
|
|
69
68
|
requireOperationSchemaHash: policy.requireOperationSchemaHash ?? true,
|
|
70
69
|
requireOperationVersion: policy.requireOperationVersion ?? true,
|
|
71
70
|
allowUnknownOperations: policy.allowUnknownOperations ?? false,
|
|
71
|
+
allowClientAssertedNamespace: policy.allowClientAssertedNamespace ?? false,
|
|
72
72
|
verifyProducerIdentity: 'function' == typeof policy.verifyProducerIdentity ? policy.verifyProducerIdentity : void 0,
|
|
73
73
|
allowedNamespaces: (policy.allowedNamespaces || []).map((item)=>item.trim().toLowerCase()).filter(Boolean),
|
|
74
74
|
envelopeHeader: normalizeHeaderName(policy.envelopeHeader, BFF_ENVELOPE_HEADER),
|
|
@@ -81,7 +81,7 @@ const createCrossProjectPolicyEvaluationState = (headers, policy)=>({
|
|
|
81
81
|
});
|
|
82
82
|
const checkNamespaceAllowlistVerifier = (state)=>{
|
|
83
83
|
if (state.allowedNamespaces.length > 0 && void 0 === state.verifyProducerIdentity) {
|
|
84
|
-
if (
|
|
84
|
+
if (!state.allowClientAssertedNamespace) return createViolation('producer_identity_mismatch', NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE, state.status);
|
|
85
85
|
warnAdvisoryNamespaceAllowlist();
|
|
86
86
|
}
|
|
87
87
|
};
|
package/dist/esm/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ const BFF_ENVELOPE_HEADER = 'x-modernjs-bff-envelope';
|
|
|
2
2
|
const BFF_OPERATION_CONTEXT_HEADER = 'x-operation-id';
|
|
3
3
|
const BFF_OPERATION_CONTEXT_DETAIL_HEADER = 'x-modernjs-bff-operation-context';
|
|
4
4
|
const DEFAULT_DENY_STATUS = 403;
|
|
5
|
-
const NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE = 'cross-project namespace allowlist requires verifyProducerIdentity
|
|
5
|
+
const NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE = 'cross-project namespace allowlist requires verifyProducerIdentity';
|
|
6
6
|
let hasWarnedAdvisoryNamespaceAllowlist = false;
|
|
7
7
|
const normalizeHeaderName = (headerName, fallback)=>(headerName || fallback).trim().toLowerCase();
|
|
8
8
|
const normalizeStatusCode = (statusCode)=>{
|
|
@@ -24,11 +24,10 @@ const createViolation = (reason, message, status)=>({
|
|
|
24
24
|
message,
|
|
25
25
|
status
|
|
26
26
|
});
|
|
27
|
-
const isProductionMode = ()=>"u" > typeof process && process.env?.NODE_ENV === 'production';
|
|
28
27
|
const warnAdvisoryNamespaceAllowlist = ()=>{
|
|
29
28
|
if (hasWarnedAdvisoryNamespaceAllowlist) return;
|
|
30
29
|
hasWarnedAdvisoryNamespaceAllowlist = true;
|
|
31
|
-
console.warn(`[Modern.js BFF] ${NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE}.
|
|
30
|
+
console.warn(`[Modern.js BFF] ${NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE}. allowClientAssertedNamespace=true will continue to evaluate allowlist against client-asserted requestId namespaces only.`);
|
|
32
31
|
};
|
|
33
32
|
const createCrossProjectPolicyEvaluationState = (headers, policy)=>({
|
|
34
33
|
headers,
|
|
@@ -40,6 +39,7 @@ const createCrossProjectPolicyEvaluationState = (headers, policy)=>({
|
|
|
40
39
|
requireOperationSchemaHash: policy.requireOperationSchemaHash ?? true,
|
|
41
40
|
requireOperationVersion: policy.requireOperationVersion ?? true,
|
|
42
41
|
allowUnknownOperations: policy.allowUnknownOperations ?? false,
|
|
42
|
+
allowClientAssertedNamespace: policy.allowClientAssertedNamespace ?? false,
|
|
43
43
|
verifyProducerIdentity: 'function' == typeof policy.verifyProducerIdentity ? policy.verifyProducerIdentity : void 0,
|
|
44
44
|
allowedNamespaces: (policy.allowedNamespaces || []).map((item)=>item.trim().toLowerCase()).filter(Boolean),
|
|
45
45
|
envelopeHeader: normalizeHeaderName(policy.envelopeHeader, BFF_ENVELOPE_HEADER),
|
|
@@ -52,7 +52,7 @@ const createCrossProjectPolicyEvaluationState = (headers, policy)=>({
|
|
|
52
52
|
});
|
|
53
53
|
const checkNamespaceAllowlistVerifier = (state)=>{
|
|
54
54
|
if (state.allowedNamespaces.length > 0 && void 0 === state.verifyProducerIdentity) {
|
|
55
|
-
if (
|
|
55
|
+
if (!state.allowClientAssertedNamespace) return createViolation('producer_identity_mismatch', NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE, state.status);
|
|
56
56
|
warnAdvisoryNamespaceAllowlist();
|
|
57
57
|
}
|
|
58
58
|
};
|
|
@@ -4,7 +4,7 @@ import { HttpMetadata } from "../types.mjs";
|
|
|
4
4
|
import { __webpack_require__ } from "../rslib-runtime.mjs";
|
|
5
5
|
import * as __rspack_external_zod from "zod";
|
|
6
6
|
__webpack_require__.add({
|
|
7
|
-
|
|
7
|
+
7347 (module) {
|
|
8
8
|
module.exports = __rspack_external_zod;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
@@ -29,7 +29,7 @@ let cachedZodToJSONSchema;
|
|
|
29
29
|
const resolveZodToJSONSchema = ()=>{
|
|
30
30
|
if (void 0 !== cachedZodToJSONSchema) return cachedZodToJSONSchema;
|
|
31
31
|
try {
|
|
32
|
-
const zod = __webpack_require__(
|
|
32
|
+
const zod = __webpack_require__(7347);
|
|
33
33
|
const candidate = zod?.toJSONSchema ?? zod?.z?.toJSONSchema;
|
|
34
34
|
cachedZodToJSONSchema = 'function' == typeof candidate ? candidate : null;
|
|
35
35
|
} catch {
|
package/dist/esm-node/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
export * from "./adapter-kit/index.mjs";
|
|
3
3
|
export * from "./client/index.mjs";
|
|
4
|
-
export * from "./contracts/eventContracts.mjs";
|
|
5
4
|
export * from "./operators/http.mjs";
|
|
6
5
|
export * from "./router/index.mjs";
|
|
7
6
|
export * from "./security/crossProjectPolicy.mjs";
|
|
@@ -3,7 +3,7 @@ const BFF_ENVELOPE_HEADER = 'x-modernjs-bff-envelope';
|
|
|
3
3
|
const BFF_OPERATION_CONTEXT_HEADER = 'x-operation-id';
|
|
4
4
|
const BFF_OPERATION_CONTEXT_DETAIL_HEADER = 'x-modernjs-bff-operation-context';
|
|
5
5
|
const DEFAULT_DENY_STATUS = 403;
|
|
6
|
-
const NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE = 'cross-project namespace allowlist requires verifyProducerIdentity
|
|
6
|
+
const NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE = 'cross-project namespace allowlist requires verifyProducerIdentity';
|
|
7
7
|
let hasWarnedAdvisoryNamespaceAllowlist = false;
|
|
8
8
|
const normalizeHeaderName = (headerName, fallback)=>(headerName || fallback).trim().toLowerCase();
|
|
9
9
|
const normalizeStatusCode = (statusCode)=>{
|
|
@@ -25,11 +25,10 @@ const createViolation = (reason, message, status)=>({
|
|
|
25
25
|
message,
|
|
26
26
|
status
|
|
27
27
|
});
|
|
28
|
-
const isProductionMode = ()=>"u" > typeof process && process.env?.NODE_ENV === 'production';
|
|
29
28
|
const warnAdvisoryNamespaceAllowlist = ()=>{
|
|
30
29
|
if (hasWarnedAdvisoryNamespaceAllowlist) return;
|
|
31
30
|
hasWarnedAdvisoryNamespaceAllowlist = true;
|
|
32
|
-
console.warn(`[Modern.js BFF] ${NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE}.
|
|
31
|
+
console.warn(`[Modern.js BFF] ${NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE}. allowClientAssertedNamespace=true will continue to evaluate allowlist against client-asserted requestId namespaces only.`);
|
|
33
32
|
};
|
|
34
33
|
const createCrossProjectPolicyEvaluationState = (headers, policy)=>({
|
|
35
34
|
headers,
|
|
@@ -41,6 +40,7 @@ const createCrossProjectPolicyEvaluationState = (headers, policy)=>({
|
|
|
41
40
|
requireOperationSchemaHash: policy.requireOperationSchemaHash ?? true,
|
|
42
41
|
requireOperationVersion: policy.requireOperationVersion ?? true,
|
|
43
42
|
allowUnknownOperations: policy.allowUnknownOperations ?? false,
|
|
43
|
+
allowClientAssertedNamespace: policy.allowClientAssertedNamespace ?? false,
|
|
44
44
|
verifyProducerIdentity: 'function' == typeof policy.verifyProducerIdentity ? policy.verifyProducerIdentity : void 0,
|
|
45
45
|
allowedNamespaces: (policy.allowedNamespaces || []).map((item)=>item.trim().toLowerCase()).filter(Boolean),
|
|
46
46
|
envelopeHeader: normalizeHeaderName(policy.envelopeHeader, BFF_ENVELOPE_HEADER),
|
|
@@ -53,7 +53,7 @@ const createCrossProjectPolicyEvaluationState = (headers, policy)=>({
|
|
|
53
53
|
});
|
|
54
54
|
const checkNamespaceAllowlistVerifier = (state)=>{
|
|
55
55
|
if (state.allowedNamespaces.length > 0 && void 0 === state.verifyProducerIdentity) {
|
|
56
|
-
if (
|
|
56
|
+
if (!state.allowClientAssertedNamespace) return createViolation('producer_identity_mismatch', NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE, state.status);
|
|
57
57
|
warnAdvisoryNamespaceAllowlist();
|
|
58
58
|
}
|
|
59
59
|
};
|
|
@@ -15,6 +15,7 @@ export declare const createParityBffConfig: () => {
|
|
|
15
15
|
crossProjectPolicy: {
|
|
16
16
|
enabled: boolean;
|
|
17
17
|
allowedNamespaces: string[];
|
|
18
|
+
allowClientAssertedNamespace: boolean;
|
|
18
19
|
};
|
|
19
20
|
};
|
|
20
21
|
export declare const createAdapterParityScenarios: () => AdapterParityScenario[];
|
|
@@ -26,7 +27,7 @@ export type ParityHttpResponse = {
|
|
|
26
27
|
body: unknown;
|
|
27
28
|
text: string;
|
|
28
29
|
};
|
|
29
|
-
|
|
30
|
+
type AdapterParityResult = {
|
|
30
31
|
status: number;
|
|
31
32
|
payload: unknown;
|
|
32
33
|
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ export * from './adapter-kit';
|
|
|
2
2
|
export { Api } from './api';
|
|
3
3
|
export * from './client';
|
|
4
4
|
export type * from './compatible';
|
|
5
|
-
export * from './contracts/eventContracts';
|
|
6
5
|
export { HttpError, ValidationError } from './errors/http';
|
|
7
6
|
export * from './operators/http';
|
|
8
7
|
export * from './router';
|
|
@@ -10,16 +10,16 @@
|
|
|
10
10
|
* authorization boundary: any caller can echo an allowed `requestId` and a
|
|
11
11
|
* matching operation context.
|
|
12
12
|
*
|
|
13
|
-
* To use `allowedNamespaces
|
|
13
|
+
* To use `allowedNamespaces`, supply
|
|
14
14
|
* {@link CrossProjectPolicyConfig.verifyProducerIdentity}: a server-side
|
|
15
15
|
* hook that derives the producer namespace from a VERIFIED channel (mTLS
|
|
16
16
|
* peer identity, gateway-authenticated JWT claims, service-mesh headers
|
|
17
|
-
* stripped at the edge, ...).
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
17
|
+
* stripped at the edge, ...). Requests fail closed when a namespace allowlist
|
|
18
|
+
* is configured without this hook unless `allowClientAssertedNamespace` is
|
|
19
|
+
* explicitly enabled. That escape hatch keeps legacy local/demo ergonomics:
|
|
20
|
+
* the client-asserted namespace must match the allowlist. When the hook is
|
|
21
|
+
* present, the client-asserted namespace must match the verified namespace
|
|
22
|
+
* and the allowlist is checked against the verified value.
|
|
23
23
|
*
|
|
24
24
|
* Client-side counterparts in `@modern-js/create-request` (identity binding,
|
|
25
25
|
* operation contract validation) are developer-experience aids that fail
|
|
@@ -48,6 +48,12 @@ export interface CrossProjectPolicyConfig {
|
|
|
48
48
|
requireOperationSchemaHash?: boolean;
|
|
49
49
|
requireOperationVersion?: boolean;
|
|
50
50
|
allowedNamespaces?: string[];
|
|
51
|
+
/**
|
|
52
|
+
* Explicit local/demo escape hatch for legacy clients that only provide
|
|
53
|
+
* client-built requestId namespaces. Defaults to false. Enabling this keeps
|
|
54
|
+
* `allowedNamespaces` advisory and must not be used as authorization.
|
|
55
|
+
*/
|
|
56
|
+
allowClientAssertedNamespace?: boolean;
|
|
51
57
|
envelopeHeader?: string;
|
|
52
58
|
operationContextHeader?: string;
|
|
53
59
|
operationContextDetailHeader?: string;
|
|
@@ -63,9 +69,9 @@ export interface CrossProjectPolicyConfig {
|
|
|
63
69
|
* against the verified value instead of the client-asserted one. Returning
|
|
64
70
|
* `undefined` (identity could not be verified) denies the request.
|
|
65
71
|
*
|
|
66
|
-
*
|
|
67
|
-
* without it.
|
|
68
|
-
*
|
|
72
|
+
* By default, `allowedNamespaces` requires this hook and fails closed
|
|
73
|
+
* without it. Set `allowClientAssertedNamespace` only for local/demo
|
|
74
|
+
* ergonomics where advisory client-asserted namespace checks are acceptable.
|
|
69
75
|
*/
|
|
70
76
|
verifyProducerIdentity?: (headers: Record<string, unknown>) => string | undefined;
|
|
71
77
|
}
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"modern",
|
|
18
18
|
"modern.js"
|
|
19
19
|
],
|
|
20
|
-
"version": "3.5.0-ultramodern.
|
|
20
|
+
"version": "3.5.0-ultramodern.38",
|
|
21
21
|
"types": "./dist/types/index.d.ts",
|
|
22
22
|
"main": "./dist/cjs/index.js",
|
|
23
23
|
"exports": {
|
|
@@ -35,19 +35,19 @@
|
|
|
35
35
|
"@swc/helpers": "^0.5.23",
|
|
36
36
|
"koa-compose": "^4.1.0",
|
|
37
37
|
"reflect-metadata": "^0.2.2",
|
|
38
|
-
"type-fest": "5.
|
|
39
|
-
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.
|
|
38
|
+
"type-fest": "5.8.0",
|
|
39
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.38"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@rslib/core": "0.23.
|
|
42
|
+
"@rslib/core": "0.23.2",
|
|
43
43
|
"@types/koa-compose": "^3.2.9",
|
|
44
|
-
"@types/node": "^26.
|
|
45
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
44
|
+
"@types/node": "^26.1.1",
|
|
45
|
+
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
46
46
|
"tsconfig-paths": "^4.2.0",
|
|
47
47
|
"zod": "^4.4.3",
|
|
48
|
-
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.
|
|
49
|
-
"@
|
|
50
|
-
"@
|
|
48
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.38",
|
|
49
|
+
"@modern-js/bff-runtime": "npm:@bleedingdev/modern-js-bff-runtime@3.5.0-ultramodern.38",
|
|
50
|
+
"@scripts/rstest-config": "2.66.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"tsconfig-paths": "^4.2.0",
|
|
@@ -1,72 +0,0 @@
|
|
|
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
|
-
const defineEventContract = (input)=>{
|
|
31
|
-
const normalizedName = input.name.trim();
|
|
32
|
-
if (!normalizedName) throw new Error('Event contract name must be non-empty');
|
|
33
|
-
if (!Number.isFinite(input.version) || input.version <= 0) throw new Error('Event contract version must be a positive number');
|
|
34
|
-
if (!input.schemaHash || !input.schemaHash.trim()) throw new Error('Event contract schemaHash must be non-empty');
|
|
35
|
-
return {
|
|
36
|
-
name: normalizedName,
|
|
37
|
-
version: Math.floor(input.version),
|
|
38
|
-
schemaHash: input.schemaHash.trim(),
|
|
39
|
-
description: input.description
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
const createEventEnvelope = (contract, payload, meta)=>({
|
|
43
|
-
name: contract.name,
|
|
44
|
-
version: contract.version,
|
|
45
|
-
schemaHash: contract.schemaHash,
|
|
46
|
-
timestamp: Date.now(),
|
|
47
|
-
payload,
|
|
48
|
-
...meta ? {
|
|
49
|
-
meta
|
|
50
|
-
} : {}
|
|
51
|
-
});
|
|
52
|
-
const isEventEnvelope = (value)=>{
|
|
53
|
-
if (!value || 'object' != typeof value || Array.isArray(value)) return false;
|
|
54
|
-
const candidate = value;
|
|
55
|
-
return 'string' == typeof candidate.name && candidate.name.length > 0 && 'number' == typeof candidate.version && candidate.version > 0 && 'string' == typeof candidate.schemaHash && candidate.schemaHash.length > 0 && 'number' == typeof candidate.timestamp && Number.isFinite(candidate.timestamp) && 'payload' in candidate;
|
|
56
|
-
};
|
|
57
|
-
__webpack_require__.d(__webpack_exports__, {}, {
|
|
58
|
-
createEventEnvelope: createEventEnvelope,
|
|
59
|
-
defineEventContract: defineEventContract,
|
|
60
|
-
isEventEnvelope: isEventEnvelope
|
|
61
|
-
});
|
|
62
|
-
exports.createEventEnvelope = __webpack_exports__.createEventEnvelope;
|
|
63
|
-
exports.defineEventContract = __webpack_exports__.defineEventContract;
|
|
64
|
-
exports.isEventEnvelope = __webpack_exports__.isEventEnvelope;
|
|
65
|
-
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
66
|
-
"createEventEnvelope",
|
|
67
|
-
"defineEventContract",
|
|
68
|
-
"isEventEnvelope"
|
|
69
|
-
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
70
|
-
Object.defineProperty(exports, '__esModule', {
|
|
71
|
-
value: true
|
|
72
|
-
});
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
const defineEventContract = (input)=>{
|
|
2
|
-
const normalizedName = input.name.trim();
|
|
3
|
-
if (!normalizedName) throw new Error('Event contract name must be non-empty');
|
|
4
|
-
if (!Number.isFinite(input.version) || input.version <= 0) throw new Error('Event contract version must be a positive number');
|
|
5
|
-
if (!input.schemaHash || !input.schemaHash.trim()) throw new Error('Event contract schemaHash must be non-empty');
|
|
6
|
-
return {
|
|
7
|
-
name: normalizedName,
|
|
8
|
-
version: Math.floor(input.version),
|
|
9
|
-
schemaHash: input.schemaHash.trim(),
|
|
10
|
-
description: input.description
|
|
11
|
-
};
|
|
12
|
-
};
|
|
13
|
-
const createEventEnvelope = (contract, payload, meta)=>({
|
|
14
|
-
name: contract.name,
|
|
15
|
-
version: contract.version,
|
|
16
|
-
schemaHash: contract.schemaHash,
|
|
17
|
-
timestamp: Date.now(),
|
|
18
|
-
payload,
|
|
19
|
-
...meta ? {
|
|
20
|
-
meta
|
|
21
|
-
} : {}
|
|
22
|
-
});
|
|
23
|
-
const isEventEnvelope = (value)=>{
|
|
24
|
-
if (!value || 'object' != typeof value || Array.isArray(value)) return false;
|
|
25
|
-
const candidate = value;
|
|
26
|
-
return 'string' == typeof candidate.name && candidate.name.length > 0 && 'number' == typeof candidate.version && candidate.version > 0 && 'string' == typeof candidate.schemaHash && candidate.schemaHash.length > 0 && 'number' == typeof candidate.timestamp && Number.isFinite(candidate.timestamp) && 'payload' in candidate;
|
|
27
|
-
};
|
|
28
|
-
export { createEventEnvelope, defineEventContract, isEventEnvelope };
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import "node:module";
|
|
2
|
-
const defineEventContract = (input)=>{
|
|
3
|
-
const normalizedName = input.name.trim();
|
|
4
|
-
if (!normalizedName) throw new Error('Event contract name must be non-empty');
|
|
5
|
-
if (!Number.isFinite(input.version) || input.version <= 0) throw new Error('Event contract version must be a positive number');
|
|
6
|
-
if (!input.schemaHash || !input.schemaHash.trim()) throw new Error('Event contract schemaHash must be non-empty');
|
|
7
|
-
return {
|
|
8
|
-
name: normalizedName,
|
|
9
|
-
version: Math.floor(input.version),
|
|
10
|
-
schemaHash: input.schemaHash.trim(),
|
|
11
|
-
description: input.description
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
const createEventEnvelope = (contract, payload, meta)=>({
|
|
15
|
-
name: contract.name,
|
|
16
|
-
version: contract.version,
|
|
17
|
-
schemaHash: contract.schemaHash,
|
|
18
|
-
timestamp: Date.now(),
|
|
19
|
-
payload,
|
|
20
|
-
...meta ? {
|
|
21
|
-
meta
|
|
22
|
-
} : {}
|
|
23
|
-
});
|
|
24
|
-
const isEventEnvelope = (value)=>{
|
|
25
|
-
if (!value || 'object' != typeof value || Array.isArray(value)) return false;
|
|
26
|
-
const candidate = value;
|
|
27
|
-
return 'string' == typeof candidate.name && candidate.name.length > 0 && 'number' == typeof candidate.version && candidate.version > 0 && 'string' == typeof candidate.schemaHash && candidate.schemaHash.length > 0 && 'number' == typeof candidate.timestamp && Number.isFinite(candidate.timestamp) && 'payload' in candidate;
|
|
28
|
-
};
|
|
29
|
-
export { createEventEnvelope, defineEventContract, isEventEnvelope };
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export type EventContract<Name extends string = string, Payload = unknown> = {
|
|
2
|
-
name: Name;
|
|
3
|
-
version: number;
|
|
4
|
-
schemaHash: string;
|
|
5
|
-
description?: string;
|
|
6
|
-
};
|
|
7
|
-
export type EventEnvelope<TContract extends EventContract = EventContract, TMeta extends Record<string, unknown> = Record<string, unknown>> = {
|
|
8
|
-
name: TContract['name'];
|
|
9
|
-
version: TContract['version'];
|
|
10
|
-
schemaHash: TContract['schemaHash'];
|
|
11
|
-
timestamp: number;
|
|
12
|
-
payload: TContract extends EventContract<string, infer TPayload> ? TPayload : unknown;
|
|
13
|
-
meta?: TMeta;
|
|
14
|
-
};
|
|
15
|
-
export declare const defineEventContract: <Name extends string, Payload = unknown>(input: {
|
|
16
|
-
name: Name;
|
|
17
|
-
version: number;
|
|
18
|
-
schemaHash: string;
|
|
19
|
-
description?: string;
|
|
20
|
-
}) => EventContract<Name, Payload>;
|
|
21
|
-
export declare const createEventEnvelope: <TContract extends EventContract, TMeta extends Record<string, unknown> = Record<string, unknown>>(contract: TContract, payload: TContract extends EventContract<string, infer TPayload> ? TPayload : unknown, meta?: TMeta) => EventEnvelope<TContract, TMeta>;
|
|
22
|
-
export declare const isEventEnvelope: (value: unknown) => value is EventEnvelope<EventContract, Record<string, unknown>>;
|