@bleedingdev/modern-js-bff-core 3.5.0-ultramodern.4 → 3.5.0-ultramodern.41

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 (46) hide show
  1. package/dist/cjs/adapter-kit/index.js +6 -2
  2. package/dist/cjs/adapter-kit/parity-scenarios/cross-project-denial.js +55 -0
  3. package/dist/cjs/adapter-kit/parity-scenarios/envelope.js +128 -0
  4. package/dist/cjs/adapter-kit/parity-scenarios/operation-context.js +158 -0
  5. package/dist/cjs/adapter-kit/parity-scenarios/schema.js +109 -0
  6. package/dist/cjs/adapter-kit/parity-scenarios/shared.js +71 -0
  7. package/dist/cjs/adapter-kit/parity.js +26 -368
  8. package/dist/cjs/index.js +29 -48
  9. package/dist/cjs/operators/http.js +2 -2
  10. package/dist/cjs/security/crossProjectPolicy.js +123 -62
  11. package/dist/esm/adapter-kit/index.mjs +6 -2
  12. package/dist/esm/adapter-kit/parity-scenarios/cross-project-denial.mjs +17 -0
  13. package/dist/esm/adapter-kit/parity-scenarios/envelope.mjs +90 -0
  14. package/dist/esm/adapter-kit/parity-scenarios/operation-context.mjs +117 -0
  15. package/dist/esm/adapter-kit/parity-scenarios/schema.mjs +71 -0
  16. package/dist/esm/adapter-kit/parity-scenarios/shared.mjs +21 -0
  17. package/dist/esm/adapter-kit/parity.mjs +19 -361
  18. package/dist/esm/index.mjs +0 -1
  19. package/dist/esm/operators/http.mjs +2 -2
  20. package/dist/esm/security/crossProjectPolicy.mjs +123 -62
  21. package/dist/esm/security/operationContracts.mjs +2 -2
  22. package/dist/esm-node/adapter-kit/index.mjs +6 -2
  23. package/dist/esm-node/adapter-kit/parity-scenarios/cross-project-denial.mjs +18 -0
  24. package/dist/esm-node/adapter-kit/parity-scenarios/envelope.mjs +91 -0
  25. package/dist/esm-node/adapter-kit/parity-scenarios/operation-context.mjs +118 -0
  26. package/dist/esm-node/adapter-kit/parity-scenarios/schema.mjs +72 -0
  27. package/dist/esm-node/adapter-kit/parity-scenarios/shared.mjs +22 -0
  28. package/dist/esm-node/adapter-kit/parity.mjs +19 -361
  29. package/dist/esm-node/index.mjs +0 -1
  30. package/dist/esm-node/operators/http.mjs +2 -2
  31. package/dist/esm-node/security/crossProjectPolicy.mjs +123 -62
  32. package/dist/types/adapter-kit/index.d.ts +2 -2
  33. package/dist/types/adapter-kit/parity-scenarios/cross-project-denial.d.ts +2 -0
  34. package/dist/types/adapter-kit/parity-scenarios/envelope.d.ts +2 -0
  35. package/dist/types/adapter-kit/parity-scenarios/operation-context.d.ts +3 -0
  36. package/dist/types/adapter-kit/parity-scenarios/schema.d.ts +2 -0
  37. package/dist/types/adapter-kit/parity-scenarios/shared.d.ts +32 -0
  38. package/dist/types/adapter-kit/parity.d.ts +12 -71
  39. package/dist/types/index.d.ts +0 -1
  40. package/dist/types/operators/http.d.ts +1 -1
  41. package/dist/types/security/crossProjectPolicy.d.ts +17 -8
  42. package/package.json +9 -9
  43. package/dist/cjs/contracts/eventContracts.js +0 -72
  44. package/dist/esm/contracts/eventContracts.mjs +0 -28
  45. package/dist/esm-node/contracts/eventContracts.mjs +0 -29
  46. package/dist/types/contracts/eventContracts.d.ts +0 -22
@@ -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
- 6284 (module) {
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__(6284);
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 {
@@ -57,10 +57,14 @@ const getResponseMetaList = (handler)=>{
57
57
  const responseMeta = Reflect.getMetadata(HttpMetadata.Response, handler);
58
58
  return Array.isArray(responseMeta) ? responseMeta : [];
59
59
  };
60
- const buildPositionalHandlerArgs = (input)=>[
61
- ...Object.values(input.params),
60
+ const buildPositionalHandlerArgs = (input, routePath)=>{
61
+ const paramNames = routePath?.match(/:(\w+)/g)?.map((param)=>param.slice(1)) ?? [];
62
+ const positionalParams = paramNames.length > 0 ? paramNames.map((param)=>input.params[param]) : Object.values(input.params);
63
+ return [
64
+ ...positionalParams,
62
65
  input
63
66
  ];
67
+ };
64
68
  const checkCrossProjectPolicy = (headers, policy)=>{
65
69
  const violation = evaluateCrossProjectPolicy(headers, policy);
66
70
  if (!violation) return null;
@@ -0,0 +1,18 @@
1
+ import "node:module";
2
+ import { deniedScenario, envelopeHeader } from "./shared.mjs";
3
+ const createCrossProjectDenialScenarios = ()=>[
4
+ deniedScenario('policy denies missing envelope', 'missing_envelope', {}),
5
+ deniedScenario('policy denies invalid envelope', 'invalid_envelope', {
6
+ 'x-modernjs-bff-envelope': 'not-json'
7
+ }),
8
+ deniedScenario('policy denies envelope that is valid JSON but not an object', 'invalid_envelope', {
9
+ 'x-modernjs-bff-envelope': '123'
10
+ }),
11
+ deniedScenario('policy denies missing requestId', 'missing_request_id', {
12
+ 'x-modernjs-bff-envelope': envelopeHeader(void 0)
13
+ }),
14
+ deniedScenario('policy denies namespace outside allowlist', 'namespace_not_allowed', {
15
+ 'x-modernjs-bff-envelope': envelopeHeader('billing.producer-z')
16
+ })
17
+ ];
18
+ export { createCrossProjectDenialScenarios };
@@ -0,0 +1,91 @@
1
+ import "node:module";
2
+ const createEnvelopeParityScenarios = ()=>[
3
+ {
4
+ name: 'plain handler returns object payload',
5
+ policy: false,
6
+ request: {
7
+ method: 'get',
8
+ path: '/hello'
9
+ },
10
+ expected: {
11
+ kind: 'payload',
12
+ status: 200,
13
+ payload: {
14
+ message: 'hello'
15
+ }
16
+ }
17
+ },
18
+ {
19
+ name: 'plain handler returns scalar payload',
20
+ policy: false,
21
+ request: {
22
+ method: 'post',
23
+ path: '/hello'
24
+ },
25
+ expected: {
26
+ kind: 'payload',
27
+ status: 200,
28
+ payload: 'hello'
29
+ }
30
+ },
31
+ {
32
+ name: 'plain handler returning undefined',
33
+ policy: false,
34
+ request: {
35
+ method: 'get',
36
+ path: '/nothing'
37
+ },
38
+ expected: {
39
+ kind: 'payload',
40
+ status: 404,
41
+ payload: '404 Not Found'
42
+ }
43
+ },
44
+ {
45
+ name: 'plain handler receives data, query and cookies',
46
+ policy: false,
47
+ request: {
48
+ method: 'post',
49
+ path: '/echo?q=z',
50
+ headers: {
51
+ 'content-type': 'application/json',
52
+ cookie: 'id=666'
53
+ },
54
+ body: {
55
+ a: 1
56
+ }
57
+ },
58
+ expected: {
59
+ kind: 'payload',
60
+ status: 200,
61
+ payload: {
62
+ data: {
63
+ a: 1
64
+ },
65
+ query: {
66
+ q: 'z'
67
+ },
68
+ cookie: 'id=666'
69
+ }
70
+ }
71
+ },
72
+ {
73
+ name: 'plain handler receives positional route params',
74
+ policy: false,
75
+ request: {
76
+ method: 'get',
77
+ path: '/items/123?q=x'
78
+ },
79
+ expected: {
80
+ kind: 'payload',
81
+ status: 200,
82
+ payload: {
83
+ id: '123',
84
+ query: {
85
+ q: 'x'
86
+ }
87
+ }
88
+ }
89
+ }
90
+ ];
91
+ export { createEnvelopeParityScenarios };
@@ -0,0 +1,118 @@
1
+ import "node:module";
2
+ import { PARITY_PRODUCER_REQUEST_ID, deniedScenario, detailHeader } from "./shared.mjs";
3
+ const createOperationContextSuccessScenarios = ({ helloContract, validEnvelope, validOperationId })=>[
4
+ {
5
+ name: 'policy pass with full operation context',
6
+ policy: true,
7
+ request: {
8
+ method: 'get',
9
+ path: '/hello',
10
+ headers: {
11
+ 'x-modernjs-bff-envelope': validEnvelope,
12
+ 'x-operation-id': validOperationId,
13
+ 'x-modernjs-bff-operation-context': detailHeader({
14
+ requestId: PARITY_PRODUCER_REQUEST_ID,
15
+ method: 'GET',
16
+ routePath: '/hello',
17
+ schemaHash: helloContract.schemaHash,
18
+ operationVersion: helloContract.operationVersion
19
+ })
20
+ }
21
+ },
22
+ expected: {
23
+ kind: 'payload',
24
+ status: 200,
25
+ payload: {
26
+ message: 'hello'
27
+ }
28
+ }
29
+ }
30
+ ];
31
+ const createOperationContextDenialScenarios = ({ helloContract, validEnvelope, validOperationId })=>[
32
+ deniedScenario('policy denies missing operation context', 'missing_operation_context', {
33
+ 'x-modernjs-bff-envelope': validEnvelope
34
+ }),
35
+ deniedScenario('policy denies operation context mismatch', 'operation_context_mismatch', {
36
+ 'x-modernjs-bff-envelope': validEnvelope,
37
+ 'x-operation-id': 'someone-else:parity'
38
+ }),
39
+ deniedScenario('policy denies missing operation context details', 'missing_operation_context_details', {
40
+ 'x-modernjs-bff-envelope': validEnvelope,
41
+ 'x-operation-id': validOperationId
42
+ }),
43
+ deniedScenario('policy denies JSON-array operation context details', 'invalid_operation_context_details', {
44
+ 'x-modernjs-bff-envelope': validEnvelope,
45
+ 'x-operation-id': validOperationId,
46
+ 'x-modernjs-bff-operation-context': '[]'
47
+ }),
48
+ deniedScenario('policy denies invalid operation context details', 'invalid_operation_context_details', {
49
+ 'x-modernjs-bff-envelope': validEnvelope,
50
+ 'x-operation-id': validOperationId,
51
+ 'x-modernjs-bff-operation-context': 'not-json'
52
+ }),
53
+ deniedScenario('policy denies detail requestId mismatch', 'operation_context_details_request_id_mismatch', {
54
+ 'x-modernjs-bff-envelope': validEnvelope,
55
+ 'x-operation-id': validOperationId,
56
+ 'x-modernjs-bff-operation-context': detailHeader({
57
+ requestId: 'crm.producer-b',
58
+ method: 'GET',
59
+ routePath: '/hello',
60
+ schemaHash: helloContract.schemaHash,
61
+ operationVersion: helloContract.operationVersion
62
+ })
63
+ }),
64
+ deniedScenario('policy denies missing operation schema hash', 'missing_operation_schema_hash', {
65
+ 'x-modernjs-bff-envelope': validEnvelope,
66
+ 'x-operation-id': validOperationId,
67
+ 'x-modernjs-bff-operation-context': detailHeader({
68
+ requestId: PARITY_PRODUCER_REQUEST_ID,
69
+ method: 'GET',
70
+ routePath: '/hello',
71
+ operationVersion: helloContract.operationVersion
72
+ })
73
+ }),
74
+ deniedScenario('policy denies missing operation version', 'missing_operation_version', {
75
+ 'x-modernjs-bff-envelope': validEnvelope,
76
+ 'x-operation-id': validOperationId,
77
+ 'x-modernjs-bff-operation-context': detailHeader({
78
+ requestId: PARITY_PRODUCER_REQUEST_ID,
79
+ method: 'GET',
80
+ routePath: '/hello',
81
+ schemaHash: helloContract.schemaHash
82
+ })
83
+ }),
84
+ deniedScenario('policy denies unknown operation contract', 'unknown_operation_contract', {
85
+ 'x-modernjs-bff-envelope': validEnvelope,
86
+ 'x-operation-id': validOperationId,
87
+ 'x-modernjs-bff-operation-context': detailHeader({
88
+ requestId: PARITY_PRODUCER_REQUEST_ID,
89
+ method: 'GET',
90
+ routePath: '/does-not-exist',
91
+ schemaHash: helloContract.schemaHash,
92
+ operationVersion: helloContract.operationVersion
93
+ })
94
+ }),
95
+ deniedScenario('policy denies operation schema hash mismatch', 'operation_schema_hash_mismatch', {
96
+ 'x-modernjs-bff-envelope': validEnvelope,
97
+ 'x-operation-id': validOperationId,
98
+ 'x-modernjs-bff-operation-context': detailHeader({
99
+ requestId: PARITY_PRODUCER_REQUEST_ID,
100
+ method: 'GET',
101
+ routePath: '/hello',
102
+ schemaHash: 'deadbeef',
103
+ operationVersion: helloContract.operationVersion
104
+ })
105
+ }),
106
+ deniedScenario('policy denies operation version mismatch', 'operation_version_mismatch', {
107
+ 'x-modernjs-bff-envelope': validEnvelope,
108
+ 'x-operation-id': validOperationId,
109
+ 'x-modernjs-bff-operation-context': detailHeader({
110
+ requestId: PARITY_PRODUCER_REQUEST_ID,
111
+ method: 'GET',
112
+ routePath: '/hello',
113
+ schemaHash: helloContract.schemaHash,
114
+ operationVersion: helloContract.operationVersion + 1
115
+ })
116
+ })
117
+ ];
118
+ export { createOperationContextDenialScenarios, createOperationContextSuccessScenarios };
@@ -0,0 +1,72 @@
1
+ import "node:module";
2
+ const createSchemaParityScenarios = ()=>[
3
+ {
4
+ name: 'schema handler success',
5
+ policy: false,
6
+ request: {
7
+ method: 'patch',
8
+ path: '/schema',
9
+ headers: {
10
+ 'content-type': 'application/json'
11
+ },
12
+ body: {
13
+ id: 777
14
+ }
15
+ },
16
+ expected: {
17
+ kind: 'payload',
18
+ status: 200,
19
+ payload: {
20
+ type: 'HandleSuccess',
21
+ value: {
22
+ id: 777
23
+ }
24
+ }
25
+ }
26
+ },
27
+ {
28
+ name: 'schema handler input validation error',
29
+ policy: false,
30
+ request: {
31
+ method: 'patch',
32
+ path: '/schema',
33
+ headers: {
34
+ 'content-type': 'application/json'
35
+ },
36
+ body: {
37
+ id: 'aaa'
38
+ }
39
+ },
40
+ expected: {
41
+ kind: 'payload',
42
+ status: 200,
43
+ payload: {
44
+ type: 'InputValidationError',
45
+ message: 'invalid input'
46
+ }
47
+ }
48
+ },
49
+ {
50
+ name: 'schema handler output validation error',
51
+ policy: false,
52
+ request: {
53
+ method: 'patch',
54
+ path: '/schema',
55
+ headers: {
56
+ 'content-type': 'application/json'
57
+ },
58
+ body: {
59
+ id: 'boom'
60
+ }
61
+ },
62
+ expected: {
63
+ kind: 'payload',
64
+ status: 200,
65
+ payload: {
66
+ type: 'OutputValidationError',
67
+ message: 'invalid output'
68
+ }
69
+ }
70
+ }
71
+ ];
72
+ export { createSchemaParityScenarios };
@@ -0,0 +1,22 @@
1
+ import "node:module";
2
+ const PARITY_REQUEST_ID = 'crm';
3
+ const PARITY_PRODUCER_REQUEST_ID = 'crm.producer-a';
4
+ const envelopeHeader = (requestId)=>JSON.stringify(void 0 === requestId ? {} : {
5
+ requestId
6
+ });
7
+ const detailHeader = (details)=>JSON.stringify(details);
8
+ const deniedScenario = (name, reason, headers)=>({
9
+ name,
10
+ policy: true,
11
+ request: {
12
+ method: 'get',
13
+ path: '/hello',
14
+ headers
15
+ },
16
+ expected: {
17
+ kind: 'denied',
18
+ status: 403,
19
+ reason
20
+ }
21
+ });
22
+ export { PARITY_PRODUCER_REQUEST_ID, PARITY_REQUEST_ID, deniedScenario, detailHeader, envelopeHeader };