@yielded/auth-persistence 0.1.0-beta.7

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 (526) hide show
  1. package/README.md +21 -0
  2. package/dist/Drizzle.d.mts +28 -0
  3. package/dist/Drizzle.mjs +28 -0
  4. package/dist/DrizzleD1.d.mts +963 -0
  5. package/dist/DrizzleD1.mjs +29 -0
  6. package/dist/DrizzleD1.mjs.map +1 -0
  7. package/dist/DrizzleLibsql.d.mts +2284 -0
  8. package/dist/DrizzleLibsql.mjs +92 -0
  9. package/dist/DrizzleLibsql.mjs.map +1 -0
  10. package/dist/DrizzleMysql2.d.mts +1820 -0
  11. package/dist/DrizzleMysql2.mjs +55 -0
  12. package/dist/DrizzleMysql2.mjs.map +1 -0
  13. package/dist/DrizzlePglite.d.mts +1826 -0
  14. package/dist/DrizzlePglite.mjs +53 -0
  15. package/dist/DrizzlePglite.mjs.map +1 -0
  16. package/dist/DrizzlePostgres.d.mts +1826 -0
  17. package/dist/DrizzlePostgres.mjs +53 -0
  18. package/dist/DrizzlePostgres.mjs.map +1 -0
  19. package/dist/DrizzleSqliteBun.d.mts +2279 -0
  20. package/dist/DrizzleSqliteBun.mjs +70 -0
  21. package/dist/DrizzleSqliteBun.mjs.map +1 -0
  22. package/dist/DrizzleSqliteDo.d.mts +2329 -0
  23. package/dist/DrizzleSqliteDo.mjs +105 -0
  24. package/dist/DrizzleSqliteDo.mjs.map +1 -0
  25. package/dist/DrizzleSqliteNode.d.mts +2279 -0
  26. package/dist/DrizzleSqliteNode.mjs +70 -0
  27. package/dist/DrizzleSqliteNode.mjs.map +1 -0
  28. package/dist/DrizzleSqliteWasm.d.mts +2282 -0
  29. package/dist/DrizzleSqliteWasm.mjs +70 -0
  30. package/dist/DrizzleSqliteWasm.mjs.map +1 -0
  31. package/dist/drizzle/D1BatchStatements.d.mts +11 -0
  32. package/dist/drizzle/D1BatchStatements.mjs +8 -0
  33. package/dist/drizzle/D1BatchStatements.mjs.map +1 -0
  34. package/dist/drizzle/PasswordPreparedPostconditions.mjs +12 -0
  35. package/dist/drizzle/PasswordPreparedPostconditions.mjs.map +1 -0
  36. package/dist/drizzle/SuppliedService.d.mts +10 -0
  37. package/dist/drizzle/d1-emails.d.mts +62 -0
  38. package/dist/drizzle/d1-emails.mjs +580 -0
  39. package/dist/drizzle/d1-emails.mjs.map +1 -0
  40. package/dist/drizzle/d1-generated-statement.d.mts +2 -0
  41. package/dist/drizzle/d1-generated-statement.mjs +79 -0
  42. package/dist/drizzle/d1-generated-statement.mjs.map +1 -0
  43. package/dist/drizzle/d1-identity.d.mts +31 -0
  44. package/dist/drizzle/d1-identity.mjs +119 -0
  45. package/dist/drizzle/d1-identity.mjs.map +1 -0
  46. package/dist/drizzle/d1-oauth.d.mts +301 -0
  47. package/dist/drizzle/d1-oauth.mjs +76 -0
  48. package/dist/drizzle/d1-oauth.mjs.map +1 -0
  49. package/dist/drizzle/d1-passkey.d.mts +51 -0
  50. package/dist/drizzle/d1-passkey.mjs +95 -0
  51. package/dist/drizzle/d1-passkey.mjs.map +1 -0
  52. package/dist/drizzle/d1-password-prepared.d.mts +25 -0
  53. package/dist/drizzle/d1-password-prepared.mjs +461 -0
  54. package/dist/drizzle/d1-password-prepared.mjs.map +1 -0
  55. package/dist/drizzle/d1-passwords.d.mts +87 -0
  56. package/dist/drizzle/d1-passwords.mjs +1004 -0
  57. package/dist/drizzle/d1-passwords.mjs.map +1 -0
  58. package/dist/drizzle/d1-phone.d.mts +26 -0
  59. package/dist/drizzle/d1-phone.mjs +30 -0
  60. package/dist/drizzle/d1-phone.mjs.map +1 -0
  61. package/dist/drizzle/d1-planning.d.mts +12 -0
  62. package/dist/drizzle/d1-planning.mjs +43 -0
  63. package/dist/drizzle/d1-planning.mjs.map +1 -0
  64. package/dist/drizzle/d1-proofs.d.mts +80 -0
  65. package/dist/drizzle/d1-proofs.mjs +830 -0
  66. package/dist/drizzle/d1-proofs.mjs.map +1 -0
  67. package/dist/drizzle/d1-sessions.d.mts +67 -0
  68. package/dist/drizzle/d1-sessions.mjs +966 -0
  69. package/dist/drizzle/d1-sessions.mjs.map +1 -0
  70. package/dist/drizzle/d1-totp.d.mts +26 -0
  71. package/dist/drizzle/d1-totp.mjs +27 -0
  72. package/dist/drizzle/d1-totp.mjs.map +1 -0
  73. package/dist/drizzle/email-model.d.mts +354 -0
  74. package/dist/drizzle/email-model.mjs +21 -0
  75. package/dist/drizzle/email-model.mjs.map +1 -0
  76. package/dist/drizzle/email-registration.d.mts +30 -0
  77. package/dist/drizzle/email-registration.mjs +238 -0
  78. package/dist/drizzle/email-registration.mjs.map +1 -0
  79. package/dist/drizzle/email-sql.d.mts +4 -0
  80. package/dist/drizzle/email-sql.mjs +9 -0
  81. package/dist/drizzle/email-sql.mjs.map +1 -0
  82. package/dist/drizzle/email-target.d.mts +20 -0
  83. package/dist/drizzle/email-target.mjs +72 -0
  84. package/dist/drizzle/email-target.mjs.map +1 -0
  85. package/dist/drizzle/model.d.mts +250 -0
  86. package/dist/drizzle/model.mjs +58 -0
  87. package/dist/drizzle/model.mjs.map +1 -0
  88. package/dist/drizzle/mysql-emails.d.mts +71 -0
  89. package/dist/drizzle/mysql-emails.mjs +38 -0
  90. package/dist/drizzle/mysql-emails.mjs.map +1 -0
  91. package/dist/drizzle/mysql-identity.d.mts +22 -0
  92. package/dist/drizzle/mysql-identity.mjs +94 -0
  93. package/dist/drizzle/mysql-identity.mjs.map +1 -0
  94. package/dist/drizzle/mysql-password-prepared.d.mts +30 -0
  95. package/dist/drizzle/mysql-password-prepared.mjs +16 -0
  96. package/dist/drizzle/mysql-password-prepared.mjs.map +1 -0
  97. package/dist/drizzle/mysql-passwords.d.mts +86 -0
  98. package/dist/drizzle/mysql-passwords.mjs +38 -0
  99. package/dist/drizzle/mysql-passwords.mjs.map +1 -0
  100. package/dist/drizzle/mysql-proofs.d.mts +75 -0
  101. package/dist/drizzle/mysql-proofs.mjs +23 -0
  102. package/dist/drizzle/mysql-proofs.mjs.map +1 -0
  103. package/dist/drizzle/mysql-sessions.d.mts +237 -0
  104. package/dist/drizzle/mysql-sessions.mjs +53 -0
  105. package/dist/drizzle/mysql-sessions.mjs.map +1 -0
  106. package/dist/drizzle/mysql.d.mts +70 -0
  107. package/dist/drizzle/mysql.mjs +218 -0
  108. package/dist/drizzle/mysql.mjs.map +1 -0
  109. package/dist/drizzle/oauth-accounts.mjs +358 -0
  110. package/dist/drizzle/oauth-accounts.mjs.map +1 -0
  111. package/dist/drizzle/oauth-connected-access.mjs +210 -0
  112. package/dist/drizzle/oauth-connected-access.mjs.map +1 -0
  113. package/dist/drizzle/oauth-connected-collection.mjs +202 -0
  114. package/dist/drizzle/oauth-connected-collection.mjs.map +1 -0
  115. package/dist/drizzle/oauth-connected-custody.mjs +146 -0
  116. package/dist/drizzle/oauth-connected-custody.mjs.map +1 -0
  117. package/dist/drizzle/oauth-connected-drivers.mjs +76 -0
  118. package/dist/drizzle/oauth-connected-drivers.mjs.map +1 -0
  119. package/dist/drizzle/oauth-connected-flow.mjs +247 -0
  120. package/dist/drizzle/oauth-connected-flow.mjs.map +1 -0
  121. package/dist/drizzle/oauth-connected-input.mjs +103 -0
  122. package/dist/drizzle/oauth-connected-input.mjs.map +1 -0
  123. package/dist/drizzle/oauth-connected-maintenance.mjs +214 -0
  124. package/dist/drizzle/oauth-connected-maintenance.mjs.map +1 -0
  125. package/dist/drizzle/oauth-connected-management.mjs +228 -0
  126. package/dist/drizzle/oauth-connected-management.mjs.map +1 -0
  127. package/dist/drizzle/oauth-connected-model.d.mts +278 -0
  128. package/dist/drizzle/oauth-connected-model.mjs +22 -0
  129. package/dist/drizzle/oauth-connected-model.mjs.map +1 -0
  130. package/dist/drizzle/oauth-connected-reference.d.mts +17 -0
  131. package/dist/drizzle/oauth-connected-reference.mjs +53 -0
  132. package/dist/drizzle/oauth-connected-reference.mjs.map +1 -0
  133. package/dist/drizzle/oauth-connected-settlement.mjs +117 -0
  134. package/dist/drizzle/oauth-connected-settlement.mjs.map +1 -0
  135. package/dist/drizzle/oauth-connected-state.d.mts +14 -0
  136. package/dist/drizzle/oauth-connected-state.mjs +422 -0
  137. package/dist/drizzle/oauth-connected-state.mjs.map +1 -0
  138. package/dist/drizzle/oauth-connected-target.d.mts +753 -0
  139. package/dist/drizzle/oauth-connected-target.mjs +91 -0
  140. package/dist/drizzle/oauth-connected-target.mjs.map +1 -0
  141. package/dist/drizzle/oauth-drivers.mjs +51 -0
  142. package/dist/drizzle/oauth-drivers.mjs.map +1 -0
  143. package/dist/drizzle/oauth-flow.d.mts +5 -0
  144. package/dist/drizzle/oauth-flow.mjs +253 -0
  145. package/dist/drizzle/oauth-flow.mjs.map +1 -0
  146. package/dist/drizzle/oauth-input.mjs +127 -0
  147. package/dist/drizzle/oauth-input.mjs.map +1 -0
  148. package/dist/drizzle/oauth-model.d.mts +463 -0
  149. package/dist/drizzle/oauth-model.mjs +49 -0
  150. package/dist/drizzle/oauth-model.mjs.map +1 -0
  151. package/dist/drizzle/oauth-mysql.mjs +8 -0
  152. package/dist/drizzle/oauth-mysql.mjs.map +1 -0
  153. package/dist/drizzle/oauth-owner.d.mts +4 -0
  154. package/dist/drizzle/oauth-owner.mjs +10 -0
  155. package/dist/drizzle/oauth-owner.mjs.map +1 -0
  156. package/dist/drizzle/oauth-registration.d.mts +6 -0
  157. package/dist/drizzle/oauth-registration.mjs +374 -0
  158. package/dist/drizzle/oauth-registration.mjs.map +1 -0
  159. package/dist/drizzle/oauth-state.mjs +107 -0
  160. package/dist/drizzle/oauth-state.mjs.map +1 -0
  161. package/dist/drizzle/oauth-target.d.mts +111 -0
  162. package/dist/drizzle/oauth-target.mjs +246 -0
  163. package/dist/drizzle/oauth-target.mjs.map +1 -0
  164. package/dist/drizzle/passkey-drivers.mjs +34 -0
  165. package/dist/drizzle/passkey-drivers.mjs.map +1 -0
  166. package/dist/drizzle/passkey-kernel.mjs +8 -0
  167. package/dist/drizzle/passkey-kernel.mjs.map +1 -0
  168. package/dist/drizzle/passkey-model.d.mts +272 -0
  169. package/dist/drizzle/passkey-model.mjs +34 -0
  170. package/dist/drizzle/passkey-model.mjs.map +1 -0
  171. package/dist/drizzle/passkey-registration-ceremony-model.d.mts +50 -0
  172. package/dist/drizzle/passkey-registration-ceremony-model.mjs +18 -0
  173. package/dist/drizzle/passkey-registration-ceremony-model.mjs.map +1 -0
  174. package/dist/drizzle/passkey-state.mjs +7 -0
  175. package/dist/drizzle/passkey-state.mjs.map +1 -0
  176. package/dist/drizzle/passkey-target.d.mts +9 -0
  177. package/dist/drizzle/passkey-target.mjs +7 -0
  178. package/dist/drizzle/passkey-target.mjs.map +1 -0
  179. package/dist/drizzle/passkey-write-drivers.mjs +32 -0
  180. package/dist/drizzle/passkey-write-drivers.mjs.map +1 -0
  181. package/dist/drizzle/passkey-write-model.d.mts +190 -0
  182. package/dist/drizzle/passkey-write-model.mjs +13 -0
  183. package/dist/drizzle/passkey-write-model.mjs.map +1 -0
  184. package/dist/drizzle/passkey-write-target.d.mts +11 -0
  185. package/dist/drizzle/passkey-write-target.mjs +7 -0
  186. package/dist/drizzle/passkey-write-target.mjs.map +1 -0
  187. package/dist/drizzle/password-model.d.mts +329 -0
  188. package/dist/drizzle/password-model.mjs +23 -0
  189. package/dist/drizzle/password-model.mjs.map +1 -0
  190. package/dist/drizzle/password-prepared-model.d.mts +94 -0
  191. package/dist/drizzle/password-prepared-model.mjs +11 -0
  192. package/dist/drizzle/password-prepared-model.mjs.map +1 -0
  193. package/dist/drizzle/password-prepared-sql.mjs +390 -0
  194. package/dist/drizzle/password-prepared-sql.mjs.map +1 -0
  195. package/dist/drizzle/password-prepared-state.mjs +104 -0
  196. package/dist/drizzle/password-prepared-state.mjs.map +1 -0
  197. package/dist/drizzle/password-prepared-target.d.mts +12 -0
  198. package/dist/drizzle/password-prepared-target.mjs +56 -0
  199. package/dist/drizzle/password-prepared-target.mjs.map +1 -0
  200. package/dist/drizzle/password-registration.d.mts +19 -0
  201. package/dist/drizzle/password-registration.mjs +135 -0
  202. package/dist/drizzle/password-registration.mjs.map +1 -0
  203. package/dist/drizzle/password-sql.d.mts +5 -0
  204. package/dist/drizzle/password-sql.mjs +9 -0
  205. package/dist/drizzle/password-sql.mjs.map +1 -0
  206. package/dist/drizzle/password-target.d.mts +17 -0
  207. package/dist/drizzle/password-target.mjs +61 -0
  208. package/dist/drizzle/password-target.mjs.map +1 -0
  209. package/dist/drizzle/pg-emails.d.mts +72 -0
  210. package/dist/drizzle/pg-emails.mjs +38 -0
  211. package/dist/drizzle/pg-emails.mjs.map +1 -0
  212. package/dist/drizzle/pg-password-prepared.d.mts +31 -0
  213. package/dist/drizzle/pg-password-prepared.mjs +16 -0
  214. package/dist/drizzle/pg-password-prepared.mjs.map +1 -0
  215. package/dist/drizzle/pg-passwords.d.mts +87 -0
  216. package/dist/drizzle/pg-passwords.mjs +38 -0
  217. package/dist/drizzle/pg-passwords.mjs.map +1 -0
  218. package/dist/drizzle/pg-proofs.d.mts +76 -0
  219. package/dist/drizzle/pg-proofs.mjs +22 -0
  220. package/dist/drizzle/pg-proofs.mjs.map +1 -0
  221. package/dist/drizzle/pg-sessions.d.mts +238 -0
  222. package/dist/drizzle/pg-sessions.mjs +53 -0
  223. package/dist/drizzle/pg-sessions.mjs.map +1 -0
  224. package/dist/drizzle/pg.d.mts +81 -0
  225. package/dist/drizzle/pg.mjs +290 -0
  226. package/dist/drizzle/pg.mjs.map +1 -0
  227. package/dist/drizzle/phone-model.d.mts +115 -0
  228. package/dist/drizzle/phone-model.mjs +39 -0
  229. package/dist/drizzle/phone-model.mjs.map +1 -0
  230. package/dist/drizzle/phone-state.mjs +16 -0
  231. package/dist/drizzle/phone-state.mjs.map +1 -0
  232. package/dist/drizzle/phone-target.d.mts +17 -0
  233. package/dist/drizzle/phone-target.mjs +170 -0
  234. package/dist/drizzle/phone-target.mjs.map +1 -0
  235. package/dist/drizzle/proof-model.d.mts +286 -0
  236. package/dist/drizzle/proof-model.mjs +17 -0
  237. package/dist/drizzle/proof-model.mjs.map +1 -0
  238. package/dist/drizzle/proof-sql.d.mts +4 -0
  239. package/dist/drizzle/proof-sql.mjs +8 -0
  240. package/dist/drizzle/proof-sql.mjs.map +1 -0
  241. package/dist/drizzle/proof-target.d.mts +12 -0
  242. package/dist/drizzle/proof-target.mjs +36 -0
  243. package/dist/drizzle/proof-target.mjs.map +1 -0
  244. package/dist/drizzle/query-operations.mjs +26 -0
  245. package/dist/drizzle/query-operations.mjs.map +1 -0
  246. package/dist/drizzle/session-model.d.mts +200 -0
  247. package/dist/drizzle/session-model.mjs +20 -0
  248. package/dist/drizzle/session-model.mjs.map +1 -0
  249. package/dist/drizzle/session-sql.mjs +8 -0
  250. package/dist/drizzle/session-sql.mjs.map +1 -0
  251. package/dist/drizzle/session-target.d.mts +28 -0
  252. package/dist/drizzle/session-target.mjs +47 -0
  253. package/dist/drizzle/session-target.mjs.map +1 -0
  254. package/dist/drizzle/sqlite-emails.mjs +45 -0
  255. package/dist/drizzle/sqlite-emails.mjs.map +1 -0
  256. package/dist/drizzle/sqlite-identity.mjs +91 -0
  257. package/dist/drizzle/sqlite-identity.mjs.map +1 -0
  258. package/dist/drizzle/sqlite-password-prepared.mjs +21 -0
  259. package/dist/drizzle/sqlite-password-prepared.mjs.map +1 -0
  260. package/dist/drizzle/sqlite-passwords.mjs +49 -0
  261. package/dist/drizzle/sqlite-passwords.mjs.map +1 -0
  262. package/dist/drizzle/sqlite-proofs.mjs +29 -0
  263. package/dist/drizzle/sqlite-proofs.mjs.map +1 -0
  264. package/dist/drizzle/sqlite-sessions.mjs +74 -0
  265. package/dist/drizzle/sqlite-sessions.mjs.map +1 -0
  266. package/dist/drizzle/sqlite.d.mts +78 -0
  267. package/dist/drizzle/sqlite.mjs +217 -0
  268. package/dist/drizzle/sqlite.mjs.map +1 -0
  269. package/dist/drizzle/standalone-guard.mjs +19 -0
  270. package/dist/drizzle/standalone-guard.mjs.map +1 -0
  271. package/dist/drizzle/step-up-model.d.mts +65 -0
  272. package/dist/drizzle/step-up-model.mjs +9 -0
  273. package/dist/drizzle/step-up-model.mjs.map +1 -0
  274. package/dist/drizzle/step-up-state.mjs +32 -0
  275. package/dist/drizzle/step-up-state.mjs.map +1 -0
  276. package/dist/drizzle/totp-model.d.mts +84 -0
  277. package/dist/drizzle/totp-model.mjs +13 -0
  278. package/dist/drizzle/totp-model.mjs.map +1 -0
  279. package/dist/drizzle/totp-state.d.mts +38 -0
  280. package/dist/drizzle/totp-state.mjs +287 -0
  281. package/dist/drizzle/totp-state.mjs.map +1 -0
  282. package/dist/drizzle/totp-target.d.mts +17 -0
  283. package/dist/drizzle/totp-target.mjs +81 -0
  284. package/dist/drizzle/totp-target.mjs.map +1 -0
  285. package/dist/drizzle/transaction-execution.d.mts +4 -0
  286. package/dist/drizzle/transaction-execution.mjs +11 -0
  287. package/dist/drizzle/transaction-execution.mjs.map +1 -0
  288. package/dist/drizzle/transaction-mysql.mjs +46 -0
  289. package/dist/drizzle/transaction-mysql.mjs.map +1 -0
  290. package/dist/drizzle/transaction-owner.d.mts +2 -0
  291. package/dist/drizzle/transaction-owner.mjs +8 -0
  292. package/dist/drizzle/transaction-owner.mjs.map +1 -0
  293. package/dist/index.d.mts +5 -0
  294. package/dist/index.mjs +5 -0
  295. package/dist/internal/configuration.d.mts +174 -0
  296. package/dist/internal/configuration.mjs +7 -0
  297. package/dist/internal/configuration.mjs.map +1 -0
  298. package/dist/internal/drizzle-migrations.mjs +19 -0
  299. package/dist/internal/drizzle-migrations.mjs.map +1 -0
  300. package/dist/internal/drizzle-postgres.mjs +39 -0
  301. package/dist/internal/drizzle-postgres.mjs.map +1 -0
  302. package/dist/internal/drizzle-sqlite.mjs +37 -0
  303. package/dist/internal/drizzle-sqlite.mjs.map +1 -0
  304. package/dist/internal/email-kernel.d.mts +13 -0
  305. package/dist/internal/email-kernel.mjs +487 -0
  306. package/dist/internal/email-kernel.mjs.map +1 -0
  307. package/dist/internal/mapping-error.d.mts +11 -0
  308. package/dist/internal/mapping-error.mjs +30 -0
  309. package/dist/internal/mapping-error.mjs.map +1 -0
  310. package/dist/internal/passkey/admission.d.mts +7 -0
  311. package/dist/internal/passkey/admission.mjs +216 -0
  312. package/dist/internal/passkey/admission.mjs.map +1 -0
  313. package/dist/internal/passkey/credentials.d.mts +6 -0
  314. package/dist/internal/passkey/credentials.mjs +249 -0
  315. package/dist/internal/passkey/credentials.mjs.map +1 -0
  316. package/dist/internal/passkey/enrollment.mjs +138 -0
  317. package/dist/internal/passkey/enrollment.mjs.map +1 -0
  318. package/dist/internal/passkey/flow.d.mts +10 -0
  319. package/dist/internal/passkey/flow.mjs +452 -0
  320. package/dist/internal/passkey/flow.mjs.map +1 -0
  321. package/dist/internal/passkey/management.mjs +231 -0
  322. package/dist/internal/passkey/management.mjs.map +1 -0
  323. package/dist/internal/passkey/registration-ceremony.d.mts +9 -0
  324. package/dist/internal/passkey/registration-ceremony.mjs +129 -0
  325. package/dist/internal/passkey/registration-ceremony.mjs.map +1 -0
  326. package/dist/internal/passkey/registration-custody.d.mts +2 -0
  327. package/dist/internal/passkey/registration-custody.mjs +52 -0
  328. package/dist/internal/passkey/registration-custody.mjs.map +1 -0
  329. package/dist/internal/passkey/registration-write.mjs +211 -0
  330. package/dist/internal/passkey/registration-write.mjs.map +1 -0
  331. package/dist/internal/passkey/state.d.mts +5 -0
  332. package/dist/internal/passkey/state.mjs +234 -0
  333. package/dist/internal/passkey/state.mjs.map +1 -0
  334. package/dist/internal/passkey/target.d.mts +19 -0
  335. package/dist/internal/passkey/target.mjs +190 -0
  336. package/dist/internal/passkey/target.mjs.map +1 -0
  337. package/dist/internal/passkey/write-state.mjs +345 -0
  338. package/dist/internal/passkey/write-state.mjs.map +1 -0
  339. package/dist/internal/passkey/write-target.mjs +192 -0
  340. package/dist/internal/passkey/write-target.mjs.map +1 -0
  341. package/dist/internal/passkey-kernel.mjs +49 -0
  342. package/dist/internal/passkey-kernel.mjs.map +1 -0
  343. package/dist/internal/passkeys.mjs +367 -0
  344. package/dist/internal/passkeys.mjs.map +1 -0
  345. package/dist/internal/password-kernel.d.mts +13 -0
  346. package/dist/internal/password-kernel.mjs +704 -0
  347. package/dist/internal/password-kernel.mjs.map +1 -0
  348. package/dist/internal/persistence.mjs +289 -0
  349. package/dist/internal/persistence.mjs.map +1 -0
  350. package/dist/internal/phone-kernel.mjs +434 -0
  351. package/dist/internal/phone-kernel.mjs.map +1 -0
  352. package/dist/internal/proof-kernel.d.mts +9 -0
  353. package/dist/internal/proof-kernel.mjs +619 -0
  354. package/dist/internal/proof-kernel.mjs.map +1 -0
  355. package/dist/internal/query-operations.d.mts +3 -0
  356. package/dist/internal/registration.mjs +65 -0
  357. package/dist/internal/registration.mjs.map +1 -0
  358. package/dist/internal/session-kernel.mjs +737 -0
  359. package/dist/internal/session-kernel.mjs.map +1 -0
  360. package/dist/internal/sql-persistence.d.mts +11 -0
  361. package/dist/internal/sql-persistence.mjs +37 -0
  362. package/dist/internal/sql-persistence.mjs.map +1 -0
  363. package/dist/internal/sql-query.mjs +207 -0
  364. package/dist/internal/sql-query.mjs.map +1 -0
  365. package/dist/internal/sql-table.d.mts +37 -0
  366. package/dist/internal/sql-table.mjs +71 -0
  367. package/dist/internal/sql-table.mjs.map +1 -0
  368. package/dist/internal/storage-mapping.mjs +687 -0
  369. package/dist/internal/storage-mapping.mjs.map +1 -0
  370. package/dist/internal/storage-tables.d.mts +914 -0
  371. package/dist/internal/storage-tables.mjs +387 -0
  372. package/dist/internal/storage-tables.mjs.map +1 -0
  373. package/dist/internal/storage-validation.mjs +58 -0
  374. package/dist/internal/storage-validation.mjs.map +1 -0
  375. package/dist/internal/transaction-execution-kernel.d.mts +18 -0
  376. package/dist/internal/transaction-execution-kernel.mjs +118 -0
  377. package/dist/internal/transaction-execution-kernel.mjs.map +1 -0
  378. package/dist/internal/transaction-kernel.d.mts +7 -0
  379. package/dist/internal/transaction-kernel.mjs +260 -0
  380. package/dist/internal/transaction-kernel.mjs.map +1 -0
  381. package/package.json +1 -0
  382. package/src/Drizzle.ts +378 -0
  383. package/src/DrizzleD1.ts +136 -0
  384. package/src/DrizzleLibsql.ts +301 -0
  385. package/src/DrizzleMysql2.ts +184 -0
  386. package/src/DrizzlePglite.ts +174 -0
  387. package/src/DrizzlePostgres.ts +174 -0
  388. package/src/DrizzleSqliteBun.ts +258 -0
  389. package/src/DrizzleSqliteDo.ts +473 -0
  390. package/src/DrizzleSqliteNode.ts +259 -0
  391. package/src/DrizzleSqliteWasm.ts +263 -0
  392. package/src/drizzle/D1BatchStatements.ts +10 -0
  393. package/src/drizzle/PasswordPreparedPostconditions.ts +30 -0
  394. package/src/drizzle/SuppliedService.ts +10 -0
  395. package/src/drizzle/d1-emails.ts +1829 -0
  396. package/src/drizzle/d1-generated-statement.ts +129 -0
  397. package/src/drizzle/d1-identity.ts +375 -0
  398. package/src/drizzle/d1-oauth.ts +340 -0
  399. package/src/drizzle/d1-passkey.ts +373 -0
  400. package/src/drizzle/d1-password-prepared.ts +1270 -0
  401. package/src/drizzle/d1-passwords.ts +2656 -0
  402. package/src/drizzle/d1-phone.ts +96 -0
  403. package/src/drizzle/d1-planning.ts +104 -0
  404. package/src/drizzle/d1-proofs.ts +2305 -0
  405. package/src/drizzle/d1-sessions.ts +2857 -0
  406. package/src/drizzle/d1-totp.ts +78 -0
  407. package/src/drizzle/email-model.ts +516 -0
  408. package/src/drizzle/email-registration.ts +698 -0
  409. package/src/drizzle/email-sql.ts +24 -0
  410. package/src/drizzle/email-target.ts +243 -0
  411. package/src/drizzle/model.ts +463 -0
  412. package/src/drizzle/mysql-emails.ts +321 -0
  413. package/src/drizzle/mysql-identity.ts +324 -0
  414. package/src/drizzle/mysql-password-prepared.ts +204 -0
  415. package/src/drizzle/mysql-passwords.ts +339 -0
  416. package/src/drizzle/mysql-proofs.ts +181 -0
  417. package/src/drizzle/mysql-sessions.ts +741 -0
  418. package/src/drizzle/mysql.ts +687 -0
  419. package/src/drizzle/oauth-accounts.ts +804 -0
  420. package/src/drizzle/oauth-connected-access.ts +354 -0
  421. package/src/drizzle/oauth-connected-collection.ts +324 -0
  422. package/src/drizzle/oauth-connected-custody.ts +277 -0
  423. package/src/drizzle/oauth-connected-drivers.ts +465 -0
  424. package/src/drizzle/oauth-connected-flow.ts +441 -0
  425. package/src/drizzle/oauth-connected-input.ts +127 -0
  426. package/src/drizzle/oauth-connected-maintenance.ts +345 -0
  427. package/src/drizzle/oauth-connected-management.ts +365 -0
  428. package/src/drizzle/oauth-connected-model.ts +347 -0
  429. package/src/drizzle/oauth-connected-reference.ts +112 -0
  430. package/src/drizzle/oauth-connected-settlement.ts +226 -0
  431. package/src/drizzle/oauth-connected-state.ts +845 -0
  432. package/src/drizzle/oauth-connected-target.ts +264 -0
  433. package/src/drizzle/oauth-drivers.ts +569 -0
  434. package/src/drizzle/oauth-flow.ts +462 -0
  435. package/src/drizzle/oauth-input.ts +201 -0
  436. package/src/drizzle/oauth-model.ts +617 -0
  437. package/src/drizzle/oauth-mysql.ts +10 -0
  438. package/src/drizzle/oauth-owner.ts +29 -0
  439. package/src/drizzle/oauth-registration.ts +640 -0
  440. package/src/drizzle/oauth-state.ts +215 -0
  441. package/src/drizzle/oauth-target.ts +750 -0
  442. package/src/drizzle/passkey-drivers.ts +385 -0
  443. package/src/drizzle/passkey-kernel.ts +3 -0
  444. package/src/drizzle/passkey-model.ts +353 -0
  445. package/src/drizzle/passkey-registration-ceremony-model.ts +69 -0
  446. package/src/drizzle/passkey-state.ts +3 -0
  447. package/src/drizzle/passkey-target.ts +23 -0
  448. package/src/drizzle/passkey-write-drivers.ts +395 -0
  449. package/src/drizzle/passkey-write-model.ts +271 -0
  450. package/src/drizzle/passkey-write-target.ts +9 -0
  451. package/src/drizzle/password-model.ts +491 -0
  452. package/src/drizzle/password-prepared-model.ts +259 -0
  453. package/src/drizzle/password-prepared-sql.ts +1191 -0
  454. package/src/drizzle/password-prepared-state.ts +231 -0
  455. package/src/drizzle/password-prepared-target.ts +160 -0
  456. package/src/drizzle/password-registration.ts +374 -0
  457. package/src/drizzle/password-sql.ts +12 -0
  458. package/src/drizzle/password-target.ts +224 -0
  459. package/src/drizzle/pg-emails.ts +321 -0
  460. package/src/drizzle/pg-password-prepared.ts +205 -0
  461. package/src/drizzle/pg-passwords.ts +338 -0
  462. package/src/drizzle/pg-proofs.ts +191 -0
  463. package/src/drizzle/pg-sessions.ts +750 -0
  464. package/src/drizzle/pg.ts +984 -0
  465. package/src/drizzle/phone-model.ts +190 -0
  466. package/src/drizzle/phone-state.ts +21 -0
  467. package/src/drizzle/phone-target.ts +472 -0
  468. package/src/drizzle/proof-model.ts +398 -0
  469. package/src/drizzle/proof-sql.ts +9 -0
  470. package/src/drizzle/proof-target.ts +122 -0
  471. package/src/drizzle/query-operations.ts +37 -0
  472. package/src/drizzle/session-model.ts +370 -0
  473. package/src/drizzle/session-sql.ts +13 -0
  474. package/src/drizzle/session-target.ts +338 -0
  475. package/src/drizzle/sqlite-emails.ts +366 -0
  476. package/src/drizzle/sqlite-identity.ts +344 -0
  477. package/src/drizzle/sqlite-password-prepared.ts +260 -0
  478. package/src/drizzle/sqlite-passwords.ts +497 -0
  479. package/src/drizzle/sqlite-proofs.ts +202 -0
  480. package/src/drizzle/sqlite-sessions.ts +971 -0
  481. package/src/drizzle/sqlite.ts +705 -0
  482. package/src/drizzle/standalone-guard.ts +38 -0
  483. package/src/drizzle/step-up-model.ts +128 -0
  484. package/src/drizzle/step-up-state.ts +139 -0
  485. package/src/drizzle/totp-model.ts +92 -0
  486. package/src/drizzle/totp-state.ts +525 -0
  487. package/src/drizzle/totp-target.ts +308 -0
  488. package/src/drizzle/transaction-execution.ts +12 -0
  489. package/src/drizzle/transaction-mysql.ts +104 -0
  490. package/src/drizzle/transaction-owner.ts +10 -0
  491. package/src/index.ts +4 -0
  492. package/src/internal/configuration.ts +282 -0
  493. package/src/internal/drizzle-migrations.ts +30 -0
  494. package/src/internal/drizzle-postgres.ts +81 -0
  495. package/src/internal/drizzle-sqlite.ts +79 -0
  496. package/src/internal/email-kernel.ts +1532 -0
  497. package/src/internal/mapping-error.ts +36 -0
  498. package/src/internal/passkey/admission.ts +362 -0
  499. package/src/internal/passkey/credentials.ts +511 -0
  500. package/src/internal/passkey/enrollment.ts +326 -0
  501. package/src/internal/passkey/flow.ts +1062 -0
  502. package/src/internal/passkey/management.ts +422 -0
  503. package/src/internal/passkey/registration-ceremony.ts +283 -0
  504. package/src/internal/passkey/registration-custody.ts +91 -0
  505. package/src/internal/passkey/registration-write.ts +426 -0
  506. package/src/internal/passkey/state.ts +354 -0
  507. package/src/internal/passkey/target.ts +586 -0
  508. package/src/internal/passkey/write-state.ts +681 -0
  509. package/src/internal/passkey/write-target.ts +452 -0
  510. package/src/internal/passkey-kernel.ts +102 -0
  511. package/src/internal/passkeys.ts +597 -0
  512. package/src/internal/password-kernel.ts +2122 -0
  513. package/src/internal/persistence.ts +590 -0
  514. package/src/internal/phone-kernel.ts +843 -0
  515. package/src/internal/proof-kernel.ts +1949 -0
  516. package/src/internal/query-operations.ts +26 -0
  517. package/src/internal/registration.ts +160 -0
  518. package/src/internal/session-kernel.ts +2210 -0
  519. package/src/internal/sql-persistence.ts +45 -0
  520. package/src/internal/sql-query.ts +298 -0
  521. package/src/internal/sql-table.ts +89 -0
  522. package/src/internal/storage-mapping.ts +706 -0
  523. package/src/internal/storage-tables.ts +408 -0
  524. package/src/internal/storage-validation.ts +117 -0
  525. package/src/internal/transaction-execution-kernel.ts +317 -0
  526. package/src/internal/transaction-kernel.ts +582 -0
@@ -0,0 +1,1062 @@
1
+ /* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */
2
+
3
+ import {
4
+ PasskeyClaim,
5
+ type PasskeyAccess,
6
+ type PasskeyAssertionVerified,
7
+ type PasskeyCeremony,
8
+ type PasskeyCredential,
9
+ type PasskeyEvidence,
10
+ type PasskeyMethodPolicy,
11
+ snapshotPasskeySync,
12
+ } from "@yielded/auth/Passkey";
13
+ import type { SubjectId } from "@yielded/auth/Schema";
14
+ import { DateTime, Effect } from "effect";
15
+
16
+ import type { PasskeyFlowState } from "../../drizzle/passkey-model";
17
+ import type { QueryOperations } from "../query-operations";
18
+ import type { makeTransactionKernel } from "../transaction-kernel";
19
+ import type { makePasskeyAdmissionKernel } from "./admission";
20
+ import type { CredentialRead, SubjectRead, makePasskeyCredentialsKernel } from "./credentials";
21
+ import type { makePasskeyRegistrationCustodyKernel } from "./registration-custody";
22
+ import { CurrentPasskeyTransaction } from "./state";
23
+ import type { makePasskeyStateKernel } from "./state";
24
+
25
+ export interface FlowRead {
26
+ readonly row: Record<string, any>;
27
+ readonly ceremony: PasskeyCeremony;
28
+ readonly policy: PasskeyMethodPolicy;
29
+ readonly state: PasskeyFlowState;
30
+ readonly claim?: PasskeyClaim;
31
+ readonly credential?: PasskeyCredential;
32
+ }
33
+
34
+ export const makePasskeyFlowKernel = (
35
+ operations: QueryOperations,
36
+ admission: Pick<
37
+ ReturnType<typeof makePasskeyAdmissionKernel>,
38
+ | "admissionCondition"
39
+ | "chargeScopes"
40
+ | "guardAdmission"
41
+ | "guardChargeSet"
42
+ | "insertCharges"
43
+ | "lockAdmission"
44
+ | "readCharges"
45
+ >,
46
+ credentials: Pick<
47
+ ReturnType<typeof makePasskeyCredentialsKernel>,
48
+ "readCredential" | "readModule" | "readPolicyGuards" | "readRevision" | "readSubject"
49
+ >,
50
+ registrationCustody: Pick<
51
+ ReturnType<typeof makePasskeyRegistrationCustodyKernel>,
52
+ "releaseRegistrationCustody" | "scrubRegistrationIntent"
53
+ >,
54
+ state: Pick<
55
+ ReturnType<typeof makePasskeyStateKernel>,
56
+ | "ceremonyStorage"
57
+ | "col"
58
+ | "credentialStorage"
59
+ | "equal"
60
+ | "existsExact"
61
+ | "invariant"
62
+ | "mappedColumns"
63
+ | "policyStorage"
64
+ | "profileStorage"
65
+ | "sameCredential"
66
+ | "sameRevision"
67
+ | "semanticCredentialColumns"
68
+ | "subjectScope"
69
+ | "targetScope"
70
+ >,
71
+ transactions: Pick<ReturnType<typeof makeTransactionKernel>, "both">,
72
+ ) => {
73
+ const { sql } = operations;
74
+
75
+ const {
76
+ admissionCondition,
77
+ chargeScopes,
78
+ guardAdmission,
79
+ guardChargeSet,
80
+ insertCharges,
81
+ lockAdmission,
82
+ readCharges,
83
+ } = admission;
84
+
85
+ const { readCredential, readModule, readPolicyGuards, readRevision, readSubject } = credentials;
86
+ const { releaseRegistrationCustody, scrubRegistrationIntent } = registrationCustody;
87
+
88
+ const {
89
+ ceremonyStorage,
90
+ col,
91
+ credentialStorage,
92
+ equal,
93
+ existsExact,
94
+ mappedColumns,
95
+ policyStorage,
96
+ profileStorage,
97
+ sameCredential,
98
+ sameRevision,
99
+ semanticCredentialColumns,
100
+ subjectScope,
101
+ targetScope,
102
+ } = state;
103
+
104
+ const invariant: (value: unknown) => asserts value = state.invariant;
105
+ const { both } = transactions;
106
+
107
+ const assertionPurposes = ["sign-in", "pending", "step-up", "action"] as const;
108
+
109
+ const contextPurpose = {
110
+ SignIn: "sign-in",
111
+ Pending: "pending",
112
+ StepUp: "step-up",
113
+ Action: "action",
114
+ Enrollment: "enrollment",
115
+ Registration: "registration",
116
+ } as const;
117
+
118
+ const knownSubject = (ceremony: PasskeyCeremony): SubjectId | undefined =>
119
+ "target" in ceremony.context
120
+ ? ceremony.context.target.revision.subjectId
121
+ : ceremony.context._tag === "Enrollment"
122
+ ? ceremony.context.revision.subjectId
123
+ : undefined;
124
+
125
+ const compatiblePolicy = (
126
+ ceremony: PasskeyCeremony,
127
+ original: PasskeyMethodPolicy,
128
+ current: PasskeyMethodPolicy,
129
+ ) => {
130
+ const selected = current.profiles.find(
131
+ (profile) => profile.profileId === ceremony.profile.profileId,
132
+ );
133
+
134
+ const issued = original.profiles.find(
135
+ (profile) => profile.profileId === ceremony.profile.profileId,
136
+ );
137
+
138
+ return (
139
+ ceremony.generation === current.generation &&
140
+ ceremony.generation === original.generation &&
141
+ selected !== undefined &&
142
+ issued !== undefined &&
143
+ profileStorage.encode(selected) === profileStorage.encode(ceremony.profile) &&
144
+ profileStorage.encode(issued) === profileStorage.encode(ceremony.profile) &&
145
+ ceremony.claimLifetimeMillis === original.claimLifetimeMillis &&
146
+ ceremony.expiresAtMillis > ceremony.issuedAtMillis &&
147
+ ceremony.expiresAtMillis <= ceremony.requestBindingExpiresAtMillis &&
148
+ ceremony.expiresAtMillis - ceremony.issuedAtMillis <= original.lifetimeMillis &&
149
+ ceremony.expiresAtMillis - ceremony.issuedAtMillis <= current.lifetimeMillis &&
150
+ ceremony.retentionUntilMillis === ceremony.issuedAtMillis + original.retentionMillis &&
151
+ contextPurpose[ceremony.context._tag] === ceremony.purpose &&
152
+ new Set(ceremony.allowedCredentials.map((item) => item.id)).size ===
153
+ ceremony.allowedCredentials.length
154
+ );
155
+ };
156
+
157
+ const liveCondition = (mapping: any, ceremony: PasskeyCeremony, claim?: PasskeyClaim) => {
158
+ const now = mapping.clock.engineNowMillis;
159
+
160
+ return both(
161
+ sql`${now} >= ${ceremony.issuedAtMillis}`,
162
+ sql`${now} < ${ceremony.expiresAtMillis}`,
163
+ sql`${now} < ${ceremony.requestBindingExpiresAtMillis}`,
164
+ "target" in ceremony.context
165
+ ? sql`${now} < ${ceremony.context.target.expiresAtMillis}`
166
+ : undefined,
167
+ claim === undefined
168
+ ? undefined
169
+ : both(
170
+ sql`${now} >= ${claim.claimedAtMillis}`,
171
+ sql`${now} < ${claim.claimExpiresAtMillis}`,
172
+ ),
173
+ );
174
+ };
175
+
176
+ const matchesAccess = (ceremony: PasskeyCeremony, access: PasskeyAccess) =>
177
+ ceremony.moduleId === access.moduleId &&
178
+ ceremony.generation === access.generation &&
179
+ ceremony.purpose === access.purpose &&
180
+ ceremony.flowId === access.flowId &&
181
+ ceremony.requestBindingVerifier === access.requestBindingVerifier &&
182
+ ceremony.requestBindingExpiresAtMillis === access.requestBindingExpiresAtMillis;
183
+
184
+ const readFlow = Effect.fn("passkey.readFlow")(function* (mapping: any, flowId: string) {
185
+ const owner = yield* CurrentPasskeyTransaction;
186
+ const table = mapping.flow;
187
+
188
+ const row = (yield* owner.read(
189
+ table.table,
190
+ equal(table.table, {
191
+ [table.moduleId]: mapping.moduleId,
192
+ [table.flowId]: flowId,
193
+ }),
194
+ { limit: 1, columns: mappedColumns(table) },
195
+ )).rows[0];
196
+
197
+ if (row === undefined) return undefined;
198
+ const ceremony = ceremonyStorage.decode(row[table.snapshot]);
199
+ const policy = policyStorage.decode(row[table.policySnapshot]);
200
+
201
+ const state = Object.keys(table.states).find(
202
+ (name) => table.states[name] === row[table.state],
203
+ ) as PasskeyFlowState | undefined;
204
+
205
+ invariant(
206
+ state !== undefined && ceremony.moduleId === mapping.moduleId && ceremony.flowId === flowId,
207
+ );
208
+ invariant(
209
+ row[table.moduleId] === ceremony.moduleId &&
210
+ row[table.flowId] === ceremony.flowId &&
211
+ row[table.commandId] === ceremony.commandId,
212
+ );
213
+ invariant(
214
+ row[table.purpose] === ceremony.purpose && row[table.generation] === ceremony.generation,
215
+ );
216
+ invariant(
217
+ row[table.requestBindingVerifier] === ceremony.requestBindingVerifier &&
218
+ mapping.clock.decodeInstant(row[table.requestBindingExpiresAt]) ===
219
+ ceremony.requestBindingExpiresAtMillis,
220
+ );
221
+ invariant(
222
+ mapping.clock.decodeInstant(row[table.issuedAt]) === ceremony.issuedAtMillis &&
223
+ mapping.clock.decodeInstant(row[table.expiresAt]) === ceremony.expiresAtMillis &&
224
+ mapping.clock.decodeInstant(row[table.retentionUntil]) === ceremony.retentionUntilMillis,
225
+ );
226
+ invariant(compatiblePolicy(ceremony, policy, policy));
227
+
228
+ const credential =
229
+ row[table.credentialSnapshot] === null
230
+ ? undefined
231
+ : credentialStorage.decode(row[table.credentialSnapshot]);
232
+
233
+ const subject = credential?.revision.subjectId ?? knownSubject(ceremony);
234
+
235
+ invariant(
236
+ row[table.subjectScope] === (subject === undefined ? null : subjectScope(subject)) &&
237
+ row[table.targetScope] === targetScope(ceremony),
238
+ );
239
+ if (state === "Pending") {
240
+ invariant(
241
+ row[table.claimId] === null &&
242
+ row[table.claimedAt] === null &&
243
+ row[table.claimExpiresAt] === null &&
244
+ credential === undefined,
245
+ );
246
+
247
+ return { row, ceremony, policy, state };
248
+ }
249
+
250
+ const claim =
251
+ row[table.claimId] === null
252
+ ? undefined
253
+ : snapshotPasskeySync(PasskeyClaim, {
254
+ ceremony,
255
+ claimId: row[table.claimId],
256
+ claimedAtMillis: mapping.clock.decodeInstant(row[table.claimedAt]),
257
+ claimExpiresAtMillis: mapping.clock.decodeInstant(row[table.claimExpiresAt]),
258
+ });
259
+
260
+ invariant(state !== "Claimed" || claim !== undefined);
261
+ if (claim !== undefined)
262
+ invariant(
263
+ claim.claimExpiresAtMillis ===
264
+ Math.min(claim.claimedAtMillis + ceremony.claimLifetimeMillis, ceremony.expiresAtMillis),
265
+ );
266
+
267
+ return {
268
+ row,
269
+ ceremony,
270
+ policy,
271
+ state,
272
+ ...(claim === undefined ? {} : { claim }),
273
+ ...(credential === undefined ? {} : { credential }),
274
+ };
275
+ });
276
+
277
+ const terminalFlow = Effect.fn("passkey.terminalFlow")(function* (
278
+ mapping: any,
279
+ read: FlowRead,
280
+ state: "Verified" | "Rejected" | "Ambiguous",
281
+ ) {
282
+ const owner = yield* CurrentPasskeyTransaction;
283
+ const table = mapping.flow;
284
+
285
+ yield* owner.update(
286
+ table.table,
287
+ { [table.moduleId]: mapping.moduleId, [table.flowId]: read.ceremony.flowId },
288
+ {
289
+ [table.state]: table.states[state],
290
+ [table.version]: owner.marker,
291
+ },
292
+ );
293
+ owner.postconditions.push(
294
+ yield* existsExact(table.table, {
295
+ [table.moduleId]: mapping.moduleId,
296
+ [table.flowId]: read.ceremony.flowId,
297
+ [table.state]: table.states[state],
298
+ [table.version]: owner.marker,
299
+ }),
300
+ );
301
+
302
+ return state;
303
+ });
304
+
305
+ const targetRevision = (ceremony: PasskeyCeremony) =>
306
+ "target" in ceremony.context
307
+ ? ceremony.context.target.revision
308
+ : ceremony.context._tag === "Enrollment"
309
+ ? ceremony.context.revision
310
+ : undefined;
311
+
312
+ const currentTarget = Effect.fn("passkey.currentTarget")(function* (
313
+ mapping: any,
314
+ subject: SubjectRead | undefined,
315
+ ceremony: PasskeyCeremony,
316
+ ) {
317
+ const target = targetRevision(ceremony);
318
+
319
+ if (target === undefined) return true;
320
+ if (
321
+ subject === undefined ||
322
+ subject.subjectId !== target.subjectId ||
323
+ subject.securityRevision !== target.securityRevision
324
+ )
325
+ return false;
326
+
327
+ const current = yield* readRevision(
328
+ mapping.read,
329
+ subject,
330
+ target.credentials.map((item) => item.credentialId),
331
+ );
332
+
333
+ return current !== undefined && sameRevision(current, target);
334
+ });
335
+
336
+ const issueAssertion = Effect.fn("passkey.issueAssertion")(function* (
337
+ mapping: any,
338
+ ceremony: PasskeyCeremony,
339
+ suppliedPolicy: PasskeyMethodPolicy,
340
+ ) {
341
+ const owner = yield* CurrentPasskeyTransaction;
342
+ const current = yield* readModule(mapping);
343
+
344
+ if (
345
+ current === undefined ||
346
+ ceremony.moduleId !== mapping.moduleId ||
347
+ !compatiblePolicy(ceremony, suppliedPolicy, current) ||
348
+ policyStorage.encode(suppliedPolicy) !== policyStorage.encode(current)
349
+ )
350
+ return { _tag: "Rejected" } as const;
351
+ yield* lockAdmission(mapping);
352
+ const subjectId = knownSubject(ceremony);
353
+
354
+ const subject =
355
+ subjectId === undefined ? undefined : yield* readSubject(mapping.read, subjectId);
356
+
357
+ yield* readPolicyGuards(mapping);
358
+ if (!(yield* currentTarget(mapping, subject, ceremony))) return { _tag: "Rejected" } as const;
359
+ if (
360
+ ceremony.context._tag === "SignIn" &&
361
+ (!ceremony.profile.primarySignIn ||
362
+ ceremony.profile.userVerification !== "required" ||
363
+ ceremony.profile.residentKey !== "required")
364
+ )
365
+ return { _tag: "Rejected" } as const;
366
+ const table = mapping.flow;
367
+
368
+ const sameCommand = yield* owner.read(
369
+ table.table,
370
+ equal(table.table, {
371
+ [table.moduleId]: mapping.moduleId,
372
+ [table.commandId]: ceremony.commandId,
373
+ }),
374
+ { limit: 1 },
375
+ );
376
+
377
+ const sameFlow = yield* readFlow(mapping, ceremony.flowId);
378
+
379
+ if (sameCommand.rows.length !== 0 || sameFlow !== undefined)
380
+ return { _tag: "Rejected" } as const;
381
+ const kinds = chargeScopes(ceremony, subjectId).map((item) => item.kind);
382
+
383
+ if (
384
+ !(yield* owner.check(liveCondition(mapping, ceremony))) ||
385
+ !(yield* owner.check(
386
+ admissionCondition(mapping, [current], ceremony, subjectId, kinds, true, false),
387
+ ))
388
+ )
389
+ return { _tag: "Rejected" } as const;
390
+
391
+ const values = {
392
+ ...table.encodeInsert({ ceremony, policy: suppliedPolicy, marker: owner.marker }),
393
+ [table.moduleId]: ceremony.moduleId,
394
+ [table.flowId]: ceremony.flowId,
395
+ [table.commandId]: ceremony.commandId,
396
+ [table.purpose]: ceremony.purpose,
397
+ [table.state]: table.states.Pending,
398
+ [table.version]: owner.marker,
399
+ [table.generation]: ceremony.generation,
400
+ [table.snapshot]: ceremonyStorage.encode(ceremony),
401
+ [table.policySnapshot]: policyStorage.encode(suppliedPolicy),
402
+ [table.requestBindingVerifier]: ceremony.requestBindingVerifier,
403
+ [table.requestBindingExpiresAt]: mapping.clock.encodeInstant(
404
+ ceremony.requestBindingExpiresAtMillis,
405
+ ),
406
+ [table.issuedAt]: mapping.clock.encodeInstant(ceremony.issuedAtMillis),
407
+ [table.expiresAt]: mapping.clock.encodeInstant(ceremony.expiresAtMillis),
408
+ [table.retentionUntil]: mapping.clock.encodeInstant(ceremony.retentionUntilMillis),
409
+ [table.claimId]: null,
410
+ [table.claimedAt]: null,
411
+ [table.claimExpiresAt]: null,
412
+ [table.credentialSnapshot]: null,
413
+ [table.subjectScope]: subjectId === undefined ? null : subjectScope(subjectId),
414
+ [table.targetScope]: targetScope(ceremony),
415
+ };
416
+
417
+ // Empty-key observations now expect this exact inserted row as well.
418
+ const inserted = yield* owner.insert(table.table, values, {
419
+ [table.moduleId]: mapping.moduleId,
420
+ [table.flowId]: ceremony.flowId,
421
+ });
422
+
423
+ sameCommand.rows = inserted.rows;
424
+ for (const observation of owner.observations)
425
+ if (observation.table === table.table && observation.rows.length === 0)
426
+ observation.rows = inserted.rows;
427
+ yield* insertCharges(mapping, ceremony, suppliedPolicy, kinds, subjectId);
428
+ yield* guardChargeSet(mapping, ceremony, subjectId);
429
+ yield* guardAdmission(mapping, [current], ceremony, subjectId);
430
+ owner.postconditions.push(liveCondition(mapping, ceremony));
431
+
432
+ return { _tag: "Issued", ceremony } as const;
433
+ });
434
+
435
+ const contextAssertion = Effect.fn("passkey.contextAssertion")(function* (
436
+ mapping: any,
437
+ access: PasskeyAccess,
438
+ ) {
439
+ const owner = yield* CurrentPasskeyTransaction;
440
+
441
+ if (access.moduleId !== mapping.moduleId) return undefined;
442
+ const current = yield* readModule(mapping);
443
+
444
+ if (current === undefined) return undefined;
445
+ yield* readPolicyGuards(mapping);
446
+ const read = yield* readFlow(mapping, access.flowId);
447
+
448
+ if (
449
+ read === undefined ||
450
+ read.state !== "Pending" ||
451
+ !matchesAccess(read.ceremony, access) ||
452
+ !compatiblePolicy(read.ceremony, read.policy, current)
453
+ )
454
+ return undefined;
455
+ if (!(yield* owner.check(liveCondition(mapping, read.ceremony)))) return undefined;
456
+ owner.postconditions.push(liveCondition(mapping, read.ceremony));
457
+
458
+ return read.ceremony;
459
+ });
460
+
461
+ const claimAssertion = Effect.fn("passkey.claimAssertion")(function* (
462
+ mapping: any,
463
+ input: {
464
+ readonly access: PasskeyAccess;
465
+ readonly policy: PasskeyMethodPolicy;
466
+ readonly ceremony: PasskeyCeremony;
467
+ readonly claimId: string;
468
+ readonly credential?: PasskeyCredential;
469
+ },
470
+ ) {
471
+ const owner = yield* CurrentPasskeyTransaction;
472
+ const current = yield* readModule(mapping);
473
+
474
+ if (current === undefined || input.access.moduleId !== mapping.moduleId)
475
+ return { _tag: "Rejected" } as const;
476
+ yield* lockAdmission(mapping);
477
+ const expectedSubject = knownSubject(input.ceremony);
478
+ const subjectId = expectedSubject ?? input.credential?.revision.subjectId;
479
+
480
+ const subject =
481
+ subjectId === undefined ? undefined : yield* readSubject(mapping.read, subjectId);
482
+
483
+ yield* readPolicyGuards(mapping);
484
+
485
+ const requested =
486
+ targetRevision(input.ceremony)?.credentials.map((item) => item.credentialId) ??
487
+ input.credential?.revision.credentials.map((item) => item.credentialId);
488
+
489
+ const credential =
490
+ input.credential === undefined || subject === undefined
491
+ ? undefined
492
+ : yield* readCredential(
493
+ mapping.read,
494
+ subject,
495
+ input.ceremony.profile.rpId,
496
+ input.credential.protocolCredentialId,
497
+ requested,
498
+ );
499
+
500
+ const read = yield* readFlow(mapping, input.access.flowId);
501
+
502
+ if (
503
+ read === undefined ||
504
+ read.state !== "Pending" ||
505
+ !matchesAccess(read.ceremony, input.access)
506
+ )
507
+ return { _tag: "Rejected" } as const;
508
+ invariant(ceremonyStorage.encode(read.ceremony) === ceremonyStorage.encode(input.ceremony));
509
+
510
+ const reject = Effect.gen(function* () {
511
+ yield* terminalFlow(mapping, read, "Rejected");
512
+
513
+ return { _tag: "Rejected" } as const;
514
+ });
515
+
516
+ if (
517
+ !compatiblePolicy(read.ceremony, read.policy, current) ||
518
+ policyStorage.encode(input.policy) !== policyStorage.encode(current) ||
519
+ !(yield* currentTarget(mapping, subject, read.ceremony))
520
+ )
521
+ return yield* reject;
522
+ if (
523
+ input.credential !== undefined &&
524
+ (credential === undefined || !sameCredential(input.credential, credential.credential))
525
+ )
526
+ return yield* reject;
527
+ if (input.credential !== undefined && credential !== undefined) {
528
+ const original = input.credential.revision;
529
+ const actual = credential.credential.revision;
530
+ const ids = [...new Set([...(requested ?? []), input.credential.credentialId])];
531
+
532
+ if (
533
+ original.subjectId !== actual.subjectId ||
534
+ original.securityRevision !== actual.securityRevision ||
535
+ ids.some((id) => {
536
+ const before = original.credentials.find((item) => item.credentialId === id);
537
+ const after = actual.credentials.find((item) => item.credentialId === id);
538
+
539
+ // A target can include non-passkey factors absent from the minimum
540
+ // lookup snapshot; currentTarget independently checks those originals.
541
+ return (
542
+ after === undefined ||
543
+ (before === undefined
544
+ ? id === input.credential?.credentialId || targetRevision(read.ceremony) === undefined
545
+ : before.revision !== after.revision)
546
+ );
547
+ })
548
+ )
549
+ return yield* reject;
550
+ }
551
+ if (!(yield* owner.check(liveCondition(mapping, read.ceremony)))) return yield* reject;
552
+ yield* readCharges(mapping, read.ceremony, read.policy, expectedSubject);
553
+ const resolved = expectedSubject === undefined && subjectId !== undefined;
554
+
555
+ if (
556
+ !(yield* owner.check(
557
+ admissionCondition(
558
+ mapping,
559
+ [read.policy, current],
560
+ read.ceremony,
561
+ subjectId,
562
+ resolved ? ["subject"] : [],
563
+ false,
564
+ resolved,
565
+ ),
566
+ ))
567
+ )
568
+ return yield* reject;
569
+ const claimedAtMillis = yield* owner.now(mapping.clock);
570
+
571
+ const claim = snapshotPasskeySync(PasskeyClaim, {
572
+ ceremony: read.ceremony,
573
+ claimId: input.claimId,
574
+ claimedAtMillis,
575
+ claimExpiresAtMillis: Math.min(
576
+ claimedAtMillis + read.ceremony.claimLifetimeMillis,
577
+ read.ceremony.expiresAtMillis,
578
+ ),
579
+ });
580
+
581
+ const table = mapping.flow;
582
+
583
+ yield* owner.update(
584
+ table.table,
585
+ { [table.moduleId]: mapping.moduleId, [table.flowId]: read.ceremony.flowId },
586
+ {
587
+ [table.state]: table.states.Claimed,
588
+ [table.version]: owner.marker,
589
+ [table.claimId]: claim.claimId,
590
+ [table.claimedAt]: mapping.clock.encodeInstant(claim.claimedAtMillis),
591
+ [table.claimExpiresAt]: mapping.clock.encodeInstant(claim.claimExpiresAtMillis),
592
+ [table.credentialSnapshot]:
593
+ input.credential === undefined ? null : credentialStorage.encode(input.credential),
594
+ [table.subjectScope]: subjectId === undefined ? null : subjectScope(subjectId),
595
+ },
596
+ );
597
+ if (resolved) yield* insertCharges(mapping, read.ceremony, read.policy, ["subject"], subjectId);
598
+ yield* guardChargeSet(mapping, read.ceremony, subjectId);
599
+ yield* guardAdmission(mapping, [read.policy, current], read.ceremony, subjectId);
600
+ owner.postconditions.push(liveCondition(mapping, read.ceremony, claim));
601
+
602
+ return { _tag: "Claimed", claim } as const;
603
+ });
604
+
605
+ const exactClaim = (read: FlowRead, claim: PasskeyClaim) =>
606
+ read.state === "Claimed" &&
607
+ read.claim !== undefined &&
608
+ read.claim.claimId === claim.claimId &&
609
+ read.claim.claimedAtMillis === claim.claimedAtMillis &&
610
+ read.claim.claimExpiresAtMillis === claim.claimExpiresAtMillis &&
611
+ ceremonyStorage.encode(read.ceremony) === ceremonyStorage.encode(claim.ceremony);
612
+
613
+ const evidenceMatches = (
614
+ ceremony: PasskeyCeremony,
615
+ captured: PasskeyCredential,
616
+ credential: PasskeyCredential,
617
+ assertion: PasskeyAssertionVerified,
618
+ evidence: typeof PasskeyEvidence.Type,
619
+ ) => {
620
+ const proof = evidence.proofs[0];
621
+ const target = targetRevision(ceremony);
622
+ const original = target ?? captured.revision;
623
+
624
+ const ids = [
625
+ ...new Set([
626
+ ...original.credentials.map((item) => item.credentialId),
627
+ credential.credentialId,
628
+ ]),
629
+ ];
630
+
631
+ const expected = {
632
+ ...credential.revision,
633
+ credentials: credential.revision.credentials.filter((item) =>
634
+ ids.includes(item.credentialId),
635
+ ),
636
+ };
637
+
638
+ const selected = captured.revision.credentials.find(
639
+ (item) => item.credentialId === credential.credentialId,
640
+ );
641
+
642
+ return (
643
+ evidence.proofs.length === 1 &&
644
+ proof !== undefined &&
645
+ proof.method === "passkey" &&
646
+ proof.credentialId === credential.credentialId &&
647
+ proof.factors.length === 1 &&
648
+ proof.factors[0] === "possession" &&
649
+ proof.userVerified === assertion.userVerified &&
650
+ proof.phishingResistant &&
651
+ DateTime.toEpochMillis(proof.verifiedAt) === ceremony.issuedAtMillis &&
652
+ evidence.flowId ===
653
+ ("target" in ceremony.context ? ceremony.context.target.flowId : ceremony.flowId) &&
654
+ evidence.bindingDigest ===
655
+ ("target" in ceremony.context
656
+ ? ceremony.context.target.bindingDigest
657
+ : ceremony.requestBindingVerifier) &&
658
+ expected.credentials.length === ids.length &&
659
+ sameRevision(evidence.revision, expected) &&
660
+ original.subjectId === expected.subjectId &&
661
+ original.securityRevision === expected.securityRevision &&
662
+ original.credentials.every((item) =>
663
+ expected.credentials.some(
664
+ (current) =>
665
+ current.credentialId === item.credentialId && current.revision === item.revision,
666
+ ),
667
+ ) &&
668
+ selected !== undefined &&
669
+ expected.credentials.some(
670
+ (item) =>
671
+ item.credentialId === selected.credentialId && item.revision === selected.revision,
672
+ )
673
+ );
674
+ };
675
+
676
+ const updateTelemetry = Effect.fn("passkey.updateTelemetry")(function* (
677
+ mapping: any,
678
+ found: CredentialRead,
679
+ assertion: PasskeyAssertionVerified,
680
+ dialect: "pg" | "mysql" | "sqlite",
681
+ ) {
682
+ const owner = yield* CurrentPasskeyTransaction;
683
+ const table = mapping.read.credential;
684
+ const counter = sql`${col(table.table, table.counter)}`;
685
+ const maximum = sql`${col(table.table, table.maximumCounter)}`;
686
+
687
+ const max = (...values: ReadonlyArray<ReturnType<typeof sql>>) =>
688
+ dialect === "sqlite"
689
+ ? sql`max(${sql.join([...values], sql`, `)})`
690
+ : sql`greatest(${sql.join([...values], sql`, `)})`;
691
+
692
+ const semantic = Object.fromEntries(
693
+ semanticCredentialColumns(table).map((column) => [column, found.row[column]]),
694
+ );
695
+
696
+ const bs = mapping.telemetry.encodeBackupState(assertion.backupState);
697
+ const synced = found.credential.backupEligible;
698
+ const merged = max(counter, maximum, sql`${assertion.counter}`);
699
+
700
+ const currentCounter = synced
701
+ ? sql`1 = 1`
702
+ : sql`((${counter} = 0 and ${assertion.counter} = 0) or ${counter} < ${assertion.counter})`;
703
+
704
+ yield* owner.updateGuarded(
705
+ table.table,
706
+ both(owner.exact(table.table, semantic), table.activeCondition, currentCounter),
707
+ {
708
+ [table.counter]: synced ? merged : assertion.counter,
709
+ [table.maximumCounter]: merged,
710
+ [table.backupState]: bs,
711
+ },
712
+ {
713
+ rows: 1,
714
+ postcondition: yield* existsExact(
715
+ table.table,
716
+ { ...semantic, [table.backupState]: bs },
717
+ both(
718
+ synced ? sql`${counter} = ${maximum}` : sql`${counter} = ${assertion.counter}`,
719
+ sql`${maximum} >= ${assertion.counter}`,
720
+ ),
721
+ ),
722
+ },
723
+ );
724
+ const earliest = yield* owner.now(mapping.clock);
725
+ const lastUsed = sql`${col(table.table, mapping.telemetry.lastUsedAt)}`;
726
+ const millis = mapping.clock.toMillis(lastUsed);
727
+
728
+ yield* owner.finalUpdate(
729
+ table.table,
730
+ owner.exact(table.table, semantic),
731
+ {
732
+ [mapping.telemetry.lastUsedAt]: mapping.clock.fromMillis(
733
+ max(sql`coalesce(${millis}, 0)`, mapping.clock.engineNowMillis),
734
+ ),
735
+ },
736
+ {
737
+ rows: 1,
738
+ postcondition: yield* existsExact(
739
+ table.table,
740
+ semantic,
741
+ both(sql`${millis} >= ${earliest}`, sql`${millis} <= ${mapping.clock.engineNowMillis}`),
742
+ ),
743
+ },
744
+ );
745
+ });
746
+
747
+ const settleAssertion = Effect.fn("passkey.settleAssertion")(function* (
748
+ mapping: any,
749
+ claim: PasskeyClaim,
750
+ outcome:
751
+ | { readonly _tag: "Rejected" | "Ambiguous" }
752
+ | {
753
+ readonly _tag: "Assertion";
754
+ readonly credential: PasskeyCredential;
755
+ readonly assertion: PasskeyAssertionVerified;
756
+ readonly evidence: typeof PasskeyEvidence.Type;
757
+ },
758
+ dialect: "pg" | "mysql" | "sqlite",
759
+ ) {
760
+ const owner = yield* CurrentPasskeyTransaction;
761
+ const assertedCredential = outcome._tag === "Assertion" ? outcome.credential : undefined;
762
+ const current = yield* readModule(mapping);
763
+
764
+ if (current === undefined || claim.ceremony.moduleId !== mapping.moduleId)
765
+ return "Rejected" as const;
766
+ yield* lockAdmission(mapping);
767
+
768
+ const originalIds =
769
+ targetRevision(claim.ceremony)?.credentials.map((item) => item.credentialId) ??
770
+ (outcome._tag === "Assertion"
771
+ ? outcome.credential.revision.credentials.map((item) => item.credentialId)
772
+ : undefined);
773
+
774
+ const subjectId =
775
+ outcome._tag === "Assertion"
776
+ ? outcome.credential.revision.subjectId
777
+ : knownSubject(claim.ceremony);
778
+
779
+ const subject =
780
+ subjectId === undefined ? undefined : yield* readSubject(mapping.read, subjectId);
781
+
782
+ yield* readPolicyGuards(mapping);
783
+
784
+ const found =
785
+ outcome._tag !== "Assertion" || subject === undefined
786
+ ? undefined
787
+ : yield* readCredential(
788
+ mapping.read,
789
+ subject,
790
+ claim.ceremony.profile.rpId,
791
+ outcome.credential.protocolCredentialId,
792
+ originalIds,
793
+ );
794
+
795
+ const read = yield* readFlow(mapping, claim.ceremony.flowId);
796
+
797
+ if (read === undefined || !exactClaim(read, claim)) return "Rejected" as const;
798
+ if (outcome._tag !== "Assertion") return yield* terminalFlow(mapping, read, outcome._tag);
799
+ invariant(assertedCredential !== undefined);
800
+ const captured = "credential" in read ? read.credential : undefined;
801
+ const assertion = outcome.assertion;
802
+
803
+ if (
804
+ captured === undefined ||
805
+ found === undefined ||
806
+ !compatiblePolicy(read.ceremony, read.policy, current) ||
807
+ !sameCredential(captured, assertedCredential) ||
808
+ !sameCredential(captured, found.credential)
809
+ )
810
+ return yield* terminalFlow(mapping, read, "Rejected");
811
+ const credential = found.credential;
812
+ const profile = read.ceremony.profile;
813
+
814
+ if (
815
+ assertion.protocolCredentialId !== credential.protocolCredentialId ||
816
+ (assertion.userHandle !== undefined && assertion.userHandle !== credential.userHandle) ||
817
+ assertion.backupEligible !== credential.backupEligible ||
818
+ (assertion.backupState && !assertion.backupEligible) ||
819
+ (profile.userVerification === "required" && !assertion.userVerified) ||
820
+ !profile.algorithms.includes(credential.algorithm) ||
821
+ (read.ceremony.allowedCredentials.length !== 0 &&
822
+ !read.ceremony.allowedCredentials.some(
823
+ (item) => item.id === credential.protocolCredentialId,
824
+ )) ||
825
+ (read.ceremony.purpose === "sign-in" &&
826
+ (assertion.userHandle !== credential.userHandle ||
827
+ !credential.primarySignIn ||
828
+ !credential.enrollmentUserVerified ||
829
+ !credential.profile.primarySignIn ||
830
+ credential.profile.userVerification !== "required" ||
831
+ !assertion.userVerified)) ||
832
+ !evidenceMatches(read.ceremony, captured, credential, assertion, outcome.evidence) ||
833
+ !(yield* owner.check(liveCondition(mapping, read.ceremony, claim)))
834
+ )
835
+ return yield* terminalFlow(mapping, read, "Rejected");
836
+ yield* readCharges(mapping, read.ceremony, read.policy, credential.revision.subjectId);
837
+ yield* guardChargeSet(mapping, read.ceremony, credential.revision.subjectId);
838
+ yield* updateTelemetry(mapping, found, assertion, dialect);
839
+ yield* terminalFlow(mapping, read, "Verified");
840
+ owner.postconditions.push(liveCondition(mapping, read.ceremony, claim));
841
+
842
+ return "Verified" as const;
843
+ });
844
+
845
+ const purposeCondition = (table: any, purposes: ReadonlyArray<string>) =>
846
+ sql`${col(table.table, table.purpose)} in (${sql.join(
847
+ purposes.map((purpose) => sql`${purpose}`),
848
+ sql`, `,
849
+ )})`;
850
+
851
+ const custodyFree = (mapping: any, flowExpression: ReturnType<typeof sql>) => {
852
+ if (mapping.intent === undefined) return sql`1 = 1`;
853
+ const intent = mapping.intent;
854
+
855
+ return sql`not exists (select 1 from ${intent.table} where ${both(
856
+ equal(intent.table, { [intent.moduleId]: mapping.moduleId }),
857
+ sql`${col(intent.table, intent.flowId)} = ${flowExpression}`,
858
+ intent.custodyCondition,
859
+ )})`;
860
+ };
861
+
862
+ const flowDue = (mapping: any, purposes: ReadonlyArray<string>) => {
863
+ const flow = mapping.flow,
864
+ charge = mapping.charge,
865
+ now = mapping.clock.engineNowMillis;
866
+
867
+ const state = sql`${col(flow.table, flow.state)}`;
868
+ const retained = sql`${mapping.clock.toMillis(sql`${col(flow.table, flow.retentionUntil)}`)} <= ${now}`;
869
+
870
+ const chargeLive = sql`exists (select 1 from ${charge.table} where ${both(
871
+ equal(charge.table, { [charge.moduleId]: mapping.moduleId }),
872
+ sql`${col(charge.table, charge.flowId)} = ${col(flow.table, flow.flowId)}`,
873
+ sql`(${col(charge.table, charge.retainUntil)} is null or ${mapping.clock.toMillis(sql`${col(charge.table, charge.retainUntil)}`)} > ${now})`,
874
+ )})`;
875
+
876
+ return both(
877
+ equal(flow.table, { [flow.moduleId]: mapping.moduleId }),
878
+ purposeCondition(flow, purposes),
879
+ sql`((${state} = ${flow.states.Pending} and ${mapping.clock.toMillis(sql`${col(flow.table, flow.expiresAt)}`)} <= ${now}) or
880
+ (${state} = ${flow.states.Claimed} and ${mapping.clock.toMillis(sql`${col(flow.table, flow.claimExpiresAt)}`)} <= ${now}) or
881
+ (${state} in (${flow.states.Verified}, ${flow.states.Rejected}, ${flow.states.Ambiguous}, ${flow.states.RegistrationAccepted}) and ${retained} and not (${chargeLive}) and ${custodyFree(mapping, sql`${col(flow.table, flow.flowId)}`)}))`,
882
+ );
883
+ };
884
+
885
+ const chargeDue = (mapping: any, purposes: ReadonlyArray<string>) => {
886
+ const charge = mapping.charge;
887
+
888
+ return both(
889
+ equal(charge.table, { [charge.moduleId]: mapping.moduleId }),
890
+ purposeCondition(charge, purposes),
891
+ sql`${col(charge.table, charge.admittedAt)} is not null and ${mapping.clock.toMillis(sql`${col(charge.table, charge.retainUntil)}`)} <= ${mapping.clock.engineNowMillis}`,
892
+ custodyFree(mapping, sql`${col(charge.table, charge.flowId)}`),
893
+ );
894
+ };
895
+
896
+ /** Flow transitions and row removals share one bound. Prepared hasMore is
897
+ * predicted from this owner's staged changes, then asserted after final writes. */
898
+ const cleanupCeremonies = Effect.fn("passkey.cleanupCeremonies")(function* (
899
+ mapping: any,
900
+ input: { readonly moduleId: string; readonly nowMillis: number; readonly limit: number },
901
+ purposes: ReadonlyArray<string>,
902
+ ) {
903
+ const owner = yield* CurrentPasskeyTransaction;
904
+
905
+ invariant(
906
+ input.moduleId === mapping.moduleId &&
907
+ Number.isSafeInteger(input.nowMillis) &&
908
+ input.nowMillis >= 0 &&
909
+ Number.isSafeInteger(input.limit) &&
910
+ input.limit >= 1 &&
911
+ input.limit <= 1000,
912
+ );
913
+ yield* readModule(mapping);
914
+ yield* lockAdmission(mapping);
915
+ yield* readPolicyGuards(mapping);
916
+
917
+ const flow = mapping.flow,
918
+ charge = mapping.charge;
919
+
920
+ const rows = (yield* owner.read(flow.table, flowDue(mapping, purposes), {
921
+ limit: input.limit,
922
+ takeOnly: true,
923
+ observe: false,
924
+ orderBy: sql`${col(flow.table, flow.flowId)}`,
925
+ })).rows;
926
+
927
+ const touched: string[] = [];
928
+ const transitioned: FlowRead[] = [];
929
+
930
+ let terminalized = 0,
931
+ removed = 0;
932
+
933
+ for (const candidate of rows) {
934
+ const read = yield* readFlow(mapping, candidate[flow.flowId]);
935
+
936
+ if (read === undefined) continue;
937
+ invariant(
938
+ yield* owner.check(
939
+ sql`exists (select 1 from ${flow.table} where ${both(equal(flow.table, { [flow.moduleId]: mapping.moduleId, [flow.flowId]: read.ceremony.flowId }), flowDue(mapping, purposes))})`,
940
+ ),
941
+ );
942
+ touched.push(read.ceremony.flowId);
943
+ if (read.state === "Pending" || read.state === "Claimed") {
944
+ const deadline =
945
+ read.state === "Pending"
946
+ ? read.ceremony.expiresAtMillis
947
+ : read.claim!.claimExpiresAtMillis;
948
+
949
+ if (read.state === "Pending") yield* releaseRegistrationCustody(mapping, read.ceremony);
950
+ yield* terminalFlow(mapping, read, read.state === "Pending" ? "Rejected" : "Ambiguous");
951
+ owner.postconditions.push(sql`${mapping.clock.engineNowMillis} >= ${deadline}`);
952
+ transitioned.push(read);
953
+ terminalized++;
954
+ } else {
955
+ yield* scrubRegistrationIntent(mapping, read.ceremony);
956
+ yield* owner.remove(flow.table, {
957
+ [flow.moduleId]: mapping.moduleId,
958
+ [flow.flowId]: read.ceremony.flowId,
959
+ });
960
+ owner.postconditions.push(
961
+ sql`${mapping.clock.engineNowMillis} >= ${read.ceremony.retentionUntilMillis}`,
962
+ custodyFree(mapping, sql`${read.ceremony.flowId}`),
963
+ sql`not exists (select 1 from ${charge.table} where ${both(
964
+ equal(charge.table, {
965
+ [charge.moduleId]: mapping.moduleId,
966
+ [charge.flowId]: read.ceremony.flowId,
967
+ }),
968
+ sql`(${col(charge.table, charge.retainUntil)} is null or ${mapping.clock.toMillis(sql`${col(charge.table, charge.retainUntil)}`)} > ${mapping.clock.engineNowMillis})`,
969
+ )})`,
970
+ );
971
+ removed++;
972
+ }
973
+ }
974
+ const removedCharges: Array<{ flowId: string; kind: string }> = [];
975
+ const remaining = input.limit - terminalized - removed;
976
+
977
+ if (remaining > 0) {
978
+ const charges = (yield* owner.read(charge.table, chargeDue(mapping, purposes), {
979
+ limit: remaining,
980
+ takeOnly: true,
981
+ observe: false,
982
+ orderBy: sql`${col(charge.table, charge.flowId)}, ${col(charge.table, charge.kind)}`,
983
+ })).rows;
984
+
985
+ for (const candidate of charges) {
986
+ const identity = {
987
+ [charge.moduleId]: mapping.moduleId,
988
+ [charge.flowId]: candidate[charge.flowId],
989
+ [charge.kind]: candidate[charge.kind],
990
+ };
991
+
992
+ const selected = (yield* owner.read(charge.table, equal(charge.table, identity), {
993
+ limit: 1,
994
+ })).rows[0];
995
+
996
+ invariant(selected !== undefined);
997
+ const horizon = mapping.clock.decodeInstant(selected[charge.retainUntil]);
998
+
999
+ invariant(Number.isSafeInteger(horizon));
1000
+ yield* owner.remove(charge.table, identity);
1001
+ owner.postconditions.push(
1002
+ sql`${mapping.clock.engineNowMillis} >= ${horizon}`,
1003
+ custodyFree(mapping, sql`${candidate[charge.flowId]}`),
1004
+ );
1005
+ removedCharges.push({ flowId: candidate[charge.flowId], kind: candidate[charge.kind] });
1006
+ removed++;
1007
+ }
1008
+ }
1009
+
1010
+ const excludeFlows =
1011
+ touched.length === 0
1012
+ ? undefined
1013
+ : sql`${col(flow.table, flow.flowId)} not in (${sql.join(
1014
+ touched.map((id) => sql`${id}`),
1015
+ sql`, `,
1016
+ )})`;
1017
+
1018
+ const excludeCharges = removedCharges.map(
1019
+ (item) =>
1020
+ sql`not (${equal(charge.table, { [charge.flowId]: item.flowId, [charge.kind]: item.kind })})`,
1021
+ );
1022
+
1023
+ const predicted = sql`exists (select 1 from ${flow.table} where ${both(flowDue(mapping, purposes), excludeFlows)}) or exists (select 1 from ${charge.table} where ${both(chargeDue(mapping, purposes), ...excludeCharges)})`;
1024
+ // A transitioned row cannot also be removed by this call, but may be due now.
1025
+ // Read through check only for unchanged rows; D1's prebatch assertion must use
1026
+ // this predictive predicate, while the final assertion uses actual states.
1027
+ let hasMore = yield* owner.check(predicted);
1028
+
1029
+ for (const read of transitioned) {
1030
+ if (read.ceremony.retentionUntilMillis > (yield* owner.now(mapping.clock))) continue;
1031
+ const liveCharges = sql`exists (select 1 from ${charge.table} where ${both(equal(charge.table, { [charge.moduleId]: mapping.moduleId, [charge.flowId]: read.ceremony.flowId }), sql`${mapping.clock.toMillis(sql`${col(charge.table, charge.retainUntil)}`)} > ${mapping.clock.engineNowMillis}`, ...excludeCharges)})`;
1032
+
1033
+ if (
1034
+ !(yield* owner.check(liveCharges)) &&
1035
+ (yield* owner.check(custodyFree(mapping, sql`${read.ceremony.flowId}`)))
1036
+ )
1037
+ hasMore = true;
1038
+ }
1039
+ const actual = sql`exists (select 1 from ${flow.table} where ${flowDue(mapping, purposes)}) or exists (select 1 from ${charge.table} where ${chargeDue(mapping, purposes)})`;
1040
+
1041
+ owner.postconditions.push(hasMore ? actual : sql`not (${actual})`);
1042
+
1043
+ return { terminalized, removed, hasMore };
1044
+ });
1045
+
1046
+ return {
1047
+ assertionPurposes,
1048
+ contextPurpose,
1049
+ knownSubject,
1050
+ compatiblePolicy,
1051
+ liveCondition,
1052
+ matchesAccess,
1053
+ readFlow,
1054
+ terminalFlow,
1055
+ issueAssertion,
1056
+ contextAssertion,
1057
+ claimAssertion,
1058
+ exactClaim,
1059
+ settleAssertion,
1060
+ cleanupCeremonies,
1061
+ };
1062
+ };