@fuzdev/fuz_app 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (457) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -0
  3. package/dist/actions/action_bridge.d.ts +65 -0
  4. package/dist/actions/action_bridge.d.ts.map +1 -0
  5. package/dist/actions/action_bridge.js +76 -0
  6. package/dist/actions/action_codegen.d.ts +97 -0
  7. package/dist/actions/action_codegen.d.ts.map +1 -0
  8. package/dist/actions/action_codegen.js +280 -0
  9. package/dist/actions/action_registry.d.ts +35 -0
  10. package/dist/actions/action_registry.d.ts.map +1 -0
  11. package/dist/actions/action_registry.js +83 -0
  12. package/dist/actions/action_spec.d.ts +169 -0
  13. package/dist/actions/action_spec.d.ts.map +1 -0
  14. package/dist/actions/action_spec.js +76 -0
  15. package/dist/auth/account_queries.d.ts +96 -0
  16. package/dist/auth/account_queries.d.ts.map +1 -0
  17. package/dist/auth/account_queries.js +172 -0
  18. package/dist/auth/account_routes.d.ts +86 -0
  19. package/dist/auth/account_routes.d.ts.map +1 -0
  20. package/dist/auth/account_routes.js +406 -0
  21. package/dist/auth/account_schema.d.ts +192 -0
  22. package/dist/auth/account_schema.d.ts.map +1 -0
  23. package/dist/auth/account_schema.js +105 -0
  24. package/dist/auth/admin_routes.d.ts +29 -0
  25. package/dist/auth/admin_routes.d.ts.map +1 -0
  26. package/dist/auth/admin_routes.js +193 -0
  27. package/dist/auth/api_token.d.ts +33 -0
  28. package/dist/auth/api_token.d.ts.map +1 -0
  29. package/dist/auth/api_token.js +36 -0
  30. package/dist/auth/api_token_queries.d.ts +80 -0
  31. package/dist/auth/api_token_queries.d.ts.map +1 -0
  32. package/dist/auth/api_token_queries.js +116 -0
  33. package/dist/auth/app_settings_queries.d.ts +33 -0
  34. package/dist/auth/app_settings_queries.d.ts.map +1 -0
  35. package/dist/auth/app_settings_queries.js +51 -0
  36. package/dist/auth/app_settings_routes.d.ts +27 -0
  37. package/dist/auth/app_settings_routes.d.ts.map +1 -0
  38. package/dist/auth/app_settings_routes.js +66 -0
  39. package/dist/auth/app_settings_schema.d.ts +35 -0
  40. package/dist/auth/app_settings_schema.d.ts.map +1 -0
  41. package/dist/auth/app_settings_schema.js +22 -0
  42. package/dist/auth/audit_log_queries.d.ts +90 -0
  43. package/dist/auth/audit_log_queries.d.ts.map +1 -0
  44. package/dist/auth/audit_log_queries.js +205 -0
  45. package/dist/auth/audit_log_routes.d.ts +33 -0
  46. package/dist/auth/audit_log_routes.d.ts.map +1 -0
  47. package/dist/auth/audit_log_routes.js +106 -0
  48. package/dist/auth/audit_log_schema.d.ts +259 -0
  49. package/dist/auth/audit_log_schema.d.ts.map +1 -0
  50. package/dist/auth/audit_log_schema.js +123 -0
  51. package/dist/auth/bearer_auth.d.ts +32 -0
  52. package/dist/auth/bearer_auth.d.ts.map +1 -0
  53. package/dist/auth/bearer_auth.js +90 -0
  54. package/dist/auth/bootstrap_account.d.ts +82 -0
  55. package/dist/auth/bootstrap_account.d.ts.map +1 -0
  56. package/dist/auth/bootstrap_account.js +97 -0
  57. package/dist/auth/bootstrap_routes.d.ts +74 -0
  58. package/dist/auth/bootstrap_routes.d.ts.map +1 -0
  59. package/dist/auth/bootstrap_routes.js +154 -0
  60. package/dist/auth/daemon_token.d.ts +49 -0
  61. package/dist/auth/daemon_token.d.ts.map +1 -0
  62. package/dist/auth/daemon_token.js +49 -0
  63. package/dist/auth/daemon_token_middleware.d.ts +93 -0
  64. package/dist/auth/daemon_token_middleware.d.ts.map +1 -0
  65. package/dist/auth/daemon_token_middleware.js +167 -0
  66. package/dist/auth/ddl.d.ts +27 -0
  67. package/dist/auth/ddl.d.ts.map +1 -0
  68. package/dist/auth/ddl.js +111 -0
  69. package/dist/auth/deps.d.ts +52 -0
  70. package/dist/auth/deps.d.ts.map +1 -0
  71. package/dist/auth/deps.js +10 -0
  72. package/dist/auth/invite_queries.d.ts +68 -0
  73. package/dist/auth/invite_queries.d.ts.map +1 -0
  74. package/dist/auth/invite_queries.js +105 -0
  75. package/dist/auth/invite_routes.d.ts +18 -0
  76. package/dist/auth/invite_routes.d.ts.map +1 -0
  77. package/dist/auth/invite_routes.js +129 -0
  78. package/dist/auth/invite_schema.d.ts +51 -0
  79. package/dist/auth/invite_schema.d.ts.map +1 -0
  80. package/dist/auth/invite_schema.js +25 -0
  81. package/dist/auth/keyring.d.ts +87 -0
  82. package/dist/auth/keyring.d.ts.map +1 -0
  83. package/dist/auth/keyring.js +142 -0
  84. package/dist/auth/middleware.d.ts +40 -0
  85. package/dist/auth/middleware.d.ts.map +1 -0
  86. package/dist/auth/middleware.js +64 -0
  87. package/dist/auth/migrations.d.ts +42 -0
  88. package/dist/auth/migrations.d.ts.map +1 -0
  89. package/dist/auth/migrations.js +79 -0
  90. package/dist/auth/password.d.ts +39 -0
  91. package/dist/auth/password.d.ts.map +1 -0
  92. package/dist/auth/password.js +25 -0
  93. package/dist/auth/password_argon2.d.ts +43 -0
  94. package/dist/auth/password_argon2.d.ts.map +1 -0
  95. package/dist/auth/password_argon2.js +76 -0
  96. package/dist/auth/permit_queries.d.ts +72 -0
  97. package/dist/auth/permit_queries.d.ts.map +1 -0
  98. package/dist/auth/permit_queries.js +116 -0
  99. package/dist/auth/request_context.d.ts +114 -0
  100. package/dist/auth/request_context.d.ts.map +1 -0
  101. package/dist/auth/request_context.js +176 -0
  102. package/dist/auth/require_keeper.d.ts +20 -0
  103. package/dist/auth/require_keeper.d.ts.map +1 -0
  104. package/dist/auth/require_keeper.js +35 -0
  105. package/dist/auth/role_schema.d.ts +69 -0
  106. package/dist/auth/role_schema.d.ts.map +1 -0
  107. package/dist/auth/role_schema.js +70 -0
  108. package/dist/auth/route_guards.d.ts +21 -0
  109. package/dist/auth/route_guards.d.ts.map +1 -0
  110. package/dist/auth/route_guards.js +32 -0
  111. package/dist/auth/session_cookie.d.ts +158 -0
  112. package/dist/auth/session_cookie.d.ts.map +1 -0
  113. package/dist/auth/session_cookie.js +135 -0
  114. package/dist/auth/session_lifecycle.d.ts +35 -0
  115. package/dist/auth/session_lifecycle.d.ts.map +1 -0
  116. package/dist/auth/session_lifecycle.js +27 -0
  117. package/dist/auth/session_middleware.d.ts +33 -0
  118. package/dist/auth/session_middleware.d.ts.map +1 -0
  119. package/dist/auth/session_middleware.js +62 -0
  120. package/dist/auth/session_queries.d.ts +135 -0
  121. package/dist/auth/session_queries.d.ts.map +1 -0
  122. package/dist/auth/session_queries.js +186 -0
  123. package/dist/auth/signup_routes.d.ts +32 -0
  124. package/dist/auth/signup_routes.d.ts.map +1 -0
  125. package/dist/auth/signup_routes.js +150 -0
  126. package/dist/cli/args.d.ts +48 -0
  127. package/dist/cli/args.d.ts.map +1 -0
  128. package/dist/cli/args.js +76 -0
  129. package/dist/cli/config.d.ts +48 -0
  130. package/dist/cli/config.d.ts.map +1 -0
  131. package/dist/cli/config.js +77 -0
  132. package/dist/cli/daemon.d.ts +82 -0
  133. package/dist/cli/daemon.d.ts.map +1 -0
  134. package/dist/cli/daemon.js +149 -0
  135. package/dist/cli/help.d.ts +85 -0
  136. package/dist/cli/help.d.ts.map +1 -0
  137. package/dist/cli/help.js +138 -0
  138. package/dist/cli/logger.d.ts +46 -0
  139. package/dist/cli/logger.d.ts.map +1 -0
  140. package/dist/cli/logger.js +48 -0
  141. package/dist/cli/util.d.ts +36 -0
  142. package/dist/cli/util.d.ts.map +1 -0
  143. package/dist/cli/util.js +50 -0
  144. package/dist/crypto.d.ts +13 -0
  145. package/dist/crypto.d.ts.map +1 -0
  146. package/dist/crypto.js +19 -0
  147. package/dist/db/assert_row.d.ts +18 -0
  148. package/dist/db/assert_row.d.ts.map +1 -0
  149. package/dist/db/assert_row.js +24 -0
  150. package/dist/db/create_db.d.ts +38 -0
  151. package/dist/db/create_db.d.ts.map +1 -0
  152. package/dist/db/create_db.js +57 -0
  153. package/dist/db/db.d.ts +97 -0
  154. package/dist/db/db.d.ts.map +1 -0
  155. package/dist/db/db.js +76 -0
  156. package/dist/db/db_pg.d.ts +21 -0
  157. package/dist/db/db_pg.d.ts.map +1 -0
  158. package/dist/db/db_pg.js +45 -0
  159. package/dist/db/db_pglite.d.ts +21 -0
  160. package/dist/db/db_pglite.d.ts.map +1 -0
  161. package/dist/db/db_pglite.js +28 -0
  162. package/dist/db/migrate.d.ts +67 -0
  163. package/dist/db/migrate.d.ts.map +1 -0
  164. package/dist/db/migrate.js +118 -0
  165. package/dist/db/pg_error.d.ts +16 -0
  166. package/dist/db/pg_error.d.ts.map +1 -0
  167. package/dist/db/pg_error.js +15 -0
  168. package/dist/db/query_deps.d.ts +14 -0
  169. package/dist/db/query_deps.d.ts.map +1 -0
  170. package/dist/db/query_deps.js +9 -0
  171. package/dist/db/sql_identifier.d.ts +27 -0
  172. package/dist/db/sql_identifier.d.ts.map +1 -0
  173. package/dist/db/sql_identifier.js +31 -0
  174. package/dist/db/status.d.ts +62 -0
  175. package/dist/db/status.d.ts.map +1 -0
  176. package/dist/db/status.js +116 -0
  177. package/dist/dev/setup.d.ts +159 -0
  178. package/dist/dev/setup.d.ts.map +1 -0
  179. package/dist/dev/setup.js +265 -0
  180. package/dist/env/dotenv.d.ts +25 -0
  181. package/dist/env/dotenv.d.ts.map +1 -0
  182. package/dist/env/dotenv.js +52 -0
  183. package/dist/env/load.d.ts +52 -0
  184. package/dist/env/load.d.ts.map +1 -0
  185. package/dist/env/load.js +79 -0
  186. package/dist/env/mask.d.ts +19 -0
  187. package/dist/env/mask.d.ts.map +1 -0
  188. package/dist/env/mask.js +26 -0
  189. package/dist/env/resolve.d.ts +126 -0
  190. package/dist/env/resolve.d.ts.map +1 -0
  191. package/dist/env/resolve.js +200 -0
  192. package/dist/hono_context.d.ts +48 -0
  193. package/dist/hono_context.d.ts.map +1 -0
  194. package/dist/hono_context.js +22 -0
  195. package/dist/http/common_routes.d.ts +52 -0
  196. package/dist/http/common_routes.d.ts.map +1 -0
  197. package/dist/http/common_routes.js +65 -0
  198. package/dist/http/db_routes.d.ts +57 -0
  199. package/dist/http/db_routes.d.ts.map +1 -0
  200. package/dist/http/db_routes.js +176 -0
  201. package/dist/http/error_schemas.d.ts +169 -0
  202. package/dist/http/error_schemas.d.ts.map +1 -0
  203. package/dist/http/error_schemas.js +178 -0
  204. package/dist/http/middleware_spec.d.ts +19 -0
  205. package/dist/http/middleware_spec.d.ts.map +1 -0
  206. package/dist/http/middleware_spec.js +9 -0
  207. package/dist/http/origin.d.ts +57 -0
  208. package/dist/http/origin.d.ts.map +1 -0
  209. package/dist/http/origin.js +207 -0
  210. package/dist/http/proxy.d.ts +112 -0
  211. package/dist/http/proxy.d.ts.map +1 -0
  212. package/dist/http/proxy.js +240 -0
  213. package/dist/http/route_spec.d.ts +197 -0
  214. package/dist/http/route_spec.d.ts.map +1 -0
  215. package/dist/http/route_spec.js +243 -0
  216. package/dist/http/schema_helpers.d.ts +64 -0
  217. package/dist/http/schema_helpers.d.ts.map +1 -0
  218. package/dist/http/schema_helpers.js +90 -0
  219. package/dist/http/surface.d.ts +132 -0
  220. package/dist/http/surface.d.ts.map +1 -0
  221. package/dist/http/surface.js +156 -0
  222. package/dist/http/surface_query.d.ts +77 -0
  223. package/dist/http/surface_query.d.ts.map +1 -0
  224. package/dist/http/surface_query.js +86 -0
  225. package/dist/rate_limiter.d.ts +94 -0
  226. package/dist/rate_limiter.d.ts.map +1 -0
  227. package/dist/rate_limiter.js +156 -0
  228. package/dist/realtime/sse.d.ts +80 -0
  229. package/dist/realtime/sse.d.ts.map +1 -0
  230. package/dist/realtime/sse.js +109 -0
  231. package/dist/realtime/sse_auth_guard.d.ts +93 -0
  232. package/dist/realtime/sse_auth_guard.d.ts.map +1 -0
  233. package/dist/realtime/sse_auth_guard.js +111 -0
  234. package/dist/realtime/subscriber_registry.d.ts +85 -0
  235. package/dist/realtime/subscriber_registry.d.ts.map +1 -0
  236. package/dist/realtime/subscriber_registry.js +108 -0
  237. package/dist/runtime/deno.d.ts +21 -0
  238. package/dist/runtime/deno.d.ts.map +1 -0
  239. package/dist/runtime/deno.js +83 -0
  240. package/dist/runtime/deps.d.ts +113 -0
  241. package/dist/runtime/deps.d.ts.map +1 -0
  242. package/dist/runtime/deps.js +10 -0
  243. package/dist/runtime/fs.d.ts +15 -0
  244. package/dist/runtime/fs.d.ts.map +1 -0
  245. package/dist/runtime/fs.js +17 -0
  246. package/dist/runtime/mock.d.ts +81 -0
  247. package/dist/runtime/mock.d.ts.map +1 -0
  248. package/dist/runtime/mock.js +195 -0
  249. package/dist/runtime/node.d.ts +17 -0
  250. package/dist/runtime/node.d.ts.map +1 -0
  251. package/dist/runtime/node.js +117 -0
  252. package/dist/schema_meta.d.ts +16 -0
  253. package/dist/schema_meta.d.ts.map +1 -0
  254. package/dist/schema_meta.js +9 -0
  255. package/dist/sensitivity.d.ts +15 -0
  256. package/dist/sensitivity.d.ts.map +1 -0
  257. package/dist/sensitivity.js +9 -0
  258. package/dist/server/app_backend.d.ts +74 -0
  259. package/dist/server/app_backend.d.ts.map +1 -0
  260. package/dist/server/app_backend.js +39 -0
  261. package/dist/server/app_server.d.ts +201 -0
  262. package/dist/server/app_server.d.ts.map +1 -0
  263. package/dist/server/app_server.js +266 -0
  264. package/dist/server/env.d.ts +68 -0
  265. package/dist/server/env.d.ts.map +1 -0
  266. package/dist/server/env.js +95 -0
  267. package/dist/server/startup.d.ts +22 -0
  268. package/dist/server/startup.d.ts.map +1 -0
  269. package/dist/server/startup.js +48 -0
  270. package/dist/server/static.d.ts +39 -0
  271. package/dist/server/static.d.ts.map +1 -0
  272. package/dist/server/static.js +38 -0
  273. package/dist/server/validate_nginx.d.ts +34 -0
  274. package/dist/server/validate_nginx.d.ts.map +1 -0
  275. package/dist/server/validate_nginx.js +118 -0
  276. package/dist/testing/CLAUDE.md +3 -0
  277. package/dist/testing/admin_integration.d.ts +45 -0
  278. package/dist/testing/admin_integration.d.ts.map +1 -0
  279. package/dist/testing/admin_integration.js +840 -0
  280. package/dist/testing/adversarial_404.d.ts +15 -0
  281. package/dist/testing/adversarial_404.d.ts.map +1 -0
  282. package/dist/testing/adversarial_404.js +118 -0
  283. package/dist/testing/adversarial_headers.d.ts +36 -0
  284. package/dist/testing/adversarial_headers.d.ts.map +1 -0
  285. package/dist/testing/adversarial_headers.js +128 -0
  286. package/dist/testing/adversarial_input.d.ts +56 -0
  287. package/dist/testing/adversarial_input.d.ts.map +1 -0
  288. package/dist/testing/adversarial_input.js +494 -0
  289. package/dist/testing/app_server.d.ts +169 -0
  290. package/dist/testing/app_server.d.ts.map +1 -0
  291. package/dist/testing/app_server.js +240 -0
  292. package/dist/testing/assert_dev_env.d.ts +10 -0
  293. package/dist/testing/assert_dev_env.d.ts.map +1 -0
  294. package/dist/testing/assert_dev_env.js +13 -0
  295. package/dist/testing/assertions.d.ts +61 -0
  296. package/dist/testing/assertions.d.ts.map +1 -0
  297. package/dist/testing/assertions.js +96 -0
  298. package/dist/testing/attack_surface.d.ts +63 -0
  299. package/dist/testing/attack_surface.d.ts.map +1 -0
  300. package/dist/testing/attack_surface.js +224 -0
  301. package/dist/testing/audit_completeness.d.ts +29 -0
  302. package/dist/testing/audit_completeness.d.ts.map +1 -0
  303. package/dist/testing/audit_completeness.js +410 -0
  304. package/dist/testing/auth_apps.d.ts +55 -0
  305. package/dist/testing/auth_apps.d.ts.map +1 -0
  306. package/dist/testing/auth_apps.js +122 -0
  307. package/dist/testing/data_exposure.d.ts +62 -0
  308. package/dist/testing/data_exposure.d.ts.map +1 -0
  309. package/dist/testing/data_exposure.js +297 -0
  310. package/dist/testing/db.d.ts +111 -0
  311. package/dist/testing/db.d.ts.map +1 -0
  312. package/dist/testing/db.js +258 -0
  313. package/dist/testing/entities.d.ts +21 -0
  314. package/dist/testing/entities.d.ts.map +1 -0
  315. package/dist/testing/entities.js +42 -0
  316. package/dist/testing/error_coverage.d.ts +78 -0
  317. package/dist/testing/error_coverage.d.ts.map +1 -0
  318. package/dist/testing/error_coverage.js +135 -0
  319. package/dist/testing/integration.d.ts +37 -0
  320. package/dist/testing/integration.d.ts.map +1 -0
  321. package/dist/testing/integration.js +1139 -0
  322. package/dist/testing/integration_helpers.d.ts +107 -0
  323. package/dist/testing/integration_helpers.d.ts.map +1 -0
  324. package/dist/testing/integration_helpers.js +246 -0
  325. package/dist/testing/middleware.d.ts +125 -0
  326. package/dist/testing/middleware.d.ts.map +1 -0
  327. package/dist/testing/middleware.js +210 -0
  328. package/dist/testing/rate_limiting.d.ts +43 -0
  329. package/dist/testing/rate_limiting.d.ts.map +1 -0
  330. package/dist/testing/rate_limiting.js +216 -0
  331. package/dist/testing/round_trip.d.ts +37 -0
  332. package/dist/testing/round_trip.d.ts.map +1 -0
  333. package/dist/testing/round_trip.js +128 -0
  334. package/dist/testing/schema_generators.d.ts +33 -0
  335. package/dist/testing/schema_generators.d.ts.map +1 -0
  336. package/dist/testing/schema_generators.js +137 -0
  337. package/dist/testing/standard.d.ts +49 -0
  338. package/dist/testing/standard.d.ts.map +1 -0
  339. package/dist/testing/standard.js +16 -0
  340. package/dist/testing/stubs.d.ts +96 -0
  341. package/dist/testing/stubs.d.ts.map +1 -0
  342. package/dist/testing/stubs.js +192 -0
  343. package/dist/testing/surface_invariants.d.ts +189 -0
  344. package/dist/testing/surface_invariants.d.ts.map +1 -0
  345. package/dist/testing/surface_invariants.js +450 -0
  346. package/dist/ui/AccountSessions.svelte +75 -0
  347. package/dist/ui/AccountSessions.svelte.d.ts +19 -0
  348. package/dist/ui/AccountSessions.svelte.d.ts.map +1 -0
  349. package/dist/ui/AdminAccounts.svelte +107 -0
  350. package/dist/ui/AdminAccounts.svelte.d.ts +19 -0
  351. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -0
  352. package/dist/ui/AdminAuditLog.svelte +144 -0
  353. package/dist/ui/AdminAuditLog.svelte.d.ts +4 -0
  354. package/dist/ui/AdminAuditLog.svelte.d.ts.map +1 -0
  355. package/dist/ui/AdminInvites.svelte +142 -0
  356. package/dist/ui/AdminInvites.svelte.d.ts +4 -0
  357. package/dist/ui/AdminInvites.svelte.d.ts.map +1 -0
  358. package/dist/ui/AdminOverview.svelte +337 -0
  359. package/dist/ui/AdminOverview.svelte.d.ts +4 -0
  360. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -0
  361. package/dist/ui/AdminPermitHistory.svelte +61 -0
  362. package/dist/ui/AdminPermitHistory.svelte.d.ts +19 -0
  363. package/dist/ui/AdminPermitHistory.svelte.d.ts.map +1 -0
  364. package/dist/ui/AdminSessions.svelte +85 -0
  365. package/dist/ui/AdminSessions.svelte.d.ts +19 -0
  366. package/dist/ui/AdminSessions.svelte.d.ts.map +1 -0
  367. package/dist/ui/AdminSettings.svelte +32 -0
  368. package/dist/ui/AdminSettings.svelte.d.ts +19 -0
  369. package/dist/ui/AdminSettings.svelte.d.ts.map +1 -0
  370. package/dist/ui/AdminSurface.svelte +42 -0
  371. package/dist/ui/AdminSurface.svelte.d.ts +4 -0
  372. package/dist/ui/AdminSurface.svelte.d.ts.map +1 -0
  373. package/dist/ui/AppShell.svelte +93 -0
  374. package/dist/ui/AppShell.svelte.d.ts +20 -0
  375. package/dist/ui/AppShell.svelte.d.ts.map +1 -0
  376. package/dist/ui/BootstrapForm.svelte +105 -0
  377. package/dist/ui/BootstrapForm.svelte.d.ts +4 -0
  378. package/dist/ui/BootstrapForm.svelte.d.ts.map +1 -0
  379. package/dist/ui/ColumnLayout.svelte +46 -0
  380. package/dist/ui/ColumnLayout.svelte.d.ts +11 -0
  381. package/dist/ui/ColumnLayout.svelte.d.ts.map +1 -0
  382. package/dist/ui/ConfirmButton.svelte +125 -0
  383. package/dist/ui/ConfirmButton.svelte.d.ts +54 -0
  384. package/dist/ui/ConfirmButton.svelte.d.ts.map +1 -0
  385. package/dist/ui/Datatable.svelte +185 -0
  386. package/dist/ui/Datatable.svelte.d.ts +35 -0
  387. package/dist/ui/Datatable.svelte.d.ts.map +1 -0
  388. package/dist/ui/LoginForm.svelte +82 -0
  389. package/dist/ui/LoginForm.svelte.d.ts +8 -0
  390. package/dist/ui/LoginForm.svelte.d.ts.map +1 -0
  391. package/dist/ui/LogoutButton.svelte +36 -0
  392. package/dist/ui/LogoutButton.svelte.d.ts +10 -0
  393. package/dist/ui/LogoutButton.svelte.d.ts.map +1 -0
  394. package/dist/ui/MenuLink.svelte +35 -0
  395. package/dist/ui/MenuLink.svelte.d.ts +12 -0
  396. package/dist/ui/MenuLink.svelte.d.ts.map +1 -0
  397. package/dist/ui/OpenSignupToggle.svelte +36 -0
  398. package/dist/ui/OpenSignupToggle.svelte.d.ts +19 -0
  399. package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -0
  400. package/dist/ui/PopoverButton.svelte +136 -0
  401. package/dist/ui/PopoverButton.svelte.d.ts +63 -0
  402. package/dist/ui/PopoverButton.svelte.d.ts.map +1 -0
  403. package/dist/ui/SignupForm.svelte +117 -0
  404. package/dist/ui/SignupForm.svelte.d.ts +7 -0
  405. package/dist/ui/SignupForm.svelte.d.ts.map +1 -0
  406. package/dist/ui/SurfaceExplorer.svelte +287 -0
  407. package/dist/ui/SurfaceExplorer.svelte.d.ts +8 -0
  408. package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -0
  409. package/dist/ui/account_sessions_state.svelte.d.ts +15 -0
  410. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -0
  411. package/dist/ui/account_sessions_state.svelte.js +45 -0
  412. package/dist/ui/admin_accounts_state.svelte.d.ts +19 -0
  413. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -0
  414. package/dist/ui/admin_accounts_state.svelte.js +65 -0
  415. package/dist/ui/admin_invites_state.svelte.d.ts +19 -0
  416. package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -0
  417. package/dist/ui/admin_invites_state.svelte.js +71 -0
  418. package/dist/ui/admin_sessions_state.svelte.d.ts +18 -0
  419. package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -0
  420. package/dist/ui/admin_sessions_state.svelte.js +62 -0
  421. package/dist/ui/app_settings_state.svelte.d.ts +14 -0
  422. package/dist/ui/app_settings_state.svelte.d.ts.map +1 -0
  423. package/dist/ui/app_settings_state.svelte.js +44 -0
  424. package/dist/ui/audit_log_state.svelte.d.ts +40 -0
  425. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -0
  426. package/dist/ui/audit_log_state.svelte.js +153 -0
  427. package/dist/ui/auth_state.svelte.d.ts +85 -0
  428. package/dist/ui/auth_state.svelte.d.ts.map +1 -0
  429. package/dist/ui/auth_state.svelte.js +238 -0
  430. package/dist/ui/datatable.d.ts +25 -0
  431. package/dist/ui/datatable.d.ts.map +1 -0
  432. package/dist/ui/datatable.js +9 -0
  433. package/dist/ui/enter_advance.d.ts +13 -0
  434. package/dist/ui/enter_advance.d.ts.map +1 -0
  435. package/dist/ui/enter_advance.js +30 -0
  436. package/dist/ui/loadable.svelte.d.ts +55 -0
  437. package/dist/ui/loadable.svelte.d.ts.map +1 -0
  438. package/dist/ui/loadable.svelte.js +75 -0
  439. package/dist/ui/popover.svelte.d.ts +137 -0
  440. package/dist/ui/popover.svelte.d.ts.map +1 -0
  441. package/dist/ui/popover.svelte.js +288 -0
  442. package/dist/ui/position_helpers.d.ts +27 -0
  443. package/dist/ui/position_helpers.d.ts.map +1 -0
  444. package/dist/ui/position_helpers.js +81 -0
  445. package/dist/ui/sidebar_state.svelte.d.ts +30 -0
  446. package/dist/ui/sidebar_state.svelte.d.ts.map +1 -0
  447. package/dist/ui/sidebar_state.svelte.js +39 -0
  448. package/dist/ui/table_state.svelte.d.ts +63 -0
  449. package/dist/ui/table_state.svelte.d.ts.map +1 -0
  450. package/dist/ui/table_state.svelte.js +117 -0
  451. package/dist/ui/ui_fetch.d.ts +29 -0
  452. package/dist/ui/ui_fetch.d.ts.map +1 -0
  453. package/dist/ui/ui_fetch.js +37 -0
  454. package/dist/ui/ui_format.d.ts +63 -0
  455. package/dist/ui/ui_format.d.ts.map +1 -0
  456. package/dist/ui/ui_format.js +196 -0
  457. package/package.json +121 -0
@@ -0,0 +1,189 @@
1
+ import './assert_dev_env.js';
2
+ import type { AppSurface } from '../http/surface.js';
3
+ /**
4
+ * Every protected route has 401 in `error_schemas`.
5
+ */
6
+ export declare const assert_protected_routes_declare_401: (surface: AppSurface) => void;
7
+ /**
8
+ * Every role/keeper route has 403 in `error_schemas`.
9
+ */
10
+ export declare const assert_role_routes_declare_403: (surface: AppSurface) => void;
11
+ /**
12
+ * Every route with non-null `input_schema` has 400 in `error_schemas`.
13
+ */
14
+ export declare const assert_input_routes_declare_400: (surface: AppSurface) => void;
15
+ /**
16
+ * Every route with non-null `params_schema` has 400 in `error_schemas`.
17
+ */
18
+ export declare const assert_params_routes_declare_400: (surface: AppSurface) => void;
19
+ /**
20
+ * Every route with non-null `query_schema` has 400 in `error_schemas`.
21
+ */
22
+ export declare const assert_query_routes_declare_400: (surface: AppSurface) => void;
23
+ /**
24
+ * Every route has a non-empty description.
25
+ */
26
+ export declare const assert_descriptions_present: (surface: AppSurface) => void;
27
+ /**
28
+ * No duplicate method+path pairs.
29
+ */
30
+ export declare const assert_no_duplicate_routes: (surface: AppSurface) => void;
31
+ /**
32
+ * Every applicable middleware that declares errors must have those status codes
33
+ * present in the route's `error_schemas`.
34
+ */
35
+ export declare const assert_middleware_errors_propagated: (surface: AppSurface) => void;
36
+ /**
37
+ * Every route's declared error schemas must have an `error` field at the top level
38
+ * (conforming to the `ApiError` base shape `{error: string}`).
39
+ *
40
+ * Catches typos in error schema definitions and ensures consumers can always
41
+ * read `.error` from error responses.
42
+ */
43
+ export declare const assert_error_schemas_structurally_valid: (surface: AppSurface) => void;
44
+ /**
45
+ * The same `z.literal()` error code should not appear at different HTTP status codes
46
+ * across routes.
47
+ *
48
+ * Extracts `const` values from error schema `error` properties (which correspond to
49
+ * `z.literal()` in the Zod source). Flags when the same literal appears at different
50
+ * status codes — e.g., `ERROR_INVALID_CREDENTIALS` at both 401 and 403 would be a bug.
51
+ *
52
+ * Only checks schemas with `const` values (literal schemas). Generic `z.string()`
53
+ * schemas (which produce `{type: 'string'}` in JSON Schema) are ignored.
54
+ */
55
+ export declare const assert_error_code_status_consistency: (surface: AppSurface) => void;
56
+ /**
57
+ * Routes declaring 404 error schemas should use specific `z.literal()` or `z.enum()`
58
+ * error codes, not generic `z.string()`.
59
+ *
60
+ * A generic 404 schema (`ApiError` with `z.string()`) means the error code is
61
+ * unconstrained — the handler could return any string, making client error handling
62
+ * fragile. Routes with params (`:id`) are the primary 404 producers; their error
63
+ * schemas should use specific constants like `ERROR_ACCOUNT_NOT_FOUND`.
64
+ *
65
+ * Only flags routes that have `params_schema` (param-driven resource lookup) — routes
66
+ * declaring 404 for other reasons (e.g., bootstrap not configured) may legitimately
67
+ * use generic schemas.
68
+ */
69
+ export declare const assert_404_schemas_use_specific_errors: (surface: AppSurface) => void;
70
+ /** Specificity level of an error schema's `error` field. */
71
+ export type ErrorSchemaSpecificity = 'literal' | 'enum' | 'generic';
72
+ /** A single entry in the error schema tightness audit report. */
73
+ export interface ErrorSchemaAuditEntry {
74
+ method: string;
75
+ route_path: string;
76
+ status: string;
77
+ specificity: ErrorSchemaSpecificity;
78
+ /** The literal value or enum values, if specific. */
79
+ error_codes: Array<string> | null;
80
+ }
81
+ /**
82
+ * Audit error schema tightness across all routes in a surface.
83
+ *
84
+ * Reports which route x status code combinations use generic `ApiError`
85
+ * (`z.string()`) vs specific `z.literal()` or `z.enum()` error codes.
86
+ * Use the output to prioritize progressive tightening of error schemas.
87
+ *
88
+ * @param surface - the app surface to audit
89
+ * @returns audit entries for every route x status combination
90
+ */
91
+ export declare const audit_error_schema_tightness: (surface: AppSurface) => Array<ErrorSchemaAuditEntry>;
92
+ /**
93
+ * Configuration for security policy invariants.
94
+ *
95
+ * All fields have sensible defaults. Pass overrides for project-specific needs.
96
+ */
97
+ export interface SurfaceSecurityPolicyOptions {
98
+ /**
99
+ * Path patterns for routes that should be rate-limited.
100
+ * Default: common sensitive patterns (login, password, bootstrap, tokens/create).
101
+ */
102
+ sensitive_route_patterns?: Array<string | RegExp>;
103
+ /**
104
+ * Routes explicitly allowed to be public mutations (e.g., webhooks, bootstrap).
105
+ * Format: `'METHOD /path'` (e.g., `'POST /api/account/login'`).
106
+ */
107
+ public_mutation_allowlist?: Array<string>;
108
+ /**
109
+ * Allowed path prefixes for keeper-protected routes.
110
+ * Default: `['/api/']`. Catches keeper routes outside expected namespaces.
111
+ */
112
+ keeper_route_prefixes?: Array<string>;
113
+ }
114
+ /**
115
+ * Sensitive routes must declare rate limiting (`rate_limit_key` is non-null)
116
+ * or have 429 in their error schemas.
117
+ *
118
+ * Matches routes against sensitive patterns and flags any that lack rate limit
119
+ * declarations. Catches forgotten rate limiting on credential-handling routes.
120
+ */
121
+ export declare const assert_sensitive_routes_rate_limited: (surface: AppSurface, sensitive_patterns?: Array<string | RegExp>) => void;
122
+ /**
123
+ * Public mutation routes (auth: none + is_mutation) must be in the allowlist.
124
+ *
125
+ * Catches accidentally unprotected POST/PUT/DELETE routes. Routes like login
126
+ * and bootstrap are public mutations by design — they go in the allowlist.
127
+ */
128
+ export declare const assert_no_unexpected_public_mutations: (surface: AppSurface, allowlist?: Array<string>) => void;
129
+ /**
130
+ * Routes with non-null input schemas should use POST (or other mutation methods),
131
+ * not GET.
132
+ *
133
+ * GET routes with request bodies are technically allowed by HTTP but semantically
134
+ * suspicious — they bypass browser security assumptions about GET being idempotent.
135
+ * Query-string-driven filtering (audit log, list endpoints) should use params schemas
136
+ * or query string parsing, not input schemas.
137
+ */
138
+ export declare const assert_mutation_routes_use_post: (surface: AppSurface) => void;
139
+ /**
140
+ * Keeper-protected routes must be under expected path prefixes.
141
+ *
142
+ * Catches keeper routes accidentally placed outside the API namespace
143
+ * (e.g., a keeper route at `/health` or `/admin/` instead of `/api/...`).
144
+ */
145
+ export declare const assert_keeper_routes_under_prefix: (surface: AppSurface, prefixes?: Array<string>) => void;
146
+ /** Options for `assert_error_schema_tightness`. */
147
+ export interface ErrorSchemaTightnessOptions {
148
+ /** Minimum specificity level. Error schemas below this threshold fail. Default: `'enum'`. */
149
+ min_specificity?: ErrorSchemaSpecificity;
150
+ /** HTTP status codes to skip (e.g., middleware-injected codes). */
151
+ ignore_statuses?: Array<number>;
152
+ /** Routes to skip, in `'METHOD /path'` format. */
153
+ allowlist?: Array<string>;
154
+ }
155
+ /**
156
+ * Recommended baseline error schema tightness for consumer projects.
157
+ *
158
+ * Uses `min_specificity: 'enum'` (the assertion default) with `ignore_statuses`
159
+ * for middleware-derived status codes that are commonly generic (auth middleware
160
+ * produces multiple error codes at 401/403, and 429 comes from rate limiters).
161
+ * Consumers can extend with project-specific `allowlist` entries.
162
+ */
163
+ export declare const DEFAULT_ERROR_SCHEMA_TIGHTNESS: ErrorSchemaTightnessOptions;
164
+ /**
165
+ * Assert that all error schemas meet a minimum specificity threshold.
166
+ *
167
+ * Calls `audit_error_schema_tightness` and fails on any entry below
168
+ * the configured threshold. Use `allowlist` and `ignore_statuses` to exclude
169
+ * known exceptions during progressive tightening.
170
+ *
171
+ * @param surface - the app surface to check
172
+ * @param options - threshold and exclusion configuration
173
+ */
174
+ export declare const assert_error_schema_tightness: (surface: AppSurface, options?: ErrorSchemaTightnessOptions) => void;
175
+ /**
176
+ * Run all structural invariants. Options-free — applies universally.
177
+ */
178
+ export declare const assert_surface_invariants: (surface: AppSurface) => void;
179
+ /**
180
+ * Run security policy invariants. Configurable with sensible defaults.
181
+ *
182
+ * Checks:
183
+ * - Sensitive routes are rate-limited
184
+ * - No unexpected public mutation routes
185
+ * - Input schemas use mutation methods (not GET)
186
+ * - Keeper routes under expected prefixes
187
+ */
188
+ export declare const assert_surface_security_policy: (surface: AppSurface, options?: SurfaceSecurityPolicyOptions) => void;
189
+ //# sourceMappingURL=surface_invariants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surface_invariants.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/surface_invariants.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,450 @@
1
+ import './assert_dev_env.js';
2
+ /**
3
+ * Surface invariant assertions for `AppSurface` data.
4
+ *
5
+ * Two categories:
6
+ * - **Structural** — validate internal consistency (error schema presence,
7
+ * descriptions, duplicates, middleware propagation, error schema validity,
8
+ * error code consistency). No options needed.
9
+ * - **Policy** — enforce security policy (sensitive routes rate-limited,
10
+ * no public mutations, mutation method conventions). Configurable with
11
+ * sensible defaults.
12
+ *
13
+ * Structural invariants catch schema/surface generation bugs. Policy invariants
14
+ * catch security misconfigurations. Both propagate automatically to consumers
15
+ * via `assert_surface_invariants`.
16
+ *
17
+ * @module
18
+ */
19
+ import { assert } from 'vitest';
20
+ import { middleware_applies } from '../http/schema_helpers.js';
21
+ import { filter_protected_routes, filter_role_routes, filter_keeper_routes, filter_routes_with_input, filter_routes_with_params, filter_routes_with_query, filter_public_routes, format_route_key, } from '../http/surface_query.js';
22
+ // --- Structural invariants ---
23
+ /**
24
+ * Every protected route has 401 in `error_schemas`.
25
+ */
26
+ export const assert_protected_routes_declare_401 = (surface) => {
27
+ const routes = filter_protected_routes(surface);
28
+ for (const route of routes) {
29
+ assert.ok(route.error_schemas && '401' in route.error_schemas, `${format_route_key(route)} is protected but missing 401 error schema`);
30
+ }
31
+ };
32
+ /**
33
+ * Every role/keeper route has 403 in `error_schemas`.
34
+ */
35
+ export const assert_role_routes_declare_403 = (surface) => {
36
+ const role_routes = filter_role_routes(surface);
37
+ const keeper_routes = filter_keeper_routes(surface);
38
+ for (const route of [...role_routes, ...keeper_routes]) {
39
+ assert.ok(route.error_schemas && '403' in route.error_schemas, `${format_route_key(route)} requires role/keeper but missing 403 error schema`);
40
+ }
41
+ };
42
+ /**
43
+ * Every route with non-null `input_schema` has 400 in `error_schemas`.
44
+ */
45
+ export const assert_input_routes_declare_400 = (surface) => {
46
+ const routes = filter_routes_with_input(surface);
47
+ for (const route of routes) {
48
+ assert.ok(route.error_schemas && '400' in route.error_schemas, `${format_route_key(route)} has input but missing 400 error schema`);
49
+ }
50
+ };
51
+ /**
52
+ * Every route with non-null `params_schema` has 400 in `error_schemas`.
53
+ */
54
+ export const assert_params_routes_declare_400 = (surface) => {
55
+ const routes = filter_routes_with_params(surface);
56
+ for (const route of routes) {
57
+ assert.ok(route.error_schemas && '400' in route.error_schemas, `${format_route_key(route)} has params but missing 400 error schema`);
58
+ }
59
+ };
60
+ /**
61
+ * Every route with non-null `query_schema` has 400 in `error_schemas`.
62
+ */
63
+ export const assert_query_routes_declare_400 = (surface) => {
64
+ const routes = filter_routes_with_query(surface);
65
+ for (const route of routes) {
66
+ assert.ok(route.error_schemas && '400' in route.error_schemas, `${format_route_key(route)} has query schema but missing 400 error schema`);
67
+ }
68
+ };
69
+ /**
70
+ * Every route has a non-empty description.
71
+ */
72
+ export const assert_descriptions_present = (surface) => {
73
+ for (const route of surface.routes) {
74
+ assert.ok(route.description.length > 0, `${format_route_key(route)} has empty description`);
75
+ }
76
+ };
77
+ /**
78
+ * No duplicate method+path pairs.
79
+ */
80
+ export const assert_no_duplicate_routes = (surface) => {
81
+ const seen = new Set();
82
+ for (const route of surface.routes) {
83
+ const key = format_route_key(route);
84
+ assert.ok(!seen.has(key), `Duplicate route: ${key}`);
85
+ seen.add(key);
86
+ }
87
+ };
88
+ /**
89
+ * Every applicable middleware that declares errors must have those status codes
90
+ * present in the route's `error_schemas`.
91
+ */
92
+ export const assert_middleware_errors_propagated = (surface) => {
93
+ const middleware_with_errors = surface.middleware.filter((m) => m.error_schemas !== null);
94
+ for (const route of surface.routes) {
95
+ for (const mw of middleware_with_errors) {
96
+ if (!middleware_applies(mw.path, route.path))
97
+ continue;
98
+ for (const status of Object.keys(mw.error_schemas)) {
99
+ assert.ok(route.error_schemas && status in route.error_schemas, `${format_route_key(route)} missing status ${status} from middleware '${mw.name}'`);
100
+ }
101
+ }
102
+ }
103
+ };
104
+ /**
105
+ * Every route's declared error schemas must have an `error` field at the top level
106
+ * (conforming to the `ApiError` base shape `{error: string}`).
107
+ *
108
+ * Catches typos in error schema definitions and ensures consumers can always
109
+ * read `.error` from error responses.
110
+ */
111
+ export const assert_error_schemas_structurally_valid = (surface) => {
112
+ for (const route of surface.routes) {
113
+ if (!route.error_schemas)
114
+ continue;
115
+ for (const [status, schema] of Object.entries(route.error_schemas)) {
116
+ if (typeof schema !== 'object' || schema === null)
117
+ continue;
118
+ const s = schema;
119
+ // JSON Schema must have properties.error or be an object type
120
+ if (s.type === 'object' && s.properties && typeof s.properties === 'object') {
121
+ const props = s.properties;
122
+ assert.ok('error' in props, `${format_route_key(route)} error schema for status ${status} missing 'error' property`);
123
+ }
124
+ }
125
+ }
126
+ };
127
+ /**
128
+ * The same `z.literal()` error code should not appear at different HTTP status codes
129
+ * across routes.
130
+ *
131
+ * Extracts `const` values from error schema `error` properties (which correspond to
132
+ * `z.literal()` in the Zod source). Flags when the same literal appears at different
133
+ * status codes — e.g., `ERROR_INVALID_CREDENTIALS` at both 401 and 403 would be a bug.
134
+ *
135
+ * Only checks schemas with `const` values (literal schemas). Generic `z.string()`
136
+ * schemas (which produce `{type: 'string'}` in JSON Schema) are ignored.
137
+ */
138
+ export const assert_error_code_status_consistency = (surface) => {
139
+ // Map from error code literal → Set of status codes where it appears
140
+ const code_to_statuses = new Map();
141
+ for (const route of surface.routes) {
142
+ if (!route.error_schemas)
143
+ continue;
144
+ for (const [status, schema] of Object.entries(route.error_schemas)) {
145
+ const error_const = extract_error_const(schema);
146
+ if (error_const === null)
147
+ continue;
148
+ let statuses = code_to_statuses.get(error_const);
149
+ if (!statuses) {
150
+ statuses = new Set();
151
+ code_to_statuses.set(error_const, statuses);
152
+ }
153
+ statuses.add(status);
154
+ }
155
+ }
156
+ // Also check middleware error schemas
157
+ for (const mw of surface.middleware) {
158
+ if (!mw.error_schemas)
159
+ continue;
160
+ for (const [status, schema] of Object.entries(mw.error_schemas)) {
161
+ const error_const = extract_error_const(schema);
162
+ if (error_const === null)
163
+ continue;
164
+ let statuses = code_to_statuses.get(error_const);
165
+ if (!statuses) {
166
+ statuses = new Set();
167
+ code_to_statuses.set(error_const, statuses);
168
+ }
169
+ statuses.add(status);
170
+ }
171
+ }
172
+ for (const [code, statuses] of code_to_statuses) {
173
+ assert.ok(statuses.size === 1, `Error code '${code}' appears at multiple status codes: ${[...statuses].sort().join(', ')}`);
174
+ }
175
+ };
176
+ /**
177
+ * Extract the `error` property's JSON Schema from a route error schema.
178
+ *
179
+ * Navigates `schema.properties.error` — the common structure for all
180
+ * `ApiError`-shaped schemas. Returns `null` if the schema doesn't conform.
181
+ */
182
+ const get_error_property = (schema) => {
183
+ if (typeof schema !== 'object' || schema === null)
184
+ return null;
185
+ const s = schema;
186
+ if (s.type !== 'object' || !s.properties || typeof s.properties !== 'object')
187
+ return null;
188
+ const props = s.properties;
189
+ if (!props.error || typeof props.error !== 'object')
190
+ return null;
191
+ return props.error;
192
+ };
193
+ /**
194
+ * Extract the `const` value from a JSON Schema error property, if present.
195
+ *
196
+ * Looks for `schema.properties.error.const` — the JSON Schema representation
197
+ * of `z.literal('some_error_code')`.
198
+ */
199
+ const extract_error_const = (schema) => {
200
+ const error_prop = get_error_property(schema);
201
+ if (!error_prop)
202
+ return null;
203
+ if (typeof error_prop.const === 'string')
204
+ return error_prop.const;
205
+ return null;
206
+ };
207
+ /**
208
+ * Check if a JSON Schema error property uses specific error codes (`const` or `enum`),
209
+ * not just generic `z.string()` (`{type: 'string'}`).
210
+ *
211
+ * Returns `true` for `z.literal()` (`{const: '...'}`) and `z.enum()` (`{enum: [...]}`).
212
+ */
213
+ const has_specific_error_schema = (schema) => {
214
+ const error_prop = get_error_property(schema);
215
+ if (!error_prop)
216
+ return false;
217
+ return typeof error_prop.const === 'string' || Array.isArray(error_prop.enum);
218
+ };
219
+ /**
220
+ * Routes declaring 404 error schemas should use specific `z.literal()` or `z.enum()`
221
+ * error codes, not generic `z.string()`.
222
+ *
223
+ * A generic 404 schema (`ApiError` with `z.string()`) means the error code is
224
+ * unconstrained — the handler could return any string, making client error handling
225
+ * fragile. Routes with params (`:id`) are the primary 404 producers; their error
226
+ * schemas should use specific constants like `ERROR_ACCOUNT_NOT_FOUND`.
227
+ *
228
+ * Only flags routes that have `params_schema` (param-driven resource lookup) — routes
229
+ * declaring 404 for other reasons (e.g., bootstrap not configured) may legitimately
230
+ * use generic schemas.
231
+ */
232
+ export const assert_404_schemas_use_specific_errors = (surface) => {
233
+ for (const route of surface.routes) {
234
+ if (!route.error_schemas || !('404' in route.error_schemas))
235
+ continue;
236
+ if (route.params_schema === null)
237
+ continue;
238
+ assert.ok(has_specific_error_schema(route.error_schemas['404']), `${format_route_key(route)} declares 404 with params but uses generic error schema — ` +
239
+ `use a specific z.literal() or z.enum() error code`);
240
+ }
241
+ };
242
+ /**
243
+ * Classify the specificity of a JSON Schema error property.
244
+ *
245
+ * - `'literal'` — `z.literal()` (`{const: '...'}`)
246
+ * - `'enum'` — `z.enum()` (`{enum: [...]}`)
247
+ * - `'generic'` — `z.string()` or unrecognized
248
+ */
249
+ const classify_error_specificity = (schema) => {
250
+ const error_prop = get_error_property(schema);
251
+ if (!error_prop)
252
+ return 'generic';
253
+ if (typeof error_prop.const === 'string')
254
+ return 'literal';
255
+ if (Array.isArray(error_prop.enum))
256
+ return 'enum';
257
+ return 'generic';
258
+ };
259
+ /**
260
+ * Extract error code values from a JSON Schema error property.
261
+ *
262
+ * Returns the literal value or enum array, or `null` for generic schemas.
263
+ */
264
+ const extract_error_codes = (schema) => {
265
+ const error_prop = get_error_property(schema);
266
+ if (!error_prop)
267
+ return null;
268
+ if (typeof error_prop.const === 'string')
269
+ return [error_prop.const];
270
+ if (Array.isArray(error_prop.enum))
271
+ return error_prop.enum.filter((v) => typeof v === 'string');
272
+ return null;
273
+ };
274
+ /**
275
+ * Audit error schema tightness across all routes in a surface.
276
+ *
277
+ * Reports which route x status code combinations use generic `ApiError`
278
+ * (`z.string()`) vs specific `z.literal()` or `z.enum()` error codes.
279
+ * Use the output to prioritize progressive tightening of error schemas.
280
+ *
281
+ * @param surface - the app surface to audit
282
+ * @returns audit entries for every route x status combination
283
+ */
284
+ export const audit_error_schema_tightness = (surface) => {
285
+ const entries = [];
286
+ for (const route of surface.routes) {
287
+ if (!route.error_schemas)
288
+ continue;
289
+ for (const [status, schema] of Object.entries(route.error_schemas)) {
290
+ const specificity = classify_error_specificity(schema);
291
+ entries.push({
292
+ method: route.method,
293
+ route_path: route.path,
294
+ status,
295
+ specificity,
296
+ error_codes: extract_error_codes(schema),
297
+ });
298
+ }
299
+ }
300
+ return entries;
301
+ };
302
+ /** Default patterns for sensitive routes that should be rate-limited. */
303
+ const DEFAULT_SENSITIVE_PATTERNS = [
304
+ /\/login$/,
305
+ /\/password$/,
306
+ /\/bootstrap$/,
307
+ /\/tokens\/create$/,
308
+ ];
309
+ /**
310
+ * Sensitive routes must declare rate limiting (`rate_limit_key` is non-null)
311
+ * or have 429 in their error schemas.
312
+ *
313
+ * Matches routes against sensitive patterns and flags any that lack rate limit
314
+ * declarations. Catches forgotten rate limiting on credential-handling routes.
315
+ */
316
+ export const assert_sensitive_routes_rate_limited = (surface, sensitive_patterns = DEFAULT_SENSITIVE_PATTERNS) => {
317
+ for (const route of surface.routes) {
318
+ const matches = sensitive_patterns.some((pattern) => typeof pattern === 'string' ? route.path.includes(pattern) : pattern.test(route.path));
319
+ if (!matches)
320
+ continue;
321
+ const has_rate_limit = route.rate_limit_key !== null ||
322
+ (route.error_schemas !== null && '429' in route.error_schemas);
323
+ assert.ok(has_rate_limit, `${format_route_key(route)} matches a sensitive pattern but has no rate limiting declared`);
324
+ }
325
+ };
326
+ /**
327
+ * Public mutation routes (auth: none + is_mutation) must be in the allowlist.
328
+ *
329
+ * Catches accidentally unprotected POST/PUT/DELETE routes. Routes like login
330
+ * and bootstrap are public mutations by design — they go in the allowlist.
331
+ */
332
+ export const assert_no_unexpected_public_mutations = (surface, allowlist = []) => {
333
+ const public_routes = filter_public_routes(surface);
334
+ const mutations = public_routes.filter((r) => r.is_mutation);
335
+ const allowset = new Set(allowlist);
336
+ for (const route of mutations) {
337
+ const key = format_route_key(route);
338
+ assert.ok(allowset.has(key), `${key} is a public mutation route not in the allowlist. ` +
339
+ `Add it to public_mutation_allowlist if intentional.`);
340
+ }
341
+ };
342
+ /**
343
+ * Routes with non-null input schemas should use POST (or other mutation methods),
344
+ * not GET.
345
+ *
346
+ * GET routes with request bodies are technically allowed by HTTP but semantically
347
+ * suspicious — they bypass browser security assumptions about GET being idempotent.
348
+ * Query-string-driven filtering (audit log, list endpoints) should use params schemas
349
+ * or query string parsing, not input schemas.
350
+ */
351
+ export const assert_mutation_routes_use_post = (surface) => {
352
+ const input_routes = filter_routes_with_input(surface);
353
+ for (const route of input_routes) {
354
+ assert.ok(route.method !== 'GET', `${format_route_key(route)} has input schema on GET route — use POST or move to params/query`);
355
+ }
356
+ };
357
+ /** Default allowed prefixes for keeper routes. */
358
+ const DEFAULT_KEEPER_ROUTE_PREFIXES = ['/api/'];
359
+ /**
360
+ * Keeper-protected routes must be under expected path prefixes.
361
+ *
362
+ * Catches keeper routes accidentally placed outside the API namespace
363
+ * (e.g., a keeper route at `/health` or `/admin/` instead of `/api/...`).
364
+ */
365
+ export const assert_keeper_routes_under_prefix = (surface, prefixes = DEFAULT_KEEPER_ROUTE_PREFIXES) => {
366
+ const keeper_routes = filter_keeper_routes(surface);
367
+ for (const route of keeper_routes) {
368
+ const under_prefix = prefixes.some((prefix) => route.path.startsWith(prefix));
369
+ assert.ok(under_prefix, `${format_route_key(route)} is keeper-protected but not under any expected prefix: ${prefixes.join(', ')}`);
370
+ }
371
+ };
372
+ // --- Error schema tightness assertion ---
373
+ /** Numeric specificity ranking for threshold comparisons. */
374
+ const SPECIFICITY_ORDER = {
375
+ literal: 2,
376
+ enum: 1,
377
+ generic: 0,
378
+ };
379
+ /**
380
+ * Recommended baseline error schema tightness for consumer projects.
381
+ *
382
+ * Uses `min_specificity: 'enum'` (the assertion default) with `ignore_statuses`
383
+ * for middleware-derived status codes that are commonly generic (auth middleware
384
+ * produces multiple error codes at 401/403, and 429 comes from rate limiters).
385
+ * Consumers can extend with project-specific `allowlist` entries.
386
+ */
387
+ export const DEFAULT_ERROR_SCHEMA_TIGHTNESS = {
388
+ ignore_statuses: [401, 403, 429],
389
+ };
390
+ /**
391
+ * Assert that all error schemas meet a minimum specificity threshold.
392
+ *
393
+ * Calls `audit_error_schema_tightness` and fails on any entry below
394
+ * the configured threshold. Use `allowlist` and `ignore_statuses` to exclude
395
+ * known exceptions during progressive tightening.
396
+ *
397
+ * @param surface - the app surface to check
398
+ * @param options - threshold and exclusion configuration
399
+ */
400
+ export const assert_error_schema_tightness = (surface, options) => {
401
+ const min_specificity = options?.min_specificity ?? 'enum';
402
+ const ignore_statuses = new Set(options?.ignore_statuses?.map(String));
403
+ const allowlist = new Set(options?.allowlist);
404
+ const threshold = SPECIFICITY_ORDER[min_specificity];
405
+ const entries = audit_error_schema_tightness(surface);
406
+ const failures = [];
407
+ for (const entry of entries) {
408
+ if (ignore_statuses.has(entry.status))
409
+ continue;
410
+ const key = `${entry.method} ${entry.route_path}`;
411
+ if (allowlist.has(key))
412
+ continue;
413
+ if (SPECIFICITY_ORDER[entry.specificity] < threshold) {
414
+ failures.push(`${key} → ${entry.status} (${entry.specificity})`);
415
+ }
416
+ }
417
+ assert.ok(failures.length === 0, `Error schemas below '${min_specificity}' threshold:\n ${failures.join('\n ')}`);
418
+ };
419
+ // --- Aggregate runners ---
420
+ /**
421
+ * Run all structural invariants. Options-free — applies universally.
422
+ */
423
+ export const assert_surface_invariants = (surface) => {
424
+ assert_protected_routes_declare_401(surface);
425
+ assert_role_routes_declare_403(surface);
426
+ assert_input_routes_declare_400(surface);
427
+ assert_params_routes_declare_400(surface);
428
+ assert_query_routes_declare_400(surface);
429
+ assert_descriptions_present(surface);
430
+ assert_no_duplicate_routes(surface);
431
+ assert_middleware_errors_propagated(surface);
432
+ assert_error_schemas_structurally_valid(surface);
433
+ assert_error_code_status_consistency(surface);
434
+ assert_404_schemas_use_specific_errors(surface);
435
+ };
436
+ /**
437
+ * Run security policy invariants. Configurable with sensible defaults.
438
+ *
439
+ * Checks:
440
+ * - Sensitive routes are rate-limited
441
+ * - No unexpected public mutation routes
442
+ * - Input schemas use mutation methods (not GET)
443
+ * - Keeper routes under expected prefixes
444
+ */
445
+ export const assert_surface_security_policy = (surface, options = {}) => {
446
+ assert_sensitive_routes_rate_limited(surface, options.sensitive_route_patterns);
447
+ assert_no_unexpected_public_mutations(surface, options.public_mutation_allowlist);
448
+ assert_mutation_routes_use_post(surface);
449
+ assert_keeper_routes_under_prefix(surface, options.keeper_route_prefixes);
450
+ };