@bleedingdev/modern-js-bff-core 3.9.0-ultramodern.2 → 3.9.0-ultramodern.20
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/client/generateClient.js +67 -138
- package/dist/cjs/index.js +24 -114
- package/dist/cjs/router/index.js +7 -5
- package/dist/cjs/utils/index.js +11 -21
- package/dist/esm/client/generateClient.mjs +65 -139
- package/dist/esm/index.mjs +1 -5
- package/dist/esm/router/index.mjs +7 -5
- package/dist/esm/utils/index.mjs +0 -1
- package/dist/esm-node/client/generateClient.mjs +65 -139
- package/dist/esm-node/index.mjs +1 -5
- package/dist/esm-node/router/index.mjs +7 -5
- package/dist/esm-node/utils/index.mjs +0 -1
- package/dist/types/client/generateClient.d.ts +24 -1
- package/dist/types/index.d.ts +1 -5
- package/dist/types/utils/index.d.ts +0 -1
- package/package.json +10 -19
- package/dist/cjs/adapter-kit/index.js +0 -144
- package/dist/cjs/adapter-kit/parity-scenarios/cross-project-denial.js +0 -55
- package/dist/cjs/adapter-kit/parity-scenarios/envelope.js +0 -128
- package/dist/cjs/adapter-kit/parity-scenarios/operation-context.js +0 -152
- package/dist/cjs/adapter-kit/parity-scenarios/schema.js +0 -109
- package/dist/cjs/adapter-kit/parity-scenarios/shared.js +0 -71
- package/dist/cjs/adapter-kit/parity.js +0 -204
- package/dist/cjs/security/crossProjectPolicy.js +0 -309
- package/dist/cjs/security/operationContracts.js +0 -183
- package/dist/cjs/security/resolveCrossProjectPolicy.js +0 -65
- package/dist/cjs/utils/alias.js +0 -112
- package/dist/esm/adapter-kit/index.mjs +0 -79
- package/dist/esm/adapter-kit/parity-scenarios/cross-project-denial.mjs +0 -17
- package/dist/esm/adapter-kit/parity-scenarios/envelope.mjs +0 -90
- package/dist/esm/adapter-kit/parity-scenarios/operation-context.mjs +0 -111
- package/dist/esm/adapter-kit/parity-scenarios/schema.mjs +0 -71
- package/dist/esm/adapter-kit/parity-scenarios/shared.mjs +0 -21
- package/dist/esm/adapter-kit/parity.mjs +0 -148
- package/dist/esm/security/crossProjectPolicy.mjs +0 -259
- package/dist/esm/security/operationContracts.mjs +0 -127
- package/dist/esm/security/resolveCrossProjectPolicy.mjs +0 -27
- package/dist/esm/utils/alias.mjs +0 -57
- package/dist/esm-node/adapter-kit/index.mjs +0 -80
- package/dist/esm-node/adapter-kit/parity-scenarios/cross-project-denial.mjs +0 -18
- package/dist/esm-node/adapter-kit/parity-scenarios/envelope.mjs +0 -91
- package/dist/esm-node/adapter-kit/parity-scenarios/operation-context.mjs +0 -112
- package/dist/esm-node/adapter-kit/parity-scenarios/schema.mjs +0 -72
- package/dist/esm-node/adapter-kit/parity-scenarios/shared.mjs +0 -22
- package/dist/esm-node/adapter-kit/parity.mjs +0 -149
- package/dist/esm-node/security/crossProjectPolicy.mjs +0 -260
- package/dist/esm-node/security/operationContracts.mjs +0 -130
- package/dist/esm-node/security/resolveCrossProjectPolicy.mjs +0 -28
- package/dist/esm-node/utils/alias.mjs +0 -58
- package/dist/types/adapter-kit/index.d.ts +0 -90
- package/dist/types/adapter-kit/parity-scenarios/cross-project-denial.d.ts +0 -2
- package/dist/types/adapter-kit/parity-scenarios/envelope.d.ts +0 -2
- package/dist/types/adapter-kit/parity-scenarios/operation-context.d.ts +0 -3
- package/dist/types/adapter-kit/parity-scenarios/schema.d.ts +0 -2
- package/dist/types/adapter-kit/parity-scenarios/shared.d.ts +0 -32
- package/dist/types/adapter-kit/parity.d.ts +0 -43
- package/dist/types/security/crossProjectPolicy.d.ts +0 -110
- package/dist/types/security/operationContracts.d.ts +0 -84
- package/dist/types/security/resolveCrossProjectPolicy.d.ts +0 -39
- package/dist/types/utils/alias.d.ts +0 -7
|
@@ -1,71 +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 PARITY_REQUEST_ID = 'crm';
|
|
31
|
-
const PARITY_PRODUCER_REQUEST_ID = 'crm.producer-a';
|
|
32
|
-
const envelopeHeader = (requestId)=>JSON.stringify(void 0 === requestId ? {} : {
|
|
33
|
-
requestId
|
|
34
|
-
});
|
|
35
|
-
const detailHeader = (details)=>JSON.stringify(details);
|
|
36
|
-
const deniedScenario = (name, reason, headers)=>({
|
|
37
|
-
name,
|
|
38
|
-
policy: true,
|
|
39
|
-
request: {
|
|
40
|
-
method: 'get',
|
|
41
|
-
path: '/hello',
|
|
42
|
-
headers
|
|
43
|
-
},
|
|
44
|
-
expected: {
|
|
45
|
-
kind: 'denied',
|
|
46
|
-
status: 403,
|
|
47
|
-
reason
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
__webpack_require__.d(__webpack_exports__, {}, {
|
|
51
|
-
PARITY_PRODUCER_REQUEST_ID: PARITY_PRODUCER_REQUEST_ID,
|
|
52
|
-
PARITY_REQUEST_ID: PARITY_REQUEST_ID,
|
|
53
|
-
deniedScenario: deniedScenario,
|
|
54
|
-
detailHeader: detailHeader,
|
|
55
|
-
envelopeHeader: envelopeHeader
|
|
56
|
-
});
|
|
57
|
-
exports.PARITY_PRODUCER_REQUEST_ID = __webpack_exports__.PARITY_PRODUCER_REQUEST_ID;
|
|
58
|
-
exports.PARITY_REQUEST_ID = __webpack_exports__.PARITY_REQUEST_ID;
|
|
59
|
-
exports.deniedScenario = __webpack_exports__.deniedScenario;
|
|
60
|
-
exports.detailHeader = __webpack_exports__.detailHeader;
|
|
61
|
-
exports.envelopeHeader = __webpack_exports__.envelopeHeader;
|
|
62
|
-
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
63
|
-
"PARITY_PRODUCER_REQUEST_ID",
|
|
64
|
-
"PARITY_REQUEST_ID",
|
|
65
|
-
"deniedScenario",
|
|
66
|
-
"detailHeader",
|
|
67
|
-
"envelopeHeader"
|
|
68
|
-
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
69
|
-
Object.defineProperty(exports, '__esModule', {
|
|
70
|
-
value: true
|
|
71
|
-
});
|
|
@@ -1,204 +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
|
-
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
-
PARITY_PRODUCER_REQUEST_ID: ()=>shared_js_namespaceObject.PARITY_PRODUCER_REQUEST_ID,
|
|
32
|
-
PARITY_REQUEST_ID: ()=>shared_js_namespaceObject.PARITY_REQUEST_ID,
|
|
33
|
-
assertParityResult: ()=>assertParityResult,
|
|
34
|
-
createAdapterParityScenarios: ()=>createAdapterParityScenarios,
|
|
35
|
-
createParityApiHandlerInfos: ()=>createParityApiHandlerInfos,
|
|
36
|
-
createParityBffConfig: ()=>createParityBffConfig,
|
|
37
|
-
toParityResult: ()=>toParityResult
|
|
38
|
-
});
|
|
39
|
-
const operationContracts_js_namespaceObject = require("../security/operationContracts.js");
|
|
40
|
-
const external_types_js_namespaceObject = require("../types.js");
|
|
41
|
-
const cross_project_denial_js_namespaceObject = require("./parity-scenarios/cross-project-denial.js");
|
|
42
|
-
const envelope_js_namespaceObject = require("./parity-scenarios/envelope.js");
|
|
43
|
-
const operation_context_js_namespaceObject = require("./parity-scenarios/operation-context.js");
|
|
44
|
-
const schema_js_namespaceObject = require("./parity-scenarios/schema.js");
|
|
45
|
-
const shared_js_namespaceObject = require("./parity-scenarios/shared.js");
|
|
46
|
-
const PARITY_FIXTURE_FILENAME = 'bff-core/adapter-kit/parity-fixture.ts';
|
|
47
|
-
const HANDLER_WITH_SCHEMA = 'HANDLER_WITH_SCHEMA';
|
|
48
|
-
const isRecord = (value)=>Boolean(value) && 'object' == typeof value;
|
|
49
|
-
const createSchemaFixtureHandler = ()=>{
|
|
50
|
-
const handler = (input)=>{
|
|
51
|
-
const data = isRecord(input) && isRecord(input.data) ? input.data : void 0;
|
|
52
|
-
const id = data?.id;
|
|
53
|
-
if ('number' == typeof id) return {
|
|
54
|
-
type: 'HandleSuccess',
|
|
55
|
-
value: {
|
|
56
|
-
id
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
if ('boom' === id) return {
|
|
60
|
-
type: 'OutputValidationError',
|
|
61
|
-
message: 'invalid output'
|
|
62
|
-
};
|
|
63
|
-
return {
|
|
64
|
-
type: 'InputValidationError',
|
|
65
|
-
message: 'invalid input'
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
|
-
return Object.assign(handler, {
|
|
69
|
-
[HANDLER_WITH_SCHEMA]: true
|
|
70
|
-
});
|
|
71
|
-
};
|
|
72
|
-
const createParityApiHandlerInfos = ()=>[
|
|
73
|
-
{
|
|
74
|
-
name: 'getHello',
|
|
75
|
-
httpMethod: external_types_js_namespaceObject.HttpMethod.Get,
|
|
76
|
-
routeName: '/hello',
|
|
77
|
-
routePath: '/hello',
|
|
78
|
-
filename: PARITY_FIXTURE_FILENAME,
|
|
79
|
-
handler: ()=>({
|
|
80
|
-
message: 'hello'
|
|
81
|
-
})
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
name: 'postHello',
|
|
85
|
-
httpMethod: external_types_js_namespaceObject.HttpMethod.Post,
|
|
86
|
-
routeName: '/hello',
|
|
87
|
-
routePath: '/hello',
|
|
88
|
-
filename: PARITY_FIXTURE_FILENAME,
|
|
89
|
-
handler: ()=>'hello'
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
name: 'getNothing',
|
|
93
|
-
httpMethod: external_types_js_namespaceObject.HttpMethod.Get,
|
|
94
|
-
routeName: '/nothing',
|
|
95
|
-
routePath: '/nothing',
|
|
96
|
-
filename: PARITY_FIXTURE_FILENAME,
|
|
97
|
-
handler: ()=>void 0
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
name: 'postEcho',
|
|
101
|
-
httpMethod: external_types_js_namespaceObject.HttpMethod.Post,
|
|
102
|
-
routeName: '/echo',
|
|
103
|
-
routePath: '/echo',
|
|
104
|
-
filename: PARITY_FIXTURE_FILENAME,
|
|
105
|
-
handler: (input)=>{
|
|
106
|
-
const normalized = isRecord(input) ? input : {};
|
|
107
|
-
return {
|
|
108
|
-
data: normalized.data ?? null,
|
|
109
|
-
query: normalized.query ?? {},
|
|
110
|
-
cookie: normalized.cookies ?? null
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
name: 'getItem',
|
|
116
|
-
httpMethod: external_types_js_namespaceObject.HttpMethod.Get,
|
|
117
|
-
routeName: '/items/:id',
|
|
118
|
-
routePath: '/items/:id',
|
|
119
|
-
filename: PARITY_FIXTURE_FILENAME,
|
|
120
|
-
handler: (id, input)=>({
|
|
121
|
-
id,
|
|
122
|
-
query: isRecord(input) ? input.query ?? {} : {}
|
|
123
|
-
})
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
name: 'patchSchema',
|
|
127
|
-
httpMethod: external_types_js_namespaceObject.HttpMethod.Patch,
|
|
128
|
-
routeName: '/schema',
|
|
129
|
-
routePath: '/schema',
|
|
130
|
-
filename: PARITY_FIXTURE_FILENAME,
|
|
131
|
-
handler: createSchemaFixtureHandler()
|
|
132
|
-
}
|
|
133
|
-
];
|
|
134
|
-
const createParityBffConfig = ()=>({
|
|
135
|
-
requestId: shared_js_namespaceObject.PARITY_PRODUCER_REQUEST_ID,
|
|
136
|
-
crossProjectPolicy: {
|
|
137
|
-
enabled: true,
|
|
138
|
-
allowedNamespaces: [
|
|
139
|
-
shared_js_namespaceObject.PARITY_REQUEST_ID
|
|
140
|
-
],
|
|
141
|
-
allowClientAssertedNamespace: true
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
const getParityContracts = ()=>(0, operationContracts_js_namespaceObject.buildOperationContractMap)({
|
|
145
|
-
handlers: createParityApiHandlerInfos(),
|
|
146
|
-
requestId: shared_js_namespaceObject.PARITY_PRODUCER_REQUEST_ID
|
|
147
|
-
});
|
|
148
|
-
const createAdapterParityScenarios = ()=>{
|
|
149
|
-
const contracts = getParityContracts();
|
|
150
|
-
const helloContract = contracts['GET:/hello'];
|
|
151
|
-
const validEnvelope = (0, shared_js_namespaceObject.envelopeHeader)(shared_js_namespaceObject.PARITY_PRODUCER_REQUEST_ID);
|
|
152
|
-
const validOperationId = helloContract.operationId;
|
|
153
|
-
const context = {
|
|
154
|
-
helloContract,
|
|
155
|
-
validEnvelope,
|
|
156
|
-
validOperationId
|
|
157
|
-
};
|
|
158
|
-
return [
|
|
159
|
-
...(0, envelope_js_namespaceObject.createEnvelopeParityScenarios)(),
|
|
160
|
-
...(0, schema_js_namespaceObject.createSchemaParityScenarios)(),
|
|
161
|
-
...(0, operation_context_js_namespaceObject.createOperationContextSuccessScenarios)(context),
|
|
162
|
-
...(0, cross_project_denial_js_namespaceObject.createCrossProjectDenialScenarios)(),
|
|
163
|
-
...(0, operation_context_js_namespaceObject.createOperationContextDenialScenarios)(context)
|
|
164
|
-
];
|
|
165
|
-
};
|
|
166
|
-
const toParityResult = (res)=>({
|
|
167
|
-
status: res.status,
|
|
168
|
-
payload: res.type.includes('json') ? res.body : '' === res.text ? void 0 : res.text
|
|
169
|
-
});
|
|
170
|
-
const formatValue = (value)=>JSON.stringify(value);
|
|
171
|
-
const assertParityResult = (scenario, res)=>{
|
|
172
|
-
const result = toParityResult(res);
|
|
173
|
-
const failures = [];
|
|
174
|
-
const { expected } = scenario;
|
|
175
|
-
if (result.status !== expected.status) failures.push(`status: expected ${expected.status}, received ${result.status}`);
|
|
176
|
-
if ('payload' === expected.kind) {
|
|
177
|
-
if (formatValue(result.payload) !== formatValue(expected.payload)) failures.push(`payload: expected ${formatValue(expected.payload)}, received ${formatValue(result.payload)}`);
|
|
178
|
-
} else {
|
|
179
|
-
const payload = isRecord(result.payload) ? result.payload : {};
|
|
180
|
-
if ('BFF_CROSS_PROJECT_POLICY_DENIED' !== payload.code) failures.push(`denial code: expected "BFF_CROSS_PROJECT_POLICY_DENIED", received ${formatValue(payload.code)}`);
|
|
181
|
-
if (payload.reason !== expected.reason) failures.push(`denial reason: expected "${expected.reason}", received ${formatValue(payload.reason)}`);
|
|
182
|
-
if ('string' != typeof payload.message || 0 === payload.message.length) failures.push(`denial message: expected non-empty string, received ${formatValue(payload.message)}`);
|
|
183
|
-
}
|
|
184
|
-
if (failures.length > 0) throw new Error(`Adapter parity scenario "${scenario.name}" failed:\n- ${failures.join('\n- ')}`);
|
|
185
|
-
};
|
|
186
|
-
exports.PARITY_PRODUCER_REQUEST_ID = __webpack_exports__.PARITY_PRODUCER_REQUEST_ID;
|
|
187
|
-
exports.PARITY_REQUEST_ID = __webpack_exports__.PARITY_REQUEST_ID;
|
|
188
|
-
exports.assertParityResult = __webpack_exports__.assertParityResult;
|
|
189
|
-
exports.createAdapterParityScenarios = __webpack_exports__.createAdapterParityScenarios;
|
|
190
|
-
exports.createParityApiHandlerInfos = __webpack_exports__.createParityApiHandlerInfos;
|
|
191
|
-
exports.createParityBffConfig = __webpack_exports__.createParityBffConfig;
|
|
192
|
-
exports.toParityResult = __webpack_exports__.toParityResult;
|
|
193
|
-
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
194
|
-
"PARITY_PRODUCER_REQUEST_ID",
|
|
195
|
-
"PARITY_REQUEST_ID",
|
|
196
|
-
"assertParityResult",
|
|
197
|
-
"createAdapterParityScenarios",
|
|
198
|
-
"createParityApiHandlerInfos",
|
|
199
|
-
"createParityBffConfig",
|
|
200
|
-
"toParityResult"
|
|
201
|
-
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
202
|
-
Object.defineProperty(exports, '__esModule', {
|
|
203
|
-
value: true
|
|
204
|
-
});
|
|
@@ -1,309 +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 BFF_ENVELOPE_HEADER = 'x-modernjs-bff-envelope';
|
|
31
|
-
const BFF_OPERATION_CONTEXT_HEADER = 'x-operation-id';
|
|
32
|
-
const BFF_OPERATION_CONTEXT_DETAIL_HEADER = 'x-modernjs-bff-operation-context';
|
|
33
|
-
const DEFAULT_DENY_STATUS = 403;
|
|
34
|
-
const NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE = 'cross-project namespace allowlist requires verifyProducerIdentity';
|
|
35
|
-
let hasWarnedAdvisoryNamespaceAllowlist = false;
|
|
36
|
-
const splitRoutePath = (routePath)=>routePath.split('/').filter(Boolean);
|
|
37
|
-
const isDynamicRouteSegment = (segment)=>segment.startsWith(':') || '*' === segment || segment.startsWith('*') || segment.startsWith('{*');
|
|
38
|
-
const hasTrailingSlash = (routePath)=>routePath.length > 1 && routePath.endsWith('/');
|
|
39
|
-
const hasUnsafePathEncoding = (requestPath)=>{
|
|
40
|
-
let decoded = requestPath;
|
|
41
|
-
for(let depth = 0; depth < 8; depth += 1){
|
|
42
|
-
if (/%(?:2f|5c)/i.test(decoded)) return true;
|
|
43
|
-
try {
|
|
44
|
-
const next = decodeURIComponent(decoded);
|
|
45
|
-
if (next === decoded) return false;
|
|
46
|
-
decoded = next;
|
|
47
|
-
} catch {
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return decoded.includes('%');
|
|
52
|
-
};
|
|
53
|
-
const routeSegmentsMatch = (templateSegments, requestSegments)=>{
|
|
54
|
-
let templateIndex = 0;
|
|
55
|
-
let requestIndex = 0;
|
|
56
|
-
while(templateIndex < templateSegments.length){
|
|
57
|
-
const segment = templateSegments[templateIndex];
|
|
58
|
-
const wildcard = '*' === segment || segment.startsWith('*') || segment.startsWith('{*');
|
|
59
|
-
const optional = segment.startsWith(':') && segment.endsWith('?');
|
|
60
|
-
if (templateIndex < templateSegments.length - 1 && (wildcard || optional)) return false;
|
|
61
|
-
if (wildcard) return true;
|
|
62
|
-
const requestSegment = requestSegments[requestIndex];
|
|
63
|
-
if (void 0 === requestSegment) return optional && templateIndex === templateSegments.length - 1;
|
|
64
|
-
if (!segment.startsWith(':') && segment !== requestSegment) return false;
|
|
65
|
-
templateIndex += 1;
|
|
66
|
-
requestIndex += 1;
|
|
67
|
-
}
|
|
68
|
-
return requestIndex === requestSegments.length;
|
|
69
|
-
};
|
|
70
|
-
const routeTemplateMatchScore = (routePath, requestPath)=>{
|
|
71
|
-
if (hasTrailingSlash(routePath) !== hasTrailingSlash(requestPath)) return;
|
|
72
|
-
const templateSegments = splitRoutePath(routePath);
|
|
73
|
-
const requestSegments = splitRoutePath(requestPath);
|
|
74
|
-
for(let prefixLength = 0; prefixLength <= templateSegments.length; prefixLength += 1){
|
|
75
|
-
const removedPrefix = templateSegments.slice(0, prefixLength);
|
|
76
|
-
if (removedPrefix.some(isDynamicRouteSegment)) break;
|
|
77
|
-
const mountedTemplate = templateSegments.slice(prefixLength);
|
|
78
|
-
if (!routeSegmentsMatch(mountedTemplate, requestSegments)) continue;
|
|
79
|
-
const literalSegments = mountedTemplate.filter((segment)=>!isDynamicRouteSegment(segment)).length;
|
|
80
|
-
const dynamicSegments = mountedTemplate.length - literalSegments;
|
|
81
|
-
return (0 === prefixLength ? 1000000 : 0) + 10000 * literalSegments - 100 * dynamicSegments - prefixLength;
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
const resolveCrossProjectRequestObservation = (request, policy)=>{
|
|
85
|
-
const requestMethod = String(request.method || '').trim().toUpperCase();
|
|
86
|
-
const routePaths = [
|
|
87
|
-
...new Set(Object.keys(policy.expectedOperationContracts ?? {}).flatMap((key)=>{
|
|
88
|
-
if (key.startsWith('operation:')) return [];
|
|
89
|
-
const separator = key.indexOf(':');
|
|
90
|
-
if (-1 === separator || key.slice(0, separator).trim().toUpperCase() !== requestMethod) return [];
|
|
91
|
-
return [
|
|
92
|
-
key.slice(separator + 1)
|
|
93
|
-
];
|
|
94
|
-
}))
|
|
95
|
-
];
|
|
96
|
-
if (0 === routePaths.length) return;
|
|
97
|
-
if (hasUnsafePathEncoding(request.pathname)) return {
|
|
98
|
-
method: request.method,
|
|
99
|
-
routePath: request.pathname
|
|
100
|
-
};
|
|
101
|
-
const matches = routePaths.flatMap((routePath)=>{
|
|
102
|
-
const score = routeTemplateMatchScore(routePath, request.pathname);
|
|
103
|
-
return void 0 === score ? [] : [
|
|
104
|
-
{
|
|
105
|
-
routePath,
|
|
106
|
-
score
|
|
107
|
-
}
|
|
108
|
-
];
|
|
109
|
-
});
|
|
110
|
-
if (0 === matches.length) return {
|
|
111
|
-
method: request.method,
|
|
112
|
-
routePath: request.pathname
|
|
113
|
-
};
|
|
114
|
-
matches.sort((left, right)=>right.score - left.score);
|
|
115
|
-
return {
|
|
116
|
-
method: request.method,
|
|
117
|
-
routePath: matches[1]?.score === matches[0]?.score ? request.pathname : matches[0].routePath
|
|
118
|
-
};
|
|
119
|
-
};
|
|
120
|
-
const normalizeHeaderName = (headerName, fallback)=>(headerName || fallback).trim().toLowerCase();
|
|
121
|
-
const normalizeStatusCode = (statusCode)=>{
|
|
122
|
-
if ('number' == typeof statusCode && Number.isFinite(statusCode) && statusCode >= 400 && statusCode <= 599) return Math.floor(statusCode);
|
|
123
|
-
return DEFAULT_DENY_STATUS;
|
|
124
|
-
};
|
|
125
|
-
const readHeader = (headers, headerName)=>{
|
|
126
|
-
const lowerHeaderName = headerName.toLowerCase();
|
|
127
|
-
for (const [name, value] of Object.entries(headers || {}))if (name.toLowerCase() === lowerHeaderName) {
|
|
128
|
-
if (Array.isArray(value)) return value.length > 0 ? String(value[0]) : void 0;
|
|
129
|
-
if (null == value) return;
|
|
130
|
-
return String(value);
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
const extractNamespace = (requestId)=>requestId.split(/[/:.]/)[0]?.trim().toLowerCase();
|
|
134
|
-
const createViolation = (reason, message, status)=>({
|
|
135
|
-
code: 'BFF_CROSS_PROJECT_POLICY_DENIED',
|
|
136
|
-
reason,
|
|
137
|
-
message,
|
|
138
|
-
status
|
|
139
|
-
});
|
|
140
|
-
const warnAdvisoryNamespaceAllowlist = ()=>{
|
|
141
|
-
if (hasWarnedAdvisoryNamespaceAllowlist) return;
|
|
142
|
-
hasWarnedAdvisoryNamespaceAllowlist = true;
|
|
143
|
-
console.warn(`[Modern.js BFF] ${NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE}. allowClientAssertedNamespace=true will continue to evaluate allowlist against client-asserted requestId namespaces only.`);
|
|
144
|
-
};
|
|
145
|
-
const createCrossProjectPolicyEvaluationState = (headers, policy, observedRequest)=>({
|
|
146
|
-
headers,
|
|
147
|
-
policy,
|
|
148
|
-
status: normalizeStatusCode(policy.denyStatus),
|
|
149
|
-
requireEnvelope: policy.requireEnvelope ?? true,
|
|
150
|
-
requireOperationContext: policy.requireOperationContext ?? true,
|
|
151
|
-
requireOperationContextDetails: policy.requireOperationContextDetails ?? true,
|
|
152
|
-
requireOperationSchemaHash: policy.requireOperationSchemaHash ?? true,
|
|
153
|
-
requireOperationVersion: policy.requireOperationVersion ?? true,
|
|
154
|
-
allowUnknownOperations: policy.allowUnknownOperations ?? false,
|
|
155
|
-
allowClientAssertedNamespace: policy.allowClientAssertedNamespace ?? false,
|
|
156
|
-
verifyProducerIdentity: 'function' == typeof policy.verifyProducerIdentity ? policy.verifyProducerIdentity : void 0,
|
|
157
|
-
allowedNamespaces: (policy.allowedNamespaces || []).map((item)=>item.trim().toLowerCase()).filter(Boolean),
|
|
158
|
-
envelopeHeader: normalizeHeaderName(policy.envelopeHeader, BFF_ENVELOPE_HEADER),
|
|
159
|
-
operationContextHeader: normalizeHeaderName(policy.operationContextHeader, BFF_OPERATION_CONTEXT_HEADER),
|
|
160
|
-
operationContextDetailHeader: normalizeHeaderName(policy.operationContextDetailHeader, BFF_OPERATION_CONTEXT_DETAIL_HEADER),
|
|
161
|
-
requestId: '',
|
|
162
|
-
detailSchemaHash: '',
|
|
163
|
-
detailOperationVersion: void 0,
|
|
164
|
-
observedRequest: observedRequest ? {
|
|
165
|
-
method: String(observedRequest.method || '').trim().toUpperCase(),
|
|
166
|
-
routePath: String(observedRequest.routePath || '').trim()
|
|
167
|
-
} : void 0,
|
|
168
|
-
complete: false
|
|
169
|
-
});
|
|
170
|
-
const checkNamespaceAllowlistVerifier = (state)=>{
|
|
171
|
-
if (state.allowedNamespaces.length > 0 && void 0 === state.verifyProducerIdentity) {
|
|
172
|
-
if (!state.allowClientAssertedNamespace) return createViolation('producer_identity_mismatch', NAMESPACE_ALLOWLIST_REQUIRES_VERIFIER_MESSAGE, state.status);
|
|
173
|
-
warnAdvisoryNamespaceAllowlist();
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
const checkEnvelopeHeader = (state)=>{
|
|
177
|
-
const envelopeRaw = readHeader(state.headers, state.envelopeHeader);
|
|
178
|
-
if (!envelopeRaw) {
|
|
179
|
-
if (!state.requireEnvelope) {
|
|
180
|
-
state.complete = true;
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
return createViolation('missing_envelope', `Missing cross-project envelope header "${state.envelopeHeader}"`, state.status);
|
|
184
|
-
}
|
|
185
|
-
let envelope;
|
|
186
|
-
try {
|
|
187
|
-
const parsed = JSON.parse(envelopeRaw);
|
|
188
|
-
if (!parsed || 'object' != typeof parsed) throw new Error('invalid envelope object');
|
|
189
|
-
envelope = parsed;
|
|
190
|
-
} catch (_error) {
|
|
191
|
-
return createViolation('invalid_envelope', `Invalid cross-project envelope header "${state.envelopeHeader}"`, state.status);
|
|
192
|
-
}
|
|
193
|
-
const requestId = String(envelope.requestId || '').trim();
|
|
194
|
-
if (!requestId) return createViolation('missing_request_id', 'Cross-project envelope does not include a valid requestId', state.status);
|
|
195
|
-
state.requestId = requestId;
|
|
196
|
-
state.claimedNamespace = extractNamespace(requestId);
|
|
197
|
-
state.effectiveNamespace = state.claimedNamespace;
|
|
198
|
-
};
|
|
199
|
-
const checkProducerIdentity = (state)=>{
|
|
200
|
-
if (!state.verifyProducerIdentity) return;
|
|
201
|
-
const verifiedNamespaceRaw = state.verifyProducerIdentity(state.headers);
|
|
202
|
-
const verifiedNamespace = 'string' == typeof verifiedNamespaceRaw ? verifiedNamespaceRaw.trim().toLowerCase() : void 0;
|
|
203
|
-
if (!verifiedNamespace) return createViolation('producer_identity_mismatch', 'Producer identity could not be verified for request', state.status);
|
|
204
|
-
if (verifiedNamespace !== state.claimedNamespace) return createViolation('producer_identity_mismatch', `Envelope namespace "${state.claimedNamespace || 'unknown'}" does not match verified producer identity "${verifiedNamespace}"`, state.status);
|
|
205
|
-
state.effectiveNamespace = verifiedNamespace;
|
|
206
|
-
};
|
|
207
|
-
const checkNamespaceAllowlist = (state)=>{
|
|
208
|
-
if (state.allowedNamespaces.length > 0) {
|
|
209
|
-
if (!state.effectiveNamespace || !state.allowedNamespaces.includes(state.effectiveNamespace)) return createViolation('namespace_not_allowed', `Producer namespace "${state.effectiveNamespace || 'unknown'}" not allowed`, state.status);
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
const checkOperationContext = (state)=>{
|
|
213
|
-
if (!state.requireOperationContext) {
|
|
214
|
-
state.complete = true;
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
const operationContext = readHeader(state.headers, state.operationContextHeader);
|
|
218
|
-
if (!operationContext) return createViolation('missing_operation_context', `Missing operation context header "${state.operationContextHeader}"`, state.status);
|
|
219
|
-
if (!operationContext.startsWith(`${state.requestId}:`)) return createViolation('operation_context_mismatch', `Operation context header "${state.operationContextHeader}" does not match requestId "${state.requestId}"`, state.status);
|
|
220
|
-
state.operationContext = operationContext;
|
|
221
|
-
};
|
|
222
|
-
const checkOperationContextDetails = (state)=>{
|
|
223
|
-
const operationContextDetailsRaw = readHeader(state.headers, state.operationContextDetailHeader);
|
|
224
|
-
if (!operationContextDetailsRaw) {
|
|
225
|
-
if (state.requireOperationContextDetails) return createViolation('missing_operation_context_details', `Missing operation context details header "${state.operationContextDetailHeader}"`, state.status);
|
|
226
|
-
state.complete = true;
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
let operationContextDetails;
|
|
230
|
-
try {
|
|
231
|
-
const parsed = JSON.parse(operationContextDetailsRaw);
|
|
232
|
-
if (!parsed || 'object' != typeof parsed || Array.isArray(parsed)) throw new Error('invalid operation context details object');
|
|
233
|
-
operationContextDetails = parsed;
|
|
234
|
-
} catch (_error) {
|
|
235
|
-
return createViolation('invalid_operation_context_details', `Invalid operation context details header "${state.operationContextDetailHeader}"`, state.status);
|
|
236
|
-
}
|
|
237
|
-
const detailRequestId = String(operationContextDetails.requestId || '').trim();
|
|
238
|
-
if (detailRequestId && detailRequestId !== state.requestId) return createViolation('operation_context_details_request_id_mismatch', `Operation context details requestId "${detailRequestId}" does not match envelope requestId "${state.requestId}"`, state.status);
|
|
239
|
-
const detailOperationId = String(operationContextDetails.operationId || '').trim();
|
|
240
|
-
if (state.operationContext && detailOperationId && detailOperationId !== state.operationContext) return createViolation('operation_context_mismatch', `Operation context details operationId "${detailOperationId}" does not match operation context "${state.operationContext}"`, state.status);
|
|
241
|
-
state.operationContextDetails = operationContextDetails;
|
|
242
|
-
state.detailSchemaHash = String(operationContextDetails.schemaHash || '').trim();
|
|
243
|
-
if (state.requireOperationSchemaHash && !state.detailSchemaHash) return createViolation('missing_operation_schema_hash', `Operation context details header "${state.operationContextDetailHeader}" must include schemaHash`, state.status);
|
|
244
|
-
state.detailOperationVersion = operationContextDetails.operationVersion;
|
|
245
|
-
if (state.requireOperationVersion && 'number' != typeof state.detailOperationVersion) return createViolation('missing_operation_version', `Operation context details header "${state.operationContextDetailHeader}" must include operationVersion`, state.status);
|
|
246
|
-
};
|
|
247
|
-
const checkOperationContract = (state)=>{
|
|
248
|
-
if (!state.operationContextDetails) return;
|
|
249
|
-
const expectedContracts = state.policy.expectedOperationContracts;
|
|
250
|
-
if (expectedContracts && 'object' == typeof expectedContracts) {
|
|
251
|
-
const method = String(state.operationContextDetails.method || '').toUpperCase();
|
|
252
|
-
const routePath = String(state.operationContextDetails.routePath || '').trim();
|
|
253
|
-
const operationId = String(state.operationContextDetails.operationId || '').trim();
|
|
254
|
-
const observedMethod = state.observedRequest?.method;
|
|
255
|
-
const observedRoutePath = state.observedRequest?.routePath;
|
|
256
|
-
if (observedMethod && observedRoutePath && (method !== observedMethod || routePath !== observedRoutePath)) return createViolation('operation_context_mismatch', `Client operation "${operationId || `${method}:${routePath}`}" does not match observed request "${observedMethod}:${observedRoutePath}"`, state.status);
|
|
257
|
-
const contractMethod = observedMethod || method;
|
|
258
|
-
const contractRoutePath = observedRoutePath || routePath;
|
|
259
|
-
const expectedContract = expectedContracts[`${contractMethod}:${contractRoutePath}`] || (state.observedRequest ? void 0 : expectedContracts[`operation:${operationId}`]);
|
|
260
|
-
if (expectedContract) {
|
|
261
|
-
const assertedOperationId = operationId || state.operationContext || '';
|
|
262
|
-
const canonicalRouteOperationId = `${state.requestId}:${contractMethod}:${contractRoutePath}`;
|
|
263
|
-
if (state.observedRequest && assertedOperationId !== expectedContract.operationId && assertedOperationId !== canonicalRouteOperationId) return createViolation('operation_context_mismatch', `Client operation "${assertedOperationId}" does not identify observed contract "${expectedContract.operationId || canonicalRouteOperationId}"`, state.status);
|
|
264
|
-
if (expectedContract.schemaHash && state.detailSchemaHash && expectedContract.schemaHash !== state.detailSchemaHash) return createViolation('operation_schema_hash_mismatch', `Operation schema hash mismatch for "${operationId || `${method}:${routePath}`}"`, state.status);
|
|
265
|
-
if ('number' == typeof expectedContract.operationVersion && 'number' == typeof state.detailOperationVersion && expectedContract.operationVersion !== state.detailOperationVersion) return createViolation('operation_version_mismatch', `Operation version mismatch for "${operationId || `${method}:${routePath}`}"`, state.status);
|
|
266
|
-
} else if (!state.allowUnknownOperations) return createViolation('unknown_operation_contract', `No expected operation contract found for operation "${operationId || `${method}:${routePath}`}"`, state.status);
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
const CROSS_PROJECT_POLICY_CHECKS = [
|
|
270
|
-
checkNamespaceAllowlistVerifier,
|
|
271
|
-
checkEnvelopeHeader,
|
|
272
|
-
checkProducerIdentity,
|
|
273
|
-
checkNamespaceAllowlist,
|
|
274
|
-
checkOperationContext,
|
|
275
|
-
checkOperationContextDetails,
|
|
276
|
-
checkOperationContract
|
|
277
|
-
];
|
|
278
|
-
const evaluateCrossProjectPolicy = (headers, policy, observedRequest)=>{
|
|
279
|
-
if (!policy?.enabled) return null;
|
|
280
|
-
const state = createCrossProjectPolicyEvaluationState(headers, policy, observedRequest);
|
|
281
|
-
for (const check of CROSS_PROJECT_POLICY_CHECKS){
|
|
282
|
-
const violation = check(state);
|
|
283
|
-
if (violation) return violation;
|
|
284
|
-
if (state.complete) break;
|
|
285
|
-
}
|
|
286
|
-
return null;
|
|
287
|
-
};
|
|
288
|
-
__webpack_require__.d(__webpack_exports__, {}, {
|
|
289
|
-
BFF_ENVELOPE_HEADER: BFF_ENVELOPE_HEADER,
|
|
290
|
-
BFF_OPERATION_CONTEXT_DETAIL_HEADER: BFF_OPERATION_CONTEXT_DETAIL_HEADER,
|
|
291
|
-
BFF_OPERATION_CONTEXT_HEADER: BFF_OPERATION_CONTEXT_HEADER,
|
|
292
|
-
evaluateCrossProjectPolicy: evaluateCrossProjectPolicy,
|
|
293
|
-
resolveCrossProjectRequestObservation: resolveCrossProjectRequestObservation
|
|
294
|
-
});
|
|
295
|
-
exports.BFF_ENVELOPE_HEADER = __webpack_exports__.BFF_ENVELOPE_HEADER;
|
|
296
|
-
exports.BFF_OPERATION_CONTEXT_DETAIL_HEADER = __webpack_exports__.BFF_OPERATION_CONTEXT_DETAIL_HEADER;
|
|
297
|
-
exports.BFF_OPERATION_CONTEXT_HEADER = __webpack_exports__.BFF_OPERATION_CONTEXT_HEADER;
|
|
298
|
-
exports.evaluateCrossProjectPolicy = __webpack_exports__.evaluateCrossProjectPolicy;
|
|
299
|
-
exports.resolveCrossProjectRequestObservation = __webpack_exports__.resolveCrossProjectRequestObservation;
|
|
300
|
-
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
301
|
-
"BFF_ENVELOPE_HEADER",
|
|
302
|
-
"BFF_OPERATION_CONTEXT_DETAIL_HEADER",
|
|
303
|
-
"BFF_OPERATION_CONTEXT_HEADER",
|
|
304
|
-
"evaluateCrossProjectPolicy",
|
|
305
|
-
"resolveCrossProjectRequestObservation"
|
|
306
|
-
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
307
|
-
Object.defineProperty(exports, '__esModule', {
|
|
308
|
-
value: true
|
|
309
|
-
});
|