@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,297 @@
1
+ import './assert_dev_env.js';
2
+ /**
3
+ * Composable data exposure test suite.
4
+ *
5
+ * Verifies that sensitive database fields never leak through HTTP responses:
6
+ * - Schema-level: walks JSON Schema output/error schemas for blocklisted property names
7
+ * - Runtime: fires real requests and checks response bodies against field blocklists
8
+ * - Cross-privilege: verifies admin routes return 403 for non-admin users,
9
+ * and non-admin responses exclude admin-only fields
10
+ *
11
+ * @module
12
+ */
13
+ import { describe, test, beforeAll, afterAll, assert } from 'vitest';
14
+ import { ROLE_ADMIN } from '../auth/role_schema.js';
15
+ import { create_test_app } from './app_server.js';
16
+ import { create_pglite_factory } from './db.js';
17
+ import { resolve_valid_path, generate_valid_body } from './schema_generators.js';
18
+ import { run_migrations } from '../db/migrate.js';
19
+ import { AUTH_MIGRATION_NS } from '../auth/migrations.js';
20
+ import { is_null_schema, is_strict_object_schema } from '../http/schema_helpers.js';
21
+ import { SENSITIVE_FIELD_BLOCKLIST, ADMIN_ONLY_FIELD_BLOCKLIST, assert_no_sensitive_fields_in_json, } from './integration_helpers.js';
22
+ // --- Schema introspection ---
23
+ /**
24
+ * Recursively collect all property names from a JSON Schema.
25
+ *
26
+ * Walks `properties`, `items`, `allOf`/`anyOf`/`oneOf`, and
27
+ * `additionalProperties` to find every declared field name at any depth.
28
+ *
29
+ * @param schema - JSON Schema object
30
+ * @returns set of all property names found
31
+ */
32
+ export const collect_json_schema_property_names = (schema) => {
33
+ const names = new Set();
34
+ const walk = (s) => {
35
+ if (s === null || s === undefined || typeof s !== 'object')
36
+ return;
37
+ const obj = s;
38
+ if (obj.properties && typeof obj.properties === 'object') {
39
+ for (const [name, prop_schema] of Object.entries(obj.properties)) {
40
+ names.add(name);
41
+ walk(prop_schema);
42
+ }
43
+ }
44
+ if (obj.items)
45
+ walk(obj.items);
46
+ for (const key of ['allOf', 'anyOf', 'oneOf']) {
47
+ if (Array.isArray(obj[key])) {
48
+ for (const sub of obj[key])
49
+ walk(sub);
50
+ }
51
+ }
52
+ if (obj.additionalProperties && typeof obj.additionalProperties === 'object') {
53
+ walk(obj.additionalProperties);
54
+ }
55
+ };
56
+ walk(schema);
57
+ return names;
58
+ };
59
+ // --- Schema-level assertions ---
60
+ /**
61
+ * Assert that no output schema in the surface contains sensitive field names.
62
+ *
63
+ * @param surface - the app surface to check
64
+ * @param sensitive_fields - field names to flag
65
+ */
66
+ export const assert_output_schemas_no_sensitive_fields = (surface, sensitive_fields = SENSITIVE_FIELD_BLOCKLIST) => {
67
+ for (const route of surface.routes) {
68
+ if (route.output_schema === null)
69
+ continue;
70
+ const prop_names = collect_json_schema_property_names(route.output_schema);
71
+ for (const field of sensitive_fields) {
72
+ assert.ok(!prop_names.has(field), `${route.method} ${route.path}: output schema contains sensitive field '${field}'`);
73
+ }
74
+ }
75
+ };
76
+ /**
77
+ * Assert that non-admin route output schemas don't contain admin-only fields.
78
+ *
79
+ * @param surface - the app surface to check
80
+ * @param admin_only_fields - field names that are admin-only
81
+ */
82
+ export const assert_non_admin_schemas_no_admin_fields = (surface, admin_only_fields = ADMIN_ONLY_FIELD_BLOCKLIST) => {
83
+ const non_admin = surface.routes.filter((r) => r.auth.type !== 'keeper' && !(r.auth.type === 'role' && r.auth.role === 'admin'));
84
+ for (const route of non_admin) {
85
+ if (route.output_schema === null)
86
+ continue;
87
+ const prop_names = collect_json_schema_property_names(route.output_schema);
88
+ for (const field of admin_only_fields) {
89
+ assert.ok(!prop_names.has(field), `${route.method} ${route.path}: non-admin output schema contains admin-only field '${field}'`);
90
+ }
91
+ }
92
+ };
93
+ /**
94
+ * Composable data exposure test suite.
95
+ *
96
+ * Three test groups:
97
+ * 1. Schema-level — walk JSON Schema output/error schemas for sensitive field names
98
+ * 2. Runtime — fire real requests and check response bodies against blocklists
99
+ * 3. Cross-privilege — admin routes return 403 for non-admin, error responses
100
+ * contain no sensitive fields
101
+ *
102
+ * @param options - test configuration
103
+ */
104
+ export const describe_data_exposure_tests = (options) => {
105
+ const { build, sensitive_fields = SENSITIVE_FIELD_BLOCKLIST, admin_only_fields = ADMIN_ONLY_FIELD_BLOCKLIST, } = options;
106
+ describe('data exposure — schema-level', () => {
107
+ const { surface } = build();
108
+ test('no sensitive fields in any output schema', () => {
109
+ assert_output_schemas_no_sensitive_fields(surface, sensitive_fields);
110
+ });
111
+ test('no admin-only fields in non-admin output schemas', () => {
112
+ assert_non_admin_schemas_no_admin_fields(surface, admin_only_fields);
113
+ });
114
+ test('no sensitive fields in any error schema', () => {
115
+ for (const route of surface.routes) {
116
+ if (!route.error_schemas)
117
+ continue;
118
+ for (const [status, schema] of Object.entries(route.error_schemas)) {
119
+ const prop_names = collect_json_schema_property_names(schema);
120
+ for (const field of sensitive_fields) {
121
+ assert.ok(!prop_names.has(field), `${route.method} ${route.path} error ${status}: contains sensitive field '${field}'`);
122
+ }
123
+ }
124
+ }
125
+ });
126
+ });
127
+ describe_data_exposure_runtime_tests(options);
128
+ };
129
+ // --- Runtime tests ---
130
+ const describe_data_exposure_runtime_tests = (options) => {
131
+ const { sensitive_fields = SENSITIVE_FIELD_BLOCKLIST, admin_only_fields = ADMIN_ONLY_FIELD_BLOCKLIST, } = options;
132
+ const skip_set = new Set(options.skip_routes);
133
+ const init_schema = async (db) => {
134
+ await run_migrations(db, [AUTH_MIGRATION_NS]);
135
+ };
136
+ const factories = options.db_factories ?? [create_pglite_factory(init_schema)];
137
+ for (const factory of factories) {
138
+ describe(`data exposure — runtime (${factory.name})`, () => {
139
+ if (factory.skip)
140
+ return;
141
+ let test_app;
142
+ let authed_account;
143
+ let admin_account;
144
+ let db;
145
+ beforeAll(async () => {
146
+ db = await factory.create();
147
+ test_app = await create_test_app({
148
+ session_options: options.session_options,
149
+ create_route_specs: options.create_route_specs,
150
+ db,
151
+ app_options: options.app_options,
152
+ });
153
+ authed_account = await test_app.create_account({
154
+ username: 'exposure_authed',
155
+ roles: [],
156
+ });
157
+ admin_account = await test_app.create_account({
158
+ username: 'exposure_admin',
159
+ roles: [ROLE_ADMIN],
160
+ });
161
+ });
162
+ afterAll(async () => {
163
+ await test_app.cleanup();
164
+ await factory.close(db);
165
+ });
166
+ // Tests that don't fire authenticated requests run first — they don't
167
+ // invalidate sessions and are independent of test order.
168
+ test('unauthenticated error responses contain no sensitive fields', async () => {
169
+ const protected_specs = test_app.route_specs.filter((s) => s.auth.type !== 'none');
170
+ for (const spec of protected_specs) {
171
+ const route_key = `${spec.method} ${spec.path}`;
172
+ if (skip_set.has(route_key))
173
+ continue;
174
+ const url = resolve_valid_path(spec.path, spec.params);
175
+ // eslint-disable-next-line no-await-in-loop
176
+ const res = await test_app.app.request(url, {
177
+ method: spec.method,
178
+ headers: { host: 'localhost', origin: 'http://localhost:5173' },
179
+ });
180
+ if (res.headers.get('Content-Type')?.includes('text/event-stream')) {
181
+ await res.body?.cancel(); // eslint-disable-line no-await-in-loop
182
+ continue;
183
+ }
184
+ let error_body;
185
+ try {
186
+ error_body = await res.clone().json(); // eslint-disable-line no-await-in-loop
187
+ }
188
+ catch {
189
+ continue;
190
+ }
191
+ assert_no_sensitive_fields_in_json(error_body, sensitive_fields, `unauthenticated ${route_key} (${res.status})`);
192
+ }
193
+ });
194
+ // Cross-privilege test runs before 2xx tests — admin routes reject
195
+ // without calling handlers, so sessions stay intact.
196
+ test('admin routes return 403 for non-admin user', async () => {
197
+ const admin_specs = test_app.route_specs.filter((s) => s.auth.type === 'role' && s.auth.role === 'admin');
198
+ for (const spec of admin_specs) {
199
+ const route_key = `${spec.method} ${spec.path}`;
200
+ if (skip_set.has(route_key))
201
+ continue;
202
+ const url = resolve_valid_path(spec.path, spec.params);
203
+ const headers = authed_account.create_session_headers();
204
+ // eslint-disable-next-line no-await-in-loop
205
+ const res = await test_app.app.request(url, {
206
+ method: spec.method,
207
+ headers,
208
+ });
209
+ assert.strictEqual(res.status, 403, `${route_key} should return 403 for non-admin user`);
210
+ let error_body;
211
+ try {
212
+ error_body = await res.clone().json(); // eslint-disable-line no-await-in-loop
213
+ }
214
+ catch {
215
+ continue;
216
+ }
217
+ assert_no_sensitive_fields_in_json(error_body, sensitive_fields, `${route_key} 403`);
218
+ }
219
+ });
220
+ // 2xx tests run last — handlers like logout and session-revoke-all
221
+ // invalidate sessions as a side effect. Sort GET before POST so
222
+ // data-returning routes are checked before destructive routes fire.
223
+ test('all 2xx responses pass field blocklists', async () => {
224
+ // sort GET before mutations to check data-returning routes
225
+ // before destructive routes (logout, revoke-all) invalidate sessions
226
+ const sorted_specs = [...test_app.route_specs].sort((a, b) => {
227
+ if (a.method === 'GET' && b.method !== 'GET')
228
+ return -1;
229
+ if (a.method !== 'GET' && b.method === 'GET')
230
+ return 1;
231
+ return 0;
232
+ });
233
+ for (const spec of sorted_specs) {
234
+ const route_key = `${spec.method} ${spec.path}`;
235
+ if (skip_set.has(route_key))
236
+ continue;
237
+ // keeper auth (daemon token) is strictly more privileged than admin
238
+ const is_elevated = spec.auth.type === 'keeper' ||
239
+ (spec.auth.type === 'role' && spec.auth.role === 'admin');
240
+ const url = resolve_valid_path(spec.path, spec.params);
241
+ const body = generate_valid_body(spec.input);
242
+ const headers = pick_auth_headers(spec, test_app, authed_account, admin_account);
243
+ const request_init = {
244
+ method: spec.method,
245
+ headers: {
246
+ ...headers,
247
+ ...(body ? { 'content-type': 'application/json' } : {}),
248
+ },
249
+ ...(body ? { body: JSON.stringify(body) } : {}),
250
+ };
251
+ const res = await test_app.app.request(url, request_init); // eslint-disable-line no-await-in-loop
252
+ if (res.headers.get('Content-Type')?.includes('text/event-stream')) {
253
+ await res.body?.cancel(); // eslint-disable-line no-await-in-loop
254
+ continue;
255
+ }
256
+ if (!res.ok)
257
+ continue;
258
+ let response_body;
259
+ try {
260
+ response_body = await res.clone().json(); // eslint-disable-line no-await-in-loop
261
+ }
262
+ catch {
263
+ continue;
264
+ }
265
+ assert_no_sensitive_fields_in_json(response_body, sensitive_fields, `${route_key} (${res.status})`);
266
+ // Admin-only field check applies to non-elevated routes with strict
267
+ // output schemas. Loose schemas (e.g. surface route returning JSON
268
+ // Schema representations) may contain admin field names as metadata.
269
+ if (!is_elevated &&
270
+ !is_null_schema(spec.output) &&
271
+ is_strict_object_schema(spec.output)) {
272
+ assert_no_sensitive_fields_in_json(response_body, admin_only_fields, `non-admin ${route_key} (${res.status})`);
273
+ }
274
+ }
275
+ });
276
+ });
277
+ }
278
+ };
279
+ /**
280
+ * Pick auth headers matching a route spec's auth requirement.
281
+ */
282
+ const pick_auth_headers = (spec, test_app, authed_account, admin_account) => {
283
+ switch (spec.auth.type) {
284
+ case 'none':
285
+ return { host: 'localhost', origin: 'http://localhost:5173' };
286
+ case 'authenticated':
287
+ return authed_account.create_session_headers();
288
+ case 'role':
289
+ if (spec.auth.role === ROLE_ADMIN) {
290
+ return admin_account.create_session_headers();
291
+ }
292
+ // keeper role uses the bootstrapped account
293
+ return test_app.create_session_headers();
294
+ case 'keeper':
295
+ return test_app.create_bearer_headers();
296
+ }
297
+ };
@@ -0,0 +1,111 @@
1
+ import './assert_dev_env.js';
2
+ import type { Db } from '../db/db.js';
3
+ /**
4
+ * CI detection — `CI=true` is set automatically by GitHub Actions, GitLab CI, etc.
5
+ */
6
+ export declare const IS_CI: boolean;
7
+ /**
8
+ * Factory interface for creating test database instances.
9
+ */
10
+ export interface DbFactory {
11
+ name: string;
12
+ create: () => Promise<Db>;
13
+ close: (db: Db) => Promise<void>;
14
+ skip: boolean;
15
+ skip_reason?: string;
16
+ }
17
+ /**
18
+ * Reset a PGlite database to a clean state by dropping and recreating the public schema.
19
+ *
20
+ * Removes all tables, sequences, indexes, types, and functions.
21
+ * The database instance remains usable after reset.
22
+ *
23
+ * @param db - the database to reset
24
+ */
25
+ export declare const reset_pglite: (db: Db) => Promise<void>;
26
+ /**
27
+ * Create a pglite (in-memory) database factory for tests.
28
+ *
29
+ * Always enabled — no external dependencies required.
30
+ * Shares a single PGlite WASM instance across all factories in the same
31
+ * vitest worker thread (one test file). Subsequent `create()` calls reset
32
+ * the schema via `DROP SCHEMA public CASCADE` instead of paying the WASM
33
+ * cold-start cost again.
34
+ *
35
+ * @param init_schema - callback to initialize the database schema
36
+ * @returns a factory that creates in-memory pglite databases
37
+ */
38
+ export declare const create_pglite_factory: (init_schema: (db: Db) => Promise<void>) => DbFactory;
39
+ /**
40
+ * Create a pg (PostgreSQL) database factory for tests.
41
+ *
42
+ * Skipped when `test_url` is not provided.
43
+ * Drops `schema_version` before running `init_schema`, forcing migrations
44
+ * to re-evaluate against the actual tables. Prevents stale version entries
45
+ * from skipping migrations when DDL changes between test sessions.
46
+ *
47
+ * For full clean-slate behavior (recommended), call `drop_auth_schema(db)`
48
+ * at the start of `init_schema` before running migrations. This handles
49
+ * upstream schema changes that go beyond adding new tables/columns.
50
+ *
51
+ * @param init_schema - callback to initialize the database schema
52
+ * @param test_url - PostgreSQL connection URL (e.g. from `TEST_DATABASE_URL`)
53
+ * @returns a factory that creates pg databases
54
+ */
55
+ export declare const create_pg_factory: (init_schema: (db: Db) => Promise<void>, test_url?: string) => DbFactory;
56
+ /**
57
+ * Auth table names in truncation order (children first for FK safety).
58
+ *
59
+ * Consumer projects can spread this into their own list and append app-specific tables.
60
+ */
61
+ export declare const AUTH_TRUNCATE_TABLES: string[];
62
+ /**
63
+ * Auth tables including `audit_log` — for integration tests that exercise
64
+ * the full middleware stack (login, admin, rate limiting).
65
+ *
66
+ * Separate from `AUTH_TRUNCATE_TABLES` because unit-level DB tests that don't
67
+ * touch audit logging don't need to truncate it.
68
+ */
69
+ export declare const AUTH_INTEGRATION_TRUNCATE_TABLES: string[];
70
+ /**
71
+ * All auth tables in drop order (children first for FK safety).
72
+ *
73
+ * The full set created by `AUTH_MIGRATIONS` — use for clean-slate
74
+ * test DB initialization. `AUTH_TRUNCATE_TABLES` is the subset for
75
+ * between-test data cleanup (excludes `audit_log`).
76
+ *
77
+ * When adding tables to `AUTH_MIGRATIONS`, add them here too.
78
+ */
79
+ export declare const AUTH_DROP_TABLES: readonly ["app_settings", "invite", "audit_log", "api_token", "auth_session", "permit", "actor", "account", "bootstrap_lock"];
80
+ /**
81
+ * Drop all auth tables and schema version tracking for a clean slate.
82
+ *
83
+ * Recommended at the start of `init_schema` callbacks for `create_pg_factory`.
84
+ * Persistent test databases can accumulate stale schema from previous fuz_app
85
+ * versions — this ensures migrations run against a truly empty database.
86
+ * Safe on fresh databases (`IF EXISTS` on all statements). No-op effect for
87
+ * PGlite (already fresh), but harmless to call unconditionally.
88
+ *
89
+ * @param db - the database to clean
90
+ */
91
+ export declare const drop_auth_schema: (db: Db) => Promise<void>;
92
+ /**
93
+ * Create a `describe_db` function bound to specific factories and truncate tables.
94
+ *
95
+ * Returns a 2-arg `(name, fn)` function that runs the test suite against each
96
+ * factory. Each factory gets its own `describe` block with a shared database
97
+ * instance, automatic `beforeEach` truncation, and `afterAll` cleanup.
98
+ * Skipped factories use `describe.skip`.
99
+ *
100
+ * @param factories - one or more database factories to run suites against
101
+ * @param truncate_tables - tables to truncate between tests (children first for FK safety)
102
+ * @returns a `describe_db` function for use in test files
103
+ */
104
+ export declare const create_describe_db: (factories: DbFactory | Array<DbFactory>, truncate_tables: Array<string>) => ((name: string, fn: (get_db: () => Db) => void) => void);
105
+ /**
106
+ * Log factory status to console.
107
+ *
108
+ * @param factories - the database factories to report on
109
+ */
110
+ export declare const log_db_factory_status: (factories: Array<DbFactory>) => void;
111
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/db.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA6B7B,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAKpC;;GAEG;AACH,eAAO,MAAM,KAAK,SAA4B,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAGvD,CAAC;AAMF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,GAAI,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,KAAG,SAkB7E,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iBAAiB,GAC7B,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACtC,WAAW,MAAM,KACf,SA2DF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,UAOhC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,UAAyC,CAAC;AAEvF;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,+HAUnB,CAAC;AAEX;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAK3D,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,GAC9B,WAAW,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,EACvC,iBAAiB,KAAK,CAAC,MAAM,CAAC,KAC5B,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,KAAK,IAAI,CAwBzD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,WAAW,KAAK,CAAC,SAAS,CAAC,KAAG,IAMnE,CAAC"}
@@ -0,0 +1,258 @@
1
+ import './assert_dev_env.js';
2
+ /**
3
+ * Test database fixtures for parameterized testing.
4
+ *
5
+ * Provides factory builders for creating test databases with pglite (in-memory)
6
+ * and pg (PostgreSQL) drivers. Consumer projects provide their own schema
7
+ * initialization via `run_migrations` and compose factories into test suites.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import {create_pglite_factory, create_pg_factory} from '@fuzdev/fuz_app/testing/db.js';
12
+ * import {run_migrations} from '@fuzdev/fuz_app/db/migrate.js';
13
+ * import {AUTH_MIGRATION_NS} from '@fuzdev/fuz_app/auth/migrations.js';
14
+ *
15
+ * const init_schema = async (db) => {
16
+ * await run_migrations(db, [AUTH_MIGRATION_NS]);
17
+ * };
18
+ * const pglite_factory = create_pglite_factory(init_schema);
19
+ * const pg_factory = create_pg_factory(init_schema, process.env.TEST_DATABASE_URL);
20
+ * export const db_factories = [pglite_factory, pg_factory];
21
+ * ```
22
+ *
23
+ * @module
24
+ */
25
+ import { describe, beforeAll, beforeEach, afterAll } from 'vitest';
26
+ import { create_pglite_db } from '../db/db_pglite.js';
27
+ import { assert_valid_sql_identifier } from '../db/sql_identifier.js';
28
+ import { create_pg_db } from '../db/db_pg.js';
29
+ /**
30
+ * CI detection — `CI=true` is set automatically by GitHub Actions, GitLab CI, etc.
31
+ */
32
+ export const IS_CI = process.env.CI === 'true';
33
+ /**
34
+ * Reset a PGlite database to a clean state by dropping and recreating the public schema.
35
+ *
36
+ * Removes all tables, sequences, indexes, types, and functions.
37
+ * The database instance remains usable after reset.
38
+ *
39
+ * @param db - the database to reset
40
+ */
41
+ export const reset_pglite = async (db) => {
42
+ await db.query('DROP SCHEMA public CASCADE');
43
+ await db.query('CREATE SCHEMA public');
44
+ };
45
+ // Module-level PGlite cache — shared across all factories in the same vitest worker (one test file).
46
+ // Each vitest file runs in its own worker thread, so no cross-file contamination.
47
+ let module_db = null;
48
+ /**
49
+ * Create a pglite (in-memory) database factory for tests.
50
+ *
51
+ * Always enabled — no external dependencies required.
52
+ * Shares a single PGlite WASM instance across all factories in the same
53
+ * vitest worker thread (one test file). Subsequent `create()` calls reset
54
+ * the schema via `DROP SCHEMA public CASCADE` instead of paying the WASM
55
+ * cold-start cost again.
56
+ *
57
+ * @param init_schema - callback to initialize the database schema
58
+ * @returns a factory that creates in-memory pglite databases
59
+ */
60
+ export const create_pglite_factory = (init_schema) => ({
61
+ name: 'pglite',
62
+ skip: false,
63
+ async create() {
64
+ if (module_db) {
65
+ await reset_pglite(module_db);
66
+ }
67
+ else {
68
+ const { PGlite } = await import('@electric-sql/pglite');
69
+ const pglite = new PGlite();
70
+ module_db = create_pglite_db(pglite).db;
71
+ }
72
+ await init_schema(module_db);
73
+ return module_db;
74
+ },
75
+ async close() {
76
+ // No-op: shared instance lives for the worker thread's lifetime.
77
+ // PGlite is cleaned up when the vitest worker exits.
78
+ },
79
+ });
80
+ /**
81
+ * Create a pg (PostgreSQL) database factory for tests.
82
+ *
83
+ * Skipped when `test_url` is not provided.
84
+ * Drops `schema_version` before running `init_schema`, forcing migrations
85
+ * to re-evaluate against the actual tables. Prevents stale version entries
86
+ * from skipping migrations when DDL changes between test sessions.
87
+ *
88
+ * For full clean-slate behavior (recommended), call `drop_auth_schema(db)`
89
+ * at the start of `init_schema` before running migrations. This handles
90
+ * upstream schema changes that go beyond adding new tables/columns.
91
+ *
92
+ * @param init_schema - callback to initialize the database schema
93
+ * @param test_url - PostgreSQL connection URL (e.g. from `TEST_DATABASE_URL`)
94
+ * @returns a factory that creates pg databases
95
+ */
96
+ export const create_pg_factory = (init_schema, test_url) => {
97
+ const should_skip = !test_url;
98
+ const skip_reason = !test_url ? 'TEST_DATABASE_URL not set' : undefined;
99
+ let pool_ref = null;
100
+ return {
101
+ name: 'pg',
102
+ skip: should_skip,
103
+ skip_reason,
104
+ async create() {
105
+ if (!test_url) {
106
+ throw new Error('TEST_DATABASE_URL required for pg tests.');
107
+ }
108
+ // Close any previous pool to prevent leaks on repeated create() calls.
109
+ // With isolate: false, the pool may have been ended by a previous file's close().
110
+ if (pool_ref) {
111
+ try {
112
+ await pool_ref.end();
113
+ }
114
+ catch {
115
+ // pool already ended — safe to ignore
116
+ }
117
+ pool_ref = null;
118
+ }
119
+ const { Pool } = await import('pg');
120
+ const pool = new Pool({ connectionString: test_url });
121
+ pool_ref = pool;
122
+ const { db } = create_pg_db(pool);
123
+ try {
124
+ // Drop schema_version so migrations re-evaluate against the actual
125
+ // tables. Prevents stale version entries from skipping migrations
126
+ // when DDL changes between test sessions. Migrations use
127
+ // IF NOT EXISTS guards, so re-running is safe.
128
+ await db.query('DROP TABLE IF EXISTS schema_version');
129
+ await init_schema(db);
130
+ }
131
+ catch (error) {
132
+ await pool.end();
133
+ const msg = error instanceof Error ? error.message : String(error);
134
+ if (msg.includes('does not exist')) {
135
+ const db_name = test_url.split('/').pop()?.split('?')[0] ?? 'test_db';
136
+ throw new Error(`Database "${db_name}" does not exist. Create it with: createdb ${db_name}`);
137
+ }
138
+ throw error;
139
+ }
140
+ return db;
141
+ },
142
+ async close() {
143
+ if (pool_ref) {
144
+ try {
145
+ await pool_ref.end();
146
+ }
147
+ catch {
148
+ // pool already ended — safe to ignore
149
+ }
150
+ pool_ref = null;
151
+ }
152
+ },
153
+ };
154
+ };
155
+ /**
156
+ * Auth table names in truncation order (children first for FK safety).
157
+ *
158
+ * Consumer projects can spread this into their own list and append app-specific tables.
159
+ */
160
+ export const AUTH_TRUNCATE_TABLES = [
161
+ 'invite',
162
+ 'api_token',
163
+ 'auth_session',
164
+ 'permit',
165
+ 'actor',
166
+ 'account',
167
+ ];
168
+ /**
169
+ * Auth tables including `audit_log` — for integration tests that exercise
170
+ * the full middleware stack (login, admin, rate limiting).
171
+ *
172
+ * Separate from `AUTH_TRUNCATE_TABLES` because unit-level DB tests that don't
173
+ * touch audit logging don't need to truncate it.
174
+ */
175
+ export const AUTH_INTEGRATION_TRUNCATE_TABLES = [...AUTH_TRUNCATE_TABLES, 'audit_log'];
176
+ /**
177
+ * All auth tables in drop order (children first for FK safety).
178
+ *
179
+ * The full set created by `AUTH_MIGRATIONS` — use for clean-slate
180
+ * test DB initialization. `AUTH_TRUNCATE_TABLES` is the subset for
181
+ * between-test data cleanup (excludes `audit_log`).
182
+ *
183
+ * When adding tables to `AUTH_MIGRATIONS`, add them here too.
184
+ */
185
+ export const AUTH_DROP_TABLES = [
186
+ 'app_settings',
187
+ 'invite',
188
+ 'audit_log',
189
+ 'api_token',
190
+ 'auth_session',
191
+ 'permit',
192
+ 'actor',
193
+ 'account',
194
+ 'bootstrap_lock',
195
+ ];
196
+ /**
197
+ * Drop all auth tables and schema version tracking for a clean slate.
198
+ *
199
+ * Recommended at the start of `init_schema` callbacks for `create_pg_factory`.
200
+ * Persistent test databases can accumulate stale schema from previous fuz_app
201
+ * versions — this ensures migrations run against a truly empty database.
202
+ * Safe on fresh databases (`IF EXISTS` on all statements). No-op effect for
203
+ * PGlite (already fresh), but harmless to call unconditionally.
204
+ *
205
+ * @param db - the database to clean
206
+ */
207
+ export const drop_auth_schema = async (db) => {
208
+ for (const table of AUTH_DROP_TABLES) {
209
+ await db.query(`DROP TABLE IF EXISTS ${assert_valid_sql_identifier(table)} CASCADE`); // eslint-disable-line no-await-in-loop
210
+ }
211
+ await db.query('DROP TABLE IF EXISTS schema_version CASCADE');
212
+ };
213
+ /**
214
+ * Create a `describe_db` function bound to specific factories and truncate tables.
215
+ *
216
+ * Returns a 2-arg `(name, fn)` function that runs the test suite against each
217
+ * factory. Each factory gets its own `describe` block with a shared database
218
+ * instance, automatic `beforeEach` truncation, and `afterAll` cleanup.
219
+ * Skipped factories use `describe.skip`.
220
+ *
221
+ * @param factories - one or more database factories to run suites against
222
+ * @param truncate_tables - tables to truncate between tests (children first for FK safety)
223
+ * @returns a `describe_db` function for use in test files
224
+ */
225
+ export const create_describe_db = (factories, truncate_tables) => {
226
+ const factory_list = Array.isArray(factories) ? factories : [factories];
227
+ return (name, fn) => {
228
+ for (const factory of factory_list) {
229
+ const describe_fn = factory.skip ? describe.skip : describe;
230
+ describe_fn(`${name} (${factory.name})`, () => {
231
+ let db;
232
+ beforeAll(async () => {
233
+ db = await factory.create();
234
+ });
235
+ beforeEach(async () => {
236
+ if (db && truncate_tables.length > 0) {
237
+ await db.query(`TRUNCATE ${truncate_tables.map(assert_valid_sql_identifier).join(', ')} CASCADE`);
238
+ }
239
+ });
240
+ afterAll(async () => {
241
+ if (db)
242
+ await factory.close(db);
243
+ });
244
+ fn(() => db);
245
+ });
246
+ }
247
+ };
248
+ };
249
+ /**
250
+ * Log factory status to console.
251
+ *
252
+ * @param factories - the database factories to report on
253
+ */
254
+ export const log_db_factory_status = (factories) => {
255
+ const enabled = factories.filter((f) => !f.skip).map((f) => f.name);
256
+ const skipped = factories.filter((f) => f.skip).map((f) => `${f.name} (${f.skip_reason})`);
257
+ console.log(`[db tests] drivers: ${enabled.join(', ')}${skipped.length ? ` | skipped: ${skipped.join(', ')}` : ''}`);
258
+ };