nestjs-ddd-cli 3.3.0 → 3.4.0

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.
@@ -90,3 +90,33 @@ are bounded at 16 KiB under a five-second deadline; redirects are refused.
90
90
  Receipts must bind the exact scope, operation and resource and expire within
91
91
  PARC's five-second bound. Never cache/reuse them as durable permissions. A failed
92
92
  request may already have consumed its lease; obtain fresh authority explicitly.
93
+
94
+ ## Scoped grant shadow comparison
95
+
96
+ Deploy a PARC version accepting optional `shadowContext` before opting a consumer
97
+ into `shadowFacts`. Omit the option for unchanged wire compatibility with older
98
+ PARC installations. The receipt, allow/deny decision, expiry and owner commit
99
+ checks remain unchanged. There is no client-side grant evaluator.
100
+
101
+ ```typescript
102
+ const receipt = await parc.check({
103
+ // ...the existing lease, credential, permission, action, resource and expected scope
104
+ contextDigest: digestOfOwnerVerifiedOperation,
105
+ shadowFacts: { amountMinor: persistedOperation.amountMinor, currency: persistedOperation.currency },
106
+ });
107
+ ```
108
+
109
+ The operation digest must cover these exact facts and the resource/version being
110
+ authorized. Read them from persisted or independently verified owner state, never
111
+ copy amount, currency or policy claims from browser headers. PARC accepts facts
112
+ only from the admitted service owning the permission. Amounts are canonical
113
+ unsigned integer strings of at most 38 digits; currency is three uppercase
114
+ letters. Invalid optional facts produce an invalid shadow observation and do not
115
+ change the existing decision. No facts are included in the returned receipt.
116
+
117
+ PARC compares imported grants against legacy decisions separately from proposed
118
+ resource, amount and expiry restrictions. Intentional proposal differences are
119
+ not compatibility failures. Reports include unobserved decisions; an empty
120
+ window, missing observations or only denied requests cannot prove full parity.
121
+ Role/proposal administration stays in the governed PARC console, never this
122
+ consumer client. Keep legacy enforcement until the scoped rollout is proven.
@@ -1,5 +1,18 @@
1
1
  import { BoundedJsonHttpClient } from '../../auth/oauth2-token-introspection/bounded-json-http.client';
2
2
 
3
+ export interface ParcShadowFacts {
4
+ /** Canonical integer string, never a floating-point amount. */
5
+ amountMinor: string;
6
+ currency: string;
7
+ }
8
+
9
+ /** Optional diagnostic facts. They cannot alter or replace the bound receipt. */
10
+ function shadowContext(facts: ParcShadowFacts, contextDigest: string) {
11
+ if (!facts || typeof facts.amountMinor !== 'string' || !/^(0|[1-9][0-9]{0,37})$/.test(facts.amountMinor) ||
12
+ typeof facts.currency !== 'string' || !/^[A-Z]{3}$/.test(facts.currency)) return null;
13
+ return { contractVersion: 1, contextDigest, facts: { amountMinor: facts.amountMinor, currency: facts.currency } };
14
+ }
15
+
3
16
  export interface ParcBoundRequest {
4
17
  /** Fresh opaque SA lease. PARC consumes it; do not introspect it first. */
5
18
  lease: string;
@@ -10,6 +23,9 @@ export interface ParcBoundRequest {
10
23
  resource: { type: string; id: string };
11
24
  /** SHA-256 of the owner-verified operation material. */
12
25
  contextDigest: string;
26
+ /** Opt-in shadow comparison only. Read from the same owner material covered by contextDigest.
27
+ * Enable only after the receiving PARC API supports shadowContext. */
28
+ shadowFacts?: ParcShadowFacts;
13
29
  /** Persisted owner scope or an untrusted route selector; trusted only after exact receipt matching. */
14
30
  expected: { businessId: string; clientAppId: string; subjectId?: string };
15
31
  }
@@ -75,6 +91,10 @@ export class ParcBoundAuthorizationClient {
75
91
  };
76
92
  if (typeof request.contextDigest !== 'string' ||
77
93
  !/^[a-f0-9]{64}$/.test(request.contextDigest)) this.invalid();
94
+ // Invalid optional facts are recorded as invalid shadow context without changing enforcement.
95
+ const payload = input.shadowFacts === undefined ? request : {
96
+ ...request, shadowContext: shadowContext(input.shadowFacts, request.contextDigest),
97
+ };
78
98
  const expected = {
79
99
  businessId: this.id(input?.expected?.businessId),
80
100
  clientAppId: this.id(input?.expected?.clientAppId),
@@ -92,7 +112,7 @@ export class ParcBoundAuthorizationClient {
92
112
  'x-request-context-lease': lease,
93
113
  'x-service-access-credential': credential,
94
114
  },
95
- }).postJson(['v1', 'decisions', 'check-bound'], request);
115
+ }).postJson(['v1', 'decisions', 'check-bound'], payload);
96
116
  } catch { throw new ParcBoundAuthorizationError('unavailable'); }
97
117
  this.requireTls();
98
118
  const fail = (): never => { throw new ParcBoundAuthorizationError('invalid_receipt'); };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestjs-ddd-cli",
3
- "version": "3.3.0",
3
+ "version": "3.4.0",
4
4
  "description": "CLI for generating NestJS DDD boilerplate code",
5
5
  "main": "dist/index.js",
6
6
  "bin": {