@bleedingdev/modern-js-bff-core 3.5.0-ultramodern.3 → 3.5.0-ultramodern.31

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 (33) 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 +24 -367
  8. package/dist/cjs/security/crossProjectPolicy.js +123 -62
  9. package/dist/esm/adapter-kit/index.mjs +6 -2
  10. package/dist/esm/adapter-kit/parity-scenarios/cross-project-denial.mjs +17 -0
  11. package/dist/esm/adapter-kit/parity-scenarios/envelope.mjs +90 -0
  12. package/dist/esm/adapter-kit/parity-scenarios/operation-context.mjs +117 -0
  13. package/dist/esm/adapter-kit/parity-scenarios/schema.mjs +71 -0
  14. package/dist/esm/adapter-kit/parity-scenarios/shared.mjs +21 -0
  15. package/dist/esm/adapter-kit/parity.mjs +17 -360
  16. package/dist/esm/security/crossProjectPolicy.mjs +123 -62
  17. package/dist/esm-node/adapter-kit/index.mjs +6 -2
  18. package/dist/esm-node/adapter-kit/parity-scenarios/cross-project-denial.mjs +18 -0
  19. package/dist/esm-node/adapter-kit/parity-scenarios/envelope.mjs +91 -0
  20. package/dist/esm-node/adapter-kit/parity-scenarios/operation-context.mjs +118 -0
  21. package/dist/esm-node/adapter-kit/parity-scenarios/schema.mjs +72 -0
  22. package/dist/esm-node/adapter-kit/parity-scenarios/shared.mjs +22 -0
  23. package/dist/esm-node/adapter-kit/parity.mjs +17 -360
  24. package/dist/esm-node/security/crossProjectPolicy.mjs +123 -62
  25. package/dist/types/adapter-kit/index.d.ts +2 -2
  26. package/dist/types/adapter-kit/parity-scenarios/cross-project-denial.d.ts +2 -0
  27. package/dist/types/adapter-kit/parity-scenarios/envelope.d.ts +2 -0
  28. package/dist/types/adapter-kit/parity-scenarios/operation-context.d.ts +3 -0
  29. package/dist/types/adapter-kit/parity-scenarios/schema.d.ts +2 -0
  30. package/dist/types/adapter-kit/parity-scenarios/shared.d.ts +32 -0
  31. package/dist/types/adapter-kit/parity.d.ts +10 -70
  32. package/dist/types/security/crossProjectPolicy.d.ts +11 -8
  33. package/package.json +4 -4
@@ -0,0 +1,2 @@
1
+ import type { AdapterParityScenario } from './shared';
2
+ export declare const createSchemaParityScenarios: () => AdapterParityScenario[];
@@ -0,0 +1,32 @@
1
+ import type { CrossProjectOperationContract, CrossProjectPolicyViolationReason } from '../../security/crossProjectPolicy';
2
+ export declare const PARITY_REQUEST_ID = "crm";
3
+ export declare const PARITY_PRODUCER_REQUEST_ID = "crm.producer-a";
4
+ export type ParityExpectation = {
5
+ kind: 'payload';
6
+ status: number;
7
+ payload: unknown;
8
+ } | {
9
+ kind: 'denied';
10
+ status: number;
11
+ reason: CrossProjectPolicyViolationReason;
12
+ };
13
+ export type AdapterParityScenario = {
14
+ name: string;
15
+ /** Run against policy-enabled server instead of the open one. */
16
+ policy: boolean;
17
+ request: {
18
+ method: 'get' | 'post' | 'patch';
19
+ path: string;
20
+ headers?: Record<string, string>;
21
+ body?: unknown;
22
+ };
23
+ expected: ParityExpectation;
24
+ };
25
+ export type AdapterParityScenarioContext = {
26
+ helloContract: CrossProjectOperationContract;
27
+ validEnvelope: string;
28
+ validOperationId: string;
29
+ };
30
+ export declare const envelopeHeader: (requestId: unknown) => string;
31
+ export declare const detailHeader: (details: Record<string, unknown>) => string;
32
+ export declare const deniedScenario: (name: string, reason: CrossProjectPolicyViolationReason, headers: Record<string, string>) => AdapterParityScenario;
@@ -1,45 +1,14 @@
1
1
  import type { APIHandlerInfo } from '../router';
2
- import type { CrossProjectPolicyViolationReason } from '../security/crossProjectPolicy';
3
- /**
4
- * Adapter parity (conformance) kit.
5
- *
6
- * One shared table of scenarios executed against every BFF server adapter
7
- * in its own test harness. Each scenario asserts the adapters produce
8
- * identical observable results: HTTP status, payload value and
9
- * policy-rejection reason.
10
- *
11
- * The express/koa adapters were removed from the fork, and this is internal
12
- * test support rather than a package subpath. Their expectations are retained
13
- * in the per-adapter drift pins as documentation of the historical behavior.
14
- * The live executable consumer of this table is the hono lane test
15
- * (`@modern-js/plugin-bff` runs it against `createHonoRoutes` plus the
16
- * cross-project policy middleware), while bff-core tests validate the table
17
- * shape and assertion helpers.
18
- *
19
- * Transport details intentionally NOT asserted: express serialized scalar
20
- * bodies as JSON while koa sent `text/plain`; {@link toParityResult}
21
- * normalizes both to the decoded payload value before comparison.
22
- *
23
- * Intentionally OUT OF SCOPE (known, accepted adapter drift — do not add
24
- * scenarios without deciding the drift first):
25
- * - operator route-middlewares: express applied them, koa ignored them;
26
- * - multipart/form-data: payload shapes differ per body parser;
27
- * - undefined-returning plain handlers are pinned via a per-adapter
28
- * scenario below: express ended the response 200/empty, koa served its
29
- * stock 404 ("Not Found"), hono serves its stock "404 Not Found";
30
- * - farrow schema-mode handlers are pinned per-adapter below: express/koa
31
- * unwrapped the result envelope (200/400/500), the hono lane has no
32
- * schema-mode unwrapping and passes the raw envelope through.
33
- */
34
- export declare const PARITY_REQUEST_ID = "crm";
35
- export declare const PARITY_PRODUCER_REQUEST_ID = "crm.producer-a";
2
+ import type { AdapterParityScenario } from './parity-scenarios/shared';
3
+ export type { AdapterParityScenario, ParityExpectation, } from './parity-scenarios/shared';
4
+ export { PARITY_PRODUCER_REQUEST_ID, PARITY_REQUEST_ID, } from './parity-scenarios/shared';
36
5
  /**
37
6
  * Handler fixtures registered in both adapters before running the table.
38
7
  */
39
8
  export declare const createParityApiHandlerInfos: () => APIHandlerInfo[];
40
9
  /**
41
- * `bff` config slice for the policy-enabled parity server. All `require*`
42
- * switches stay at their strict defaults.
10
+ * `bff` config slice for policy-enabled parity server. All `require*`
11
+ * switches stay at strict defaults.
43
12
  */
44
13
  export declare const createParityBffConfig: () => {
45
14
  requestId: string;
@@ -48,37 +17,8 @@ export declare const createParityBffConfig: () => {
48
17
  allowedNamespaces: string[];
49
18
  };
50
19
  };
51
- export type ParityAdapterId = 'express' | 'koa' | 'hono';
52
- export type ParityExpectation = {
53
- kind: 'payload';
54
- status: number;
55
- payload: unknown;
56
- } | {
57
- kind: 'denied';
58
- status: number;
59
- reason: CrossProjectPolicyViolationReason;
60
- } | {
61
- /** Pinned, intentional adapter drift: each adapter has its own expectation. */
62
- kind: 'perAdapter';
63
- expectations: Record<ParityAdapterId, {
64
- status: number;
65
- payload: unknown;
66
- }>;
67
- };
68
- export type AdapterParityScenario = {
69
- name: string;
70
- /** Run against the policy-enabled server instead of the open one. */
71
- policy: boolean;
72
- request: {
73
- method: 'get' | 'post' | 'patch';
74
- path: string;
75
- headers?: Record<string, string>;
76
- body?: unknown;
77
- };
78
- expected: ParityExpectation;
79
- };
80
20
  export declare const createAdapterParityScenarios: () => AdapterParityScenario[];
81
- /** Structural slice of a supertest response used for normalization. */
21
+ /** Structural slice of supertest response used for normalization. */
82
22
  export type ParityHttpResponse = {
83
23
  status: number;
84
24
  /** Content-type mime, e.g. `application/json`. */
@@ -91,12 +31,12 @@ export type AdapterParityResult = {
91
31
  payload: unknown;
92
32
  };
93
33
  /**
94
- * Normalizes a raw HTTP response to the observable payload value so JSON
95
- * (express) and text (koa) encodings of the same scalar compare equal.
34
+ * Normalizes raw HTTP response into an observable payload value so JSON and
35
+ * text encodings of the same scalar compare equal.
96
36
  */
97
37
  export declare const toParityResult: (res: ParityHttpResponse) => AdapterParityResult;
98
38
  /**
99
- * Framework-agnostic assertion: throws a descriptive error when the adapter
39
+ * Framework-agnostic assertion: throws a descriptive error when adapter
100
40
  * response deviates from the scenario expectation.
101
41
  */
102
- export declare const assertParityResult: (scenario: AdapterParityScenario, res: ParityHttpResponse, adapter?: ParityAdapterId) => void;
42
+ export declare const assertParityResult: (scenario: AdapterParityScenario, res: ParityHttpResponse) => void;
@@ -10,14 +10,16 @@
10
10
  * authorization boundary: any caller can echo an allowed `requestId` and a
11
11
  * matching operation context.
12
12
  *
13
- * To turn `allowedNamespaces` into a real control, supply
13
+ * To use `allowedNamespaces` in production, 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, ...). When the hook is present, the client-asserted
18
- * namespace must match the verified namespace and the allowlist is checked
19
- * against the verified value the envelope is no longer trusted for the
20
- * authorization decision.
17
+ * stripped at the edge, ...). Production requests fail closed when a
18
+ * namespace allowlist is configured without this hook. In non-production,
19
+ * the evaluator logs a one-time warning and keeps the legacy advisory
20
+ * behavior: the client-asserted namespace must match the allowlist. When the
21
+ * hook is present, the client-asserted namespace must match the verified
22
+ * namespace and the allowlist is checked against the verified value.
21
23
  *
22
24
  * Client-side counterparts in `@modern-js/create-request` (identity binding,
23
25
  * operation contract validation) are developer-experience aids that fail
@@ -53,7 +55,7 @@ export interface CrossProjectPolicyConfig {
53
55
  allowUnknownOperations?: boolean;
54
56
  denyStatus?: number;
55
57
  /**
56
- * Server-side hook binding the producer namespace to a VERIFIED identity
58
+ * Server-side hook binding producer namespace to VERIFIED identity
57
59
  * channel (mTLS peer, gateway-authenticated JWT, mesh identity headers).
58
60
  *
59
61
  * When provided, the namespace asserted by the client envelope must match
@@ -61,8 +63,9 @@ export interface CrossProjectPolicyConfig {
61
63
  * against the verified value instead of the client-asserted one. Returning
62
64
  * `undefined` (identity could not be verified) denies the request.
63
65
  *
64
- * Without this hook the namespace checks are advisory only — see the
65
- * module-level threat model.
66
+ * In production, `allowedNamespaces` requires this hook and fails closed
67
+ * without it. In non-production, namespace checks without this hook remain
68
+ * advisory only — see the module-level threat model.
66
69
  */
67
70
  verifyProducerIdentity?: (headers: Record<string, unknown>) => string | undefined;
68
71
  }
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.5.0-ultramodern.3",
20
+ "version": "3.5.0-ultramodern.31",
21
21
  "types": "./dist/types/index.d.ts",
22
22
  "main": "./dist/cjs/index.js",
23
23
  "exports": {
@@ -36,7 +36,7 @@
36
36
  "koa-compose": "^4.1.0",
37
37
  "reflect-metadata": "^0.2.2",
38
38
  "type-fest": "5.7.0",
39
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.3"
39
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.31"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@rslib/core": "0.23.1",
@@ -45,8 +45,8 @@
45
45
  "@typescript/native-preview": "7.0.0-dev.20260628.1",
46
46
  "tsconfig-paths": "^4.2.0",
47
47
  "zod": "^4.4.3",
48
- "@modern-js/bff-runtime": "npm:@bleedingdev/modern-js-bff-runtime@3.5.0-ultramodern.3",
49
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.3",
48
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.31",
49
+ "@modern-js/bff-runtime": "npm:@bleedingdev/modern-js-bff-runtime@3.5.0-ultramodern.31",
50
50
  "@scripts/rstest-config": "2.66.0"
51
51
  },
52
52
  "peerDependencies": {