@astram/guard 0.1.1 → 0.3.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.
Files changed (183) hide show
  1. package/dist/auth/account.controller.d.ts +245 -0
  2. package/dist/auth/account.controller.d.ts.map +1 -0
  3. package/dist/auth/account.controller.js +673 -0
  4. package/dist/auth/account.controller.js.map +1 -0
  5. package/dist/auth/auth.controller.d.ts +316 -0
  6. package/dist/auth/auth.controller.d.ts.map +1 -0
  7. package/dist/auth/auth.controller.js +1091 -0
  8. package/dist/auth/auth.controller.js.map +1 -0
  9. package/dist/auth/auth.module.d.ts +202 -0
  10. package/dist/auth/auth.module.d.ts.map +1 -0
  11. package/dist/auth/auth.module.js +610 -0
  12. package/dist/auth/auth.module.js.map +1 -0
  13. package/dist/auth/connections.d.ts +61 -0
  14. package/dist/auth/connections.d.ts.map +1 -0
  15. package/dist/auth/connections.js +116 -0
  16. package/dist/auth/connections.js.map +1 -0
  17. package/dist/auth/dto.d.ts +154 -0
  18. package/dist/auth/dto.d.ts.map +1 -0
  19. package/dist/auth/dto.js +29 -0
  20. package/dist/auth/dto.js.map +1 -0
  21. package/dist/auth/pending-mfa-store.d.ts +72 -0
  22. package/dist/auth/pending-mfa-store.d.ts.map +1 -0
  23. package/dist/auth/pending-mfa-store.js +106 -0
  24. package/dist/auth/pending-mfa-store.js.map +1 -0
  25. package/dist/auth/persistence.module.d.ts +42 -0
  26. package/dist/auth/persistence.module.d.ts.map +1 -0
  27. package/dist/auth/persistence.module.js +94 -0
  28. package/dist/auth/persistence.module.js.map +1 -0
  29. package/dist/auth/ports/audit-sink.d.ts +47 -0
  30. package/dist/auth/ports/audit-sink.d.ts.map +1 -0
  31. package/dist/auth/ports/audit-sink.js +20 -0
  32. package/dist/auth/ports/audit-sink.js.map +1 -0
  33. package/dist/auth/ports/credential-writer.d.ts +23 -0
  34. package/dist/auth/ports/credential-writer.d.ts.map +1 -0
  35. package/dist/auth/ports/credential-writer.js +2 -0
  36. package/dist/auth/ports/credential-writer.js.map +1 -0
  37. package/dist/auth/ports/email-sender.d.ts +14 -0
  38. package/dist/auth/ports/email-sender.d.ts.map +1 -0
  39. package/dist/auth/ports/email-sender.js +2 -0
  40. package/dist/auth/ports/email-sender.js.map +1 -0
  41. package/dist/auth/ports/mfa-device-store.d.ts +45 -0
  42. package/dist/auth/ports/mfa-device-store.d.ts.map +1 -0
  43. package/dist/auth/ports/mfa-device-store.js +13 -0
  44. package/dist/auth/ports/mfa-device-store.js.map +1 -0
  45. package/dist/auth/ports/profile-writer.d.ts +12 -0
  46. package/dist/auth/ports/profile-writer.d.ts.map +1 -0
  47. package/dist/auth/ports/profile-writer.js +2 -0
  48. package/dist/auth/ports/profile-writer.js.map +1 -0
  49. package/dist/auth/ports/recovery-code-store.d.ts +10 -0
  50. package/dist/auth/ports/recovery-code-store.d.ts.map +1 -0
  51. package/dist/auth/ports/recovery-code-store.js +2 -0
  52. package/dist/auth/ports/recovery-code-store.js.map +1 -0
  53. package/dist/auth/ports/reset-token-store.d.ts +12 -0
  54. package/dist/auth/ports/reset-token-store.d.ts.map +1 -0
  55. package/dist/auth/ports/reset-token-store.js +2 -0
  56. package/dist/auth/ports/reset-token-store.js.map +1 -0
  57. package/dist/auth/ports/session-facts-projector.d.ts +44 -0
  58. package/dist/auth/ports/session-facts-projector.d.ts.map +1 -0
  59. package/dist/auth/ports/session-facts-projector.js +30 -0
  60. package/dist/auth/ports/session-facts-projector.js.map +1 -0
  61. package/dist/auth/ports/session-sweeper.d.ts +12 -0
  62. package/dist/auth/ports/session-sweeper.d.ts.map +1 -0
  63. package/dist/auth/ports/session-sweeper.js +2 -0
  64. package/dist/auth/ports/session-sweeper.js.map +1 -0
  65. package/dist/auth/profile-resolver.d.ts +73 -0
  66. package/dist/auth/profile-resolver.d.ts.map +1 -0
  67. package/dist/auth/profile-resolver.js +28 -0
  68. package/dist/auth/profile-resolver.js.map +1 -0
  69. package/dist/auth/rate-limit/attempt-limiter.d.ts +91 -0
  70. package/dist/auth/rate-limit/attempt-limiter.d.ts.map +1 -0
  71. package/dist/auth/rate-limit/attempt-limiter.js +133 -0
  72. package/dist/auth/rate-limit/attempt-limiter.js.map +1 -0
  73. package/dist/auth/tokens.d.ts +236 -0
  74. package/dist/auth/tokens.d.ts.map +1 -0
  75. package/dist/auth/tokens.js +254 -0
  76. package/dist/auth/tokens.js.map +1 -0
  77. package/dist/auth/totp-enrollment.d.ts +42 -0
  78. package/dist/auth/totp-enrollment.d.ts.map +1 -0
  79. package/dist/auth/totp-enrollment.js +96 -0
  80. package/dist/auth/totp-enrollment.js.map +1 -0
  81. package/dist/authentication/argon2id-password-hasher.js.map +1 -0
  82. package/dist/authentication/mariadb-credential-resolver.d.ts +110 -0
  83. package/dist/authentication/mariadb-credential-resolver.d.ts.map +1 -0
  84. package/dist/authentication/mariadb-credential-resolver.js +170 -0
  85. package/dist/authentication/mariadb-credential-resolver.js.map +1 -0
  86. package/dist/authz/cross-tenant-grant-writer.d.ts +259 -0
  87. package/dist/authz/cross-tenant-grant-writer.d.ts.map +1 -0
  88. package/dist/authz/cross-tenant-grant-writer.js +397 -0
  89. package/dist/authz/cross-tenant-grant-writer.js.map +1 -0
  90. package/dist/authz/manifest-operation-authority-adapter.d.ts +65 -0
  91. package/dist/authz/manifest-operation-authority-adapter.d.ts.map +1 -0
  92. package/dist/authz/manifest-operation-authority-adapter.js +108 -0
  93. package/dist/authz/manifest-operation-authority-adapter.js.map +1 -0
  94. package/dist/authz/mariadb-cross-tenant-grant-adapter.d.ts +90 -0
  95. package/dist/authz/mariadb-cross-tenant-grant-adapter.d.ts.map +1 -0
  96. package/dist/authz/mariadb-cross-tenant-grant-adapter.js +211 -0
  97. package/dist/authz/mariadb-cross-tenant-grant-adapter.js.map +1 -0
  98. package/dist/authz/mariadb-record-rule-adapter.d.ts +60 -0
  99. package/dist/authz/mariadb-record-rule-adapter.d.ts.map +1 -0
  100. package/dist/authz/mariadb-record-rule-adapter.js +154 -0
  101. package/dist/authz/mariadb-record-rule-adapter.js.map +1 -0
  102. package/dist/authz/mariadb-record-share-adapter.d.ts +75 -0
  103. package/dist/authz/mariadb-record-share-adapter.d.ts.map +1 -0
  104. package/dist/authz/mariadb-record-share-adapter.js +106 -0
  105. package/dist/authz/mariadb-record-share-adapter.js.map +1 -0
  106. package/dist/authz/record-share-cascade.d.ts +52 -0
  107. package/dist/authz/record-share-cascade.d.ts.map +1 -0
  108. package/dist/authz/record-share-cascade.js +49 -0
  109. package/dist/authz/record-share-cascade.js.map +1 -0
  110. package/dist/authz/record-share-writer.d.ts +175 -0
  111. package/dist/authz/record-share-writer.d.ts.map +1 -0
  112. package/dist/authz/record-share-writer.js +225 -0
  113. package/dist/authz/record-share-writer.js.map +1 -0
  114. package/dist/authz/wire-record-authorization.d.ts +66 -0
  115. package/dist/authz/wire-record-authorization.d.ts.map +1 -0
  116. package/dist/authz/wire-record-authorization.js +34 -0
  117. package/dist/authz/wire-record-authorization.js.map +1 -0
  118. package/dist/boot/boot-sequence.js.map +1 -0
  119. package/dist/boot/boot.module.js.map +1 -0
  120. package/dist/completeness/catalog-reconciler.d.ts +21 -0
  121. package/dist/completeness/catalog-reconciler.d.ts.map +1 -0
  122. package/dist/completeness/catalog-reconciler.js +74 -0
  123. package/dist/completeness/catalog-reconciler.js.map +1 -0
  124. package/dist/completeness/operation-inventory-scanner.d.ts +17 -0
  125. package/dist/completeness/operation-inventory-scanner.d.ts.map +1 -0
  126. package/dist/completeness/operation-inventory-scanner.js +79 -0
  127. package/dist/completeness/operation-inventory-scanner.js.map +1 -0
  128. package/dist/completeness/ports/catalog-store.d.ts +33 -0
  129. package/dist/completeness/ports/catalog-store.d.ts.map +1 -0
  130. package/dist/completeness/ports/catalog-store.js +2 -0
  131. package/dist/completeness/ports/catalog-store.js.map +1 -0
  132. package/dist/config/app-config.d.ts +134 -1
  133. package/dist/config/app-config.d.ts.map +1 -1
  134. package/dist/config/app-config.js +144 -2
  135. package/dist/config/app-config.js.map +1 -0
  136. package/dist/config/config.module.js.map +1 -0
  137. package/dist/enforcement/classification.decorator.d.ts +33 -14
  138. package/dist/enforcement/classification.decorator.d.ts.map +1 -1
  139. package/dist/enforcement/classification.decorator.js +34 -14
  140. package/dist/enforcement/classification.decorator.js.map +1 -0
  141. package/dist/enforcement/core-seam.js.map +1 -0
  142. package/dist/enforcement/enforcement.module.js.map +1 -0
  143. package/dist/enforcement/global-enforcement.guard.d.ts +29 -19
  144. package/dist/enforcement/global-enforcement.guard.d.ts.map +1 -1
  145. package/dist/enforcement/global-enforcement.guard.js +108 -25
  146. package/dist/enforcement/global-enforcement.guard.js.map +1 -0
  147. package/dist/enforcement/reenroll-allowlist.d.ts +49 -0
  148. package/dist/enforcement/reenroll-allowlist.d.ts.map +1 -0
  149. package/dist/enforcement/reenroll-allowlist.js +77 -0
  150. package/dist/enforcement/reenroll-allowlist.js.map +1 -0
  151. package/dist/index.d.ts +43 -3
  152. package/dist/index.d.ts.map +1 -1
  153. package/dist/index.js +198 -1
  154. package/dist/index.js.map +1 -0
  155. package/dist/module/shield-tokens.d.ts +15 -0
  156. package/dist/module/shield-tokens.d.ts.map +1 -0
  157. package/dist/module/shield-tokens.js +17 -0
  158. package/dist/module/shield-tokens.js.map +1 -0
  159. package/dist/module/shield.module.d.ts +203 -8
  160. package/dist/module/shield.module.d.ts.map +1 -1
  161. package/dist/module/shield.module.js +232 -14
  162. package/dist/module/shield.module.js.map +1 -0
  163. package/dist/request-auth/request-principal.d.ts +42 -0
  164. package/dist/request-auth/request-principal.d.ts.map +1 -0
  165. package/dist/request-auth/request-principal.js +87 -0
  166. package/dist/request-auth/request-principal.js.map +1 -0
  167. package/dist/request-auth/session-facts-als.d.ts +65 -0
  168. package/dist/request-auth/session-facts-als.d.ts.map +1 -0
  169. package/dist/request-auth/session-facts-als.js +57 -0
  170. package/dist/request-auth/session-facts-als.js.map +1 -0
  171. package/dist/request-auth/session-token-binder.d.ts +130 -0
  172. package/dist/request-auth/session-token-binder.d.ts.map +1 -0
  173. package/dist/request-auth/session-token-binder.js +277 -0
  174. package/dist/request-auth/session-token-binder.js.map +1 -0
  175. package/dist/request-auth/tokens.d.ts +14 -0
  176. package/dist/request-auth/tokens.d.ts.map +1 -0
  177. package/dist/request-auth/tokens.js +16 -0
  178. package/dist/request-auth/tokens.js.map +1 -0
  179. package/dist/session/crypto-adapters.js.map +1 -0
  180. package/dist/session/index.js.map +1 -0
  181. package/dist/session/mariadb-session-record-store.js.map +1 -0
  182. package/dist/session/redis-hot-session-store.js.map +1 -0
  183. package/package.json +2 -2
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SCHEMA_SVC_POOL_HARD_CAP = exports.DEFAULT_POOL_BUDGET = void 0;
3
+ exports.DEFAULT_REDIS_URL = exports.DEFAULT_DB_CONFIG = exports.SCHEMA_SVC_POOL_HARD_CAP = exports.DEFAULT_POOL_BUDGET = void 0;
4
4
  exports.loadPoolBudget = loadPoolBudget;
5
+ exports.loadDbConfig = loadDbConfig;
6
+ exports.loadRedisConfig = loadRedisConfig;
7
+ exports.loadSessionLifetimes = loadSessionLifetimes;
8
+ exports.loadRefreshHmacKey = loadRefreshHmacKey;
9
+ exports.assertRefreshHmacKey = assertRefreshHmacKey;
5
10
  exports.loadAppConfig = loadAppConfig;
6
11
  exports.evaluatePoolBudget = evaluatePoolBudget;
7
12
  /** Documented MVP single-node defaults (see the class doc + my.cnf). */
@@ -61,15 +66,152 @@ function loadPoolBudget(env = process.env) {
61
66
  },
62
67
  };
63
68
  }
69
+ // ---------------------------------------------------------------------------
70
+ // Phase 0b Auth-surface config loaders (additive) — DB / Redis / secret /
71
+ // lifetimes. All pure env-readers with documented defaults; the ONLY hard-
72
+ // required value is the refresh HMAC key (fail-closed).
73
+ // ---------------------------------------------------------------------------
74
+ /** Documented defaults for the runtime MariaDB descriptor (local docker substrate). */
75
+ exports.DEFAULT_DB_CONFIG = {
76
+ host: 'localhost',
77
+ port: 3306,
78
+ user: 'shield_core',
79
+ database: 'shield_identity',
80
+ };
81
+ /** Documented default Redis url (local docker substrate). */
82
+ exports.DEFAULT_REDIS_URL = 'redis://localhost:6379';
83
+ /** Read a string from env with a fallback (trims; blank ⇒ fallback). */
84
+ function parseString(raw, fallback) {
85
+ if (raw === undefined || raw.trim() === '')
86
+ return fallback;
87
+ return raw.trim();
88
+ }
89
+ /**
90
+ * Read an OPTIONAL positive-int env override, or `undefined` when absent/blank/
91
+ * invalid — so an omitted lifetime falls through to the core default rather than
92
+ * being coerced to a bogus number. (Distinct from {@link parsePositiveInt}, which
93
+ * always returns a concrete fallback for the pool budget.)
94
+ */
95
+ function parseOptionalPositiveInt(raw) {
96
+ if (raw === undefined || raw.trim() === '')
97
+ return undefined;
98
+ const n = Number.parseInt(raw, 10);
99
+ return Number.isInteger(n) && n > 0 ? n : undefined;
100
+ }
101
+ /**
102
+ * Build the runtime {@link DbConfig} from env with documented defaults.
103
+ * Field ↔ env name:
104
+ * host ← SHIELD_DB_HOST port ← SHIELD_DB_PORT
105
+ * user ← SHIELD_DB_USER password ← SHIELD_DB_PASSWORD (secret)
106
+ * database ← SHIELD_DB_NAME
107
+ * The password defaults to '' when absent — a live connection then fails at
108
+ * connect time (loud), which is correct for a runtime that needs real creds; the
109
+ * secret is deliberately NOT hard-required at config load (unlike the HMAC key,
110
+ * which is a fail-closed security invariant even for unit tests).
111
+ */
112
+ function loadDbConfig(env = process.env) {
113
+ return {
114
+ host: parseString(env.SHIELD_DB_HOST, exports.DEFAULT_DB_CONFIG.host),
115
+ port: parsePort(env.SHIELD_DB_PORT, exports.DEFAULT_DB_CONFIG.port),
116
+ user: parseString(env.SHIELD_DB_USER, exports.DEFAULT_DB_CONFIG.user),
117
+ password: parseString(env.SHIELD_DB_PASSWORD, ''),
118
+ database: parseString(env.SHIELD_DB_NAME, exports.DEFAULT_DB_CONFIG.database),
119
+ };
120
+ }
121
+ /**
122
+ * Build the {@link RedisConfig} from env. Prefers `SHIELD_REDIS_URL`; falls back
123
+ * to composing `redis://host:port` from `SHIELD_REDIS_HOST`/`SHIELD_REDIS_PORT`,
124
+ * else the documented default.
125
+ */
126
+ function loadRedisConfig(env = process.env) {
127
+ const explicit = env.SHIELD_REDIS_URL;
128
+ if (explicit !== undefined && explicit.trim() !== '') {
129
+ return { url: explicit.trim() };
130
+ }
131
+ const host = env.SHIELD_REDIS_HOST;
132
+ const port = env.SHIELD_REDIS_PORT;
133
+ if ((host !== undefined && host.trim() !== '') || (port !== undefined && port.trim() !== '')) {
134
+ const h = parseString(host, 'localhost');
135
+ const p = parsePort(port, 6379);
136
+ return { url: `redis://${h}:${p}` };
137
+ }
138
+ return { url: exports.DEFAULT_REDIS_URL };
139
+ }
140
+ /**
141
+ * Build the optional {@link SessionLifetimeOverrides} from env. Every field is
142
+ * optional (omitted ⇒ core ADR-0015 default). The bounds validation is the
143
+ * core's `resolveLifetimes` job (throws LOUD at composition) — this loader only
144
+ * reads the numbers.
145
+ * Field ↔ env name:
146
+ * accessTtlMs ← SHIELD_ACCESS_TTL_MS
147
+ * refreshIdleMs ← SHIELD_REFRESH_IDLE_MS
148
+ * refreshAbsoluteCapMs ← SHIELD_REFRESH_ABSOLUTE_CAP_MS
149
+ */
150
+ function loadSessionLifetimes(env = process.env) {
151
+ const overrides = {};
152
+ const access = parseOptionalPositiveInt(env.SHIELD_ACCESS_TTL_MS);
153
+ const idle = parseOptionalPositiveInt(env.SHIELD_REFRESH_IDLE_MS);
154
+ const cap = parseOptionalPositiveInt(env.SHIELD_REFRESH_ABSOLUTE_CAP_MS);
155
+ if (access !== undefined)
156
+ overrides.accessTtlMs = access;
157
+ if (idle !== undefined)
158
+ overrides.refreshIdleMs = idle;
159
+ if (cap !== undefined)
160
+ overrides.refreshAbsoluteCapMs = cap;
161
+ return overrides;
162
+ }
163
+ /**
164
+ * Read the refresh-token HMAC key from env (raw; `''` when absent/blank). Total —
165
+ * does NOT throw, so {@link loadAppConfig} stays boot-safe for a non-auth scaffold.
166
+ * The FAIL-CLOSED enforcement lives in {@link assertRefreshHmacKey}. Never logs
167
+ * the key's value.
168
+ */
169
+ function loadRefreshHmacKey(env = process.env) {
170
+ return parseString(env.SHIELD_REFRESH_HMAC_KEY, '');
171
+ }
172
+ /**
173
+ * ⛔ FAIL-CLOSED GATE for the refresh-token HMAC key. Called by the auth
174
+ * composition (AuthModule / persistence factory) at the moment auth is wired:
175
+ * throws LOUD when the key is blank so the app can NEVER compose a working-looking
176
+ * authenticator/session-authority on an empty HMAC key (a leaked DB would then be
177
+ * offline-brute-forceable, and every refresh-token hash would be unkeyed). Mirrors
178
+ * `Sha256RefreshTokenHasher`'s constructor guard but surfaces a clear composition
179
+ * error. Returns the validated non-empty key. NEVER logs the key's value.
180
+ *
181
+ * WHY here and not in `loadAppConfig`: an enforcement-only app (example-backend, a
182
+ * freshly-scaffolded app with no auth wired) must still boot with no `.env`
183
+ * (`create-astram-app/template/_env.example` promises exactly this). Gating at the
184
+ * auth boundary keeps that promise while making auth fail closed the instant it is
185
+ * actually composed.
186
+ */
187
+ function assertRefreshHmacKey(key) {
188
+ if (typeof key !== 'string' || key.trim() === '') {
189
+ throw new Error('Shield auth composition: SHIELD_REFRESH_HMAC_KEY is required and must be ' +
190
+ 'non-empty (fail-closed — refusing to compose auth with a blank refresh-token ' +
191
+ 'HMAC key). Set it from your secrets manager; never commit the value.');
192
+ }
193
+ return key;
194
+ }
64
195
  /**
65
196
  * Build the {@link AppConfig} from `process.env` (or an injected env bag, for
66
- * testability). Pure + side-effect-free so it is trivially unit-testable.
197
+ * testability). Pure, total, and side-effect-free — trivially unit-testable and
198
+ * boot-safe with NO `.env`.
199
+ *
200
+ * Every Phase 0b field has a documented default (DB/Redis descriptors, lifetimes)
201
+ * and `refreshHmacKey` is read-but-not-required HERE, so existing callers
202
+ * (example-backend / template `main.ts`, which read only `.port`) are unaffected.
203
+ * The refresh-key fail-closed check is deferred to {@link assertRefreshHmacKey} at
204
+ * auth-composition time (see its doc).
67
205
  */
68
206
  function loadAppConfig(env = process.env) {
69
207
  return {
70
208
  port: parsePort(env.PORT, 3000),
71
209
  environment: parseEnvironment(env.NODE_ENV),
72
210
  pool: loadPoolBudget(env),
211
+ db: loadDbConfig(env),
212
+ redis: loadRedisConfig(env),
213
+ refreshHmacKey: loadRefreshHmacKey(env),
214
+ lifetimes: loadSessionLifetimes(env),
73
215
  };
74
216
  }
75
217
  /**
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../src/config/app-config.ts"],"names":[],"mappings":";;;AAoMA,wCAuBC;AAgDD,oCAQC;AAOD,0CAaC;AAYD,oDAeC;AAQD,gDAEC;AAiBD,oDASC;AAaD,sCAUC;AA0BD,gDAgBC;AAlRD,wEAAwE;AAC3D,QAAA,mBAAmB,GAAqB;IACnD,OAAO,EAAE,CAAC;IACV,cAAc,EAAE,GAAG;IACnB,QAAQ,EAAE,EAAE;IACZ,aAAa,EAAE;QACb,WAAW,EAAE,EAAE;QACf,eAAe,EAAE,CAAC;QAClB,iBAAiB,EAAE,CAAC;KACrB;CACF,CAAC;AAEF,gFAAgF;AACnE,QAAA,wBAAwB,GAAG,CAAC,CAAC;AAE1C,SAAS,SAAS,CAAC,GAAuB,EAAE,QAAgB;IAC1D,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IAC5D,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnE,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAuB;IAC/C,IAAI,GAAG,KAAK,YAAY,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,GAAG,CAAC;IACvD,OAAO,aAAa,CAAC;AACvB,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB,CAAC,GAAuB,EAAE,QAAgB;IACjE,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IAC5D,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACrD,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,cAAc,CAAC,MAAyB,OAAO,CAAC,GAAG;IACjE,OAAO;QACL,OAAO,EAAE,gBAAgB,CAAC,GAAG,CAAC,mBAAmB,EAAE,2BAAmB,CAAC,OAAO,CAAC;QAC/E,cAAc,EAAE,gBAAgB,CAC9B,GAAG,CAAC,yBAAyB,EAC7B,2BAAmB,CAAC,cAAc,CACnC;QACD,QAAQ,EAAE,gBAAgB,CAAC,GAAG,CAAC,oBAAoB,EAAE,2BAAmB,CAAC,QAAQ,CAAC;QAClF,aAAa,EAAE;YACb,WAAW,EAAE,gBAAgB,CAC3B,GAAG,CAAC,oBAAoB,EACxB,2BAAmB,CAAC,aAAa,CAAC,WAAW,CAC9C;YACD,eAAe,EAAE,gBAAgB,CAC/B,GAAG,CAAC,wBAAwB,EAC5B,2BAAmB,CAAC,aAAa,CAAC,eAAe,CAClD;YACD,iBAAiB,EAAE,gBAAgB,CACjC,GAAG,CAAC,0BAA0B,EAC9B,2BAAmB,CAAC,aAAa,CAAC,iBAAiB,CACpD;SACF;KACF,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,0EAA0E;AAC1E,2EAA2E;AAC3E,wDAAwD;AACxD,8EAA8E;AAE9E,uFAAuF;AAC1E,QAAA,iBAAiB,GAA+B;IAC3D,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,aAAa;IACnB,QAAQ,EAAE,iBAAiB;CAC5B,CAAC;AAEF,6DAA6D;AAChD,QAAA,iBAAiB,GAAG,wBAAwB,CAAC;AAE1D,wEAAwE;AACxE,SAAS,WAAW,CAAC,GAAuB,EAAE,QAAgB;IAC5D,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IAC5D,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,SAAS,wBAAwB,CAAC,GAAuB;IACvD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC7D,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACtD,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,YAAY,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC/D,OAAO;QACL,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,cAAc,EAAE,yBAAiB,CAAC,IAAI,CAAC;QAC7D,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,cAAc,EAAE,yBAAiB,CAAC,IAAI,CAAC;QAC3D,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,cAAc,EAAE,yBAAiB,CAAC,IAAI,CAAC;QAC7D,QAAQ,EAAE,WAAW,CAAC,GAAG,CAAC,kBAAkB,EAAE,EAAE,CAAC;QACjD,QAAQ,EAAE,WAAW,CAAC,GAAG,CAAC,cAAc,EAAE,yBAAiB,CAAC,QAAQ,CAAC;KACtE,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,eAAe,CAAC,MAAyB,OAAO,CAAC,GAAG;IAClE,MAAM,QAAQ,GAAG,GAAG,CAAC,gBAAgB,CAAC;IACtC,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;IAClC,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,CAAC,iBAAiB,CAAC;IACnC,MAAM,IAAI,GAAG,GAAG,CAAC,iBAAiB,CAAC;IACnC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAC7F,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QACzC,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAChC,OAAO,EAAE,GAAG,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;IACtC,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,yBAAiB,EAAE,CAAC;AACpC,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,oBAAoB,CAClC,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,SAAS,GAIX,EAAE,CAAC;IACP,MAAM,MAAM,GAAG,wBAAwB,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,wBAAwB,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IAClE,MAAM,GAAG,GAAG,wBAAwB,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IACzE,IAAI,MAAM,KAAK,SAAS;QAAE,SAAS,CAAC,WAAW,GAAG,MAAM,CAAC;IACzD,IAAI,IAAI,KAAK,SAAS;QAAE,SAAS,CAAC,aAAa,GAAG,IAAI,CAAC;IACvD,IAAI,GAAG,KAAK,SAAS;QAAE,SAAS,CAAC,oBAAoB,GAAG,GAAG,CAAC;IAC5D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,kBAAkB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACrE,OAAO,WAAW,CAAC,GAAG,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,oBAAoB,CAAC,GAAW;IAC9C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CACb,2EAA2E;YACzE,+EAA+E;YAC/E,sEAAsE,CACzE,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,aAAa,CAAC,MAAyB,OAAO,CAAC,GAAG;IAChE,OAAO;QACL,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC;QAC/B,WAAW,EAAE,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC3C,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC;QACzB,EAAE,EAAE,YAAY,CAAC,GAAG,CAAC;QACrB,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC;QAC3B,cAAc,EAAE,kBAAkB,CAAC,GAAG,CAAC;QACvC,SAAS,EAAE,oBAAoB,CAAC,GAAG,CAAC;KACrC,CAAC;AACJ,CAAC;AAsBD;;;GAGG;AACH,SAAgB,kBAAkB,CAAC,GAAqB;IACtD,MAAM,oBAAoB,GACxB,GAAG,CAAC,aAAa,CAAC,WAAW;QAC7B,GAAG,CAAC,aAAa,CAAC,eAAe;QACjC,GAAG,CAAC,aAAa,CAAC,iBAAiB,CAAC;IACtC,MAAM,kBAAkB,GAAG,GAAG,CAAC,OAAO,GAAG,oBAAoB,CAAC;IAC9D,MAAM,mBAAmB,GAAG,kBAAkB,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC9D,MAAM,KAAK,GAAG,GAAG,CAAC,cAAc,GAAG,mBAAmB,CAAC;IACvD,OAAO;QACL,oBAAoB;QACpB,kBAAkB;QAClB,mBAAmB;QACnB,KAAK;QACL,YAAY,EAAE,mBAAmB,IAAI,GAAG,CAAC,cAAc;QACvD,sBAAsB,EAAE,GAAG,CAAC,aAAa,CAAC,iBAAiB,IAAI,gCAAwB;KACxF,CAAC;AACJ,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.module.js","sourceRoot":"","sources":["../../src/config/config.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,mDAAgE;AAEhE,mDAAmD;AACtC,QAAA,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;AAE/C;;;;;;GAMG;AAWI,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IAVxB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,kBAAU;gBACnB,UAAU,EAAE,GAAc,EAAE,CAAC,IAAA,6BAAa,GAAE;aAC7C;SACF;QACD,OAAO,EAAE,CAAC,kBAAU,CAAC;KACtB,CAAC;GACW,YAAY,CAAG"}
@@ -4,22 +4,40 @@
4
4
  * Every HTTP handler in Shield must carry a classification. The global
5
5
  * enforcement guard (see {@link GlobalEnforcementGuard}) reads these keys and,
6
6
  * for any route WITHOUT one, denies the request (deny-by-default). This is the
7
- * structurally-unavoidable, fail-closed posture ADR-0002 mandates — present from
8
- * day one, even though the Scaffold stub does not yet consult a real decision
9
- * authority.
7
+ * structurally-unavoidable, fail-closed posture ADR-0002 mandates.
8
+ *
9
+ * The `@Classified(...)` value now carries the {@link Classification} the guard
10
+ * maps to a `decide(principal, action, resource, context)` call (the request-auth
11
+ * layer): `{ typeKey, action }`. A bare string is still accepted (deprecated
12
+ * back-compat) and treated as `typeKey` with a default `action` of `'access'`.
10
13
  */
11
14
  export declare const CLASSIFICATION_METADATA: {
12
15
  /** Marks a route as an explicit, code-reviewed public opt-out. */
13
16
  readonly PUBLIC: "shield:classification:public";
14
17
  /**
15
- * Marks a route as classified against a permission/tier. In the Scaffold this
16
- * is a placeholder value only — a LATER layer (ADR-0004 tiers, ADR-0012
17
- * authoring) resolves it to a real decision. Its mere PRESENCE is what the
18
- * stub guard checks: a classified route is "declared", an undeclared route is
19
- * denied.
18
+ * Marks a route as classified. The value is a {@link Classification}
19
+ * (`{ typeKey, action }`) the guard uses to build the `decide()` request; a bare
20
+ * string is accepted for back-compat (→ `{ typeKey: <string>, action: 'access' }`).
20
21
  */
21
22
  readonly CLASSIFIED: "shield:classification:classified";
22
23
  };
24
+ /**
25
+ * What a `@Classified` route gates on — the object-type key + the action the
26
+ * caller is attempting. The guard maps this to the Core `decide()` inputs:
27
+ * `Action = { key: action }`, `Resource = { level: 'object_type', typeKey,
28
+ * tenantId: <principal's tenant> }`. Stable keys (never route strings), matching
29
+ * the Core metadata contract (ADR-0004/ADR-0012).
30
+ */
31
+ export interface Classification {
32
+ /** Stable object-type key the action operates on (e.g. `'company'`). */
33
+ readonly typeKey: string;
34
+ /** Stable action key (e.g. `'read'`, `'create'`, `'update'`, `'delete'`). */
35
+ readonly action: string;
36
+ }
37
+ /** Default action when a bare-string `@Classified('typeKey')` is used (back-compat). */
38
+ export declare const DEFAULT_CLASSIFIED_ACTION = "access";
39
+ /** Normalize either form of the decorator value to a {@link Classification}. */
40
+ export declare function normalizeClassification(value: string | Classification | undefined): Classification | null;
23
41
  /**
24
42
  * `@Public()` — the explicit, deny-by-default opt-OUT (ADR-0002).
25
43
  *
@@ -30,12 +48,13 @@ export declare const CLASSIFICATION_METADATA: {
30
48
  */
31
49
  export declare const Public: () => MethodDecorator & ClassDecorator;
32
50
  /**
33
- * `@Classified(...)` — declare a route's permission/tier classification.
51
+ * `@Classified({ typeKey, action })` — declare what a route gates on.
34
52
  *
35
- * SCAFFOLD STUB: this only records that the route was classified; it does not
36
- * yet gate on a real permission. A later authorization layer replaces the stub
37
- * with an actual decision. Present here so business routes can be authored with
38
- * the final API shape from the start.
53
+ * The global enforcement guard reconstructs the caller's `Principal` from the
54
+ * request's bearer token, builds `Action`/`Resource` from this classification, and
55
+ * calls the Core `decide()` — allowing the request iff the decision is `allow`
56
+ * (deny-by-default otherwise). A bare string (`@Classified('company')`) is accepted
57
+ * for back-compat (→ action `'access'`), but the structured form is preferred.
39
58
  */
40
- export declare const Classified: (classification: string) => MethodDecorator & ClassDecorator;
59
+ export declare const Classified: (classification: string | Classification) => MethodDecorator & ClassDecorator;
41
60
  //# sourceMappingURL=classification.decorator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"classification.decorator.d.ts","sourceRoot":"","sources":["../../src/enforcement/classification.decorator.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB;IAClC,kEAAkE;;IAElE;;;;;;OAMG;;CAEK,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,QAAO,eAAe,GAAG,cACO,CAAC;AAEpD;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,gBAAgB,MAAM,KAAG,eAAe,GAAG,cACL,CAAC"}
1
+ {"version":3,"file":"classification.decorator.d.ts","sourceRoot":"","sources":["../../src/enforcement/classification.decorator.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB;IAClC,kEAAkE;;IAElE;;;;OAIG;;CAEK,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,wFAAwF;AACxF,eAAO,MAAM,yBAAyB,WAAW,CAAC;AAElD,gFAAgF;AAChF,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,GACzC,cAAc,GAAG,IAAI,CAcvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,QAAO,eAAe,GAAG,cACO,CAAC;AAEpD;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GACrB,gBAAgB,MAAM,GAAG,cAAc,KACtC,eAAe,GAAG,cAC4C,CAAC"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Classified = exports.Public = exports.CLASSIFICATION_METADATA = void 0;
3
+ exports.Classified = exports.Public = exports.DEFAULT_CLASSIFIED_ACTION = exports.CLASSIFICATION_METADATA = void 0;
4
+ exports.normalizeClassification = normalizeClassification;
4
5
  const common_1 = require("@nestjs/common");
5
6
  /**
6
7
  * Route-classification metadata keys (ADR-0002).
@@ -8,22 +9,40 @@ const common_1 = require("@nestjs/common");
8
9
  * Every HTTP handler in Shield must carry a classification. The global
9
10
  * enforcement guard (see {@link GlobalEnforcementGuard}) reads these keys and,
10
11
  * for any route WITHOUT one, denies the request (deny-by-default). This is the
11
- * structurally-unavoidable, fail-closed posture ADR-0002 mandates — present from
12
- * day one, even though the Scaffold stub does not yet consult a real decision
13
- * authority.
12
+ * structurally-unavoidable, fail-closed posture ADR-0002 mandates.
13
+ *
14
+ * The `@Classified(...)` value now carries the {@link Classification} the guard
15
+ * maps to a `decide(principal, action, resource, context)` call (the request-auth
16
+ * layer): `{ typeKey, action }`. A bare string is still accepted (deprecated
17
+ * back-compat) and treated as `typeKey` with a default `action` of `'access'`.
14
18
  */
15
19
  exports.CLASSIFICATION_METADATA = {
16
20
  /** Marks a route as an explicit, code-reviewed public opt-out. */
17
21
  PUBLIC: 'shield:classification:public',
18
22
  /**
19
- * Marks a route as classified against a permission/tier. In the Scaffold this
20
- * is a placeholder value only — a LATER layer (ADR-0004 tiers, ADR-0012
21
- * authoring) resolves it to a real decision. Its mere PRESENCE is what the
22
- * stub guard checks: a classified route is "declared", an undeclared route is
23
- * denied.
23
+ * Marks a route as classified. The value is a {@link Classification}
24
+ * (`{ typeKey, action }`) the guard uses to build the `decide()` request; a bare
25
+ * string is accepted for back-compat (→ `{ typeKey: <string>, action: 'access' }`).
24
26
  */
25
27
  CLASSIFIED: 'shield:classification:classified',
26
28
  };
29
+ /** Default action when a bare-string `@Classified('typeKey')` is used (back-compat). */
30
+ exports.DEFAULT_CLASSIFIED_ACTION = 'access';
31
+ /** Normalize either form of the decorator value to a {@link Classification}. */
32
+ function normalizeClassification(value) {
33
+ if (value === undefined)
34
+ return null;
35
+ if (typeof value === 'string') {
36
+ return value.length > 0 ? { typeKey: value, action: exports.DEFAULT_CLASSIFIED_ACTION } : null;
37
+ }
38
+ if (typeof value.typeKey === 'string' &&
39
+ value.typeKey.length > 0 &&
40
+ typeof value.action === 'string' &&
41
+ value.action.length > 0) {
42
+ return { typeKey: value.typeKey, action: value.action };
43
+ }
44
+ return null;
45
+ }
27
46
  /**
28
47
  * `@Public()` — the explicit, deny-by-default opt-OUT (ADR-0002).
29
48
  *
@@ -35,12 +54,13 @@ exports.CLASSIFICATION_METADATA = {
35
54
  const Public = () => (0, common_1.SetMetadata)(exports.CLASSIFICATION_METADATA.PUBLIC, true);
36
55
  exports.Public = Public;
37
56
  /**
38
- * `@Classified(...)` — declare a route's permission/tier classification.
57
+ * `@Classified({ typeKey, action })` — declare what a route gates on.
39
58
  *
40
- * SCAFFOLD STUB: this only records that the route was classified; it does not
41
- * yet gate on a real permission. A later authorization layer replaces the stub
42
- * with an actual decision. Present here so business routes can be authored with
43
- * the final API shape from the start.
59
+ * The global enforcement guard reconstructs the caller's `Principal` from the
60
+ * request's bearer token, builds `Action`/`Resource` from this classification, and
61
+ * calls the Core `decide()` — allowing the request iff the decision is `allow`
62
+ * (deny-by-default otherwise). A bare string (`@Classified('company')`) is accepted
63
+ * for back-compat (→ action `'access'`), but the structured form is preferred.
44
64
  */
45
65
  const Classified = (classification) => (0, common_1.SetMetadata)(exports.CLASSIFICATION_METADATA.CLASSIFIED, classification);
46
66
  exports.Classified = Classified;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classification.decorator.js","sourceRoot":"","sources":["../../src/enforcement/classification.decorator.ts"],"names":[],"mappings":";;;AA4CA,0DAgBC;AA5DD,2CAA6C;AAE7C;;;;;;;;;;;;GAYG;AACU,QAAA,uBAAuB,GAAG;IACrC,kEAAkE;IAClE,MAAM,EAAE,8BAA8B;IACtC;;;;OAIG;IACH,UAAU,EAAE,kCAAkC;CACtC,CAAC;AAgBX,wFAAwF;AAC3E,QAAA,yBAAyB,GAAG,QAAQ,CAAC;AAElD,gFAAgF;AAChF,SAAgB,uBAAuB,CACrC,KAA0C;IAE1C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,iCAAyB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACzF,CAAC;IACD,IACE,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QACjC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;QACxB,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;QAChC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EACvB,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACI,MAAM,MAAM,GAAG,GAAqC,EAAE,CAC3D,IAAA,oBAAW,EAAC,+BAAuB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AADvC,QAAA,MAAM,UACiC;AAEpD;;;;;;;;GAQG;AACI,MAAM,UAAU,GAAG,CACxB,cAAuC,EACL,EAAE,CACpC,IAAA,oBAAW,EAAC,+BAAuB,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;AAHrD,QAAA,UAAU,cAG2C"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core-seam.js","sourceRoot":"","sources":["../../src/enforcement/core-seam.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAqBH;;;;;;;;;;;GAWG;AACU,QAAA,qBAAqB,GAA0B;IAC1D,MAAM;QACJ,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IACxE,CAAC;CACF,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforcement.module.js","sourceRoot":"","sources":["../../src/enforcement/enforcement.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,uCAAyC;AACzC,+EAAuE;AAEvE;;;;;;;;;;;;;;GAcG;AASI,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAAG,CAAA;AAApB,8CAAiB;4BAAjB,iBAAiB;IAR7B,IAAA,eAAM,EAAC;QACN,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,gBAAS;gBAClB,QAAQ,EAAE,oDAAsB;aACjC;SACF;KACF,CAAC;GACW,iBAAiB,CAAG"}
@@ -1,31 +1,41 @@
1
1
  import { CanActivate, ExecutionContext } from '@nestjs/common';
2
2
  import { Reflector } from '@nestjs/core';
3
+ import type { CoreDecisionAuthority } from './core-seam.js';
4
+ import type { SessionTokenBinder } from '../request-auth/session-token-binder.js';
3
5
  /**
4
- * Global, fail-closed enforcement guard (ADR-0002).
6
+ * Global, fail-closed enforcement guard (ADR-0002) — now with REAL request
7
+ * authorization for classified routes.
5
8
  *
6
9
  * This guard is the SINGLE, structurally-unavoidable point every request passes
7
- * through. It is bound as an `APP_GUARD` through the ONE registration seam,
8
- * `ShieldModule.forRoot()` (which composes the internal `EnforcementModule` that
9
- * holds the sole binding) — NOT opt-in per controller — so no endpoint can escape
10
- * it. Its posture is **deny-by-default**:
10
+ * through (bound as the sole `APP_GUARD` via `ShieldModule.forRoot()`). Posture is
11
+ * **deny-by-default**:
11
12
  *
12
- * - A route marked `@Public()` → ALLOWED (explicit, code-reviewed opt-out).
13
- * - A route marked `@Classified()` → allowed to PROCEED in the Scaffold stub
14
- * (a LATER layer will consult the real Core `DecisionAuthority` here; see
15
- * {@link ../enforcement/core-seam.ts}). Its presence proves the route was
16
- * declared.
17
- * - A route with NO classification → DENIED (403). A forgotten declaration
18
- * locks the door instead of opening it.
13
+ * - `@Public()` → ALLOWED (explicit, code-reviewed opt-out).
14
+ * - `@Classified({typeKey, action})` → the request-auth path runs:
15
+ * 1. extract the bearer access token from `Authorization`,
16
+ * 2. resolve it to a `Principal` (via the {@link SessionTokenBinder}),
17
+ * 3. build `Action`/`Resource` from the classification,
18
+ * 4. call the Core `decide()` — ALLOW iff `effect === 'allow'`, else 403.
19
+ * No/invalid token → 401 (fail-closed). This replaces the old scaffold stub
20
+ * that let classified routes through unconditionally.
21
+ * - No classification → DENIED (403). A forgotten declaration locks the door.
19
22
  *
20
- * SCAFFOLD SCOPE: this does not yet perform real authorization. It only proves
21
- * the fail-closed, globally-unavoidable, deny-by-default seam exists from day
22
- * one. When the Authorization layer lands, the `@Classified` branch calls the
23
- * Core decision authority (wired via the core-seam) — the deny-by-default
24
- * structure here does not change.
23
+ * OPT-IN / GRACEFUL DEGRADE: the request-auth dependencies (the token binder) exist
24
+ * only when `auth: true`. When they are ABSENT (enforcement-only, no-`.env` boot),
25
+ * the guard cannot authenticate a request — so a classified route with the
26
+ * request-auth path unavailable is DENIED (403), never allowed-through. This keeps
27
+ * the enforcement-only app booting AND fail-closed: an app that declares classified
28
+ * routes but hasn't enabled auth gets a clear deny, not a silent open door. (The
29
+ * decision authority defaults to the deny-by-default stub, so even a wired-but-
30
+ * unconfigured authority denies.)
25
31
  */
26
32
  export declare class GlobalEnforcementGuard implements CanActivate {
27
33
  private readonly reflector;
28
- constructor(reflector: Reflector);
29
- canActivate(context: ExecutionContext): boolean;
34
+ private readonly decisionAuthority?;
35
+ private readonly tokenBinder?;
36
+ constructor(reflector: Reflector, decisionAuthority?: CoreDecisionAuthority | undefined, tokenBinder?: SessionTokenBinder | undefined);
37
+ canActivate(context: ExecutionContext): Promise<boolean>;
38
+ /** The real authorization path for a classified route (fail-closed throughout). */
39
+ private authorizeClassified;
30
40
  }
31
41
  //# sourceMappingURL=global-enforcement.guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"global-enforcement.guard.d.ts","sourceRoot":"","sources":["../../src/enforcement/global-enforcement.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAkC,MAAM,gBAAgB,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBACa,sBAAuB,YAAW,WAAW;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,SAAS;IAEjD,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO;CAsChD"}
1
+ {"version":3,"file":"global-enforcement.guard.d.ts","sourceRoot":"","sources":["../../src/enforcement/global-enforcement.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,gBAAgB,EAMjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAOzC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAQlF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBACa,sBAAuB,YAAW,WAAW;IAEtD,OAAO,CAAC,QAAQ,CAAC,SAAS;IAG1B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;IAGnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;gBANZ,SAAS,EAAE,SAAS,EAGpB,iBAAiB,CAAC,EAAE,qBAAqB,YAAA,EAGzC,WAAW,CAAC,EAAE,kBAAkB,YAAA;IAG7C,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAuC9D,mFAAmF;YACrE,mBAAmB;CA0FlC"}
@@ -8,40 +8,56 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
8
8
  var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
10
  };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
15
  exports.GlobalEnforcementGuard = void 0;
13
16
  const common_1 = require("@nestjs/common");
14
17
  const core_1 = require("@nestjs/core");
15
18
  const classification_decorator_js_1 = require("./classification.decorator.js");
19
+ const shield_tokens_js_1 = require("../module/shield-tokens.js");
20
+ const tokens_js_1 = require("../request-auth/tokens.js");
21
+ const request_principal_js_1 = require("../request-auth/request-principal.js");
22
+ const session_facts_als_js_1 = require("../request-auth/session-facts-als.js");
23
+ const reenroll_allowlist_js_1 = require("./reenroll-allowlist.js");
16
24
  /**
17
- * Global, fail-closed enforcement guard (ADR-0002).
25
+ * Global, fail-closed enforcement guard (ADR-0002) — now with REAL request
26
+ * authorization for classified routes.
18
27
  *
19
28
  * This guard is the SINGLE, structurally-unavoidable point every request passes
20
- * through. It is bound as an `APP_GUARD` through the ONE registration seam,
21
- * `ShieldModule.forRoot()` (which composes the internal `EnforcementModule` that
22
- * holds the sole binding) — NOT opt-in per controller — so no endpoint can escape
23
- * it. Its posture is **deny-by-default**:
29
+ * through (bound as the sole `APP_GUARD` via `ShieldModule.forRoot()`). Posture is
30
+ * **deny-by-default**:
24
31
  *
25
- * - A route marked `@Public()` → ALLOWED (explicit, code-reviewed opt-out).
26
- * - A route marked `@Classified()` → allowed to PROCEED in the Scaffold stub
27
- * (a LATER layer will consult the real Core `DecisionAuthority` here; see
28
- * {@link ../enforcement/core-seam.ts}). Its presence proves the route was
29
- * declared.
30
- * - A route with NO classification → DENIED (403). A forgotten declaration
31
- * locks the door instead of opening it.
32
+ * - `@Public()` → ALLOWED (explicit, code-reviewed opt-out).
33
+ * - `@Classified({typeKey, action})` → the request-auth path runs:
34
+ * 1. extract the bearer access token from `Authorization`,
35
+ * 2. resolve it to a `Principal` (via the {@link SessionTokenBinder}),
36
+ * 3. build `Action`/`Resource` from the classification,
37
+ * 4. call the Core `decide()` — ALLOW iff `effect === 'allow'`, else 403.
38
+ * No/invalid token → 401 (fail-closed). This replaces the old scaffold stub
39
+ * that let classified routes through unconditionally.
40
+ * - No classification → DENIED (403). A forgotten declaration locks the door.
32
41
  *
33
- * SCAFFOLD SCOPE: this does not yet perform real authorization. It only proves
34
- * the fail-closed, globally-unavoidable, deny-by-default seam exists from day
35
- * one. When the Authorization layer lands, the `@Classified` branch calls the
36
- * Core decision authority (wired via the core-seam) — the deny-by-default
37
- * structure here does not change.
42
+ * OPT-IN / GRACEFUL DEGRADE: the request-auth dependencies (the token binder) exist
43
+ * only when `auth: true`. When they are ABSENT (enforcement-only, no-`.env` boot),
44
+ * the guard cannot authenticate a request — so a classified route with the
45
+ * request-auth path unavailable is DENIED (403), never allowed-through. This keeps
46
+ * the enforcement-only app booting AND fail-closed: an app that declares classified
47
+ * routes but hasn't enabled auth gets a clear deny, not a silent open door. (The
48
+ * decision authority defaults to the deny-by-default stub, so even a wired-but-
49
+ * unconfigured authority denies.)
38
50
  */
39
51
  let GlobalEnforcementGuard = class GlobalEnforcementGuard {
40
52
  reflector;
41
- constructor(reflector) {
53
+ decisionAuthority;
54
+ tokenBinder;
55
+ constructor(reflector, decisionAuthority, tokenBinder) {
42
56
  this.reflector = reflector;
57
+ this.decisionAuthority = decisionAuthority;
58
+ this.tokenBinder = tokenBinder;
43
59
  }
44
- canActivate(context) {
60
+ async canActivate(context) {
45
61
  // Only HTTP requests are classified here. Non-HTTP contexts (if ever added)
46
62
  // must be handled explicitly rather than silently allowed.
47
63
  if (context.getType() !== 'http') {
@@ -57,20 +73,87 @@ let GlobalEnforcementGuard = class GlobalEnforcementGuard {
57
73
  if (isPublic === true) {
58
74
  return true;
59
75
  }
60
- // 2. Classified route → declared. Scaffold stub lets it proceed (a real
61
- // decision authority is wired in a later layer via the core-seam).
62
- const classification = this.reflector.getAllAndOverride(classification_decorator_js_1.CLASSIFICATION_METADATA.CLASSIFIED, [handler, controller]);
63
- if (typeof classification === 'string' && classification.length > 0) {
64
- return true;
76
+ // 2. Classified route → run the request-auth + decide() path.
77
+ const raw = this.reflector.getAllAndOverride(classification_decorator_js_1.CLASSIFICATION_METADATA.CLASSIFIED, [handler, controller]);
78
+ const classification = (0, classification_decorator_js_1.normalizeClassification)(raw);
79
+ if (classification !== null) {
80
+ return this.authorizeClassified(context, classification);
65
81
  }
66
82
  // 3. No classification at all → DENY (ADR-0002 deny-by-default / fail-closed).
67
83
  throw new common_1.ForbiddenException('Shield enforcement: this route carries no access classification. ' +
68
84
  'It is denied by default (ADR-0002). Mark it @Public() to opt out, or ' +
69
85
  '@Classified(...) to declare its permission.');
70
86
  }
87
+ /** The real authorization path for a classified route (fail-closed throughout). */
88
+ async authorizeClassified(context, classification) {
89
+ // Request-auth is only possible when the token binder is wired (auth: true).
90
+ // Absent → we cannot authenticate the caller → DENY (never allow-through).
91
+ if (this.tokenBinder === undefined || this.decisionAuthority === undefined) {
92
+ throw new common_1.ForbiddenException('Shield enforcement: this route is @Classified but request authentication is not ' +
93
+ "enabled (ShieldModule.forRoot({ auth: true })). It is denied by default.");
94
+ }
95
+ const req = context.switchToHttp().getRequest();
96
+ const authHeader = req?.headers?.['authorization'];
97
+ const resolved = await (0, request_principal_js_1.resolveRequestAuth)(authHeader, this.tokenBinder);
98
+ if (resolved === null) {
99
+ // No/invalid/expired token → not authenticated. 401 (distinct from a deny).
100
+ throw new common_1.UnauthorizedException('Shield enforcement: missing or invalid access token for a classified route.');
101
+ }
102
+ const { principal, claims } = resolved;
103
+ // ENFORCED-RE-ENROLL GATE (spec §4.2): a session established via a recovery-code
104
+ // login is restricted to the re-secure allowlist until a fresh device is confirmed.
105
+ // Checked AFTER authentication, BEFORE decide() — a marked session hitting any route
106
+ // NOT on REENROLL_ALLOWED is 403 `reenrollment_required` (a distinct code the
107
+ // frontend recognizes → forces the re-enroll screen). Fail-closed: this is an
108
+ // allowlist, so an unknown/unclassified route with a marked session also 403s. This
109
+ // runs only on @Classified routes (the @Public re-secure routes self-authenticate
110
+ // and check nothing here). The classified route being gated here is, by definition,
111
+ // NOT on the allowlist (the allowlist is @Public), so a marked session is always
112
+ // denied — the isReenrollAllowed check below is defense-in-depth for a future
113
+ // classified allowlisted route.
114
+ if (claims.mustReenroll === true) {
115
+ const method = req?.method;
116
+ const url = req?.originalUrl ?? req?.url;
117
+ if (!(0, reenroll_allowlist_js_1.isReenrollAllowed)(method, url)) {
118
+ throw new common_1.ForbiddenException({ error: 'reenrollment_required' });
119
+ }
120
+ }
121
+ // Attach the resolved principal so downstream handlers can act as this user.
122
+ req.principal = principal;
123
+ // ── ADR-0011 SESSION-FACTS HOT PATH ──────────────────────────────────────────
124
+ // Enter the per-request session-facts ALS scope with THIS request's cached grants
125
+ // (from the binding — same bearer token → same principal → same facts) around the
126
+ // decide() call. The app-owned `SessionFactsReader` (an ALS-backed reader wired into
127
+ // the decision authority) reads the SAME `SHIELD_SESSION_FACTS_ALS` store inside
128
+ // decide(), so gate 5 gets these facts and can serve the coarse decision from cache
129
+ // (no DB reload) when the cached version is still fresh.
130
+ //
131
+ // CROSS-PRINCIPAL ISOLATION: `als.run(store, cb)` binds `store` to this async
132
+ // context and everything decide() awaits, and ONLY that context — a concurrent
133
+ // request runs its own `run()` with its own store, so getStore() never returns
134
+ // another request's facts. The store is built HERE from THIS request's claims.
135
+ //
136
+ // FAIL-CLOSED: no projector wired / absent / malformed binding → `claims.cachedFacts`
137
+ // is undefined → the store carries `cachedFacts: null` → gate 5 DB-reloads (today's
138
+ // behavior). The cache is only ever consulted; it never fabricates a permit.
139
+ const factsStore = {
140
+ cachedFacts: claims.cachedFacts ?? null,
141
+ permissionsVersion: claims.cachedFacts?.permissionsVersion ?? 0,
142
+ };
143
+ const decision = await session_facts_als_js_1.SHIELD_SESSION_FACTS_ALS.run(factsStore, () => this.decisionAuthority.decide(principal, { key: classification.action }, { level: 'object_type', typeKey: classification.typeKey, tenantId: principal.tenantId }, { isAuthenticated: true }));
144
+ if (decision.effect === 'allow') {
145
+ return true;
146
+ }
147
+ // Deny (or any non-allow) → 403. Do NOT leak the reason detail to the caller.
148
+ throw new common_1.ForbiddenException(`Shield enforcement: access denied for ${classification.action} on ${classification.typeKey}.`);
149
+ }
71
150
  };
72
151
  exports.GlobalEnforcementGuard = GlobalEnforcementGuard;
73
152
  exports.GlobalEnforcementGuard = GlobalEnforcementGuard = __decorate([
74
153
  (0, common_1.Injectable)(),
75
- __metadata("design:paramtypes", [core_1.Reflector])
154
+ __param(1, (0, common_1.Optional)()),
155
+ __param(1, (0, common_1.Inject)(shield_tokens_js_1.SHIELD_DECISION_AUTHORITY)),
156
+ __param(2, (0, common_1.Optional)()),
157
+ __param(2, (0, common_1.Inject)(tokens_js_1.SHIELD_SESSION_TOKEN_BINDER)),
158
+ __metadata("design:paramtypes", [core_1.Reflector, Object, Function])
76
159
  ], GlobalEnforcementGuard);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-enforcement.guard.js","sourceRoot":"","sources":["../../src/enforcement/global-enforcement.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAQwB;AACxB,uCAAyC;AACzC,+EAIuC;AACvC,iEAAuE;AAEvE,yDAAwE;AAExE,+EAA0E;AAC1E,+EAG8C;AAC9C,mEAA4D;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEI,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IAEd;IAGA;IAGA;IAPnB,YACmB,SAAoB,EAGpB,iBAAyC,EAGzC,WAAgC;QANhC,cAAS,GAAT,SAAS,CAAW;QAGpB,sBAAiB,GAAjB,iBAAiB,CAAwB;QAGzC,gBAAW,GAAX,WAAW,CAAqB;IAChD,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,4EAA4E;QAC5E,2DAA2D;QAC3D,IAAI,OAAO,CAAC,OAAO,EAAE,KAAK,MAAM,EAAE,CAAC;YACjC,MAAM,IAAI,2BAAkB,CAC1B,qFAAqF,CACtF,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAEtC,0EAA0E;QAC1E,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAU,qDAAuB,CAAC,MAAM,EAAE;YACzF,OAAO;YACP,UAAU;SACX,CAAC,CAAC;QACH,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,8DAA8D;QAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAC1C,qDAAuB,CAAC,UAAU,EAClC,CAAC,OAAO,EAAE,UAAU,CAAC,CACtB,CAAC;QACF,MAAM,cAAc,GAAG,IAAA,qDAAuB,EAAC,GAAG,CAAC,CAAC;QACpD,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QAC3D,CAAC;QAED,+EAA+E;QAC/E,MAAM,IAAI,2BAAkB,CAC1B,mEAAmE;YACjE,uEAAuE;YACvE,6CAA6C,CAChD,CAAC;IACJ,CAAC;IAED,mFAAmF;IAC3E,KAAK,CAAC,mBAAmB,CAC/B,OAAyB,EACzB,cAA8B;QAE9B,6EAA6E;QAC7E,2EAA2E;QAC3E,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,IAAI,IAAI,CAAC,iBAAiB,KAAK,SAAS,EAAE,CAAC;YAC3E,MAAM,IAAI,2BAAkB,CAC1B,kFAAkF;gBAChF,0EAA0E,CAC7E,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAMzC,CAAC;QACL,MAAM,UAAU,GAAG,GAAG,EAAE,OAAO,EAAE,CAAC,eAAe,CAAC,CAAC;QAEnD,MAAM,QAAQ,GAAG,MAAM,IAAA,yCAAkB,EAAC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACxE,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,4EAA4E;YAC5E,MAAM,IAAI,8BAAqB,CAC7B,6EAA6E,CAC9E,CAAC;QACJ,CAAC;QACD,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC;QAEvC,iFAAiF;QACjF,oFAAoF;QACpF,qFAAqF;QACrF,8EAA8E;QAC9E,8EAA8E;QAC9E,oFAAoF;QACpF,kFAAkF;QAClF,oFAAoF;QACpF,iFAAiF;QACjF,8EAA8E;QAC9E,gCAAgC;QAChC,IAAI,MAAM,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,GAAG,EAAE,MAAM,CAAC;YAC3B,MAAM,GAAG,GAAG,GAAG,EAAE,WAAW,IAAI,GAAG,EAAE,GAAG,CAAC;YACzC,IAAI,CAAC,IAAA,yCAAiB,EAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;gBACpC,MAAM,IAAI,2BAAkB,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;QAED,6EAA6E;QAC7E,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC;QAE1B,gFAAgF;QAChF,kFAAkF;QAClF,kFAAkF;QAClF,qFAAqF;QACrF,iFAAiF;QACjF,oFAAoF;QACpF,yDAAyD;QACzD,EAAE;QACF,8EAA8E;QAC9E,+EAA+E;QAC/E,+EAA+E;QAC/E,+EAA+E;QAC/E,EAAE;QACF,sFAAsF;QACtF,oFAAoF;QACpF,6EAA6E;QAC7E,MAAM,UAAU,GAAsB;YACpC,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,IAAI;YACvC,kBAAkB,EAAE,MAAM,CAAC,WAAW,EAAE,kBAAkB,IAAI,CAAC;SAChE,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,+CAAwB,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,EAAE,CACnE,IAAI,CAAC,iBAAkB,CAAC,MAAM,CAC5B,SAAS,EACT,EAAE,GAAG,EAAE,cAAc,CAAC,MAAM,EAAE,EAC9B,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,QAAQ,EAAE,EACvF,EAAE,eAAe,EAAE,IAAI,EAAE,CAC1B,CACF,CAAC;QAEF,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,8EAA8E;QAC9E,MAAM,IAAI,2BAAkB,CAC1B,yCAAyC,cAAc,CAAC,MAAM,OAAO,cAAc,CAAC,OAAO,GAAG,CAC/F,CAAC;IACJ,CAAC;CACF,CAAA;AA7IY,wDAAsB;iCAAtB,sBAAsB;IADlC,IAAA,mBAAU,GAAE;IAIR,WAAA,IAAA,iBAAQ,GAAE,CAAA;IACV,WAAA,IAAA,eAAM,EAAC,4CAAyB,CAAC,CAAA;IAEjC,WAAA,IAAA,iBAAQ,GAAE,CAAA;IACV,WAAA,IAAA,eAAM,EAAC,uCAA2B,CAAC,CAAA;qCALR,gBAAS;GAF5B,sBAAsB,CA6IlC"}