@bleedingdev/modern-js-bff-core 3.2.0-ultramodern.120 → 3.2.0-ultramodern.122

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.
Files changed (30) hide show
  1. package/dist/cjs/adapter-kit/index.js +140 -0
  2. package/dist/cjs/adapter-kit/parity.js +546 -0
  3. package/dist/cjs/client/generateClient.js +65 -12
  4. package/dist/cjs/index.js +72 -34
  5. package/dist/cjs/security/crossProjectPolicy.js +10 -2
  6. package/dist/cjs/security/operationContracts.js +146 -54
  7. package/dist/cjs/security/resolveCrossProjectPolicy.js +65 -0
  8. package/dist/esm/adapter-kit/index.mjs +75 -0
  9. package/dist/esm/adapter-kit/parity.mjs +490 -0
  10. package/dist/esm/client/generateClient.mjs +66 -13
  11. package/dist/esm/index.mjs +2 -0
  12. package/dist/esm/rslib-runtime.mjs +18 -0
  13. package/dist/esm/security/crossProjectPolicy.mjs +10 -2
  14. package/dist/esm/security/operationContracts.mjs +111 -37
  15. package/dist/esm/security/resolveCrossProjectPolicy.mjs +27 -0
  16. package/dist/esm-node/adapter-kit/index.mjs +76 -0
  17. package/dist/esm-node/adapter-kit/parity.mjs +491 -0
  18. package/dist/esm-node/client/generateClient.mjs +66 -13
  19. package/dist/esm-node/index.mjs +2 -0
  20. package/dist/esm-node/rslib-runtime.mjs +19 -0
  21. package/dist/esm-node/security/crossProjectPolicy.mjs +10 -2
  22. package/dist/esm-node/security/operationContracts.mjs +111 -37
  23. package/dist/esm-node/security/resolveCrossProjectPolicy.mjs +28 -0
  24. package/dist/types/adapter-kit/index.d.ts +90 -0
  25. package/dist/types/adapter-kit/parity.d.ts +102 -0
  26. package/dist/types/index.d.ts +2 -0
  27. package/dist/types/security/crossProjectPolicy.d.ts +40 -1
  28. package/dist/types/security/operationContracts.d.ts +60 -4
  29. package/dist/types/security/resolveCrossProjectPolicy.d.ts +48 -0
  30. package/package.json +5 -3
@@ -0,0 +1,140 @@
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
+ HANDLER_WITH_SCHEMA: ()=>HANDLER_WITH_SCHEMA,
32
+ buildPositionalHandlerArgs: ()=>buildPositionalHandlerArgs,
33
+ checkCrossProjectPolicy: ()=>checkCrossProjectPolicy,
34
+ getApiHandlerMode: ()=>getApiHandlerMode,
35
+ getResponseMetaList: ()=>getResponseMetaList,
36
+ getRouteMiddlewares: ()=>getRouteMiddlewares,
37
+ isSchemaApiHandler: ()=>isSchemaApiHandler,
38
+ mapSchemaHandlerResult: ()=>mapSchemaHandlerResult,
39
+ planApiRoutes: ()=>planApiRoutes,
40
+ toApiRouteMethod: ()=>toApiRouteMethod
41
+ });
42
+ require("reflect-metadata");
43
+ const crossProjectPolicy_js_namespaceObject = require("../security/crossProjectPolicy.js");
44
+ const external_types_js_namespaceObject = require("../types.js");
45
+ const index_js_namespaceObject = require("../utils/index.js");
46
+ const API_ROUTE_METHODS = {
47
+ [external_types_js_namespaceObject.HttpMethod.Get]: 'get',
48
+ [external_types_js_namespaceObject.HttpMethod.Post]: 'post',
49
+ [external_types_js_namespaceObject.HttpMethod.Put]: 'put',
50
+ [external_types_js_namespaceObject.HttpMethod.Delete]: 'delete',
51
+ [external_types_js_namespaceObject.HttpMethod.Connect]: 'connect',
52
+ [external_types_js_namespaceObject.HttpMethod.Trace]: 'trace',
53
+ [external_types_js_namespaceObject.HttpMethod.Patch]: 'patch',
54
+ [external_types_js_namespaceObject.HttpMethod.Options]: 'options',
55
+ [external_types_js_namespaceObject.HttpMethod.Head]: 'head'
56
+ };
57
+ const toApiRouteMethod = (httpMethod)=>{
58
+ const method = API_ROUTE_METHODS[httpMethod];
59
+ if (!method) throw new Error(`[bff-core] Unsupported HTTP method "${String(httpMethod)}" in API handler info`);
60
+ return method;
61
+ };
62
+ const getRouteMiddlewares = (handler)=>{
63
+ const middlewares = Reflect.getMetadata('middleware', handler);
64
+ return Array.isArray(middlewares) ? middlewares : [];
65
+ };
66
+ const planApiRoutes = (handlerInfos)=>handlerInfos.map(({ routePath, handler, httpMethod })=>({
67
+ method: toApiRouteMethod(httpMethod),
68
+ routePath,
69
+ handler,
70
+ middlewares: getRouteMiddlewares(handler)
71
+ }));
72
+ const HANDLER_WITH_SCHEMA = 'HANDLER_WITH_SCHEMA';
73
+ const isSchemaApiHandler = (handler)=>{
74
+ if ('function' != typeof handler) return false;
75
+ const marked = handler;
76
+ return true === marked[HANDLER_WITH_SCHEMA];
77
+ };
78
+ const getApiHandlerMode = (handler)=>{
79
+ if ((0, index_js_namespaceObject.isWithMetaHandler)(handler)) return 'meta';
80
+ if (isSchemaApiHandler(handler)) return 'schema';
81
+ if ((0, index_js_namespaceObject.isInputParamsDeciderHandler)(handler)) return 'inputParamsDecider';
82
+ return 'plain';
83
+ };
84
+ const mapSchemaHandlerResult = (result)=>{
85
+ if ('HandleSuccess' === result.type) return {
86
+ success: true,
87
+ status: 200,
88
+ body: result.value
89
+ };
90
+ return {
91
+ success: false,
92
+ status: 'InputValidationError' === result.type ? 400 : 500,
93
+ body: result.message
94
+ };
95
+ };
96
+ const getResponseMetaList = (handler)=>{
97
+ const responseMeta = Reflect.getMetadata(external_types_js_namespaceObject.HttpMetadata.Response, handler);
98
+ return Array.isArray(responseMeta) ? responseMeta : [];
99
+ };
100
+ const buildPositionalHandlerArgs = (input)=>[
101
+ ...Object.values(input.params),
102
+ input
103
+ ];
104
+ const checkCrossProjectPolicy = (headers, policy)=>{
105
+ const violation = (0, crossProjectPolicy_js_namespaceObject.evaluateCrossProjectPolicy)(headers, policy);
106
+ if (!violation) return null;
107
+ return {
108
+ status: violation.status,
109
+ body: {
110
+ code: violation.code,
111
+ reason: violation.reason,
112
+ message: violation.message
113
+ }
114
+ };
115
+ };
116
+ exports.HANDLER_WITH_SCHEMA = __webpack_exports__.HANDLER_WITH_SCHEMA;
117
+ exports.buildPositionalHandlerArgs = __webpack_exports__.buildPositionalHandlerArgs;
118
+ exports.checkCrossProjectPolicy = __webpack_exports__.checkCrossProjectPolicy;
119
+ exports.getApiHandlerMode = __webpack_exports__.getApiHandlerMode;
120
+ exports.getResponseMetaList = __webpack_exports__.getResponseMetaList;
121
+ exports.getRouteMiddlewares = __webpack_exports__.getRouteMiddlewares;
122
+ exports.isSchemaApiHandler = __webpack_exports__.isSchemaApiHandler;
123
+ exports.mapSchemaHandlerResult = __webpack_exports__.mapSchemaHandlerResult;
124
+ exports.planApiRoutes = __webpack_exports__.planApiRoutes;
125
+ exports.toApiRouteMethod = __webpack_exports__.toApiRouteMethod;
126
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
127
+ "HANDLER_WITH_SCHEMA",
128
+ "buildPositionalHandlerArgs",
129
+ "checkCrossProjectPolicy",
130
+ "getApiHandlerMode",
131
+ "getResponseMetaList",
132
+ "getRouteMiddlewares",
133
+ "isSchemaApiHandler",
134
+ "mapSchemaHandlerResult",
135
+ "planApiRoutes",
136
+ "toApiRouteMethod"
137
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
138
+ Object.defineProperty(exports, '__esModule', {
139
+ value: true
140
+ });
@@ -0,0 +1,546 @@
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: ()=>PARITY_PRODUCER_REQUEST_ID,
32
+ PARITY_REQUEST_ID: ()=>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 PARITY_REQUEST_ID = 'crm';
42
+ const PARITY_PRODUCER_REQUEST_ID = 'crm.producer-a';
43
+ const PARITY_FIXTURE_FILENAME = 'bff-core/adapter-kit/parity-fixture.ts';
44
+ const HANDLER_WITH_SCHEMA = 'HANDLER_WITH_SCHEMA';
45
+ const isRecord = (value)=>Boolean(value) && 'object' == typeof value;
46
+ const createSchemaFixtureHandler = ()=>{
47
+ const handler = (input)=>{
48
+ const data = isRecord(input) && isRecord(input.data) ? input.data : void 0;
49
+ const id = data?.id;
50
+ if ('number' == typeof id) return {
51
+ type: 'HandleSuccess',
52
+ value: {
53
+ id
54
+ }
55
+ };
56
+ if ('boom' === id) return {
57
+ type: 'OutputValidationError',
58
+ message: 'invalid output'
59
+ };
60
+ return {
61
+ type: 'InputValidationError',
62
+ message: 'invalid input'
63
+ };
64
+ };
65
+ return Object.assign(handler, {
66
+ [HANDLER_WITH_SCHEMA]: true
67
+ });
68
+ };
69
+ const createParityApiHandlerInfos = ()=>[
70
+ {
71
+ name: 'getHello',
72
+ httpMethod: external_types_js_namespaceObject.HttpMethod.Get,
73
+ routeName: '/hello',
74
+ routePath: '/hello',
75
+ filename: PARITY_FIXTURE_FILENAME,
76
+ handler: ()=>({
77
+ message: 'hello'
78
+ })
79
+ },
80
+ {
81
+ name: 'postHello',
82
+ httpMethod: external_types_js_namespaceObject.HttpMethod.Post,
83
+ routeName: '/hello',
84
+ routePath: '/hello',
85
+ filename: PARITY_FIXTURE_FILENAME,
86
+ handler: ()=>'hello'
87
+ },
88
+ {
89
+ name: 'getNothing',
90
+ httpMethod: external_types_js_namespaceObject.HttpMethod.Get,
91
+ routeName: '/nothing',
92
+ routePath: '/nothing',
93
+ filename: PARITY_FIXTURE_FILENAME,
94
+ handler: ()=>void 0
95
+ },
96
+ {
97
+ name: 'postEcho',
98
+ httpMethod: external_types_js_namespaceObject.HttpMethod.Post,
99
+ routeName: '/echo',
100
+ routePath: '/echo',
101
+ filename: PARITY_FIXTURE_FILENAME,
102
+ handler: (input)=>{
103
+ const normalized = isRecord(input) ? input : {};
104
+ return {
105
+ data: normalized.data ?? null,
106
+ query: normalized.query ?? {},
107
+ cookie: normalized.cookies ?? null
108
+ };
109
+ }
110
+ },
111
+ {
112
+ name: 'getItem',
113
+ httpMethod: external_types_js_namespaceObject.HttpMethod.Get,
114
+ routeName: '/items/:id',
115
+ routePath: '/items/:id',
116
+ filename: PARITY_FIXTURE_FILENAME,
117
+ handler: (id, input)=>({
118
+ id,
119
+ query: isRecord(input) ? input.query ?? {} : {}
120
+ })
121
+ },
122
+ {
123
+ name: 'patchSchema',
124
+ httpMethod: external_types_js_namespaceObject.HttpMethod.Patch,
125
+ routeName: '/schema',
126
+ routePath: '/schema',
127
+ filename: PARITY_FIXTURE_FILENAME,
128
+ handler: createSchemaFixtureHandler()
129
+ }
130
+ ];
131
+ const createParityBffConfig = ()=>({
132
+ requestId: PARITY_REQUEST_ID,
133
+ crossProjectPolicy: {
134
+ enabled: true,
135
+ allowedNamespaces: [
136
+ PARITY_REQUEST_ID
137
+ ]
138
+ }
139
+ });
140
+ const getParityContracts = ()=>(0, operationContracts_js_namespaceObject.buildOperationContractMap)({
141
+ handlers: createParityApiHandlerInfos(),
142
+ requestId: PARITY_REQUEST_ID
143
+ });
144
+ const envelopeHeader = (requestId)=>JSON.stringify(void 0 === requestId ? {} : {
145
+ requestId
146
+ });
147
+ const detailHeader = (details)=>JSON.stringify(details);
148
+ const deniedScenario = (name, reason, headers)=>({
149
+ name,
150
+ policy: true,
151
+ request: {
152
+ method: 'get',
153
+ path: '/hello',
154
+ headers
155
+ },
156
+ expected: {
157
+ kind: 'denied',
158
+ status: 403,
159
+ reason
160
+ }
161
+ });
162
+ const createAdapterParityScenarios = ()=>{
163
+ const contracts = getParityContracts();
164
+ const helloContract = contracts['GET:/hello'];
165
+ const validEnvelope = envelopeHeader(PARITY_PRODUCER_REQUEST_ID);
166
+ const validOperationId = `${PARITY_PRODUCER_REQUEST_ID}:parity`;
167
+ return [
168
+ {
169
+ name: 'plain handler returns object payload',
170
+ policy: false,
171
+ request: {
172
+ method: 'get',
173
+ path: '/hello'
174
+ },
175
+ expected: {
176
+ kind: 'payload',
177
+ status: 200,
178
+ payload: {
179
+ message: 'hello'
180
+ }
181
+ }
182
+ },
183
+ {
184
+ name: 'plain handler returns scalar payload',
185
+ policy: false,
186
+ request: {
187
+ method: 'post',
188
+ path: '/hello'
189
+ },
190
+ expected: {
191
+ kind: 'payload',
192
+ status: 200,
193
+ payload: 'hello'
194
+ }
195
+ },
196
+ {
197
+ name: 'plain handler returning undefined (pinned adapter drift)',
198
+ policy: false,
199
+ request: {
200
+ method: 'get',
201
+ path: '/nothing'
202
+ },
203
+ expected: {
204
+ kind: 'perAdapter',
205
+ expectations: {
206
+ express: {
207
+ status: 200,
208
+ payload: void 0
209
+ },
210
+ koa: {
211
+ status: 404,
212
+ payload: 'Not Found'
213
+ },
214
+ hono: {
215
+ status: 404,
216
+ payload: '404 Not Found'
217
+ }
218
+ }
219
+ }
220
+ },
221
+ {
222
+ name: 'plain handler receives data, query and cookies',
223
+ policy: false,
224
+ request: {
225
+ method: 'post',
226
+ path: '/echo?q=z',
227
+ headers: {
228
+ 'content-type': 'application/json',
229
+ cookie: 'id=666'
230
+ },
231
+ body: {
232
+ a: 1
233
+ }
234
+ },
235
+ expected: {
236
+ kind: 'payload',
237
+ status: 200,
238
+ payload: {
239
+ data: {
240
+ a: 1
241
+ },
242
+ query: {
243
+ q: 'z'
244
+ },
245
+ cookie: 'id=666'
246
+ }
247
+ }
248
+ },
249
+ {
250
+ name: 'plain handler receives positional route params',
251
+ policy: false,
252
+ request: {
253
+ method: 'get',
254
+ path: '/items/123?q=x'
255
+ },
256
+ expected: {
257
+ kind: 'payload',
258
+ status: 200,
259
+ payload: {
260
+ id: '123',
261
+ query: {
262
+ q: 'x'
263
+ }
264
+ }
265
+ }
266
+ },
267
+ {
268
+ name: 'schema handler success (pinned adapter drift)',
269
+ policy: false,
270
+ request: {
271
+ method: 'patch',
272
+ path: '/schema',
273
+ headers: {
274
+ 'content-type': 'application/json'
275
+ },
276
+ body: {
277
+ id: 777
278
+ }
279
+ },
280
+ expected: {
281
+ kind: 'perAdapter',
282
+ expectations: {
283
+ express: {
284
+ status: 200,
285
+ payload: {
286
+ id: 777
287
+ }
288
+ },
289
+ koa: {
290
+ status: 200,
291
+ payload: {
292
+ id: 777
293
+ }
294
+ },
295
+ hono: {
296
+ status: 200,
297
+ payload: {
298
+ type: 'HandleSuccess',
299
+ value: {
300
+ id: 777
301
+ }
302
+ }
303
+ }
304
+ }
305
+ }
306
+ },
307
+ {
308
+ name: 'schema handler input validation error (pinned adapter drift)',
309
+ policy: false,
310
+ request: {
311
+ method: 'patch',
312
+ path: '/schema',
313
+ headers: {
314
+ 'content-type': 'application/json'
315
+ },
316
+ body: {
317
+ id: 'aaa'
318
+ }
319
+ },
320
+ expected: {
321
+ kind: 'perAdapter',
322
+ expectations: {
323
+ express: {
324
+ status: 400,
325
+ payload: 'invalid input'
326
+ },
327
+ koa: {
328
+ status: 400,
329
+ payload: 'invalid input'
330
+ },
331
+ hono: {
332
+ status: 200,
333
+ payload: {
334
+ type: 'InputValidationError',
335
+ message: 'invalid input'
336
+ }
337
+ }
338
+ }
339
+ }
340
+ },
341
+ {
342
+ name: 'schema handler output validation error (pinned adapter drift)',
343
+ policy: false,
344
+ request: {
345
+ method: 'patch',
346
+ path: '/schema',
347
+ headers: {
348
+ 'content-type': 'application/json'
349
+ },
350
+ body: {
351
+ id: 'boom'
352
+ }
353
+ },
354
+ expected: {
355
+ kind: 'perAdapter',
356
+ expectations: {
357
+ express: {
358
+ status: 500,
359
+ payload: 'invalid output'
360
+ },
361
+ koa: {
362
+ status: 500,
363
+ payload: 'invalid output'
364
+ },
365
+ hono: {
366
+ status: 200,
367
+ payload: {
368
+ type: 'OutputValidationError',
369
+ message: 'invalid output'
370
+ }
371
+ }
372
+ }
373
+ }
374
+ },
375
+ {
376
+ name: 'policy pass with full operation context',
377
+ policy: true,
378
+ request: {
379
+ method: 'get',
380
+ path: '/hello',
381
+ headers: {
382
+ 'x-modernjs-bff-envelope': validEnvelope,
383
+ 'x-operation-id': validOperationId,
384
+ 'x-modernjs-bff-operation-context': detailHeader({
385
+ requestId: PARITY_PRODUCER_REQUEST_ID,
386
+ method: 'GET',
387
+ routePath: '/hello',
388
+ schemaHash: helloContract.schemaHash,
389
+ operationVersion: helloContract.operationVersion
390
+ })
391
+ }
392
+ },
393
+ expected: {
394
+ kind: 'payload',
395
+ status: 200,
396
+ payload: {
397
+ message: 'hello'
398
+ }
399
+ }
400
+ },
401
+ deniedScenario('policy denies missing envelope', 'missing_envelope', {}),
402
+ deniedScenario('policy denies invalid envelope', 'invalid_envelope', {
403
+ 'x-modernjs-bff-envelope': 'not-json'
404
+ }),
405
+ deniedScenario('policy denies envelope that is valid JSON but not an object', 'invalid_envelope', {
406
+ 'x-modernjs-bff-envelope': '123'
407
+ }),
408
+ deniedScenario('policy denies missing requestId', 'missing_request_id', {
409
+ 'x-modernjs-bff-envelope': envelopeHeader(void 0)
410
+ }),
411
+ deniedScenario('policy denies namespace outside allowlist', 'namespace_not_allowed', {
412
+ 'x-modernjs-bff-envelope': envelopeHeader('billing.producer-z')
413
+ }),
414
+ deniedScenario('policy denies missing operation context', 'missing_operation_context', {
415
+ 'x-modernjs-bff-envelope': validEnvelope
416
+ }),
417
+ deniedScenario('policy denies operation context mismatch', 'operation_context_mismatch', {
418
+ 'x-modernjs-bff-envelope': validEnvelope,
419
+ 'x-operation-id': 'someone-else:parity'
420
+ }),
421
+ deniedScenario('policy denies missing operation context details', 'missing_operation_context_details', {
422
+ 'x-modernjs-bff-envelope': validEnvelope,
423
+ 'x-operation-id': validOperationId
424
+ }),
425
+ deniedScenario('policy denies JSON-array operation context details', 'invalid_operation_context_details', {
426
+ 'x-modernjs-bff-envelope': validEnvelope,
427
+ 'x-operation-id': validOperationId,
428
+ 'x-modernjs-bff-operation-context': '[]'
429
+ }),
430
+ deniedScenario('policy denies invalid operation context details', 'invalid_operation_context_details', {
431
+ 'x-modernjs-bff-envelope': validEnvelope,
432
+ 'x-operation-id': validOperationId,
433
+ 'x-modernjs-bff-operation-context': 'not-json'
434
+ }),
435
+ deniedScenario('policy denies detail requestId mismatch', 'operation_context_details_request_id_mismatch', {
436
+ 'x-modernjs-bff-envelope': validEnvelope,
437
+ 'x-operation-id': validOperationId,
438
+ 'x-modernjs-bff-operation-context': detailHeader({
439
+ requestId: 'crm.producer-b',
440
+ method: 'GET',
441
+ routePath: '/hello',
442
+ schemaHash: helloContract.schemaHash,
443
+ operationVersion: helloContract.operationVersion
444
+ })
445
+ }),
446
+ deniedScenario('policy denies missing operation schema hash', 'missing_operation_schema_hash', {
447
+ 'x-modernjs-bff-envelope': validEnvelope,
448
+ 'x-operation-id': validOperationId,
449
+ 'x-modernjs-bff-operation-context': detailHeader({
450
+ requestId: PARITY_PRODUCER_REQUEST_ID,
451
+ method: 'GET',
452
+ routePath: '/hello',
453
+ operationVersion: helloContract.operationVersion
454
+ })
455
+ }),
456
+ deniedScenario('policy denies missing operation version', 'missing_operation_version', {
457
+ 'x-modernjs-bff-envelope': validEnvelope,
458
+ 'x-operation-id': validOperationId,
459
+ 'x-modernjs-bff-operation-context': detailHeader({
460
+ requestId: PARITY_PRODUCER_REQUEST_ID,
461
+ method: 'GET',
462
+ routePath: '/hello',
463
+ schemaHash: helloContract.schemaHash
464
+ })
465
+ }),
466
+ deniedScenario('policy denies unknown operation contract', 'unknown_operation_contract', {
467
+ 'x-modernjs-bff-envelope': validEnvelope,
468
+ 'x-operation-id': validOperationId,
469
+ 'x-modernjs-bff-operation-context': detailHeader({
470
+ requestId: PARITY_PRODUCER_REQUEST_ID,
471
+ method: 'GET',
472
+ routePath: '/does-not-exist',
473
+ schemaHash: helloContract.schemaHash,
474
+ operationVersion: helloContract.operationVersion
475
+ })
476
+ }),
477
+ deniedScenario('policy denies operation schema hash mismatch', 'operation_schema_hash_mismatch', {
478
+ 'x-modernjs-bff-envelope': validEnvelope,
479
+ 'x-operation-id': validOperationId,
480
+ 'x-modernjs-bff-operation-context': detailHeader({
481
+ requestId: PARITY_PRODUCER_REQUEST_ID,
482
+ method: 'GET',
483
+ routePath: '/hello',
484
+ schemaHash: 'deadbeef',
485
+ operationVersion: helloContract.operationVersion
486
+ })
487
+ }),
488
+ deniedScenario('policy denies operation version mismatch', 'operation_version_mismatch', {
489
+ 'x-modernjs-bff-envelope': validEnvelope,
490
+ 'x-operation-id': validOperationId,
491
+ 'x-modernjs-bff-operation-context': detailHeader({
492
+ requestId: PARITY_PRODUCER_REQUEST_ID,
493
+ method: 'GET',
494
+ routePath: '/hello',
495
+ schemaHash: helloContract.schemaHash,
496
+ operationVersion: helloContract.operationVersion + 1
497
+ })
498
+ })
499
+ ];
500
+ };
501
+ const toParityResult = (res)=>({
502
+ status: res.status,
503
+ payload: res.type.includes('json') ? res.body : '' === res.text ? void 0 : res.text
504
+ });
505
+ const formatValue = (value)=>JSON.stringify(value);
506
+ const assertParityResult = (scenario, res, adapter)=>{
507
+ const result = toParityResult(res);
508
+ const failures = [];
509
+ let { expected } = scenario;
510
+ if ('perAdapter' === expected.kind) {
511
+ if (void 0 === adapter) throw new Error(`Adapter parity scenario "${scenario.name}" pins per-adapter drift; pass the adapter id to assertParityResult.`);
512
+ expected = {
513
+ kind: 'payload',
514
+ ...expected.expectations[adapter]
515
+ };
516
+ }
517
+ if (result.status !== expected.status) failures.push(`status: expected ${expected.status}, received ${result.status}`);
518
+ if ('payload' === expected.kind) {
519
+ if (formatValue(result.payload) !== formatValue(expected.payload)) failures.push(`payload: expected ${formatValue(expected.payload)}, received ${formatValue(result.payload)}`);
520
+ } else {
521
+ const payload = isRecord(result.payload) ? result.payload : {};
522
+ if ('BFF_CROSS_PROJECT_POLICY_DENIED' !== payload.code) failures.push(`denial code: expected "BFF_CROSS_PROJECT_POLICY_DENIED", received ${formatValue(payload.code)}`);
523
+ if (payload.reason !== expected.reason) failures.push(`denial reason: expected "${expected.reason}", received ${formatValue(payload.reason)}`);
524
+ if ('string' != typeof payload.message || 0 === payload.message.length) failures.push(`denial message: expected non-empty string, received ${formatValue(payload.message)}`);
525
+ }
526
+ if (failures.length > 0) throw new Error(`Adapter parity scenario "${scenario.name}" failed:\n- ${failures.join('\n- ')}`);
527
+ };
528
+ exports.PARITY_PRODUCER_REQUEST_ID = __webpack_exports__.PARITY_PRODUCER_REQUEST_ID;
529
+ exports.PARITY_REQUEST_ID = __webpack_exports__.PARITY_REQUEST_ID;
530
+ exports.assertParityResult = __webpack_exports__.assertParityResult;
531
+ exports.createAdapterParityScenarios = __webpack_exports__.createAdapterParityScenarios;
532
+ exports.createParityApiHandlerInfos = __webpack_exports__.createParityApiHandlerInfos;
533
+ exports.createParityBffConfig = __webpack_exports__.createParityBffConfig;
534
+ exports.toParityResult = __webpack_exports__.toParityResult;
535
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
536
+ "PARITY_PRODUCER_REQUEST_ID",
537
+ "PARITY_REQUEST_ID",
538
+ "assertParityResult",
539
+ "createAdapterParityScenarios",
540
+ "createParityApiHandlerInfos",
541
+ "createParityBffConfig",
542
+ "toParityResult"
543
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
544
+ Object.defineProperty(exports, '__esModule', {
545
+ value: true
546
+ });