@ttt-productions/repo-guards 0.1.0 → 0.1.2

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 (276) hide show
  1. package/dist/config.d.ts +6 -1
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +4 -0
  4. package/dist/config.js.map +1 -1
  5. package/dist/guard.d.ts +7 -1
  6. package/dist/guard.d.ts.map +1 -1
  7. package/dist/guard.js.map +1 -1
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/modules/backend/audit.d.ts +4 -3
  13. package/dist/modules/backend/audit.d.ts.map +1 -1
  14. package/dist/modules/backend/audit.js +9 -12
  15. package/dist/modules/backend/audit.js.map +1 -1
  16. package/dist/modules/backend/auth.d.ts +11 -2
  17. package/dist/modules/backend/auth.d.ts.map +1 -1
  18. package/dist/modules/backend/auth.js +69 -13
  19. package/dist/modules/backend/auth.js.map +1 -1
  20. package/dist/modules/backend/failure-handling.d.ts +5 -1
  21. package/dist/modules/backend/failure-handling.d.ts.map +1 -1
  22. package/dist/modules/backend/failure-handling.js +31 -12
  23. package/dist/modules/backend/failure-handling.js.map +1 -1
  24. package/dist/modules/backend/functions-build.d.ts +4 -4
  25. package/dist/modules/backend/functions-build.d.ts.map +1 -1
  26. package/dist/modules/backend/functions-build.js +15 -23
  27. package/dist/modules/backend/functions-build.js.map +1 -1
  28. package/dist/modules/backend/index.d.ts +1 -1
  29. package/dist/modules/backend/index.d.ts.map +1 -1
  30. package/dist/modules/backend/index.js.map +1 -1
  31. package/dist/modules/backend/monitoring.d.ts +2 -2
  32. package/dist/modules/backend/monitoring.d.ts.map +1 -1
  33. package/dist/modules/backend/monitoring.js +13 -25
  34. package/dist/modules/backend/monitoring.js.map +1 -1
  35. package/dist/modules/backend/shared.d.ts +0 -30
  36. package/dist/modules/backend/shared.d.ts.map +1 -1
  37. package/dist/modules/backend/shared.js +7 -97
  38. package/dist/modules/backend/shared.js.map +1 -1
  39. package/dist/modules/core-definitions/contracts.d.ts +4 -3
  40. package/dist/modules/core-definitions/contracts.d.ts.map +1 -1
  41. package/dist/modules/core-definitions/contracts.js +95 -23
  42. package/dist/modules/core-definitions/contracts.js.map +1 -1
  43. package/dist/modules/core-definitions/document-id-segment.d.ts +1 -1
  44. package/dist/modules/core-definitions/document-id-segment.d.ts.map +1 -1
  45. package/dist/modules/core-definitions/document-id-segment.js +10 -10
  46. package/dist/modules/core-definitions/document-id-segment.js.map +1 -1
  47. package/dist/modules/core-definitions/redeclaration.d.ts.map +1 -1
  48. package/dist/modules/core-definitions/redeclaration.js +15 -22
  49. package/dist/modules/core-definitions/redeclaration.js.map +1 -1
  50. package/dist/modules/core-definitions/schema-length-literal.d.ts.map +1 -1
  51. package/dist/modules/core-definitions/schema-length-literal.js +11 -7
  52. package/dist/modules/core-definitions/schema-length-literal.js.map +1 -1
  53. package/dist/modules/core-definitions/shared.d.ts +0 -13
  54. package/dist/modules/core-definitions/shared.d.ts.map +1 -1
  55. package/dist/modules/core-definitions/shared.js +2 -42
  56. package/dist/modules/core-definitions/shared.js.map +1 -1
  57. package/dist/modules/core-definitions/singleton-bucket-audience.d.ts +3 -5
  58. package/dist/modules/core-definitions/singleton-bucket-audience.d.ts.map +1 -1
  59. package/dist/modules/core-definitions/singleton-bucket-audience.js +6 -5
  60. package/dist/modules/core-definitions/singleton-bucket-audience.js.map +1 -1
  61. package/dist/modules/core-definitions/text-field-declarations.d.ts +4 -4
  62. package/dist/modules/core-definitions/text-field-declarations.d.ts.map +1 -1
  63. package/dist/modules/core-definitions/text-field-declarations.js +17 -14
  64. package/dist/modules/core-definitions/text-field-declarations.js.map +1 -1
  65. package/dist/modules/firestore/analyzer.d.ts +6 -6
  66. package/dist/modules/firestore/analyzer.d.ts.map +1 -1
  67. package/dist/modules/firestore/analyzer.js +82 -121
  68. package/dist/modules/firestore/analyzer.js.map +1 -1
  69. package/dist/modules/firestore/client-guards.d.ts +3 -8
  70. package/dist/modules/firestore/client-guards.d.ts.map +1 -1
  71. package/dist/modules/firestore/client-guards.js +34 -39
  72. package/dist/modules/firestore/client-guards.js.map +1 -1
  73. package/dist/modules/firestore/guard.d.ts +4 -5
  74. package/dist/modules/firestore/guard.d.ts.map +1 -1
  75. package/dist/modules/firestore/guard.js +16 -23
  76. package/dist/modules/firestore/guard.js.map +1 -1
  77. package/dist/modules/firestore/index-guard.d.ts.map +1 -1
  78. package/dist/modules/firestore/index-guard.js +12 -8
  79. package/dist/modules/firestore/index-guard.js.map +1 -1
  80. package/dist/modules/firestore/index.js.map +1 -1
  81. package/dist/modules/firestore/path-guards.d.ts +2 -4
  82. package/dist/modules/firestore/path-guards.d.ts.map +1 -1
  83. package/dist/modules/firestore/path-guards.js +20 -17
  84. package/dist/modules/firestore/path-guards.js.map +1 -1
  85. package/dist/modules/firestore/rules-claim-guard.d.ts +9 -11
  86. package/dist/modules/firestore/rules-claim-guard.d.ts.map +1 -1
  87. package/dist/modules/firestore/rules-claim-guard.js +29 -49
  88. package/dist/modules/firestore/rules-claim-guard.js.map +1 -1
  89. package/dist/modules/firestore/rules-mirror-guard.d.ts +7 -8
  90. package/dist/modules/firestore/rules-mirror-guard.d.ts.map +1 -1
  91. package/dist/modules/firestore/rules-mirror-guard.js +20 -30
  92. package/dist/modules/firestore/rules-mirror-guard.js.map +1 -1
  93. package/dist/modules/firestore/rules-syntax.d.ts +1 -3
  94. package/dist/modules/firestore/rules-syntax.d.ts.map +1 -1
  95. package/dist/modules/firestore/rules-syntax.js +2 -2
  96. package/dist/modules/firestore/rules-syntax.js.map +1 -1
  97. package/dist/modules/firestore/server-guards.d.ts +2 -4
  98. package/dist/modules/firestore/server-guards.d.ts.map +1 -1
  99. package/dist/modules/firestore/server-guards.js +12 -8
  100. package/dist/modules/firestore/server-guards.js.map +1 -1
  101. package/dist/modules/firestore/storage-guards.d.ts +4 -6
  102. package/dist/modules/firestore/storage-guards.d.ts.map +1 -1
  103. package/dist/modules/firestore/storage-guards.js +29 -23
  104. package/dist/modules/firestore/storage-guards.js.map +1 -1
  105. package/dist/modules/firestore/support.d.ts +0 -20
  106. package/dist/modules/firestore/support.d.ts.map +1 -1
  107. package/dist/modules/firestore/support.js +4 -75
  108. package/dist/modules/firestore/support.js.map +1 -1
  109. package/dist/modules/firestore/test-paths.js +4 -3
  110. package/dist/modules/firestore/test-paths.js.map +1 -1
  111. package/dist/modules/frontend-actions/auth.d.ts +10 -5
  112. package/dist/modules/frontend-actions/auth.d.ts.map +1 -1
  113. package/dist/modules/frontend-actions/auth.js +37 -35
  114. package/dist/modules/frontend-actions/auth.js.map +1 -1
  115. package/dist/modules/frontend-actions/callables.d.ts +30 -53
  116. package/dist/modules/frontend-actions/callables.d.ts.map +1 -1
  117. package/dist/modules/frontend-actions/callables.js +225 -264
  118. package/dist/modules/frontend-actions/callables.js.map +1 -1
  119. package/dist/modules/frontend-actions/client.d.ts +1 -1
  120. package/dist/modules/frontend-actions/client.d.ts.map +1 -1
  121. package/dist/modules/frontend-actions/client.js +13 -22
  122. package/dist/modules/frontend-actions/client.js.map +1 -1
  123. package/dist/modules/frontend-actions/declarations.d.ts +34 -0
  124. package/dist/modules/frontend-actions/declarations.d.ts.map +1 -0
  125. package/dist/modules/frontend-actions/declarations.js +103 -0
  126. package/dist/modules/frontend-actions/declarations.js.map +1 -0
  127. package/dist/modules/frontend-actions/index.d.ts +2 -1
  128. package/dist/modules/frontend-actions/index.d.ts.map +1 -1
  129. package/dist/modules/frontend-actions/index.js +2 -1
  130. package/dist/modules/frontend-actions/index.js.map +1 -1
  131. package/dist/modules/frontend-actions/text.d.ts +4 -34
  132. package/dist/modules/frontend-actions/text.d.ts.map +1 -1
  133. package/dist/modules/frontend-actions/text.js +8 -68
  134. package/dist/modules/frontend-actions/text.js.map +1 -1
  135. package/dist/modules/frontend-reads/cache.d.ts +2 -6
  136. package/dist/modules/frontend-reads/cache.d.ts.map +1 -1
  137. package/dist/modules/frontend-reads/cache.js +49 -53
  138. package/dist/modules/frontend-reads/cache.js.map +1 -1
  139. package/dist/modules/frontend-reads/query-keys.d.ts.map +1 -1
  140. package/dist/modules/frontend-reads/query-keys.js +19 -10
  141. package/dist/modules/frontend-reads/query-keys.js.map +1 -1
  142. package/dist/modules/frontend-reads/read-failures.d.ts +22 -4
  143. package/dist/modules/frontend-reads/read-failures.d.ts.map +1 -1
  144. package/dist/modules/frontend-reads/read-failures.js +192 -88
  145. package/dist/modules/frontend-reads/read-failures.js.map +1 -1
  146. package/dist/modules/frontend-reads/shared.d.ts +10 -21
  147. package/dist/modules/frontend-reads/shared.d.ts.map +1 -1
  148. package/dist/modules/frontend-reads/shared.js +31 -88
  149. package/dist/modules/frontend-reads/shared.js.map +1 -1
  150. package/dist/modules/import-paths/analyzer.d.ts.map +1 -1
  151. package/dist/modules/import-paths/analyzer.js +60 -112
  152. package/dist/modules/import-paths/analyzer.js.map +1 -1
  153. package/dist/modules/import-paths/guard.d.ts.map +1 -1
  154. package/dist/modules/import-paths/guard.js +3 -12
  155. package/dist/modules/import-paths/guard.js.map +1 -1
  156. package/dist/modules/repo/ci-parity.d.ts.map +1 -1
  157. package/dist/modules/repo/ci-parity.js +8 -7
  158. package/dist/modules/repo/ci-parity.js.map +1 -1
  159. package/dist/modules/repo/cross-root.d.ts +2 -5
  160. package/dist/modules/repo/cross-root.d.ts.map +1 -1
  161. package/dist/modules/repo/cross-root.js +33 -58
  162. package/dist/modules/repo/cross-root.js.map +1 -1
  163. package/dist/modules/repo/dependency-overrides.d.ts.map +1 -1
  164. package/dist/modules/repo/dependency-overrides.js +15 -11
  165. package/dist/modules/repo/dependency-overrides.js.map +1 -1
  166. package/dist/modules/repo/document-drift.d.ts.map +1 -1
  167. package/dist/modules/repo/document-drift.js +17 -13
  168. package/dist/modules/repo/document-drift.js.map +1 -1
  169. package/dist/modules/repo/duplicate-exports.d.ts.map +1 -1
  170. package/dist/modules/repo/duplicate-exports.js +9 -11
  171. package/dist/modules/repo/duplicate-exports.js.map +1 -1
  172. package/dist/modules/repo/env-public-values.d.ts.map +1 -1
  173. package/dist/modules/repo/env-public-values.js +3 -16
  174. package/dist/modules/repo/env-public-values.js.map +1 -1
  175. package/dist/modules/repo/installed-packages-agnostic.d.ts.map +1 -1
  176. package/dist/modules/repo/installed-packages-agnostic.js +14 -20
  177. package/dist/modules/repo/installed-packages-agnostic.js.map +1 -1
  178. package/dist/modules/repo/local-only-variables.d.ts.map +1 -1
  179. package/dist/modules/repo/local-only-variables.js +30 -38
  180. package/dist/modules/repo/local-only-variables.js.map +1 -1
  181. package/dist/modules/repo/project-scripts.d.ts.map +1 -1
  182. package/dist/modules/repo/project-scripts.js +7 -12
  183. package/dist/modules/repo/project-scripts.js.map +1 -1
  184. package/dist/modules/repo/secret-bindings.d.ts +3 -5
  185. package/dist/modules/repo/secret-bindings.d.ts.map +1 -1
  186. package/dist/modules/repo/secret-bindings.js +64 -140
  187. package/dist/modules/repo/secret-bindings.js.map +1 -1
  188. package/dist/modules/repo/shell-safe.d.ts +3 -3
  189. package/dist/modules/repo/shell-safe.d.ts.map +1 -1
  190. package/dist/modules/repo/shell-safe.js +33 -88
  191. package/dist/modules/repo/shell-safe.js.map +1 -1
  192. package/dist/modules/repo/specifiers.d.ts +0 -6
  193. package/dist/modules/repo/specifiers.d.ts.map +1 -1
  194. package/dist/modules/repo/specifiers.js +9 -73
  195. package/dist/modules/repo/specifiers.js.map +1 -1
  196. package/dist/modules/repo/test-source-root.d.ts.map +1 -1
  197. package/dist/modules/repo/test-source-root.js +3 -5
  198. package/dist/modules/repo/test-source-root.js.map +1 -1
  199. package/dist/modules/repo/walker-read-failure.d.ts.map +1 -1
  200. package/dist/modules/repo/walker-read-failure.js +2 -5
  201. package/dist/modules/repo/walker-read-failure.js.map +1 -1
  202. package/dist/modules/repo/worker-bounded-body.d.ts.map +1 -1
  203. package/dist/modules/repo/worker-bounded-body.js +13 -23
  204. package/dist/modules/repo/worker-bounded-body.js.map +1 -1
  205. package/dist/modules/repo/worker-config.d.ts.map +1 -1
  206. package/dist/modules/repo/worker-config.js +15 -21
  207. package/dist/modules/repo/worker-config.js.map +1 -1
  208. package/dist/modules/ui/cascade.d.ts.map +1 -1
  209. package/dist/modules/ui/cascade.js +19 -19
  210. package/dist/modules/ui/cascade.js.map +1 -1
  211. package/dist/modules/ui/definitions.d.ts.map +1 -1
  212. package/dist/modules/ui/definitions.js +9 -9
  213. package/dist/modules/ui/definitions.js.map +1 -1
  214. package/dist/modules/ui/facts.d.ts +0 -10
  215. package/dist/modules/ui/facts.d.ts.map +1 -1
  216. package/dist/modules/ui/facts.js +6 -50
  217. package/dist/modules/ui/facts.js.map +1 -1
  218. package/dist/modules/ui/interaction.d.ts.map +1 -1
  219. package/dist/modules/ui/interaction.js +49 -25
  220. package/dist/modules/ui/interaction.js.map +1 -1
  221. package/dist/modules/ui/motion.d.ts.map +1 -1
  222. package/dist/modules/ui/motion.js +5 -4
  223. package/dist/modules/ui/motion.js.map +1 -1
  224. package/dist/modules/ui/source.d.ts +0 -7
  225. package/dist/modules/ui/source.d.ts.map +1 -1
  226. package/dist/modules/ui/source.js +4 -26
  227. package/dist/modules/ui/source.js.map +1 -1
  228. package/dist/modules/ui/stylesheets.d.ts.map +1 -1
  229. package/dist/modules/ui/stylesheets.js +3 -4
  230. package/dist/modules/ui/stylesheets.js.map +1 -1
  231. package/dist/modules/ui/styling.js +9 -9
  232. package/dist/modules/ui/styling.js.map +1 -1
  233. package/dist/reader/layout.d.ts +23 -4
  234. package/dist/reader/layout.d.ts.map +1 -1
  235. package/dist/reader/layout.js +19 -1
  236. package/dist/reader/layout.js.map +1 -1
  237. package/dist/reader/reader.d.ts +7 -2
  238. package/dist/reader/reader.d.ts.map +1 -1
  239. package/dist/reader/reader.js +113 -44
  240. package/dist/reader/reader.js.map +1 -1
  241. package/dist/reader/view.d.ts +15 -3
  242. package/dist/reader/view.d.ts.map +1 -1
  243. package/dist/reader/view.js +4 -0
  244. package/dist/reader/view.js.map +1 -1
  245. package/dist/shapes/shapes.d.ts +2 -3
  246. package/dist/shapes/shapes.d.ts.map +1 -1
  247. package/dist/shapes/shapes.js +4 -20
  248. package/dist/shapes/shapes.js.map +1 -1
  249. package/dist/suite/index.d.ts +7 -1
  250. package/dist/suite/index.d.ts.map +1 -1
  251. package/dist/suite/index.js +5 -0
  252. package/dist/suite/index.js.map +1 -1
  253. package/dist/suite/run.d.ts.map +1 -1
  254. package/dist/suite/run.js +30 -12
  255. package/dist/suite/run.js.map +1 -1
  256. package/dist/support/ast.d.ts +13 -0
  257. package/dist/support/ast.d.ts.map +1 -0
  258. package/dist/support/ast.js +37 -0
  259. package/dist/support/ast.js.map +1 -0
  260. package/dist/support/facts.d.ts +51 -0
  261. package/dist/support/facts.d.ts.map +1 -0
  262. package/dist/support/facts.js +140 -0
  263. package/dist/support/facts.js.map +1 -0
  264. package/dist/support/imports.d.ts +46 -0
  265. package/dist/support/imports.d.ts.map +1 -0
  266. package/dist/support/imports.js +93 -0
  267. package/dist/support/imports.js.map +1 -0
  268. package/dist/support/paths.d.ts +14 -0
  269. package/dist/support/paths.d.ts.map +1 -0
  270. package/dist/support/paths.js +31 -0
  271. package/dist/support/paths.js.map +1 -0
  272. package/dist/support/text.d.ts +5 -0
  273. package/dist/support/text.d.ts.map +1 -0
  274. package/dist/support/text.js +6 -0
  275. package/dist/support/text.js.map +1 -0
  276. package/package.json +1 -1
package/dist/config.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Guard } from './guard.js';
2
- import type { RepoLayout } from './reader/layout.js';
2
+ import { type RepoLayout, type RootRole } from './reader/layout.js';
3
3
  /** An app's statement that a generic guard does not apply to it, and why. */
4
4
  export interface Absent {
5
5
  absent: string;
@@ -10,6 +10,11 @@ export interface GenericGuardDefinition<Facts> {
10
10
  id: string;
11
11
  /** When an app may declare the guard absent (a feature it does not have), or false when never. */
12
12
  absentWhen: string | false;
13
+ /**
14
+ * The root whose tree the guard checks, when the absence it allows is that tree's: such a guard may
15
+ * be declared absent only when the layout declares no root of this role.
16
+ */
17
+ absentOnlyWithoutRoot?: RootRole;
13
18
  factProblems(facts: Facts): string[];
14
19
  build(facts: Facts): Guard[];
15
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,6EAA6E;AAC7E,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,+FAA+F;AAC/F,MAAM,WAAW,sBAAsB,CAAC,KAAK;IAC3C,2CAA2C;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,kGAAkG;IAClG,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;IAC3B,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC;IACrC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,CAAC;CAC9B;AAED,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACsE,CAAC;AAE1H,qGAAqG;AACrG,wBAAgB,cAAc,CAC5B,MAAM,EAAE;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAAC,QAAQ,EAAE,SAAS,KAAK,EAAE,CAAA;CAAE,EACtG,WAAW,EAAE,aAAa,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,GACxD,MAAM,EAAE,CA+BV;AAED,uFAAuF;AACvF,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC1C,WAAW,EAAE,aAAa,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,GACxD,KAAK,EAAE,CAET"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAiB,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnF,6EAA6E;AAC7E,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,+FAA+F;AAC/F,MAAM,WAAW,sBAAsB,CAAC,KAAK;IAC3C,2CAA2C;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,kGAAkG;IAClG,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;IAC3B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,QAAQ,CAAC;IACjC,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC;IACrC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,CAAC;CAC9B;AAED,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MACsE,CAAC;AAE1H,qGAAqG;AACrG,wBAAgB,cAAc,CAC5B,MAAM,EAAE;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAAC,QAAQ,EAAE,SAAS,KAAK,EAAE,CAAA;CAAE,EACtG,WAAW,EAAE,aAAa,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,GACxD,MAAM,EAAE,CAkCV;AAED,uFAAuF;AACvF,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC1C,WAAW,EAAE,aAAa,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,GACxD,KAAK,EAAE,CAET"}
package/dist/config.js CHANGED
@@ -2,6 +2,7 @@
2
2
  // package carries needs its facts in the config — or, where the guard allows it, an explicit
3
3
  // absence with its why. Nothing is skipped by leaving it out.
4
4
  import { layoutProblems } from './reader/reader.js';
5
+ import { declaredRoots } from './reader/layout.js';
5
6
  export const isAbsent = (value) => !!value && typeof value === 'object' && Object.keys(value).length === 1 && typeof value.absent === 'string';
6
7
  /** Every problem with a config, in words, against the package's generic guards. Empty = runnable. */
7
8
  export function configProblems(config, definitions) {
@@ -25,6 +26,9 @@ export function configProblems(config, definitions) {
25
26
  problems.push(`generic.${definition.id} cannot be declared absent: every app runs it`);
26
27
  else if (!value.absent.trim())
27
28
  problems.push(`generic.${definition.id} is declared absent with no why`);
29
+ else if (definition.absentOnlyWithoutRoot && config.layout?.roots && declaredRoots(config.layout).some((r) => r.role === definition.absentOnlyWithoutRoot)) {
30
+ problems.push(`generic.${definition.id} is declared absent, but the layout declares a ${definition.absentOnlyWithoutRoot} root for it to check`);
31
+ }
28
32
  continue;
29
33
  }
30
34
  for (const problem of definition.factProblems(value))
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["// An app's guard config, and the one check that decides whether it can run. Every generic guard the\n// package carries needs its facts in the config — or, where the guard allows it, an explicit\n// absence with its why. Nothing is skipped by leaving it out.\n\nimport type { Guard } from './guard.js';\nimport { layoutProblems } from './reader/reader.js';\nimport type { RepoLayout } from './reader/layout.js';\n\n/** An app's statement that a generic guard does not apply to it, and why. */\nexport interface Absent {\n absent: string;\n}\n\n/** One generic guard as the package defines it: how to check its facts and build its tests. */\nexport interface GenericGuardDefinition<Facts> {\n /** The guard's key in the app's config. */\n id: string;\n /** When an app may declare the guard absent (a feature it does not have), or false when never. */\n absentWhen: string | false;\n factProblems(facts: Facts): string[];\n build(facts: Facts): Guard[];\n}\n\nexport const isAbsent = (value: unknown): value is Absent =>\n !!value && typeof value === 'object' && Object.keys(value).length === 1 && typeof (value as Absent).absent === 'string';\n\n/** Every problem with a config, in words, against the package's generic guards. Empty = runnable. */\nexport function configProblems(\n config: { layout: RepoLayout; generic: Readonly<Record<string, unknown>>; business: readonly Guard[] },\n definitions: ReadonlyArray<GenericGuardDefinition<never>>,\n): string[] {\n const problems: string[] = [];\n if (!config || typeof config !== 'object') return ['the guard config is missing'];\n problems.push(...layoutProblems(config.layout));\n const generic = config.generic ?? {};\n const known = new Set(definitions.map((d) => d.id));\n for (const key of Object.keys(generic)) if (!known.has(key)) problems.push(`generic.${key} names no guard this package carries`);\n for (const definition of definitions) {\n if (!(definition.id in generic)) {\n problems.push(`generic.${definition.id} is missing — every guard needs its facts${definition.absentWhen ? ' or an { absent: why }' : ''}`);\n continue;\n }\n const value = generic[definition.id];\n if (isAbsent(value)) {\n if (!definition.absentWhen) problems.push(`generic.${definition.id} cannot be declared absent: every app runs it`);\n else if (!value.absent.trim()) problems.push(`generic.${definition.id} is declared absent with no why`);\n continue;\n }\n for (const problem of definition.factProblems(value as never)) problems.push(`generic.${definition.id}: ${problem}`);\n }\n const titles = new Set<string>();\n for (const guard of config.business ?? []) {\n if (!guard || typeof guard.run !== 'function' || !guard.title) {\n problems.push('a business guard was not built from a shape');\n continue;\n }\n if (titles.has(guard.title)) problems.push(`two business guards share the title \"${guard.title}\"`);\n titles.add(guard.title);\n if (!guard.samples) problems.push(`business guard \"${guard.title}\" carries no samples`);\n }\n return problems;\n}\n\n/** The generic guards' tests, built from the app's facts; absent guards build none. */\nexport function buildGenericGuards(\n generic: Readonly<Record<string, unknown>>,\n definitions: ReadonlyArray<GenericGuardDefinition<never>>,\n): Guard[] {\n return definitions.flatMap((d) => (isAbsent(generic[d.id]) ? [] : d.build(generic[d.id] as never)));\n}\n"]}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["// An app's guard config, and the one check that decides whether it can run. Every generic guard the\n// package carries needs its facts in the config — or, where the guard allows it, an explicit\n// absence with its why. Nothing is skipped by leaving it out.\n\nimport type { Guard } from './guard.js';\nimport { layoutProblems } from './reader/reader.js';\nimport { declaredRoots, type RepoLayout, type RootRole } from './reader/layout.js';\n\n/** An app's statement that a generic guard does not apply to it, and why. */\nexport interface Absent {\n absent: string;\n}\n\n/** One generic guard as the package defines it: how to check its facts and build its tests. */\nexport interface GenericGuardDefinition<Facts> {\n /** The guard's key in the app's config. */\n id: string;\n /** When an app may declare the guard absent (a feature it does not have), or false when never. */\n absentWhen: string | false;\n /**\n * The root whose tree the guard checks, when the absence it allows is that tree's: such a guard may\n * be declared absent only when the layout declares no root of this role.\n */\n absentOnlyWithoutRoot?: RootRole;\n factProblems(facts: Facts): string[];\n build(facts: Facts): Guard[];\n}\n\nexport const isAbsent = (value: unknown): value is Absent =>\n !!value && typeof value === 'object' && Object.keys(value).length === 1 && typeof (value as Absent).absent === 'string';\n\n/** Every problem with a config, in words, against the package's generic guards. Empty = runnable. */\nexport function configProblems(\n config: { layout: RepoLayout; generic: Readonly<Record<string, unknown>>; business: readonly Guard[] },\n definitions: ReadonlyArray<GenericGuardDefinition<never>>,\n): string[] {\n const problems: string[] = [];\n if (!config || typeof config !== 'object') return ['the guard config is missing'];\n problems.push(...layoutProblems(config.layout));\n const generic = config.generic ?? {};\n const known = new Set(definitions.map((d) => d.id));\n for (const key of Object.keys(generic)) if (!known.has(key)) problems.push(`generic.${key} names no guard this package carries`);\n for (const definition of definitions) {\n if (!(definition.id in generic)) {\n problems.push(`generic.${definition.id} is missing — every guard needs its facts${definition.absentWhen ? ' or an { absent: why }' : ''}`);\n continue;\n }\n const value = generic[definition.id];\n if (isAbsent(value)) {\n if (!definition.absentWhen) problems.push(`generic.${definition.id} cannot be declared absent: every app runs it`);\n else if (!value.absent.trim()) problems.push(`generic.${definition.id} is declared absent with no why`);\n else if (definition.absentOnlyWithoutRoot && config.layout?.roots && declaredRoots(config.layout).some((r) => r.role === definition.absentOnlyWithoutRoot)) {\n problems.push(`generic.${definition.id} is declared absent, but the layout declares a ${definition.absentOnlyWithoutRoot} root for it to check`);\n }\n continue;\n }\n for (const problem of definition.factProblems(value as never)) problems.push(`generic.${definition.id}: ${problem}`);\n }\n const titles = new Set<string>();\n for (const guard of config.business ?? []) {\n if (!guard || typeof guard.run !== 'function' || !guard.title) {\n problems.push('a business guard was not built from a shape');\n continue;\n }\n if (titles.has(guard.title)) problems.push(`two business guards share the title \"${guard.title}\"`);\n titles.add(guard.title);\n if (!guard.samples) problems.push(`business guard \"${guard.title}\" carries no samples`);\n }\n return problems;\n}\n\n/** The generic guards' tests, built from the app's facts; absent guards build none. */\nexport function buildGenericGuards(\n generic: Readonly<Record<string, unknown>>,\n definitions: ReadonlyArray<GenericGuardDefinition<never>>,\n): Guard[] {\n return definitions.flatMap((d) => (isAbsent(generic[d.id]) ? [] : d.build(generic[d.id] as never)));\n}\n"]}
package/dist/guard.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { RulesService } from './reader/layout.js';
1
2
  import type { RepoReader, Scope } from './reader/reader.js';
2
3
  import type { FileView } from './reader/view.js';
3
4
  /** One place a guard failed. */
@@ -7,11 +8,16 @@ export interface Finding {
7
8
  line?: number;
8
9
  detail: string;
9
10
  }
10
- /** What a guard run reads through. Every file it reads is counted, so a vacuous run fails. */
11
+ /**
12
+ * What a guard run reads through. Every scope a guard reads is counted on its own, so a run whose
13
+ * scope reached no file fails as vacuous even when it also read a named file.
14
+ */
11
15
  export interface GuardContext {
12
16
  readonly reader: RepoReader;
13
17
  files(scope: Scope): FileView[];
14
18
  file(relPath: string): FileView;
19
+ /** Every rules file the layout lists, or those written for one service: a scope like any other. */
20
+ rulesFiles(service?: RulesService): FileView[];
15
21
  }
16
22
  /** A must-fail or must-pass input for a guard's matcher. A bare string is the text of `sample.ts`. */
17
23
  export type Sample = string | {
@@ -1 +1 @@
1
- {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,gCAAgC;AAChC,MAAM,WAAW,OAAO;IACtB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,8FAA8F;AAC9F,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,EAAE,CAAC;IAChC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAAC;CACjC;AAED,sGAAsG;AACtG,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,MAAM,WAAW,KAAK;IACpB,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gGAAgG;IAChG,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2FAA2F;IAC3F,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3D;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QACzB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;QAC1B,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,OAAO,EAAE,CAAC;KAC7C,CAAC;IACF,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,UAAU,CAAC,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;CAChD;AAED,gDAAgD;AAChD,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAAC,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAIhH"}
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,gCAAgC;AAChC,MAAM,WAAW,OAAO;IACtB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,EAAE,CAAC;IAChC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAAC;IAChC,mGAAmG;IACnG,UAAU,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,QAAQ,EAAE,CAAC;CAChD;AAED,sGAAsG;AACtG,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,MAAM,WAAW,KAAK;IACpB,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gGAAgG;IAChG,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2FAA2F;IAC3F,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3D;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QACzB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;QAC1B,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,OAAO,EAAE,CAAC;KAC7C,CAAC;IACF,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,UAAU,CAAC,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;CAChD;AAED,gDAAgD;AAChD,wBAAgB,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAAC,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAIhH"}
package/dist/guard.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,mDAAmD;AAmDnD,gDAAgD;AAChD,MAAM,UAAU,cAAc,CAAC,KAA2C,EAAE,QAA4B;IACtG,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,MAAM,aAAa,EAAE,EAAE,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC3F,KAAK,MAAM,CAAC,IAAI,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/F,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC","sourcesContent":["// What a guard is: a named check over the files the reader hands it. Generic guards and an app's\n// business guards are the same thing to the suite.\n\nimport type { RepoReader, Scope } from './reader/reader.js';\nimport type { FileView } from './reader/view.js';\n\n/** One place a guard failed. */\nexport interface Finding {\n /** Repo-relative path, or the config entry the finding is about. */\n file: string;\n line?: number;\n detail: string;\n}\n\n/** What a guard run reads through. Every file it reads is counted, so a vacuous run fails. */\nexport interface GuardContext {\n readonly reader: RepoReader;\n files(scope: Scope): FileView[];\n file(relPath: string): FileView;\n}\n\n/** A must-fail or must-pass input for a guard's matcher. A bare string is the text of `sample.ts`. */\nexport type Sample = string | { path: string; text: string };\n\nexport interface Guard {\n /** The behavior the guard proves, in words; it names the guard's test. */\n readonly title: string;\n /** What the check requires and how a failure is fixed, in words. Printed with every failure. */\n readonly description: string;\n /** Loads what the guard needs before its samples or its run (an app's declared values). */\n prepare?(): Promise<void>;\n run(context: GuardContext): Finding[] | Promise<Finding[]>;\n /**\n * The guard's own proof that it still catches what it exists to catch: every `flags` sample must\n * produce a hit and every `passes` sample none. Generic guards prove themselves in the package's\n * own tests; a business guard carries its samples here.\n */\n readonly samples?: {\n flags: readonly Sample[];\n passes: readonly Sample[];\n probe(sample: FileView): readonly unknown[];\n };\n /** A whole-repository sweep can take longer than a unit test's default. */\n readonly timeoutMs?: number;\n /**\n * Why this guard cannot run here, or null when it can. Only a guard whose input lies outside the\n * checkout (a sibling repository CI never checks out) has one; the suite shows the guard SKIPPED\n * with this reason, never passed.\n */\n skipReason?(reader: RepoReader): string | null;\n}\n\n/** A printable failure report for one guard. */\nexport function formatFindings(guard: Pick<Guard, 'title' | 'description'>, findings: readonly Finding[]): string {\n const lines = [`${guard.title}: ${findings.length} finding(s)`, '', guard.description, ''];\n for (const f of findings) lines.push(` ${f.file}${f.line ? `:${f.line}` : ''} — ${f.detail}`);\n return lines.join('\\n');\n}\n"]}
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,mDAAmD;AAyDnD,gDAAgD;AAChD,MAAM,UAAU,cAAc,CAAC,KAA2C,EAAE,QAA4B;IACtG,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,MAAM,aAAa,EAAE,EAAE,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAC3F,KAAK,MAAM,CAAC,IAAI,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/F,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC","sourcesContent":["// What a guard is: a named check over the files the reader hands it. Generic guards and an app's\n// business guards are the same thing to the suite.\n\nimport type { RulesService } from './reader/layout.js';\nimport type { RepoReader, Scope } from './reader/reader.js';\nimport type { FileView } from './reader/view.js';\n\n/** One place a guard failed. */\nexport interface Finding {\n /** Repo-relative path, or the config entry the finding is about. */\n file: string;\n line?: number;\n detail: string;\n}\n\n/**\n * What a guard run reads through. Every scope a guard reads is counted on its own, so a run whose\n * scope reached no file fails as vacuous even when it also read a named file.\n */\nexport interface GuardContext {\n readonly reader: RepoReader;\n files(scope: Scope): FileView[];\n file(relPath: string): FileView;\n /** Every rules file the layout lists, or those written for one service: a scope like any other. */\n rulesFiles(service?: RulesService): FileView[];\n}\n\n/** A must-fail or must-pass input for a guard's matcher. A bare string is the text of `sample.ts`. */\nexport type Sample = string | { path: string; text: string };\n\nexport interface Guard {\n /** The behavior the guard proves, in words; it names the guard's test. */\n readonly title: string;\n /** What the check requires and how a failure is fixed, in words. Printed with every failure. */\n readonly description: string;\n /** Loads what the guard needs before its samples or its run (an app's declared values). */\n prepare?(): Promise<void>;\n run(context: GuardContext): Finding[] | Promise<Finding[]>;\n /**\n * The guard's own proof that it still catches what it exists to catch: every `flags` sample must\n * produce a hit and every `passes` sample none. Generic guards prove themselves in the package's\n * own tests; a business guard carries its samples here.\n */\n readonly samples?: {\n flags: readonly Sample[];\n passes: readonly Sample[];\n probe(sample: FileView): readonly unknown[];\n };\n /** A whole-repository sweep can take longer than a unit test's default. */\n readonly timeoutMs?: number;\n /**\n * Why this guard cannot run here, or null when it can. Only a guard whose input lies outside the\n * checkout (a sibling repository CI never checks out) has one; the suite shows the guard SKIPPED\n * with this reason, never passed.\n */\n skipReason?(reader: RepoReader): string | null;\n}\n\n/** A printable failure report for one guard. */\nexport function formatFindings(guard: Pick<Guard, 'title' | 'description'>, findings: readonly Finding[]): string {\n const lines = [`${guard.title}: ${findings.length} finding(s)`, '', guard.description, ''];\n for (const f of findings) lines.push(` ${f.file}${f.line ? `:${f.line}` : ''} — ${f.detail}`);\n return lines.join('\\n');\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { declaredLiteral, matchPattern, ownerOnly, reviewedAllowlist, sampleHits, type AllowlistEntry, type Hit, type Literal, type Matcher, } from './shapes/shapes.js';
2
2
  export { formatFindings, type Finding, type Guard, type GuardContext, type Sample } from './guard.js';
3
3
  export { viewOfText, type FileView } from './reader/view.js';
4
+ export { MODULE_LOADS, type ImportedName, type ImportKind, type ModuleImport } from './support/imports.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,eAAe,EACf,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,KAAK,cAAc,EACnB,KAAK,GAAG,EACR,KAAK,OAAO,EACZ,KAAK,OAAO,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AACtG,OAAO,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,eAAe,EACf,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,KAAK,cAAc,EACnB,KAAK,GAAG,EACR,KAAK,OAAO,EACZ,KAAK,OAAO,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AACtG,OAAO,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -3,4 +3,5 @@
3
3
  export { declaredLiteral, matchPattern, ownerOnly, reviewedAllowlist, sampleHits, } from './shapes/shapes.js';
4
4
  export { formatFindings } from './guard.js';
5
5
  export { viewOfText } from './reader/view.js';
6
+ export { MODULE_LOADS } from './support/imports.js';
6
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,wFAAwF;AAExF,OAAO,EACL,eAAe,EACf,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,UAAU,GAKX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAA4D,MAAM,YAAY,CAAC;AACtG,OAAO,EAAE,UAAU,EAAiB,MAAM,kBAAkB,CAAC","sourcesContent":["// The pure entry: the three shapes an app builds its business guards on, and the guard contract.\n// Reading the repository is the `./node` entry's job; running the suite is `./suite`'s.\n\nexport {\n declaredLiteral,\n matchPattern,\n ownerOnly,\n reviewedAllowlist,\n sampleHits,\n type AllowlistEntry,\n type Hit,\n type Literal,\n type Matcher,\n} from './shapes/shapes.js';\nexport { formatFindings, type Finding, type Guard, type GuardContext, type Sample } from './guard.js';\nexport { viewOfText, type FileView } from './reader/view.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,wFAAwF;AAExF,OAAO,EACL,eAAe,EACf,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,UAAU,GAKX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAA4D,MAAM,YAAY,CAAC;AACtG,OAAO,EAAE,UAAU,EAAiB,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAyD,MAAM,sBAAsB,CAAC","sourcesContent":["// The pure entry: the three shapes an app builds its business guards on, and the guard contract.\n// Reading the repository is the `./node` entry's job; running the suite is `./suite`'s.\n\nexport {\n declaredLiteral,\n matchPattern,\n ownerOnly,\n reviewedAllowlist,\n sampleHits,\n type AllowlistEntry,\n type Hit,\n type Literal,\n type Matcher,\n} from './shapes/shapes.js';\nexport { formatFindings, type Finding, type Guard, type GuardContext, type Sample } from './guard.js';\nexport { viewOfText, type FileView } from './reader/view.js';\nexport { MODULE_LOADS, type ImportedName, type ImportKind, type ModuleImport } from './support/imports.js';\n"]}
@@ -1,8 +1,9 @@
1
1
  import ts from 'typescript';
2
2
  import type { GenericGuardDefinition } from '../../config.js';
3
3
  import type { FileView } from '../../reader/view.js';
4
+ import type { Reviewed } from '../../reader/layout.js';
4
5
  import { type Matcher } from '../../shapes/shapes.js';
5
- import { type ReviewedFile, type ServerZone } from './shared.js';
6
+ import { type ServerZone } from './shared.js';
6
7
  export interface AuditWriterFacts {
7
8
  /** The one module that writes audit documents. */
8
9
  writer: string;
@@ -13,7 +14,7 @@ export interface AuditWriterFacts {
13
14
  /** Packages that write audit documents themselves; only the writer imports them. */
14
15
  writerPackages: readonly string[];
15
16
  /** Modules that name the collection to READ it, never to write; a stale entry fails. */
16
- readers: readonly ReviewedFile[];
17
+ readers: readonly Reviewed[];
17
18
  /** The one module that builds a person-actor, the field that says an actor's mode, and the person modes. */
18
19
  personActor: {
19
20
  constructors: string;
@@ -62,7 +63,7 @@ export interface FireAndForgetFacts {
62
63
  /** The audit writer's success entry points, whose failure is never swallowed by a `.catch`. */
63
64
  auditWrites: readonly string[];
64
65
  /** Files whose `void`-ed calls are reviewed, each with its why; a file that no longer has one is stale. */
65
- voidSites: readonly ReviewedFile[];
66
+ voidSites: readonly Reviewed[];
66
67
  }
67
68
  /** Every `void`-ed call, every `.catch` chained onto an audit write, and every audit write call. */
68
69
  export declare function fireAndForgetShapes(file: FileView, auditWrites: ReadonlySet<string>): {
@@ -1 +1 @@
1
- {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../../src/modules/backend/audit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../../src/modules/backend/audit.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAG9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAA0C,KAAK,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAI9F,OAAO,EAA6D,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAOzG,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAC;IACvB,4FAA4F;IAC5F,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,oFAAoF;IACpF,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,wFAAwF;IACxF,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC7B,4GAA4G;IAC5G,WAAW,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAC;IACnF,iFAAiF;IACjF,aAAa,EAAE,UAAU,CAAC;CAC3B;AASD,yFAAyF;AACzF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,mBAAmB,GAAG,gBAAgB,CAAC,GAAG,OAAO,CA0BxI;AAED,oGAAoG;AACpG,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,CAAC,aAAa,CAAC,GAAG,OAAO,CAiBlF;AAED,eAAO,MAAM,gBAAgB,EAAE,sBAAsB,CAAC,gBAAgB,CA8DrE,CAAC;AAKF,MAAM,WAAW,iBAAiB;IAChC,uFAAuF;IACvF,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,uGAAuG;IACvG,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,8FAA8F;IAC9F,YAAY,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5D;AAOD,yGAAyG;AACzG,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,GAAG,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAA;CAAE,CAAC,CAmCxH;AAED,eAAO,MAAM,iBAAiB,EAAE,sBAAsB,CAAC,iBAAiB,CAyCvE,CAAC;AAKF,MAAM,WAAW,iBAAiB;IAChC,kGAAkG;IAClG,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,kGAAkG;AAClG,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAexI;AAED,eAAO,MAAM,iBAAiB,EAAE,sBAAsB,CAAC,iBAAiB,CAiCvE,CAAC;AAKF,MAAM,WAAW,kBAAkB;IACjC,+FAA+F;IAC/F,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,2GAA2G;IAC3G,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAC;CAChC;AAED,oGAAoG;AACpG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC;;;cAGnD,EAAE,CAAC,IAAI;cAAQ,MAAM;;;EAarD;AAED,eAAO,MAAM,kBAAkB,EAAE,sBAAsB,CAAC,kBAAkB,CA2CzE,CAAC"}
@@ -2,7 +2,10 @@
2
2
  // member-written text in an audit payload, and no handler wrapped around an audit write.
3
3
  import ts from 'typescript';
4
4
  import { ownerOnly, reviewedAllowlist } from '../../shapes/shapes.js';
5
- import { calleeName, forEachNode, lineOfNode, namesProblem, reviewedProblems, serverZoneContext, serverZoneProblems, staleServerFolders, textProblem, unwrap, } from './shared.js';
5
+ import { calleeName, forEachNode, isPlainString, lineOfNode, unwrap } from '../../support/ast.js';
6
+ import { namesProblem, reviewedProblems, textProblem } from '../../support/facts.js';
7
+ import { MODULE_LOADS, moduleImports, namesModule } from '../../support/imports.js';
8
+ import { serverZoneContext, serverZoneProblems, staleServerFolders } from './shared.js';
6
9
  const FUNCTIONS_CODE = { roots: ['functions'], kinds: ['code'], tests: false };
7
10
  const AUDIT_WRITER_SCOPE = {
8
11
  roots: ['functions', 'scripts', 'app'],
@@ -17,24 +20,18 @@ export function auditCollectionMatcher(facts) {
17
20
  return (file) => {
18
21
  const hits = [];
19
22
  const sf = file.sourceFile;
23
+ const loads = new Map(moduleImports(sf, MODULE_LOADS).map((site) => [site.node, site.spec]));
20
24
  forEachNode(sf, (node) => {
21
25
  if (ts.isPropertyAccessExpression(node) && handles.has(`${node.expression.getText(sf)}.${node.name.text}`)) {
22
26
  hits.push({ index: node.getStart(sf), detail: node.getText(sf) });
23
27
  }
24
- const specifier = (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)
25
- ? node.moduleSpecifier.text
26
- : ts.isCallExpression(node) &&
27
- (node.expression.kind === ts.SyntaxKind.ImportKeyword || (ts.isIdentifier(node.expression) && node.expression.text === 'require')) &&
28
- node.arguments[0] &&
29
- ts.isStringLiteralLike(node.arguments[0])
30
- ? node.arguments[0].text
31
- : undefined;
32
- if (specifier !== undefined && facts.writerPackages.some((p) => specifier === p || specifier.startsWith(`${p}/`))) {
28
+ const specifier = loads.get(node);
29
+ if (specifier !== undefined && facts.writerPackages.some((p) => namesModule(specifier, p))) {
33
30
  hits.push({ index: node.getStart(sf), detail: `imports ${specifier}` });
34
31
  return;
35
32
  }
36
33
  const isSpecifier = !!node.parent && (ts.isImportDeclaration(node.parent) || ts.isExportDeclaration(node.parent));
37
- if ((ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) && !isSpecifier && namesCollection(node.text)) {
34
+ if (isPlainString(node) && !isSpecifier && namesCollection(node.text)) {
38
35
  hits.push({ index: node.getStart(sf), detail: `'${node.text}'` });
39
36
  }
40
37
  if (ts.isTemplateExpression(node) && namesCollection(node.head.text)) {
@@ -184,7 +181,7 @@ export const auditPayloadGuard = {
184
181
  namesProblem(facts?.entryPoints, 'entryPoints', problems);
185
182
  textProblem(facts?.payloadKey, 'payloadKey', problems);
186
183
  namesProblem(facts?.forwardedPayloadKeys, 'forwardedPayloadKeys', problems, { nonEmpty: false });
187
- reviewedProblems(facts?.operatorCopy, 'operatorCopy', problems, 'site');
184
+ reviewedProblems(facts?.operatorCopy, 'operatorCopy', problems, { key: 'site' });
188
185
  for (const entry of facts?.operatorCopy ?? []) {
189
186
  if (typeof entry?.site === 'string' && !/^[^#]+#[^#]+$/.test(entry.site))
190
187
  problems.push(`operatorCopy site ${entry.site} is not file#key`);
@@ -1 +1 @@
1
- {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../src/modules/backend/audit.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,yFAAyF;AAEzF,OAAO,EAAE,MAAM,YAAY,CAAC;AAK5B,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAA0B,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EACL,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,MAAM,GAGP,MAAM,aAAa,CAAC;AAErB,MAAM,cAAc,GAAU,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAsBtF,MAAM,kBAAkB,GAAU;IAChC,KAAK,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,CAAC;IACtC,aAAa,EAAE,CAAC,SAAS,CAAC;IAC1B,KAAK,EAAE,CAAC,MAAM,CAAC;IACf,KAAK,EAAE,KAAK;CACb,CAAC;AAEF,yFAAyF;AACzF,MAAM,UAAU,sBAAsB,CAAC,KAAwF;IAC7H,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACjD,MAAM,eAAe,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,KAAK,KAAK,CAAC,cAAc,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC;IACvH,OAAO,CAAC,IAAI,EAAE,EAAE;QACd,MAAM,IAAI,GAAU,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC;QAC3B,WAAW,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;YACvB,IAAI,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;gBAC3G,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACpE,CAAC;YACD,MAAM,SAAS,GACb,CAAC,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,eAAe,CAAC;gBAChI,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI;gBAC3B,CAAC,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;oBACvB,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,aAAa,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;oBAClI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;oBACjB,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;oBAC3C,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI;oBACxB,CAAC,CAAC,SAAS,CAAC;YAClB,IAAI,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,KAAK,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAClH,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,SAAS,EAAE,EAAE,CAAC,CAAC;gBACxE,OAAO;YACT,CAAC;YACD,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YAClH,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,+BAA+B,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzH,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;YACpE,CAAC;YACD,IAAI,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrE,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,cAAc,gBAAgB,EAAE,CAAC,CAAC;YAC3F,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,kBAAkB,CAAC,KAAsC;IACvE,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,EAAE,EAAE;QACd,MAAM,IAAI,GAAU,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC;QAC3B,WAAW,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;YACvB,IAAI,CAAC,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC;gBAAE,OAAO;YAChD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACnC,IAAI,CAAC,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS;oBAAE,SAAS;gBACtH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBACtC,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBACzD,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,SAAS,MAAM,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;gBACxF,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAA6C;IACxE,EAAE,EAAE,aAAa;IACjB,UAAU,EAAE,iCAAiC;IAC7C,YAAY,CAAC,KAAK;QAChB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC/C,WAAW,CAAC,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,QAAQ,CAAC,CAAC;QAC/D,YAAY,CAAC,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,QAAQ,CAAC,CAAC;QACtE,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,iBAAiB,IAAI,EAAE,EAAE,CAAC;YACpD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,qBAAqB,MAAM,sBAAsB,CAAC,CAAC;QACvI,CAAC;QACD,YAAY,CAAC,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QACrF,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QACtD,IAAI,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,KAAK,CAAC,MAAM,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;QAC7G,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,0BAA0B,EAAE,QAAQ,CAAC,CAAC;QACpF,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,uBAAuB,EAAE,QAAQ,CAAC,CAAC;QAC9E,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,CAAC,CAAC;QACvE,kBAAkB,CAAC,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;QACpE,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,KAAK,CAAC,KAAK;QACT,MAAM,UAAU,GAAG,iBAAiB,CAAC;YACnC,KAAK,EAAE,qDAAqD;YAC5D,WAAW,EACT,QAAQ,KAAK,CAAC,MAAM,gCAAgC,KAAK,CAAC,cAAc,KAAK,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;gBACnH,+GAA+G;gBAC/G,yGAAyG;gBACzG,mGAAmG;YACrG,KAAK,EAAE,kBAAkB;YACzB,KAAK,EAAE,sBAAsB,CAAC,KAAK,CAAC;YACpC,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,sBAAsB,EAAE,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC;YAClF,OAAO,EAAE;gBACP,KAAK,EAAE;oBACL,kBAAkB,KAAK,CAAC,cAAc,gBAAgB;oBACtD,YAAY,KAAK,CAAC,cAAc,wBAAwB;oBACxD,iBAAiB,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,uBAAuB;iBACnE;gBACD,MAAM,EAAE,CAAC,aAAa,KAAK,CAAC,cAAc,YAAY,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,oBAAoB,KAAK,CAAC,cAAc,SAAS,CAAC;aACnI;SACF,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,CAAC;YACvB,KAAK,EAAE,wDAAwD;YAC/D,WAAW,EACT,QAAQ,KAAK,CAAC,WAAW,CAAC,YAAY,2BAA2B,KAAK,CAAC,WAAW,CAAC,SAAS,oBAAoB;gBAChH,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,wEAAwE;gBAC9G,yFAAyF;YAC3F,KAAK,EAAE,kBAAkB;YACzB,KAAK,EAAE,kBAAkB,CAAC,KAAK,CAAC,WAAW,CAAC;YAC5C,MAAM,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC;YACxC,OAAO,EAAE;gBACP,KAAK,EAAE,CAAC,mBAAmB,KAAK,CAAC,WAAW,CAAC,SAAS,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC;gBAC3G,MAAM,EAAE,CAAC,cAAc,KAAK,CAAC,WAAW,CAAC,SAAS,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,0BAA0B,KAAK,CAAC,WAAW,CAAC,SAAS,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;aAC/K;SACF,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,CAAC,KAAY,EAAS,EAAE,CAAC,CAAC;YACvC,GAAG,KAAK;YACR,KAAK,CAAC,GAAG,CAAC,OAAqB;gBAC7B,OAAO,CAAC,GAAG,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,OAAO,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;YACtI,CAAC;SACF,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9C,CAAC;CACF,CAAC;AAgBF,+DAA+D;AAC/D,MAAM,SAAS,GAAG,kGAAkG,CAAC;AACrH,yFAAyF;AACzF,MAAM,cAAc,GAAG,mBAAmB,CAAC;AAE3C,yGAAyG;AACzG,MAAM,UAAU,wBAAwB,CAAC,IAAc,EAAE,KAAwB;IAC/E,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC;IAC3B,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACtD,MAAM,IAAI,GAA0C,EAAE,CAAC;IACvD,MAAM,KAAK,GAAG,CAAC,IAAiC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;IAChH,MAAM,WAAW,GAAG,CAAC,GAA+B,EAAE,EAAE;QACtD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;YAClC,IAAI,CAAC,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC;gBAAE,SAAS;YACxF,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;YACxB,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACxD,IAAI,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBACvC,IAAI,EAAE,CAAC,yBAAyB,CAAC,KAAK,CAAC;oBAAE,WAAW,CAAC,KAAK,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,MAAM,SAAS,GAAG,CAAC,OAAmC,EAAE,IAAyB,EAAE,EAAE;QACnF,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACtC,IAAI,CAAC,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAAE,SAAS;YACvE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACvC,IAAI,EAAE,CAAC,yBAAyB,CAAC,KAAK,CAAC;gBAAE,WAAW,CAAC,KAAK,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CAAC;IACF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IAChD,WAAW,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;QACvB,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YACzE,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC1B,IAAI,EAAE,CAAC,yBAAyB,CAAC,KAAK,CAAC;oBAAE,SAAS,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QACD,IAAI,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC;YAAE,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAA8C;IAC1E,EAAE,EAAE,cAAc;IAClB,UAAU,EAAE,iCAAiC;IAC7C,YAAY,CAAC,KAAK;QAChB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;QAC1D,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;QACvD,YAAY,CAAC,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QACjG,gBAAgB,CAAC,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACxE,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,YAAY,IAAI,EAAE,EAAE,CAAC;YAC9C,IAAI,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,qBAAqB,KAAK,CAAC,IAAI,kBAAkB,CAAC,CAAC;QAC7I,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,KAAK,CAAC,KAAK;QACT,OAAO;YACL;gBACE,KAAK,EAAE,wDAAwD;gBAC/D,WAAW,EACT,kHAAkH;oBAClH,+GAA+G;oBAC/G,6GAA6G;oBAC7G,gHAAgH;oBAChH,kGAAkG;gBACpG,GAAG,CAAC,OAAO;oBACT,MAAM,QAAQ,GAAc,EAAE,CAAC;oBAC/B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;oBAChE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;oBAC/B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;wBACjD,KAAK,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,wBAAwB,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;4BAClE,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;4BACnC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;4BACf,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;gCAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,2BAA2B,GAAG,EAAE,EAAE,CAAC,CAAC;wBACtI,CAAC;oBACH,CAAC;oBACD,KAAK,MAAM,IAAI,IAAI,QAAQ;wBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;4BAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,qDAAqD,EAAE,CAAC,CAAC;oBAC/I,OAAO,QAAQ,CAAC;gBAClB,CAAC;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAUF,kGAAkG;AAClG,MAAM,UAAU,oBAAoB,CAAC,IAAc,EAAE,KAA0B;IAC7E,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC;IAC3B,MAAM,IAAI,GAA4D,EAAE,CAAC;IACzE,WAAW,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;QACvB,IAAI,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,YAAY,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACtI,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,IAAI,CAAC,YAAY,CAAC,IAAI,qBAAqB,EAAE,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,IAAI,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACtE,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACzD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC3B,MAAM,OAAO,GAAG,EAAE,CAAC,0BAA0B,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/I,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,WAAY,MAAsC,CAAC,IAAI,CAAC,IAAI,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChI,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAA8C;IAC1E,EAAE,EAAE,mBAAmB;IACvB,UAAU,EAAE,iCAAiC;IAC7C,YAAY,CAAC,KAAK;QAChB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,YAAY,CAAC,KAAK,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,QAAQ,CAAC,CAAC;QACxE,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,KAAK,CAAC,KAAK;QACT,OAAO;YACL;gBACE,KAAK,EAAE,wDAAwD;gBAC/D,WAAW,EACT,mCAAmC,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,2CAA2C;oBACjH,mHAAmH;oBACnH,+GAA+G;oBAC/G,6FAA6F;gBAC/F,GAAG,CAAC,OAAO;oBACT,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;oBAChD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;oBACjC,MAAM,QAAQ,GAAc,EAAE,CAAC;oBAC/B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;wBACjD,KAAK,MAAM,GAAG,IAAI,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;4BACpD,IAAI,GAAG,CAAC,IAAI;gCAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;4BACnC,IAAI,GAAG,CAAC,MAAM;gCAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;wBAC3G,CAAC;oBACH,CAAC;oBACD,KAAK,MAAM,IAAI,IAAI,KAAK;wBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;4BAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,GAAG,IAAI,oBAAoB,EAAE,CAAC,CAAC;oBACtI,OAAO,QAAQ,CAAC;gBAClB,CAAC;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAYF,oGAAoG;AACpG,MAAM,UAAU,mBAAmB,CAAC,IAAc,EAAE,WAAgC;IAClF,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC;IAC3B,MAAM,MAAM,GAAc,EAAE,CAAC;IAC7B,MAAM,SAAS,GAA2C,EAAE,CAAC;IAC7D,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;QACvB,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjG,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;YAAE,OAAO;QACvC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,IAAI,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrD,IAAI,IAAI,KAAK,OAAO,IAAI,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1H,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;YACrD,IAAI,KAAK,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,MAAM,kBAAkB,GAA+C;IAC5E,EAAE,EAAE,eAAe;IACnB,UAAU,EAAE,KAAK;IACjB,YAAY,CAAC,KAAK;QAChB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/E,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;QAC1D,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,KAAK,CAAC,KAAK;QACT,OAAO;YACL;gBACE,KAAK,EAAE,yEAAyE;gBAChF,WAAW,EACT,kHAAkH;oBAClH,0GAA0G;oBAC1G,8GAA8G;oBAC9G,8GAA8G;oBAC9G,8GAA8G;oBAC9G,kHAAkH;oBAClH,sBAAsB;gBACxB,GAAG,CAAC,OAAO;oBACT,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;oBAC/C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;oBAC7D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;oBACpC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;oBACjC,MAAM,QAAQ,GAAc,EAAE,CAAC;oBAC/B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;wBACjD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,mBAAmB,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;wBAC7E,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;wBACrC,IAAI,MAAM,CAAC,MAAM;4BAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;wBAC5C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;4BAC7B,KAAK,MAAM,IAAI,IAAI,MAAM;gCAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,oBAAoB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;wBACxK,CAAC;wBACD,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,SAAS;4BAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,sCAAsC,EAAE,CAAC,CAAC;oBAClK,CAAC;oBACD,KAAK,MAAM,IAAI,IAAI,QAAQ;wBAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;4BAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,+CAA+C,EAAE,CAAC,CAAC;oBAC9I,KAAK,MAAM,IAAI,IAAI,WAAW;wBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;4BAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,oBAAoB,EAAE,CAAC,CAAC;oBACrI,OAAO,QAAQ,CAAC;gBAClB,CAAC;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["// The audit guards: one writer of the audit collection, one builder of person-actors, no\n// member-written text in an audit payload, and no handler wrapped around an audit write.\n\nimport ts from 'typescript';\nimport type { GenericGuardDefinition } from '../../config.js';\nimport type { Finding, Guard, GuardContext } from '../../guard.js';\nimport type { Scope } from '../../reader/reader.js';\nimport type { FileView } from '../../reader/view.js';\nimport { ownerOnly, reviewedAllowlist, type Hit, type Matcher } from '../../shapes/shapes.js';\nimport {\n calleeName,\n forEachNode,\n lineOfNode,\n namesProblem,\n reviewedProblems,\n serverZoneContext,\n serverZoneProblems,\n staleServerFolders,\n textProblem,\n unwrap,\n type ReviewedFile,\n type ServerZone,\n} from './shared.js';\n\nconst FUNCTIONS_CODE: Scope = { roots: ['functions'], kinds: ['code'], tests: false };\n\n// ---------------------------------------------------------------------------------------------\n// One audit writer.\n\nexport interface AuditWriterFacts {\n /** The one module that writes audit documents. */\n writer: string;\n /** The audit collection's name as stored (`auditEvents`). */\n collectionName: string;\n /** The core's handles on the collection, as `OWNER.member` (`COLLECTIONS.AUDIT_EVENTS`). */\n collectionHandles: readonly string[];\n /** Packages that write audit documents themselves; only the writer imports them. */\n writerPackages: readonly string[];\n /** Modules that name the collection to READ it, never to write; a stale entry fails. */\n readers: readonly ReviewedFile[];\n /** The one module that builds a person-actor, the field that says an actor's mode, and the person modes. */\n personActor: { constructors: string; modeField: string; modes: readonly string[] };\n /** Server code in the app root, held to the same rules as the Functions tree. */\n appServerZone: ServerZone;\n}\n\nconst AUDIT_WRITER_SCOPE: Scope = {\n roots: ['functions', 'scripts', 'app'],\n optionalRoots: ['scripts'],\n kinds: ['code'],\n tests: false,\n};\n\n/** Every place a file names the audit collection or imports a package that writes it. */\nexport function auditCollectionMatcher(facts: Pick<AuditWriterFacts, 'collectionName' | 'collectionHandles' | 'writerPackages'>): Matcher {\n const handles = new Set(facts.collectionHandles);\n const namesCollection = (text: string) => text === facts.collectionName || text.startsWith(`${facts.collectionName}/`);\n return (file) => {\n const hits: Hit[] = [];\n const sf = file.sourceFile;\n forEachNode(sf, (node) => {\n if (ts.isPropertyAccessExpression(node) && handles.has(`${node.expression.getText(sf)}.${node.name.text}`)) {\n hits.push({ index: node.getStart(sf), detail: node.getText(sf) });\n }\n const specifier =\n (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)\n ? node.moduleSpecifier.text\n : ts.isCallExpression(node) &&\n (node.expression.kind === ts.SyntaxKind.ImportKeyword || (ts.isIdentifier(node.expression) && node.expression.text === 'require')) &&\n node.arguments[0] &&\n ts.isStringLiteralLike(node.arguments[0])\n ? node.arguments[0].text\n : undefined;\n if (specifier !== undefined && facts.writerPackages.some((p) => specifier === p || specifier.startsWith(`${p}/`))) {\n hits.push({ index: node.getStart(sf), detail: `imports ${specifier}` });\n return;\n }\n const isSpecifier = !!node.parent && (ts.isImportDeclaration(node.parent) || ts.isExportDeclaration(node.parent));\n if ((ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) && !isSpecifier && namesCollection(node.text)) {\n hits.push({ index: node.getStart(sf), detail: `'${node.text}'` });\n }\n if (ts.isTemplateExpression(node) && namesCollection(node.head.text)) {\n hits.push({ index: node.getStart(sf), detail: `${facts.collectionName} template path` });\n }\n });\n return hits;\n };\n}\n\n/** Every object literal that builds a person-actor by hand: its mode field set to a person mode. */\nexport function personActorMatcher(facts: AuditWriterFacts['personActor']): Matcher {\n const modes = new Set(facts.modes);\n return (file) => {\n const hits: Hit[] = [];\n const sf = file.sourceFile;\n forEachNode(sf, (node) => {\n if (!ts.isObjectLiteralExpression(node)) return;\n for (const prop of node.properties) {\n if (!ts.isPropertyAssignment(prop) || prop.name.getText(sf).replace(/^['\"]|['\"]$/g, '') !== facts.modeField) continue;\n const init = unwrap(prop.initializer);\n if (ts.isStringLiteralLike(init) && modes.has(init.text)) {\n hits.push({ index: prop.getStart(sf), detail: `${facts.modeField}: '${init.text}'` });\n }\n }\n });\n return hits;\n };\n}\n\nexport const auditWriterGuard: GenericGuardDefinition<AuditWriterFacts> = {\n id: 'auditWriter',\n absentWhen: 'the app records no audit events',\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.writer, 'writer', problems);\n textProblem(facts?.collectionName, 'collectionName', problems);\n namesProblem(facts?.collectionHandles, 'collectionHandles', problems);\n for (const handle of facts?.collectionHandles ?? []) {\n if (typeof handle === 'string' && !/^[\\w$]+\\.[\\w$]+$/.test(handle)) problems.push(`collection handle ${handle} is not OWNER.member`);\n }\n namesProblem(facts?.writerPackages, 'writerPackages', problems, { nonEmpty: false });\n reviewedProblems(facts?.readers, 'readers', problems);\n if (facts?.readers?.some((r) => r?.path === facts.writer)) problems.push('the writer is listed as a reader');\n textProblem(facts?.personActor?.constructors, 'personActor.constructors', problems);\n textProblem(facts?.personActor?.modeField, 'personActor.modeField', problems);\n namesProblem(facts?.personActor?.modes, 'personActor.modes', problems);\n serverZoneProblems(facts?.appServerZone, 'appServerZone', problems);\n return problems;\n },\n build(facts) {\n const collection = reviewedAllowlist({\n title: 'audit documents are written by the one audit writer',\n description:\n `Only ${facts.writer} names the audit collection (${facts.collectionName}, ${facts.collectionHandles.join(', ')}) ` +\n 'or imports a package that writes it; a second writer would skip the attribution, the request origin, and the ' +\n 'append-only rules the writer owns. Write audit events through the writer. A module that only READS the ' +\n 'collection is a reviewed reader with its why; a reader that stops naming the collection is stale.',\n scope: AUDIT_WRITER_SCOPE,\n match: auditCollectionMatcher(facts),\n allowlist: [{ path: facts.writer, why: 'the one audit writer' }, ...facts.readers],\n samples: {\n flags: [\n `db.collection('${facts.collectionName}').add(event);`,\n `db.doc(\\`${facts.collectionName}/\\${id}\\`).set(event);`,\n `db.collection(${facts.collectionHandles[0]}).doc(id).set(event);`,\n ],\n passes: [`// writes ${facts.collectionName} through ${facts.collectionHandles[0]}\\nconst label = '${facts.collectionName}Count';`],\n },\n });\n const actors = ownerOnly({\n title: 'person-actors are built only by the actor constructors',\n description:\n `Only ${facts.personActor.constructors} writes an object whose ${facts.personActor.modeField} is a person mode ` +\n `(${facts.personActor.modes.join(', ')}). A person-actor carries the origin of the request it acted through; ` +\n 'built by hand it can carry none, or a forged one. Build it with the actor constructors.',\n scope: AUDIT_WRITER_SCOPE,\n match: personActorMatcher(facts.personActor),\n owners: [facts.personActor.constructors],\n samples: {\n flags: [`const actor = { ${facts.personActor.modeField}: '${facts.personActor.modes[0]}' as const, uid };`],\n passes: [`type A = { ${facts.personActor.modeField}: '${facts.personActor.modes[0]}' };\\nconst b = { other${facts.personActor.modeField}: '${facts.personActor.modes[0]}' };`],\n },\n });\n const inZone = (guard: Guard): Guard => ({\n ...guard,\n async run(context: GuardContext) {\n return [...staleServerFolders(context, facts.appServerZone), ...(await guard.run(serverZoneContext(context, facts.appServerZone)))];\n },\n });\n return [inZone(collection), inZone(actors)];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// No member-written text in an audit payload.\n\nexport interface AuditPayloadFacts {\n /** The audit writer's entry points (`writeAuditEventAs`, the failure entry points). */\n entryPoints: readonly string[];\n /** The key under which an entry point takes its payload (`payload`). */\n payloadKey: string;\n /** Keys elsewhere whose object literal is handed to an entry point as its payload (`auditPayload`). */\n forwardedPayloadKeys: readonly string[];\n /** Prose-named keys an operator (never a member) writes, as `file#key`, each with its why. */\n operatorCopy: ReadonlyArray<{ site: string; why: string }>;\n}\n\n/** A key named like prose: a title, a name, a body of text. */\nconst PROSE_KEY = /(title|name|description|subject|body|text|message|content|caption|narrative|comment|bio|words)$/i;\n/** A map whose keys NAME fields rather than carry their text (`changedFieldLengths`). */\nconst NAMES_NOT_TEXT = /(Lengths|Fields)$/;\n\n/** Every prose-named key, at any depth, of the payload literals a file hands to an audit entry point. */\nexport function proseKeysInAuditPayloads(file: FileView, facts: AuditPayloadFacts): Array<{ key: string; node: ts.Node }> {\n const sf = file.sourceFile;\n const entryPoints = new Set(facts.entryPoints);\n const forwarded = new Set(facts.forwardedPayloadKeys);\n const hits: Array<{ key: string; node: ts.Node }> = [];\n const keyOf = (prop: ts.ObjectLiteralElementLike) => prop.name?.getText(sf).replace(/^['\"`]|['\"`]$/g, '') ?? '';\n const walkPayload = (obj: ts.ObjectLiteralExpression) => {\n for (const prop of obj.properties) {\n if (!ts.isPropertyAssignment(prop) && !ts.isShorthandPropertyAssignment(prop)) continue;\n const key = keyOf(prop);\n if (PROSE_KEY.test(key)) hits.push({ key, node: prop });\n if (ts.isPropertyAssignment(prop) && !NAMES_NOT_TEXT.test(key)) {\n const value = unwrap(prop.initializer);\n if (ts.isObjectLiteralExpression(value)) walkPayload(value);\n }\n }\n };\n const payloadOf = (details: ts.ObjectLiteralExpression, keys: ReadonlySet<string>) => {\n for (const prop of details.properties) {\n if (!ts.isPropertyAssignment(prop) || !keys.has(keyOf(prop))) continue;\n const value = unwrap(prop.initializer);\n if (ts.isObjectLiteralExpression(value)) walkPayload(value);\n }\n };\n const payloadKeys = new Set([facts.payloadKey]);\n forEachNode(sf, (node) => {\n if (ts.isCallExpression(node) && entryPoints.has(calleeName(node) ?? '')) {\n for (const arg of node.arguments) {\n const value = unwrap(arg);\n if (ts.isObjectLiteralExpression(value)) payloadOf(value, payloadKeys);\n }\n }\n if (ts.isObjectLiteralExpression(node)) payloadOf(node, forwarded);\n });\n return hits;\n}\n\nexport const auditPayloadGuard: GenericGuardDefinition<AuditPayloadFacts> = {\n id: 'auditPayload',\n absentWhen: 'the app records no audit events',\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.entryPoints, 'entryPoints', problems);\n textProblem(facts?.payloadKey, 'payloadKey', problems);\n namesProblem(facts?.forwardedPayloadKeys, 'forwardedPayloadKeys', problems, { nonEmpty: false });\n reviewedProblems(facts?.operatorCopy, 'operatorCopy', problems, 'site');\n for (const entry of facts?.operatorCopy ?? []) {\n if (typeof entry?.site === 'string' && !/^[^#]+#[^#]+$/.test(entry.site)) problems.push(`operatorCopy site ${entry.site} is not file#key`);\n }\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'no member-written text is copied into an audit payload',\n description:\n 'An audit payload records identifiers, field names, lengths, and decisions — never member-written text. An audit ' +\n 'event outlives every edit and every erasure, so a title, name, description, subject, body, or message copied ' +\n \"into one survives the member's own change and their account's deletion. Record the changed field names and \" +\n 'their lengths instead. A key under a `…Lengths` or `…Fields` map names fields and is exempt; text an operator ' +\n 'writes is a reviewed `file#key` entry with its why, and an entry that no longer occurs is stale.',\n run(context) {\n const findings: Finding[] = [];\n const reviewed = new Set(facts.operatorCopy.map((e) => e.site));\n const seen = new Set<string>();\n for (const file of context.files(FUNCTIONS_CODE)) {\n for (const { key, node } of proseKeysInAuditPayloads(file, facts)) {\n const site = `${file.path}#${key}`;\n seen.add(site);\n if (!reviewed.has(site)) findings.push({ file: file.path, line: lineOfNode(file, node), detail: `prose-named payload key ${key}` });\n }\n }\n for (const site of reviewed) if (!seen.has(site)) findings.push({ file: site, detail: 'stale operator-copy entry: the key no longer occurs' });\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// No handler chained onto a failure-audit entry point.\n\nexport interface FailureAuditFacts {\n /** The audit writer's best-effort failure entry points, which log and capture and never throw. */\n failureEntryPoints: readonly string[];\n}\n\n/** Each aliased import of a failure entry point, and each call with a handler chained onto it. */\nexport function chainedFailureAudits(file: FileView, names: ReadonlySet<string>): Array<{ node: ts.Node; detail: string; name?: string }> {\n const sf = file.sourceFile;\n const hits: Array<{ node: ts.Node; detail: string; name?: string }> = [];\n forEachNode(sf, (node) => {\n if (ts.isImportSpecifier(node) && node.propertyName && names.has(node.propertyName.text) && node.propertyName.text !== node.name.text) {\n hits.push({ node, detail: `imports ${node.propertyName.text} under another name` });\n }\n const name = ts.isCallExpression(node) ? calleeName(node) : undefined;\n if (ts.isCallExpression(node) && name && names.has(name)) {\n const parent = node.parent;\n const chained = ts.isPropertyAccessExpression(parent) && parent.expression === node && ['catch', 'then', 'finally'].includes(parent.name.text);\n hits.push({ node, name, detail: chained ? `chains .${(parent as ts.PropertyAccessExpression).name.text} onto ${name}` : '' });\n }\n });\n return hits;\n}\n\nexport const failureAuditGuard: GenericGuardDefinition<FailureAuditFacts> = {\n id: 'failureAuditChain',\n absentWhen: 'the app records no audit events',\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.failureEntryPoints, 'failureEntryPoints', problems);\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'no handler is chained onto a failure-audit entry point',\n description:\n `The failure-audit entry points (${facts.failureEntryPoints.join(', ')}) never throw — each logs and captures a ` +\n 'write that failed — so a `.catch`, `.then`, or `.finally` chained onto one is dead code that hides the contract, ' +\n 'and an aliased import hides the call from this check. Import each under its own name and call it bare. Every ' +\n 'declared entry point must still be called somewhere: one renamed away would pass unchecked.',\n run(context) {\n const names = new Set(facts.failureEntryPoints);\n const called = new Set<string>();\n const findings: Finding[] = [];\n for (const file of context.files(FUNCTIONS_CODE)) {\n for (const hit of chainedFailureAudits(file, names)) {\n if (hit.name) called.add(hit.name);\n if (hit.detail) findings.push({ file: file.path, line: lineOfNode(file, hit.node), detail: hit.detail });\n }\n }\n for (const name of names) if (!called.has(name)) findings.push({ file: '(failureEntryPoints)', detail: `${name} is called nowhere` });\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// No fire-and-forget work and no swallowed audit write.\n\nexport interface FireAndForgetFacts {\n /** The audit writer's success entry points, whose failure is never swallowed by a `.catch`. */\n auditWrites: readonly string[];\n /** Files whose `void`-ed calls are reviewed, each with its why; a file that no longer has one is stale. */\n voidSites: readonly ReviewedFile[];\n}\n\n/** Every `void`-ed call, every `.catch` chained onto an audit write, and every audit write call. */\nexport function fireAndForgetShapes(file: FileView, auditWrites: ReadonlySet<string>) {\n const sf = file.sourceFile;\n const voided: ts.Node[] = [];\n const swallowed: Array<{ node: ts.Node; name: string }> = [];\n const writes: string[] = [];\n forEachNode(sf, (node) => {\n if (ts.isVoidExpression(node) && ts.isCallExpression(unwrap(node.expression))) voided.push(node);\n if (!ts.isCallExpression(node)) return;\n const name = calleeName(node);\n if (name && auditWrites.has(name)) writes.push(name);\n if (name === 'catch' && ts.isPropertyAccessExpression(node.expression) && ts.isCallExpression(node.expression.expression)) {\n const inner = calleeName(node.expression.expression);\n if (inner && auditWrites.has(inner)) swallowed.push({ node, name: inner });\n }\n });\n return { voided, swallowed, writes };\n}\n\nexport const fireAndForgetGuard: GenericGuardDefinition<FireAndForgetFacts> = {\n id: 'fireAndForget',\n absentWhen: false,\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.auditWrites, 'auditWrites', problems, { nonEmpty: false });\n reviewedProblems(facts?.voidSites, 'voidSites', problems);\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'no backend work is fired and forgotten, and no audit write is swallowed',\n description:\n 'A `void`-ed call in a callable, trigger, or worker is never awaited and never has its failure captured, and the ' +\n 'runtime throttles the instance once it has answered, so the work may never run. Await it inside its own ' +\n 'try/catch with a capture, commit it in the primary transaction, or enqueue it to a durable outbox — a write ' +\n \"composed into a caller's transaction is awaited like any other. A `.catch` chained onto an audit write is a \" +\n 'second, silent owner of a failure the best-effort failure-audit entry points already own. A reviewed `void` ' +\n 'site is a file with its why; one with no `void`-ed call left is stale, and each declared audit write must still ' +\n 'be called somewhere.',\n run(context) {\n const auditWrites = new Set(facts.auditWrites);\n const reviewed = new Set(facts.voidSites.map((s) => s.path));\n const voidFiles = new Set<string>();\n const called = new Set<string>();\n const findings: Finding[] = [];\n for (const file of context.files(FUNCTIONS_CODE)) {\n const { voided, swallowed, writes } = fireAndForgetShapes(file, auditWrites);\n writes.forEach((w) => called.add(w));\n if (voided.length) voidFiles.add(file.path);\n if (!reviewed.has(file.path)) {\n for (const node of voided) findings.push({ file: file.path, line: lineOfNode(file, node), detail: `fire-and-forget: ${node.getText(file.sourceFile).slice(0, 80)}` });\n }\n for (const { node, name } of swallowed) findings.push({ file: file.path, line: lineOfNode(file, node), detail: `${name}(…).catch(…) swallows an audit write` });\n }\n for (const path of reviewed) if (!voidFiles.has(path)) findings.push({ file: path, detail: 'stale void site: the file has no void-ed call' });\n for (const name of auditWrites) if (!called.has(name)) findings.push({ file: '(auditWrites)', detail: `${name} is called nowhere` });\n return findings;\n },\n },\n ];\n },\n};\n"]}
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../src/modules/backend/audit.ts"],"names":[],"mappings":"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","sourcesContent":["// The audit guards: one writer of the audit collection, one builder of person-actors, no\n// member-written text in an audit payload, and no handler wrapped around an audit write.\n\nimport ts from 'typescript';\nimport type { GenericGuardDefinition } from '../../config.js';\nimport type { Finding, Guard, GuardContext } from '../../guard.js';\nimport type { Scope } from '../../reader/reader.js';\nimport type { FileView } from '../../reader/view.js';\nimport type { Reviewed } from '../../reader/layout.js';\nimport { ownerOnly, reviewedAllowlist, type Hit, type Matcher } from '../../shapes/shapes.js';\nimport { calleeName, forEachNode, isPlainString, lineOfNode, unwrap } from '../../support/ast.js';\nimport { namesProblem, reviewedProblems, textProblem } from '../../support/facts.js';\nimport { MODULE_LOADS, moduleImports, namesModule } from '../../support/imports.js';\nimport { serverZoneContext, serverZoneProblems, staleServerFolders, type ServerZone } from './shared.js';\n\nconst FUNCTIONS_CODE: Scope = { roots: ['functions'], kinds: ['code'], tests: false };\n\n// ---------------------------------------------------------------------------------------------\n// One audit writer.\n\nexport interface AuditWriterFacts {\n /** The one module that writes audit documents. */\n writer: string;\n /** The audit collection's name as stored (`auditEvents`). */\n collectionName: string;\n /** The core's handles on the collection, as `OWNER.member` (`COLLECTIONS.AUDIT_EVENTS`). */\n collectionHandles: readonly string[];\n /** Packages that write audit documents themselves; only the writer imports them. */\n writerPackages: readonly string[];\n /** Modules that name the collection to READ it, never to write; a stale entry fails. */\n readers: readonly Reviewed[];\n /** The one module that builds a person-actor, the field that says an actor's mode, and the person modes. */\n personActor: { constructors: string; modeField: string; modes: readonly string[] };\n /** Server code in the app root, held to the same rules as the Functions tree. */\n appServerZone: ServerZone;\n}\n\nconst AUDIT_WRITER_SCOPE: Scope = {\n roots: ['functions', 'scripts', 'app'],\n optionalRoots: ['scripts'],\n kinds: ['code'],\n tests: false,\n};\n\n/** Every place a file names the audit collection or imports a package that writes it. */\nexport function auditCollectionMatcher(facts: Pick<AuditWriterFacts, 'collectionName' | 'collectionHandles' | 'writerPackages'>): Matcher {\n const handles = new Set(facts.collectionHandles);\n const namesCollection = (text: string) => text === facts.collectionName || text.startsWith(`${facts.collectionName}/`);\n return (file) => {\n const hits: Hit[] = [];\n const sf = file.sourceFile;\n const loads = new Map(moduleImports(sf, MODULE_LOADS).map((site) => [site.node, site.spec]));\n forEachNode(sf, (node) => {\n if (ts.isPropertyAccessExpression(node) && handles.has(`${node.expression.getText(sf)}.${node.name.text}`)) {\n hits.push({ index: node.getStart(sf), detail: node.getText(sf) });\n }\n const specifier = loads.get(node);\n if (specifier !== undefined && facts.writerPackages.some((p) => namesModule(specifier, p))) {\n hits.push({ index: node.getStart(sf), detail: `imports ${specifier}` });\n return;\n }\n const isSpecifier = !!node.parent && (ts.isImportDeclaration(node.parent) || ts.isExportDeclaration(node.parent));\n if (isPlainString(node) && !isSpecifier && namesCollection(node.text)) {\n hits.push({ index: node.getStart(sf), detail: `'${node.text}'` });\n }\n if (ts.isTemplateExpression(node) && namesCollection(node.head.text)) {\n hits.push({ index: node.getStart(sf), detail: `${facts.collectionName} template path` });\n }\n });\n return hits;\n };\n}\n\n/** Every object literal that builds a person-actor by hand: its mode field set to a person mode. */\nexport function personActorMatcher(facts: AuditWriterFacts['personActor']): Matcher {\n const modes = new Set(facts.modes);\n return (file) => {\n const hits: Hit[] = [];\n const sf = file.sourceFile;\n forEachNode(sf, (node) => {\n if (!ts.isObjectLiteralExpression(node)) return;\n for (const prop of node.properties) {\n if (!ts.isPropertyAssignment(prop) || prop.name.getText(sf).replace(/^['\"]|['\"]$/g, '') !== facts.modeField) continue;\n const init = unwrap(prop.initializer);\n if (ts.isStringLiteralLike(init) && modes.has(init.text)) {\n hits.push({ index: prop.getStart(sf), detail: `${facts.modeField}: '${init.text}'` });\n }\n }\n });\n return hits;\n };\n}\n\nexport const auditWriterGuard: GenericGuardDefinition<AuditWriterFacts> = {\n id: 'auditWriter',\n absentWhen: 'the app records no audit events',\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.writer, 'writer', problems);\n textProblem(facts?.collectionName, 'collectionName', problems);\n namesProblem(facts?.collectionHandles, 'collectionHandles', problems);\n for (const handle of facts?.collectionHandles ?? []) {\n if (typeof handle === 'string' && !/^[\\w$]+\\.[\\w$]+$/.test(handle)) problems.push(`collection handle ${handle} is not OWNER.member`);\n }\n namesProblem(facts?.writerPackages, 'writerPackages', problems, { nonEmpty: false });\n reviewedProblems(facts?.readers, 'readers', problems);\n if (facts?.readers?.some((r) => r?.path === facts.writer)) problems.push('the writer is listed as a reader');\n textProblem(facts?.personActor?.constructors, 'personActor.constructors', problems);\n textProblem(facts?.personActor?.modeField, 'personActor.modeField', problems);\n namesProblem(facts?.personActor?.modes, 'personActor.modes', problems);\n serverZoneProblems(facts?.appServerZone, 'appServerZone', problems);\n return problems;\n },\n build(facts) {\n const collection = reviewedAllowlist({\n title: 'audit documents are written by the one audit writer',\n description:\n `Only ${facts.writer} names the audit collection (${facts.collectionName}, ${facts.collectionHandles.join(', ')}) ` +\n 'or imports a package that writes it; a second writer would skip the attribution, the request origin, and the ' +\n 'append-only rules the writer owns. Write audit events through the writer. A module that only READS the ' +\n 'collection is a reviewed reader with its why; a reader that stops naming the collection is stale.',\n scope: AUDIT_WRITER_SCOPE,\n match: auditCollectionMatcher(facts),\n allowlist: [{ path: facts.writer, why: 'the one audit writer' }, ...facts.readers],\n samples: {\n flags: [\n `db.collection('${facts.collectionName}').add(event);`,\n `db.doc(\\`${facts.collectionName}/\\${id}\\`).set(event);`,\n `db.collection(${facts.collectionHandles[0]}).doc(id).set(event);`,\n ],\n passes: [`// writes ${facts.collectionName} through ${facts.collectionHandles[0]}\\nconst label = '${facts.collectionName}Count';`],\n },\n });\n const actors = ownerOnly({\n title: 'person-actors are built only by the actor constructors',\n description:\n `Only ${facts.personActor.constructors} writes an object whose ${facts.personActor.modeField} is a person mode ` +\n `(${facts.personActor.modes.join(', ')}). A person-actor carries the origin of the request it acted through; ` +\n 'built by hand it can carry none, or a forged one. Build it with the actor constructors.',\n scope: AUDIT_WRITER_SCOPE,\n match: personActorMatcher(facts.personActor),\n owners: [facts.personActor.constructors],\n samples: {\n flags: [`const actor = { ${facts.personActor.modeField}: '${facts.personActor.modes[0]}' as const, uid };`],\n passes: [`type A = { ${facts.personActor.modeField}: '${facts.personActor.modes[0]}' };\\nconst b = { other${facts.personActor.modeField}: '${facts.personActor.modes[0]}' };`],\n },\n });\n const inZone = (guard: Guard): Guard => ({\n ...guard,\n async run(context: GuardContext) {\n return [...staleServerFolders(context, facts.appServerZone), ...(await guard.run(serverZoneContext(context, facts.appServerZone)))];\n },\n });\n return [inZone(collection), inZone(actors)];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// No member-written text in an audit payload.\n\nexport interface AuditPayloadFacts {\n /** The audit writer's entry points (`writeAuditEventAs`, the failure entry points). */\n entryPoints: readonly string[];\n /** The key under which an entry point takes its payload (`payload`). */\n payloadKey: string;\n /** Keys elsewhere whose object literal is handed to an entry point as its payload (`auditPayload`). */\n forwardedPayloadKeys: readonly string[];\n /** Prose-named keys an operator (never a member) writes, as `file#key`, each with its why. */\n operatorCopy: ReadonlyArray<{ site: string; why: string }>;\n}\n\n/** A key named like prose: a title, a name, a body of text. */\nconst PROSE_KEY = /(title|name|description|subject|body|text|message|content|caption|narrative|comment|bio|words)$/i;\n/** A map whose keys NAME fields rather than carry their text (`changedFieldLengths`). */\nconst NAMES_NOT_TEXT = /(Lengths|Fields)$/;\n\n/** Every prose-named key, at any depth, of the payload literals a file hands to an audit entry point. */\nexport function proseKeysInAuditPayloads(file: FileView, facts: AuditPayloadFacts): Array<{ key: string; node: ts.Node }> {\n const sf = file.sourceFile;\n const entryPoints = new Set(facts.entryPoints);\n const forwarded = new Set(facts.forwardedPayloadKeys);\n const hits: Array<{ key: string; node: ts.Node }> = [];\n const keyOf = (prop: ts.ObjectLiteralElementLike) => prop.name?.getText(sf).replace(/^['\"`]|['\"`]$/g, '') ?? '';\n const walkPayload = (obj: ts.ObjectLiteralExpression) => {\n for (const prop of obj.properties) {\n if (!ts.isPropertyAssignment(prop) && !ts.isShorthandPropertyAssignment(prop)) continue;\n const key = keyOf(prop);\n if (PROSE_KEY.test(key)) hits.push({ key, node: prop });\n if (ts.isPropertyAssignment(prop) && !NAMES_NOT_TEXT.test(key)) {\n const value = unwrap(prop.initializer);\n if (ts.isObjectLiteralExpression(value)) walkPayload(value);\n }\n }\n };\n const payloadOf = (details: ts.ObjectLiteralExpression, keys: ReadonlySet<string>) => {\n for (const prop of details.properties) {\n if (!ts.isPropertyAssignment(prop) || !keys.has(keyOf(prop))) continue;\n const value = unwrap(prop.initializer);\n if (ts.isObjectLiteralExpression(value)) walkPayload(value);\n }\n };\n const payloadKeys = new Set([facts.payloadKey]);\n forEachNode(sf, (node) => {\n if (ts.isCallExpression(node) && entryPoints.has(calleeName(node) ?? '')) {\n for (const arg of node.arguments) {\n const value = unwrap(arg);\n if (ts.isObjectLiteralExpression(value)) payloadOf(value, payloadKeys);\n }\n }\n if (ts.isObjectLiteralExpression(node)) payloadOf(node, forwarded);\n });\n return hits;\n}\n\nexport const auditPayloadGuard: GenericGuardDefinition<AuditPayloadFacts> = {\n id: 'auditPayload',\n absentWhen: 'the app records no audit events',\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.entryPoints, 'entryPoints', problems);\n textProblem(facts?.payloadKey, 'payloadKey', problems);\n namesProblem(facts?.forwardedPayloadKeys, 'forwardedPayloadKeys', problems, { nonEmpty: false });\n reviewedProblems(facts?.operatorCopy, 'operatorCopy', problems, { key: 'site' });\n for (const entry of facts?.operatorCopy ?? []) {\n if (typeof entry?.site === 'string' && !/^[^#]+#[^#]+$/.test(entry.site)) problems.push(`operatorCopy site ${entry.site} is not file#key`);\n }\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'no member-written text is copied into an audit payload',\n description:\n 'An audit payload records identifiers, field names, lengths, and decisions — never member-written text. An audit ' +\n 'event outlives every edit and every erasure, so a title, name, description, subject, body, or message copied ' +\n \"into one survives the member's own change and their account's deletion. Record the changed field names and \" +\n 'their lengths instead. A key under a `…Lengths` or `…Fields` map names fields and is exempt; text an operator ' +\n 'writes is a reviewed `file#key` entry with its why, and an entry that no longer occurs is stale.',\n run(context) {\n const findings: Finding[] = [];\n const reviewed = new Set(facts.operatorCopy.map((e) => e.site));\n const seen = new Set<string>();\n for (const file of context.files(FUNCTIONS_CODE)) {\n for (const { key, node } of proseKeysInAuditPayloads(file, facts)) {\n const site = `${file.path}#${key}`;\n seen.add(site);\n if (!reviewed.has(site)) findings.push({ file: file.path, line: lineOfNode(file, node), detail: `prose-named payload key ${key}` });\n }\n }\n for (const site of reviewed) if (!seen.has(site)) findings.push({ file: site, detail: 'stale operator-copy entry: the key no longer occurs' });\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// No handler chained onto a failure-audit entry point.\n\nexport interface FailureAuditFacts {\n /** The audit writer's best-effort failure entry points, which log and capture and never throw. */\n failureEntryPoints: readonly string[];\n}\n\n/** Each aliased import of a failure entry point, and each call with a handler chained onto it. */\nexport function chainedFailureAudits(file: FileView, names: ReadonlySet<string>): Array<{ node: ts.Node; detail: string; name?: string }> {\n const sf = file.sourceFile;\n const hits: Array<{ node: ts.Node; detail: string; name?: string }> = [];\n forEachNode(sf, (node) => {\n if (ts.isImportSpecifier(node) && node.propertyName && names.has(node.propertyName.text) && node.propertyName.text !== node.name.text) {\n hits.push({ node, detail: `imports ${node.propertyName.text} under another name` });\n }\n const name = ts.isCallExpression(node) ? calleeName(node) : undefined;\n if (ts.isCallExpression(node) && name && names.has(name)) {\n const parent = node.parent;\n const chained = ts.isPropertyAccessExpression(parent) && parent.expression === node && ['catch', 'then', 'finally'].includes(parent.name.text);\n hits.push({ node, name, detail: chained ? `chains .${(parent as ts.PropertyAccessExpression).name.text} onto ${name}` : '' });\n }\n });\n return hits;\n}\n\nexport const failureAuditGuard: GenericGuardDefinition<FailureAuditFacts> = {\n id: 'failureAuditChain',\n absentWhen: 'the app records no audit events',\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.failureEntryPoints, 'failureEntryPoints', problems);\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'no handler is chained onto a failure-audit entry point',\n description:\n `The failure-audit entry points (${facts.failureEntryPoints.join(', ')}) never throw — each logs and captures a ` +\n 'write that failed — so a `.catch`, `.then`, or `.finally` chained onto one is dead code that hides the contract, ' +\n 'and an aliased import hides the call from this check. Import each under its own name and call it bare. Every ' +\n 'declared entry point must still be called somewhere: one renamed away would pass unchecked.',\n run(context) {\n const names = new Set(facts.failureEntryPoints);\n const called = new Set<string>();\n const findings: Finding[] = [];\n for (const file of context.files(FUNCTIONS_CODE)) {\n for (const hit of chainedFailureAudits(file, names)) {\n if (hit.name) called.add(hit.name);\n if (hit.detail) findings.push({ file: file.path, line: lineOfNode(file, hit.node), detail: hit.detail });\n }\n }\n for (const name of names) if (!called.has(name)) findings.push({ file: '(failureEntryPoints)', detail: `${name} is called nowhere` });\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// No fire-and-forget work and no swallowed audit write.\n\nexport interface FireAndForgetFacts {\n /** The audit writer's success entry points, whose failure is never swallowed by a `.catch`. */\n auditWrites: readonly string[];\n /** Files whose `void`-ed calls are reviewed, each with its why; a file that no longer has one is stale. */\n voidSites: readonly Reviewed[];\n}\n\n/** Every `void`-ed call, every `.catch` chained onto an audit write, and every audit write call. */\nexport function fireAndForgetShapes(file: FileView, auditWrites: ReadonlySet<string>) {\n const sf = file.sourceFile;\n const voided: ts.Node[] = [];\n const swallowed: Array<{ node: ts.Node; name: string }> = [];\n const writes: string[] = [];\n forEachNode(sf, (node) => {\n if (ts.isVoidExpression(node) && ts.isCallExpression(unwrap(node.expression))) voided.push(node);\n if (!ts.isCallExpression(node)) return;\n const name = calleeName(node);\n if (name && auditWrites.has(name)) writes.push(name);\n if (name === 'catch' && ts.isPropertyAccessExpression(node.expression) && ts.isCallExpression(node.expression.expression)) {\n const inner = calleeName(node.expression.expression);\n if (inner && auditWrites.has(inner)) swallowed.push({ node, name: inner });\n }\n });\n return { voided, swallowed, writes };\n}\n\nexport const fireAndForgetGuard: GenericGuardDefinition<FireAndForgetFacts> = {\n id: 'fireAndForget',\n absentWhen: false,\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.auditWrites, 'auditWrites', problems, { nonEmpty: false });\n reviewedProblems(facts?.voidSites, 'voidSites', problems);\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'no backend work is fired and forgotten, and no audit write is swallowed',\n description:\n 'A `void`-ed call in a callable, trigger, or worker is never awaited and never has its failure captured, and the ' +\n 'runtime throttles the instance once it has answered, so the work may never run. Await it inside its own ' +\n 'try/catch with a capture, commit it in the primary transaction, or enqueue it to a durable outbox — a write ' +\n \"composed into a caller's transaction is awaited like any other. A `.catch` chained onto an audit write is a \" +\n 'second, silent owner of a failure the best-effort failure-audit entry points already own. A reviewed `void` ' +\n 'site is a file with its why; one with no `void`-ed call left is stale, and each declared audit write must still ' +\n 'be called somewhere.',\n run(context) {\n const auditWrites = new Set(facts.auditWrites);\n const reviewed = new Set(facts.voidSites.map((s) => s.path));\n const voidFiles = new Set<string>();\n const called = new Set<string>();\n const findings: Finding[] = [];\n for (const file of context.files(FUNCTIONS_CODE)) {\n const { voided, swallowed, writes } = fireAndForgetShapes(file, auditWrites);\n writes.forEach((w) => called.add(w));\n if (voided.length) voidFiles.add(file.path);\n if (!reviewed.has(file.path)) {\n for (const node of voided) findings.push({ file: file.path, line: lineOfNode(file, node), detail: `fire-and-forget: ${node.getText(file.sourceFile).slice(0, 80)}` });\n }\n for (const { node, name } of swallowed) findings.push({ file: file.path, line: lineOfNode(file, node), detail: `${name}(…).catch(…) swallows an audit write` });\n }\n for (const path of reviewed) if (!voidFiles.has(path)) findings.push({ file: path, detail: 'stale void site: the file has no void-ed call' });\n for (const name of auditWrites) if (!called.has(name)) findings.push({ file: '(auditWrites)', detail: `${name} is called nowhere` });\n return findings;\n },\n },\n ];\n },\n};\n"]}
@@ -1,13 +1,15 @@
1
1
  import ts from 'typescript';
2
2
  import type { GenericGuardDefinition } from '../../config.js';
3
+ import type { Reviewed } from '../../reader/layout.js';
3
4
  import type { FileView } from '../../reader/view.js';
4
5
  import { type Matcher } from '../../shapes/shapes.js';
5
- import { type ReviewedFile, type ServerZone } from './shared.js';
6
+ import { type CallableDeclarationFacts } from '../frontend-actions/declarations.js';
7
+ import { type ServerZone } from './shared.js';
6
8
  export interface AdminClaimFacts {
7
9
  /** The token claims that say a caller is an admin (`isAdmin`). */
8
10
  claimKeys: readonly string[];
9
11
  /** The modules that may read the claim — the resolver, and metadata-only copies — each with its why. */
10
- readers: readonly ReviewedFile[];
12
+ readers: readonly Reviewed[];
11
13
  /** Server code in the app root, held to the same rule as the Functions tree. */
12
14
  appServerZone: ServerZone;
13
15
  }
@@ -39,6 +41,13 @@ export interface AppCheckFacts {
39
41
  callable: string;
40
42
  why: string;
41
43
  }>;
44
+ /** The app's callable declarations, which present the token the server consumes. */
45
+ clientDeclarations: CallableDeclarationFacts;
46
+ /** Consuming callables no client declaration names (no client calls them), each with its why. */
47
+ noClientDeclaration: ReadonlyArray<{
48
+ callable: string;
49
+ why: string;
50
+ }>;
42
51
  }
43
52
  interface Callable {
44
53
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/modules/backend/auth.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAG9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAA0C,KAAK,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAYL,KAAK,YAAY,EACjB,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AASrB,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,wGAAwG;IACxG,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,gFAAgF;IAChF,aAAa,EAAE,UAAU,CAAC;CAC3B;AAKD,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAevE;AAED,eAAO,MAAM,eAAe,EAAE,sBAAsB,CAAC,eAAe,CAyCnE,CAAC;AAKF,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;CAChB;AAKD,eAAO,MAAM,kBAAkB,EAAE,sBAAsB,CAAC,kBAAkB,CAgCzE,CAAC;AAKF,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAK5D,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAS9F;AAED,eAAO,MAAM,uBAAuB,EAAE,sBAAsB,CAAC,uBAAuB,CAuBnF,CAAC;AAKF,MAAM,WAAW,aAAa;IAC5B,4GAA4G;IAC5G,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,sGAAsG;IACtG,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,6GAA6G;IAC7G,iBAAiB,EAAE,aAAa,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrE;AAED,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACd,OAAO,EAAE,EAAE,CAAC,uBAAuB,GAAG,IAAI,CAAC;CAC5C;AAUD,+GAA+G;AAC/G,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,CA6BtD;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,OAAO,CAS3F;AAED,eAAO,MAAM,aAAa,EAAE,sBAAsB,CAAC,aAAa,CA2D/D,CAAC"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/modules/backend/auth.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAG9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAA0C,KAAK,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAK9F,OAAO,EAA6C,KAAK,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/H,OAAO,EAA6D,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAOzG,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,wGAAwG;IACxG,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC7B,gFAAgF;IAChF,aAAa,EAAE,UAAU,CAAC;CAC3B;AAKD,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAevE;AAED,eAAO,MAAM,eAAe,EAAE,sBAAsB,CAAC,eAAe,CAwCnE,CAAC;AAKF,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;CAChB;AAKD,eAAO,MAAM,kBAAkB,EAAE,sBAAsB,CAAC,kBAAkB,CAgCzE,CAAC;AAKF,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAK5D,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAS9F;AAED,eAAO,MAAM,uBAAuB,EAAE,sBAAsB,CAAC,uBAAuB,CAqBnF,CAAC;AAKF,MAAM,WAAW,aAAa;IAC5B,4GAA4G;IAC5G,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,sGAAsG;IACtG,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,6GAA6G;IAC7G,iBAAiB,EAAE,aAAa,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpE,oFAAoF;IACpF,kBAAkB,EAAE,wBAAwB,CAAC;IAC7C,iGAAiG;IACjG,mBAAmB,EAAE,aAAa,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvE;AAED,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACd,OAAO,EAAE,EAAE,CAAC,uBAAuB,GAAG,IAAI,CAAC;CAC5C;AAUD,+GAA+G;AAC/G,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,CA6BtD;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,OAAO,CAS3F;AAED,eAAO,MAAM,aAAa,EAAE,sBAAsB,CAAC,aAAa,CA0G/D,CAAC"}