@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,1829 @@
1
+ /* oxlint-disable no-explicit-any -- D1 batches bridge consumer-owned Drizzle tables. */
2
+ import type { D1Client } from "@effect/sql-d1/D1Client";
3
+ import {
4
+ EmailAddressPersistence,
5
+ EmailSignInTargets,
6
+ EmailUnavailable,
7
+ type EmailAction,
8
+ type EmailAddressDecision,
9
+ type EmailRegistrationDecision,
10
+ } from "@yielded/auth/Email";
11
+ import {
12
+ coordinateCommit,
13
+ CurrentCommitJournal,
14
+ hasCommitScope,
15
+ type CommitJournal,
16
+ type PreparedCommit,
17
+ LifecycleHooks,
18
+ HookConfigurationError,
19
+ } from "@yielded/auth/Hooks";
20
+ import type { LoginIdentifier } from "@yielded/auth/Identity";
21
+ import { reportPersistenceFailure } from "@yielded/auth/Persistence";
22
+ import {
23
+ type ProofCompletionPlan,
24
+ type ProofCompletionInput,
25
+ ProofUnavailable,
26
+ } from "@yielded/auth/Proofs";
27
+ import type { TokenDigest } from "@yielded/auth/Schema";
28
+ import {
29
+ assessAuthentication,
30
+ type AuthenticationRequirement,
31
+ type SecurityRevision,
32
+ } from "@yielded/auth/Sessions";
33
+ import { and, eq, inArray, lte, sql, type AnyRelations, type SQL, type Table } from "drizzle-orm";
34
+ import type { EffectDrizzleQueryError } from "drizzle-orm/effect-core";
35
+ import type { EffectSQLiteD1Database } from "drizzle-orm/effect-d1";
36
+ import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
37
+ import { Schema, Cause, DateTime, Effect, Option, Context } from "effect";
38
+ import type { Statement } from "effect/unstable/sql/Statement";
39
+
40
+ import { CurrentD1PlanningDatabase, makeD1Owner } from "./d1-planning";
41
+ import { compileD1ProofCompletionPlan } from "./d1-proofs";
42
+ import { D1BatchStatements } from "./D1BatchStatements";
43
+ import {
44
+ requiredEmailAddressConstraints,
45
+ requiredEmailRegistrationConstraints,
46
+ type D1EmailAddressMapping,
47
+ type D1EmailRegistrationMapping,
48
+ type EmailRegistrationMapping,
49
+ type EmailSignInMapping,
50
+ } from "./email-model";
51
+ import type { EmailRegistrationAuthority } from "./email-registration";
52
+ import {
53
+ CurrentEmailSql,
54
+ currentAddress,
55
+ decodeEmailSnapshot,
56
+ emailLookupQuery,
57
+ sameEmailRevision,
58
+ snapshotEmailMutation,
59
+ validEmailSignInConstraints,
60
+ validateEmailAuthority,
61
+ type CurrentAddress,
62
+ type EmailSqlDatabase,
63
+ } from "./email-sql";
64
+ import { column, isMappedConstraintConflict, PersistenceMappingError, updateValues } from "./model";
65
+ import type { D1ProofPersistenceMapping } from "./proof-model";
66
+ import type { SuppliedService } from "./SuppliedService";
67
+
68
+ type PlanPrepare<Method extends (...args: any[]) => any, A> = (
69
+ value: Parameters<Parameters<Method>[1]>[0],
70
+ journal: CommitJournal,
71
+ ) => PreparedCommit<A>;
72
+
73
+ type Database = EffectSQLiteD1Database<AnyRelations> & { readonly $client: D1Client };
74
+ type AddressMapping = D1EmailAddressMapping<Table, Table, Table, Table, Table, unknown>;
75
+ type RegistrationMapping<Registration> = EmailRegistrationMapping<
76
+ Registration,
77
+ Table,
78
+ Table,
79
+ Table,
80
+ Table,
81
+ Table,
82
+ unknown
83
+ >;
84
+ type ProofMapping = D1ProofPersistenceMapping<
85
+ any,
86
+ any,
87
+ any,
88
+ any,
89
+ any,
90
+ any,
91
+ any,
92
+ any,
93
+ any,
94
+ any,
95
+ any,
96
+ any
97
+ >;
98
+
99
+ const emailLookupRows = Effect.fn("DrizzleD1.emailLookupRows")(function* (
100
+ mapping: EmailSignInMapping<Table, Table, Table, unknown>,
101
+ moduleId: string,
102
+ identifier: LoginIdentifier,
103
+ ) {
104
+ const database = yield* CurrentD1PlanningDatabase;
105
+ const query = emailLookupQuery(mapping, moduleId, identifier);
106
+
107
+ return yield* database
108
+ .select(query.fields)
109
+ .from(query.from)
110
+ .innerJoin(query.subjectTable, query.subjectJoin)
111
+ .innerJoin(query.credentialTable, query.credentialJoin)
112
+ .where(query.predicate)
113
+ .limit(query.limit);
114
+ });
115
+
116
+ const unavailable = () => EmailUnavailable.make({});
117
+
118
+ const everyFailureMatches = (
119
+ cause: Cause.Cause<unknown>,
120
+ classify: ((cause: unknown) => boolean) | undefined,
121
+ ): boolean =>
122
+ classify !== undefined &&
123
+ cause.reasons.length > 0 &&
124
+ cause.reasons.every((reason) => Cause.isFailReason(reason) && classify(Cause.fail(reason.error)));
125
+
126
+ interface Planned<A> {
127
+ readonly receipt: A;
128
+ readonly statements: ReadonlyArray<Statement<any>>;
129
+ readonly retryable?: (cause: unknown) => boolean;
130
+ readonly journalGuard?: PreparedCommit<void>;
131
+ }
132
+
133
+ const statement = Effect.fn(function* (query: {
134
+ readonly toSQL: () => { readonly sql: string; readonly params: unknown[] };
135
+ }) {
136
+ const database = yield* CurrentD1PlanningDatabase;
137
+ const rendered = query.toSQL();
138
+
139
+ return database.$client.unsafe(rendered.sql, rendered.params);
140
+ });
141
+
142
+ const containsFailure = (failure: unknown, predicate: (value: unknown) => boolean): boolean => {
143
+ const pending: unknown[] = [failure];
144
+ const seen = new Set<unknown>();
145
+
146
+ for (let index = 0; index < 32 && pending.length > 0; index++) {
147
+ const value = pending.shift();
148
+
149
+ if (value === null || value === undefined || seen.has(value)) continue;
150
+ seen.add(value);
151
+ if (predicate(value)) return true;
152
+ if (Cause.isCause(value)) {
153
+ for (const reason of value.reasons) {
154
+ if (Cause.isFailReason(reason)) pending.push(reason.error);
155
+ else if (Cause.isDieReason(reason)) pending.push(reason.defect);
156
+ }
157
+ } else if (typeof value === "object") {
158
+ const wrapped = value as {
159
+ readonly cause?: unknown;
160
+ readonly reason?: unknown;
161
+ readonly message?: unknown;
162
+ };
163
+
164
+ if (wrapped.cause !== undefined) pending.push(wrapped.cause);
165
+ if (wrapped.reason !== undefined) pending.push(wrapped.reason);
166
+ if (wrapped.message !== undefined) pending.push(wrapped.message);
167
+ }
168
+ }
169
+
170
+ return false;
171
+ };
172
+
173
+ const guardFailure = (cause: unknown, marker: string) => {
174
+ const path = `$[${marker.replaceAll("'", "''")}]`;
175
+ const native = `bad JSON path: '${path}': SQLITE_ERROR`;
176
+
177
+ return containsFailure(cause, (value) => value === native || value === `D1_ERROR: ${native}`);
178
+ };
179
+
180
+ const assertion = Effect.fn(function* (condition: SQL, marker: string) {
181
+ const database = yield* CurrentD1PlanningDatabase;
182
+
183
+ return yield* statement(
184
+ database
185
+ .select({
186
+ ok: sql`case when ${condition} then 1 else json_extract('[]', ${`$[${marker}]`}) end`.as(
187
+ "ok",
188
+ ),
189
+ })
190
+ .from(sql`(select 1)`),
191
+ );
192
+ });
193
+
194
+ const existsSql = (query: SQL) => sql`exists(${query})`;
195
+ const notExistsSql = (query: SQL) => sql`not exists(${query})`;
196
+
197
+ const readEngineNowMillis = Effect.fn("Drizzle.readEngineNowMillis")(function* (
198
+ clock: AddressMapping["d1"],
199
+ ) {
200
+ const database = yield* CurrentD1PlanningDatabase;
201
+
202
+ return yield* awaitRows(
203
+ database
204
+ .select({
205
+ value: sql<number>`${clock.engineNowMillis}`.mapWith(Number),
206
+ })
207
+ .from(sql`(select 1)`),
208
+ ).pipe(
209
+ Effect.flatMap((rows) =>
210
+ typeof rows[0]?.value === "number" && Number.isFinite(rows[0].value)
211
+ ? Effect.succeed(rows[0].value)
212
+ : Effect.fail(unavailable()),
213
+ ),
214
+ );
215
+ });
216
+
217
+ const driverValues = (values: object, entries: ReadonlyArray<readonly [string, unknown]>) => ({
218
+ ...values,
219
+ ...Object.fromEntries(entries),
220
+ });
221
+
222
+ const allocate = <A>(
223
+ asynchronous: Effect.Effect<A, PersistenceMappingError> | undefined,
224
+ synchronous: (() => A) | undefined,
225
+ ) =>
226
+ asynchronous !== undefined
227
+ ? asynchronous
228
+ : synchronous === undefined
229
+ ? Effect.fail(unavailable())
230
+ : Effect.try({
231
+ try: synchronous,
232
+ catch: (cause) => new PersistenceMappingError({ operation: "mapping", cause }),
233
+ });
234
+
235
+ const validAddress = (mapping: AddressMapping) =>
236
+ Object.entries(requiredEmailAddressConstraints).every(
237
+ ([key, value]) =>
238
+ mapping.constraints[key as keyof typeof requiredEmailAddressConstraints] === value,
239
+ );
240
+
241
+ const evidenceCondition = (
242
+ mapping: AddressMapping,
243
+ evidence: Parameters<typeof assessAuthentication>[0],
244
+ requirement: AuthenticationRequirement,
245
+ ) => {
246
+ const fresh = (proof: (typeof evidence.proofs)[number]) => {
247
+ const verified = DateTime.toEpochMillis(proof.verifiedAt);
248
+
249
+ return sql`${mapping.d1.engineNowMillis} >= ${verified} and ${mapping.d1.engineNowMillis} - ${verified} < ${requirement.maximumAgeMillis}`;
250
+ };
251
+
252
+ const alternatives = requirement.alternatives.flatMap((alternative) => {
253
+ const byCredential = new Map<string, SQL[]>();
254
+
255
+ for (const proof of evidence.proofs)
256
+ byCredential.set(proof.credentialId, [
257
+ ...(byCredential.get(proof.credentialId) ?? []),
258
+ fresh(proof),
259
+ ]);
260
+
261
+ const credentials = [...byCredential.values()].map(
262
+ (conditions) => sql`(${sql.join(conditions, sql` or `)})`,
263
+ );
264
+
265
+ const factors = alternative.factors.map((factor) => {
266
+ const matches = evidence.proofs.filter((proof) => proof.factors.includes(factor)).map(fresh);
267
+
268
+ return matches.length === 0 ? sql`false` : sql`(${sql.join(matches, sql` or `)})`;
269
+ });
270
+
271
+ const assurance = evidence.proofs
272
+ .filter(
273
+ (proof) =>
274
+ (!alternative.userVerified || proof.userVerified) &&
275
+ (!alternative.phishingResistant || proof.phishingResistant),
276
+ )
277
+ .map(fresh);
278
+
279
+ if (assurance.length === 0 || credentials.length < alternative.minimumCredentials) return [];
280
+
281
+ const minimum =
282
+ alternative.minimumCredentials === 0
283
+ ? sql`true`
284
+ : sql`(${sql.join(
285
+ credentials.map((condition) => sql`case when ${condition} then 1 else 0 end`),
286
+ sql` + `,
287
+ )}) >= ${alternative.minimumCredentials}`;
288
+
289
+ return [and(...factors, sql`(${sql.join(assurance, sql` or `)})`, minimum)!];
290
+ });
291
+
292
+ const noFuture = evidence.proofs.map(
293
+ (proof) => sql`${mapping.d1.engineNowMillis} >= ${DateTime.toEpochMillis(proof.verifiedAt)}`,
294
+ );
295
+
296
+ return alternatives.length === 0
297
+ ? sql`false`
298
+ : and(...noFuture, sql`(${sql.join(alternatives, sql` or `)})`)!;
299
+ };
300
+
301
+ const subjectCondition = (
302
+ mapping: AddressMapping,
303
+ current: CurrentAddress,
304
+ revision: SecurityRevision,
305
+ ) => {
306
+ const id = column(mapping.subject.table, mapping.subject.id);
307
+ const status = column(mapping.subject.table, mapping.subject.status);
308
+ const security = column(mapping.subject.table, mapping.subject.securityRevision);
309
+
310
+ return existsSql(
311
+ sql`select 1 from ${mapping.subject.table} where ${id}=${sql.param(current.nativeSubjectId, id)} and ${status}=${sql.param(mapping.subject.d1ActiveStatusValue, status)} and ${security}=${sql.param(revision, security)}`,
312
+ );
313
+ };
314
+
315
+ const authorityRevisionCondition = (mapping: AddressMapping, current: CurrentAddress) => {
316
+ const a = mapping.authorityCredential;
317
+ const subject = column(a.table, a.subjectId);
318
+ const credential = column(a.table, a.credentialId);
319
+ const revision = column(a.table, a.revision);
320
+ const status = column(a.table, a.status);
321
+ const expected = current.revision.credentials;
322
+
323
+ const matches = expected.map((item) =>
324
+ existsSql(
325
+ sql`select 1 from ${a.table} where ${subject}=${sql.param(current.nativeSubjectId, subject)} and ${credential}=${sql.param(item.credentialId, credential)} and ${revision}=${sql.param(item.revision, revision)} and ${status}=${sql.param(a.d1ActiveStatusValue, status)}`,
326
+ ),
327
+ );
328
+
329
+ return and(
330
+ ...matches,
331
+ sql`(select count(*) from ${a.table} where ${subject}=${sql.param(current.nativeSubjectId, subject)} and ${status}=${sql.param(a.d1ActiveStatusValue, status)})=${expected.length}`,
332
+ )!;
333
+ };
334
+
335
+ const mutationPlan = Effect.fn("Drizzle.mutationPlan")(function* <A>(
336
+ mapping: AddressMapping,
337
+ proofMapping: ProofMapping,
338
+ uncaptured: Parameters<EmailAddressPersistence["Service"]["verifyWithProof"]>[0],
339
+ action: EmailAction,
340
+ prepare: (value: EmailAddressDecision, journal: CommitJournal) => PreparedCommit<A>,
341
+ ) {
342
+ const database = yield* CurrentD1PlanningDatabase;
343
+
344
+ const journal = yield* CurrentCommitJournal;
345
+
346
+ if (!validAddress(mapping)) return yield* unavailable();
347
+ const input = yield* snapshotEmailMutation(uncaptured).pipe(Effect.mapError(unavailable));
348
+
349
+ const current = yield* validateEmailAuthority(
350
+ mapping,
351
+ { mode: "interactive", locking: false, standaloneGuard: Effect.void },
352
+ input,
353
+ action,
354
+ );
355
+
356
+ if (current === undefined) return { receipt: prepare("rejected", journal), statements: [] };
357
+
358
+ const existingCommand = awaitRows(
359
+ database
360
+ .select()
361
+ .from(mapping.command.table)
362
+ .where(
363
+ and(
364
+ eq(column(mapping.command.table, mapping.command.moduleId), input.moduleId),
365
+ eq(column(mapping.command.table, mapping.command.commandId), input.commandId),
366
+ ),
367
+ )
368
+ .limit(1),
369
+ );
370
+
371
+ if ((yield* existingCommand).length > 0)
372
+ return { receipt: prepare("rejected", journal), statements: [] };
373
+ const requirement = yield* mapping.subject.decodeActionRequirement(current.subject, action);
374
+
375
+ const original = yield* assessAuthentication(
376
+ input.authorization.evidence,
377
+ input.authorization.requirement,
378
+ ).pipe(Effect.mapError(unavailable));
379
+
380
+ const configured = yield* assessAuthentication(input.authorization.evidence, requirement).pipe(
381
+ Effect.mapError(unavailable),
382
+ );
383
+
384
+ if (!original.satisfied || !configured.satisfied)
385
+ return { receipt: prepare("rejected", journal), statements: [] };
386
+ const transitionMillis = yield* readEngineNowMillis(mapping.d1);
387
+ const transitionInstant = mapping.encodeInstant(transitionMillis);
388
+
389
+ const targetCredentialId =
390
+ current.targetCredential?.[mapping.credential.credentialId] ??
391
+ (yield* allocate(mapping.allocateCredentialId, mapping.allocateCredentialIdSync));
392
+
393
+ const targetIdentifierRevision = yield* allocate(
394
+ mapping.allocateRevision,
395
+ mapping.allocateRevisionSync,
396
+ );
397
+
398
+ const targetCredentialRevision = yield* allocate(
399
+ mapping.allocateRevision,
400
+ mapping.allocateRevisionSync,
401
+ );
402
+
403
+ const sourceIdentifierRevision = yield* allocate(
404
+ mapping.allocateRevision,
405
+ mapping.allocateRevisionSync,
406
+ );
407
+
408
+ const sourceCredentialRevision = yield* allocate(
409
+ mapping.allocateRevision,
410
+ mapping.allocateRevisionSync,
411
+ );
412
+
413
+ const confirmsExisting =
414
+ action === "verify-address" && input.captured.targetIdentifierRevision !== undefined;
415
+
416
+ const nextRevision = confirmsExisting
417
+ ? input.captured.revision.securityRevision
418
+ : yield* allocate(
419
+ mapping.subject.nextSecurityRevision?.(input.captured.revision.securityRevision) ??
420
+ mapping.allocateRevision,
421
+ mapping.subject.nextSecurityRevisionSync === undefined
422
+ ? mapping.allocateRevisionSync
423
+ : () =>
424
+ mapping.subject.nextSecurityRevisionSync!(input.captured.revision.securityRevision),
425
+ );
426
+
427
+ if (
428
+ (!confirmsExisting && nextRevision === input.captured.revision.securityRevision) ||
429
+ (current.targetIdentifier !== undefined &&
430
+ targetIdentifierRevision === current.targetIdentifier[mapping.identifier.bindingRevision]) ||
431
+ (current.targetCredential !== undefined &&
432
+ targetCredentialRevision ===
433
+ current.targetCredential[mapping.credential.credentialRevision]) ||
434
+ (current.source !== undefined &&
435
+ (sourceIdentifierRevision === current.source.snapshot.identifierRevision ||
436
+ sourceCredentialRevision === current.source.snapshot.credentialRevision))
437
+ )
438
+ return yield* unavailable();
439
+ const statements: Statement<any>[] = [];
440
+ const i = mapping.identifier;
441
+ const c = mapping.credential;
442
+ const a = mapping.authorityCredential;
443
+ const sc = current.source;
444
+ const authorityMarker = `effect-auth-email-guard:authority:${input.commandId}`;
445
+
446
+ const targetIdentifierPrecondition =
447
+ current.targetIdentifier === undefined
448
+ ? notExistsSql(
449
+ sql`select 1 from ${i.table} where ${column(i.table, i.namespace)}=${sql.param(input.target.namespace, column(i.table, i.namespace))} and ${column(i.table, i.value)}=${sql.param(input.target.value, column(i.table, i.value))}`,
450
+ )
451
+ : existsSql(
452
+ sql`select 1 from ${i.table} where ${column(i.table, i.bindingRevision)}=${sql.param(current.targetIdentifier[i.bindingRevision], column(i.table, i.bindingRevision))} and ${i.d1MutableTargetCondition(
453
+ {
454
+ identifier: input.target,
455
+ nativeSubjectId: current.nativeSubjectId,
456
+ },
457
+ )}`,
458
+ );
459
+
460
+ const targetCredentialPrecondition =
461
+ current.targetCredential === undefined
462
+ ? notExistsSql(
463
+ sql`select 1 from ${c.table} where ${column(c.table, c.moduleId)}=${sql.param(input.moduleId, column(c.table, c.moduleId))} and ${column(c.table, c.identifierNamespace)}=${sql.param(input.target.namespace, column(c.table, c.identifierNamespace))} and ${column(c.table, c.identifierValue)}=${sql.param(input.target.value, column(c.table, c.identifierValue))}`,
464
+ )
465
+ : existsSql(
466
+ sql`select 1 from ${c.table} where ${column(c.table, c.moduleId)}=${sql.param(input.moduleId, column(c.table, c.moduleId))} and ${column(c.table, c.credentialId)}=${sql.param(current.targetCredential[c.credentialId], column(c.table, c.credentialId))} and ${column(c.table, c.subjectId)}=${sql.param(current.nativeSubjectId, column(c.table, c.subjectId))} and ${column(c.table, c.credentialRevision)}=${sql.param(current.targetCredential[c.credentialRevision], column(c.table, c.credentialRevision))} and ${column(c.table, c.status)}<>${sql.param(c.d1ActiveStatusValue, column(c.table, c.status))}`,
467
+ );
468
+
469
+ const sourcePrecondition =
470
+ action === "verify-address"
471
+ ? sql`true`
472
+ : and(
473
+ existsSql(
474
+ sql`select 1 from ${i.table} where ${i.d1CurrentCondition({
475
+ identifier: sc!.snapshot.identifier,
476
+ nativeSubjectId: current.nativeSubjectId,
477
+ bindingRevision: sc!.snapshot.identifierRevision,
478
+ })}`,
479
+ ),
480
+ existsSql(
481
+ sql`select 1 from ${c.table} where ${column(c.table, c.moduleId)}=${sql.param(input.moduleId, column(c.table, c.moduleId))} and ${column(c.table, c.credentialId)}=${sql.param(sc!.snapshot.credentialId, column(c.table, c.credentialId))} and ${column(c.table, c.subjectId)}=${sql.param(current.nativeSubjectId, column(c.table, c.subjectId))} and ${column(c.table, c.credentialRevision)}=${sql.param(sc!.snapshot.credentialRevision, column(c.table, c.credentialRevision))} and ${column(c.table, c.status)}=${sql.param(c.d1ActiveStatusValue, column(c.table, c.status))}`,
482
+ ),
483
+ )!;
484
+
485
+ statements.push(
486
+ yield* assertion(
487
+ and(
488
+ subjectCondition(mapping, current, input.captured.revision.securityRevision),
489
+ authorityRevisionCondition(mapping, current),
490
+ targetIdentifierPrecondition,
491
+ targetCredentialPrecondition,
492
+ sourcePrecondition,
493
+ evidenceCondition(mapping, input.authorization.evidence, input.authorization.requirement),
494
+ evidenceCondition(mapping, input.authorization.evidence, requirement),
495
+ )!,
496
+ authorityMarker,
497
+ ),
498
+ );
499
+
500
+ if (action === "change-address") {
501
+ statements.push(
502
+ yield* statement(
503
+ database
504
+ .update(i.table)
505
+ .set(
506
+ i.encodeRetirement({
507
+ source: sc!.snapshot.identifier,
508
+ bindingRevision: sourceIdentifierRevision,
509
+ }),
510
+ )
511
+ .where(
512
+ and(
513
+ eq(column(i.table, i.namespace), sc!.snapshot.identifier.namespace),
514
+ eq(column(i.table, i.value), sc!.snapshot.identifier.value),
515
+ eq(column(i.table, i.subjectId), current.nativeSubjectId),
516
+ eq(column(i.table, i.bindingRevision), sc!.snapshot.identifierRevision),
517
+ ),
518
+ ),
519
+ ),
520
+ );
521
+ statements.push(
522
+ yield* statement(
523
+ database
524
+ .update(c.table)
525
+ .set(
526
+ c.encodeRetirement({
527
+ source: sc!.snapshot.identifier,
528
+ credentialRevision: sourceCredentialRevision,
529
+ }),
530
+ )
531
+ .where(
532
+ and(
533
+ eq(column(c.table, c.moduleId), input.moduleId),
534
+ eq(column(c.table, c.credentialId), sc!.snapshot.credentialId),
535
+ eq(column(c.table, c.subjectId), current.nativeSubjectId),
536
+ eq(column(c.table, c.credentialRevision), sc!.snapshot.credentialRevision),
537
+ ),
538
+ ),
539
+ ),
540
+ );
541
+ statements.push(
542
+ yield* statement(
543
+ database
544
+ .update(a.table)
545
+ .set(a.encodeRetirement(sourceCredentialRevision))
546
+ .where(
547
+ and(
548
+ eq(column(a.table, a.subjectId), current.nativeSubjectId),
549
+ eq(column(a.table, a.credentialId), sc!.snapshot.credentialId),
550
+ eq(column(a.table, a.revision), sc!.snapshot.credentialRevision),
551
+ ),
552
+ ),
553
+ ),
554
+ );
555
+ }
556
+ if (current.targetIdentifier === undefined)
557
+ statements.push(
558
+ yield* statement(
559
+ database.insert(i.table).values(
560
+ driverValues(
561
+ i.encodeVerifiedInsert({
562
+ identifier: input.target,
563
+ subjectId: current.nativeSubjectId,
564
+ verifiedAtMillis: transitionMillis,
565
+ bindingRevision: targetIdentifierRevision,
566
+ }),
567
+ [[i.verifiedAt, transitionInstant]],
568
+ ),
569
+ ),
570
+ ),
571
+ );
572
+ else
573
+ statements.push(
574
+ yield* statement(
575
+ database
576
+ .update(i.table)
577
+ .set(
578
+ driverValues(
579
+ i.encodeVerification({
580
+ verifiedAtMillis: transitionMillis,
581
+ bindingRevision: targetIdentifierRevision,
582
+ }),
583
+ [[i.verifiedAt, transitionInstant]],
584
+ ),
585
+ )
586
+ .where(
587
+ and(
588
+ eq(column(i.table, i.namespace), input.target.namespace),
589
+ eq(column(i.table, i.value), input.target.value),
590
+ eq(column(i.table, i.subjectId), current.nativeSubjectId),
591
+ eq(column(i.table, i.bindingRevision), current.targetIdentifier![i.bindingRevision]),
592
+ ),
593
+ ),
594
+ ),
595
+ );
596
+ if (current.targetCredential === undefined) {
597
+ statements.push(
598
+ yield* statement(
599
+ database.insert(c.table).values(
600
+ c.encodeVerifiedInsert({
601
+ moduleId: input.moduleId,
602
+ subjectId: current.nativeSubjectId,
603
+ credentialId: targetCredentialId,
604
+ identifier: input.target,
605
+ credentialRevision: targetCredentialRevision,
606
+ }),
607
+ ),
608
+ ),
609
+ );
610
+ statements.push(
611
+ yield* statement(
612
+ database.insert(a.table).values(
613
+ a.encodeInsert({
614
+ subjectId: current.nativeSubjectId,
615
+ credentialId: targetCredentialId,
616
+ revision: targetCredentialRevision,
617
+ }),
618
+ ),
619
+ ),
620
+ );
621
+ } else {
622
+ const oldRevision = current.targetCredential[c.credentialRevision];
623
+
624
+ statements.push(
625
+ yield* statement(
626
+ database
627
+ .update(c.table)
628
+ .set(
629
+ c.encodeActivation({
630
+ identifier: input.target,
631
+ credentialRevision: targetCredentialRevision,
632
+ }),
633
+ )
634
+ .where(
635
+ and(
636
+ eq(column(c.table, c.moduleId), input.moduleId),
637
+ eq(column(c.table, c.credentialId), targetCredentialId),
638
+ eq(column(c.table, c.subjectId), current.nativeSubjectId),
639
+ eq(column(c.table, c.credentialRevision), oldRevision),
640
+ ),
641
+ ),
642
+ ),
643
+ );
644
+ statements.push(
645
+ yield* statement(
646
+ database
647
+ .update(a.table)
648
+ .set(a.encodeActivation(targetCredentialRevision))
649
+ .where(
650
+ and(
651
+ eq(column(a.table, a.subjectId), current.nativeSubjectId),
652
+ eq(column(a.table, a.credentialId), targetCredentialId),
653
+ eq(column(a.table, a.revision), oldRevision),
654
+ ),
655
+ ),
656
+ ),
657
+ );
658
+ }
659
+ statements.push(
660
+ yield* statement(
661
+ database
662
+ .update(mapping.subject.table)
663
+ .set(updateValues<any>([[mapping.subject.securityRevision, nextRevision]]))
664
+ .where(
665
+ and(
666
+ eq(column(mapping.subject.table, mapping.subject.id), current.nativeSubjectId),
667
+ eq(
668
+ column(mapping.subject.table, mapping.subject.securityRevision),
669
+ input.captured.revision.securityRevision,
670
+ ),
671
+ ),
672
+ ),
673
+ ),
674
+ );
675
+ statements.push(
676
+ yield* statement(
677
+ database.insert(mapping.command.table).values(
678
+ driverValues(
679
+ mapping.command.encodeInsert({
680
+ moduleId: input.moduleId,
681
+ commandId: input.commandId,
682
+ action,
683
+ bindingDigest: input.authorization.challenge.bindingDigest,
684
+ retentionUntilMillis: transitionMillis + mapping.commandRetentionMillis,
685
+ }),
686
+ [
687
+ [
688
+ mapping.command.retentionUntil,
689
+ mapping.encodeInstant(transitionMillis + mapping.commandRetentionMillis),
690
+ ],
691
+ ],
692
+ ),
693
+ ),
694
+ ),
695
+ );
696
+
697
+ const targetExists = existsSql(
698
+ sql`select 1 from ${i.table} where ${column(i.table, i.bindingRevision)}=${sql.param(targetIdentifierRevision, column(i.table, i.bindingRevision))} and ${column(i.table, i.verifiedAt)}=${sql.param(transitionInstant, column(i.table, i.verifiedAt))} and ${i.d1CurrentCondition({ identifier: input.target, nativeSubjectId: current.nativeSubjectId, bindingRevision: targetIdentifierRevision })}`,
699
+ );
700
+
701
+ const credentialExists = existsSql(
702
+ sql`select 1 from ${c.table} where ${column(c.table, c.moduleId)}=${sql.param(input.moduleId, column(c.table, c.moduleId))} and ${column(c.table, c.credentialId)}=${sql.param(targetCredentialId, column(c.table, c.credentialId))} and ${column(c.table, c.subjectId)}=${sql.param(current.nativeSubjectId, column(c.table, c.subjectId))} and ${column(c.table, c.identifierNamespace)}=${sql.param(input.target.namespace, column(c.table, c.identifierNamespace))} and ${column(c.table, c.identifierValue)}=${sql.param(input.target.value, column(c.table, c.identifierValue))} and ${column(c.table, c.credentialRevision)}=${sql.param(targetCredentialRevision, column(c.table, c.credentialRevision))} and ${column(c.table, c.status)}=${sql.param(c.d1ActiveStatusValue, column(c.table, c.status))}`,
703
+ );
704
+
705
+ const authorityExists = existsSql(
706
+ sql`select 1 from ${a.table} where ${column(a.table, a.subjectId)}=${sql.param(current.nativeSubjectId, column(a.table, a.subjectId))} and ${column(a.table, a.credentialId)}=${sql.param(targetCredentialId, column(a.table, a.credentialId))} and ${column(a.table, a.revision)}=${sql.param(targetCredentialRevision, column(a.table, a.revision))} and ${column(a.table, a.status)}=${sql.param(a.d1ActiveStatusValue, column(a.table, a.status))}`,
707
+ );
708
+
709
+ const commandExists = existsSql(
710
+ sql`select 1 from ${mapping.command.table} where ${column(mapping.command.table, mapping.command.moduleId)}=${sql.param(input.moduleId, column(mapping.command.table, mapping.command.moduleId))} and ${column(mapping.command.table, mapping.command.commandId)}=${sql.param(input.commandId, column(mapping.command.table, mapping.command.commandId))} and ${column(mapping.command.table, mapping.command.action)}=${sql.param(action, column(mapping.command.table, mapping.command.action))} and ${column(mapping.command.table, mapping.command.bindingDigest)}=${sql.param(input.authorization.challenge.bindingDigest, column(mapping.command.table, mapping.command.bindingDigest))} and ${column(mapping.command.table, mapping.command.retentionUntil)}=${sql.param(mapping.encodeInstant(transitionMillis + mapping.commandRetentionMillis), column(mapping.command.table, mapping.command.retentionUntil))}`,
711
+ );
712
+
713
+ const sourceRetired =
714
+ action === "verify-address"
715
+ ? sql`true`
716
+ : and(
717
+ existsSql(
718
+ sql`select 1 from ${i.table} where ${column(i.table, i.namespace)}=${sql.param(sc!.snapshot.identifier.namespace, column(i.table, i.namespace))} and ${column(i.table, i.value)}=${sql.param(sc!.snapshot.identifier.value, column(i.table, i.value))} and ${column(i.table, i.subjectId)}=${sql.param(current.nativeSubjectId, column(i.table, i.subjectId))} and ${column(i.table, i.bindingRevision)}=${sql.param(sourceIdentifierRevision, column(i.table, i.bindingRevision))} and not (${i.d1CurrentCondition({ identifier: sc!.snapshot.identifier, nativeSubjectId: current.nativeSubjectId, bindingRevision: sourceIdentifierRevision })})`,
719
+ ),
720
+ existsSql(
721
+ sql`select 1 from ${c.table} where ${column(c.table, c.moduleId)}=${sql.param(input.moduleId, column(c.table, c.moduleId))} and ${column(c.table, c.credentialId)}=${sql.param(sc!.snapshot.credentialId, column(c.table, c.credentialId))} and ${column(c.table, c.subjectId)}=${sql.param(current.nativeSubjectId, column(c.table, c.subjectId))} and ${column(c.table, c.identifierNamespace)}=${sql.param(sc!.snapshot.identifier.namespace, column(c.table, c.identifierNamespace))} and ${column(c.table, c.identifierValue)}=${sql.param(sc!.snapshot.identifier.value, column(c.table, c.identifierValue))} and ${column(c.table, c.credentialRevision)}=${sql.param(sourceCredentialRevision, column(c.table, c.credentialRevision))} and ${column(c.table, c.status)}<>${sql.param(c.d1ActiveStatusValue, column(c.table, c.status))}`,
722
+ ),
723
+ existsSql(
724
+ sql`select 1 from ${a.table} where ${column(a.table, a.subjectId)}=${sql.param(current.nativeSubjectId, column(a.table, a.subjectId))} and ${column(a.table, a.credentialId)}=${sql.param(sc!.snapshot.credentialId, column(a.table, a.credentialId))} and ${column(a.table, a.revision)}=${sql.param(sourceCredentialRevision, column(a.table, a.revision))} and ${column(a.table, a.status)}<>${sql.param(a.d1ActiveStatusValue, column(a.table, a.status))}`,
725
+ ),
726
+ )!;
727
+
728
+ const applied = and(
729
+ subjectCondition(mapping, current, nextRevision),
730
+ targetExists,
731
+ credentialExists,
732
+ authorityExists,
733
+ commandExists,
734
+ sourceRetired,
735
+ evidenceCondition(mapping, input.authorization.evidence, input.authorization.requirement),
736
+ evidenceCondition(mapping, input.authorization.evidence, requirement),
737
+ )!;
738
+
739
+ const compiled = yield* compileD1ProofCompletionPlan(
740
+ proofMapping,
741
+ input.completion,
742
+ { statements, appliedCondition: applied },
743
+ (decision) => decision,
744
+ );
745
+
746
+ const marker = `effect-auth-proof-guard:protected:${input.completion.input.continuationId}`;
747
+
748
+ return {
749
+ receipt: prepare(compiled.statements.length === 0 ? "rejected" : "changed", journal),
750
+ statements: compiled.statements,
751
+ retryable: (cause: unknown) =>
752
+ guardFailure(cause, marker) ||
753
+ guardFailure(cause, authorityMarker) ||
754
+ isMappedConstraintConflict(mapping.isCommandConflict, cause) ||
755
+ isMappedConstraintConflict(mapping.isIdentifierConflict, cause) ||
756
+ isMappedConstraintConflict(mapping.isCredentialConflict, cause),
757
+ };
758
+ });
759
+
760
+ const awaitRows = <A>(
761
+ effect: Effect.Effect<A, PersistenceMappingError | EffectDrizzleQueryError>,
762
+ ) => effect;
763
+
764
+ const probeCompletion = Effect.fn("DrizzleD1.probeCompletion")(function* (
765
+ proofMapping: ProofMapping,
766
+ input: ProofCompletionInput,
767
+ ) {
768
+ const database = yield* CurrentD1PlanningDatabase;
769
+
770
+ const plan: ProofCompletionPlan = {
771
+ input,
772
+ prepare: (decision, journal, project) => journal.prepare(project(decision)),
773
+ };
774
+
775
+ const marker = `effect-auth-proof-guard:protected:${input.continuationId}`;
776
+
777
+ const once = coordinateCommit(
778
+ () =>
779
+ Effect.gen(function* () {
780
+ const compiled = yield* compileD1ProofCompletionPlan(
781
+ proofMapping,
782
+ plan,
783
+ { statements: [], appliedCondition: sql`false` },
784
+ () => false,
785
+ );
786
+
787
+ if (compiled.statements.length === 0) return false;
788
+ yield* database.$client.batch(compiled.statements);
789
+
790
+ return false;
791
+ }),
792
+ { mode: "batch" },
793
+ );
794
+
795
+ return yield* once.pipe(
796
+ Effect.as(false),
797
+ Effect.catchCause((cause) =>
798
+ guardFailure(cause, marker) ? Effect.succeed(true) : Effect.failCause(cause),
799
+ ),
800
+ );
801
+ });
802
+
803
+ const makeAddressPlans = (mapping: AddressMapping, proofMapping: ProofMapping) => ({
804
+ target: (input: Parameters<EmailAddressPersistence["Service"]["target"]>[0]) =>
805
+ currentAddress(mapping, input, false).pipe(
806
+ Effect.flatMap((current) =>
807
+ current === undefined
808
+ ? Effect.fail(unavailable())
809
+ : Effect.succeed({
810
+ revision: current.revision,
811
+ eligible: current.eligible,
812
+ ...(current.targetIdentifierRevision === undefined
813
+ ? {}
814
+ : { targetIdentifierRevision: current.targetIdentifierRevision }),
815
+ ...(current.source === undefined ? {} : { source: current.source.snapshot }),
816
+ }),
817
+ ),
818
+ ),
819
+ checkCompletion: (
820
+ input: Parameters<EmailAddressPersistence["Service"]["checkCompletion"]>[0],
821
+ ) => {
822
+ if (input.binding._tag !== "IdentifierChange") return Effect.succeed(false);
823
+ const binding = input.binding;
824
+
825
+ const action: EmailAction | undefined =
826
+ input.moduleId.endsWith("/verify-address") && input.purpose === "email-address-verification"
827
+ ? "verify-address"
828
+ : input.moduleId.endsWith("/change-address") && input.purpose === "email-address-change"
829
+ ? "change-address"
830
+ : undefined;
831
+
832
+ if (action === undefined) return Effect.succeed(false);
833
+ const moduleId = input.moduleId.slice(0, -`/${action}`.length);
834
+
835
+ return currentAddress(
836
+ mapping,
837
+ {
838
+ moduleId,
839
+ subjectId: binding.revision.subjectId,
840
+ target: binding.identifier,
841
+ },
842
+ false,
843
+ ).pipe(
844
+ Effect.flatMap((current) =>
845
+ current !== undefined && sameEmailRevision(current.revision, binding.revision)
846
+ ? probeCompletion(proofMapping, input)
847
+ : Effect.succeed(false),
848
+ ),
849
+ );
850
+ },
851
+ verifyWithProof: <A>(
852
+ input: Parameters<EmailAddressPersistence["Service"]["verifyWithProof"]>[0],
853
+ prepare: PlanPrepare<EmailAddressPersistence["Service"]["verifyWithProof"], A>,
854
+ ) => mutationPlan(mapping, proofMapping, input, "verify-address", prepare),
855
+ changeWithProof: <A>(
856
+ input: Parameters<EmailAddressPersistence["Service"]["changeWithProof"]>[0],
857
+ prepare: PlanPrepare<EmailAddressPersistence["Service"]["changeWithProof"], A>,
858
+ ) => mutationPlan(mapping, proofMapping, input, "change-address", prepare),
859
+ cleanup: <A>(
860
+ input: Parameters<EmailAddressPersistence["Service"]["cleanup"]>[0],
861
+ prepare: PlanPrepare<EmailAddressPersistence["Service"]["cleanup"], A>,
862
+ ) =>
863
+ Effect.gen(function* () {
864
+ const database = yield* CurrentD1PlanningDatabase;
865
+
866
+ const journal = yield* CurrentCommitJournal;
867
+
868
+ const module = column(mapping.command.table, mapping.command.moduleId);
869
+ const id = column(mapping.command.table, mapping.command.commandId);
870
+ const retention = column(mapping.command.table, mapping.command.retentionUntil);
871
+
872
+ const rows = yield* awaitRows(
873
+ database
874
+ .select({ commandId: id })
875
+ .from(mapping.command.table)
876
+ .where(and(eq(module, input.moduleId), lte(retention, mapping.d1.engineNow)))
877
+ .limit(input.limit + 1),
878
+ );
879
+
880
+ const selected = rows.slice(0, input.limit);
881
+ const marker = `effect-auth-email-guard:cleanup:${input.moduleId}`;
882
+ const selectedIds = selected.map((row: any) => row.commandId);
883
+
884
+ const selectedWhere = and(
885
+ eq(module, input.moduleId),
886
+ inArray(id, selectedIds),
887
+ lte(retention, mapping.d1.engineNow),
888
+ );
889
+
890
+ const statements =
891
+ selected.length === 0
892
+ ? []
893
+ : [
894
+ yield* assertion(
895
+ sql`(select count(*) from ${mapping.command.table} where ${selectedWhere})=${selected.length}`,
896
+ marker,
897
+ ),
898
+ yield* statement(database.delete(mapping.command.table).where(selectedWhere)),
899
+ ];
900
+
901
+ return {
902
+ receipt: prepare({ removed: selected.length, hasMore: rows.length > input.limit }, journal),
903
+ statements,
904
+ retryable: (cause: unknown) => guardFailure(cause, marker),
905
+ };
906
+ }),
907
+ });
908
+
909
+ const registrationRows = Effect.fn("Drizzle.registrationRows")(function* <Registration>(
910
+ mapping: RegistrationMapping<Registration>,
911
+ moduleId: string,
912
+ commandId: string,
913
+ ) {
914
+ const database = yield* CurrentD1PlanningDatabase;
915
+
916
+ return yield* awaitRows(
917
+ database
918
+ .select()
919
+ .from(mapping.registration.table)
920
+ .where(
921
+ and(
922
+ eq(column(mapping.registration.table, mapping.registration.moduleId), moduleId),
923
+ eq(column(mapping.registration.table, mapping.registration.commandId), commandId),
924
+ ),
925
+ )
926
+ .limit(1),
927
+ );
928
+ });
929
+
930
+ const sameIdentifier = (left: LoginIdentifier, right: LoginIdentifier) =>
931
+ left.namespace === right.namespace && left.value === right.value;
932
+
933
+ const registrationCompletionMatches = <Registration>(
934
+ mapping: RegistrationMapping<Registration>,
935
+ input: {
936
+ readonly moduleId: string;
937
+ readonly identifier: LoginIdentifier;
938
+ readonly completion: ProofCompletionPlan;
939
+ },
940
+ ) =>
941
+ input.completion.input.moduleId === `${input.moduleId}/registration` &&
942
+ input.completion.input.purpose === "email-code-registration" &&
943
+ input.completion.input.binding._tag === "Identifier" &&
944
+ sameIdentifier(input.completion.input.binding.identifier, input.identifier) &&
945
+ mapping.constraints.request === requiredEmailRegistrationConstraints.request &&
946
+ mapping.constraints.pendingReference === requiredEmailRegistrationConstraints.pendingReference &&
947
+ (mapping.mode === "pending" ||
948
+ Object.entries(requiredEmailRegistrationConstraints).every(
949
+ ([key, value]) =>
950
+ mapping.constraints[key as keyof typeof requiredEmailRegistrationConstraints] === value,
951
+ ));
952
+
953
+ const registrationPlan = Effect.fn("Drizzle.registrationPlan")(function* <Registration, A>(
954
+ mapping: RegistrationMapping<Registration> & { readonly d1: AddressMapping["d1"] },
955
+ proofMapping: ProofMapping,
956
+ input: {
957
+ readonly moduleId: string;
958
+ readonly commandId: string;
959
+ readonly identifier: LoginIdentifier;
960
+ readonly registration: Registration;
961
+ readonly fingerprint: TokenDigest;
962
+ readonly completion: ProofCompletionPlan;
963
+ },
964
+ inspected: { readonly fingerprint: TokenDigest; readonly eligible: boolean },
965
+ allocated: {
966
+ readonly credentialId?: string;
967
+ readonly securityRevision?: SecurityRevision;
968
+ readonly identifierRevision?: SecurityRevision;
969
+ readonly credentialRevision?: SecurityRevision;
970
+ readonly nativeSubjectId?: unknown;
971
+ readonly pendingReference?: string;
972
+ },
973
+ prepare: (value: EmailRegistrationDecision, journal: CommitJournal) => PreparedCommit<A>,
974
+ ) {
975
+ const database = yield* CurrentD1PlanningDatabase;
976
+
977
+ const journal = yield* CurrentCommitJournal;
978
+
979
+ const existing = (yield* registrationRows(mapping, input.moduleId, input.commandId))[0];
980
+
981
+ if (existing !== undefined) {
982
+ const decision =
983
+ existing[mapping.registration.fingerprint] !== input.fingerprint
984
+ ? ({ _tag: "Rejected" } as const)
985
+ : yield* mapping.registration.decodeReplay(existing);
986
+
987
+ return { receipt: prepare(decision, journal), statements: [] };
988
+ }
989
+ if (
990
+ !registrationCompletionMatches(mapping, input) ||
991
+ !inspected.eligible ||
992
+ inspected.fingerprint !== input.fingerprint ||
993
+ (mapping.mode === "atomic" &&
994
+ (mapping.provisioning.idMode === "generated" ||
995
+ mapping.subject.d1ActiveStatusValue === undefined ||
996
+ mapping.identifier.d1CurrentCondition === undefined ||
997
+ mapping.credential.d1ActiveStatusValue === undefined ||
998
+ mapping.authorityCredential.d1ActiveStatusValue === undefined))
999
+ )
1000
+ return { receipt: prepare({ _tag: "Rejected" }, journal), statements: [] };
1001
+
1002
+ const intent = {
1003
+ moduleId: input.moduleId,
1004
+ commandId: input.commandId,
1005
+ identifier: input.identifier,
1006
+ registration: input.registration,
1007
+ fingerprint: input.fingerprint,
1008
+ completion: {
1009
+ moduleId: input.completion.input.moduleId,
1010
+ purpose: input.completion.input.purpose,
1011
+ continuationId: input.completion.input.continuationId,
1012
+ binding: input.completion.input.binding,
1013
+ },
1014
+ };
1015
+
1016
+ const transitionMillis = yield* readEngineNowMillis(mapping.d1);
1017
+ const retentionInstant = mapping.encodeInstant(transitionMillis + mapping.retentionMillis);
1018
+ const statements: Statement<any>[] = [];
1019
+ let applied: SQL;
1020
+ let desired: EmailRegistrationDecision;
1021
+ const registrationModule = column(mapping.registration.table, mapping.registration.moduleId);
1022
+ const registrationCommand = column(mapping.registration.table, mapping.registration.commandId);
1023
+
1024
+ const registrationFingerprint = column(
1025
+ mapping.registration.table,
1026
+ mapping.registration.fingerprint,
1027
+ );
1028
+
1029
+ const registrationState = column(mapping.registration.table, mapping.registration.state);
1030
+
1031
+ if (mapping.mode === "pending") {
1032
+ if (allocated.pendingReference === undefined)
1033
+ return { receipt: prepare({ _tag: "Rejected" }, journal), statements: [] };
1034
+ desired = { _tag: "ProvisioningPending", reference: allocated.pendingReference };
1035
+ statements.push(
1036
+ yield* statement(
1037
+ database.insert(mapping.registration.table).values(
1038
+ driverValues(
1039
+ mapping.registration.encodeInsert(intent, {
1040
+ state: "pending",
1041
+ pendingReference: allocated.pendingReference,
1042
+ retentionUntilMillis: transitionMillis + mapping.retentionMillis,
1043
+ }),
1044
+ [[mapping.registration.retentionUntil, retentionInstant]],
1045
+ ),
1046
+ ),
1047
+ ),
1048
+ );
1049
+ applied = existsSql(
1050
+ sql`select 1 from ${mapping.registration.table} where ${registrationModule}=${sql.param(input.moduleId, registrationModule)} and ${registrationCommand}=${sql.param(input.commandId, registrationCommand)} and ${registrationFingerprint}=${sql.param(input.fingerprint, registrationFingerprint)} and ${registrationState}='pending' and ${column(mapping.registration.table, mapping.registration.pendingReference)}=${sql.param(allocated.pendingReference, column(mapping.registration.table, mapping.registration.pendingReference))} and ${column(mapping.registration.table, mapping.registration.subjectId)} is null and ${column(mapping.registration.table, mapping.registration.retentionUntil)}=${sql.param(retentionInstant, column(mapping.registration.table, mapping.registration.retentionUntil))}`,
1051
+ );
1052
+ } else {
1053
+ if (
1054
+ allocated.nativeSubjectId === undefined ||
1055
+ allocated.credentialId === undefined ||
1056
+ allocated.securityRevision === undefined ||
1057
+ allocated.identifierRevision === undefined ||
1058
+ allocated.credentialRevision === undefined
1059
+ )
1060
+ return { receipt: prepare({ _tag: "Rejected" }, journal), statements: [] };
1061
+ const nativeSubjectId = allocated.nativeSubjectId;
1062
+
1063
+ desired = { _tag: "Registered" };
1064
+ statements.push(
1065
+ yield* statement(
1066
+ database.insert(mapping.subject.table).values(
1067
+ mapping.provisioning.encodeSubjectInsert(intent, {
1068
+ nativeSubjectId,
1069
+ securityRevision: allocated.securityRevision,
1070
+ }),
1071
+ ),
1072
+ ),
1073
+ yield* statement(
1074
+ database.insert(mapping.identifier.table).values(
1075
+ driverValues(
1076
+ mapping.identifier.encodeVerifiedInsert({
1077
+ identifier: input.identifier,
1078
+ subjectId: nativeSubjectId,
1079
+ verifiedAtMillis: transitionMillis,
1080
+ bindingRevision: allocated.identifierRevision,
1081
+ }),
1082
+ [[mapping.identifier.verifiedAt, mapping.encodeInstant(transitionMillis)]],
1083
+ ),
1084
+ ),
1085
+ ),
1086
+ yield* statement(
1087
+ database.insert(mapping.credential.table).values(
1088
+ mapping.credential.encodeVerifiedInsert({
1089
+ moduleId: input.moduleId,
1090
+ subjectId: nativeSubjectId,
1091
+ credentialId: allocated.credentialId,
1092
+ identifier: input.identifier,
1093
+ credentialRevision: allocated.credentialRevision,
1094
+ }),
1095
+ ),
1096
+ ),
1097
+ yield* statement(
1098
+ database.insert(mapping.authorityCredential.table).values(
1099
+ mapping.authorityCredential.encodeInsert({
1100
+ subjectId: nativeSubjectId,
1101
+ credentialId: allocated.credentialId,
1102
+ revision: allocated.credentialRevision,
1103
+ }),
1104
+ ),
1105
+ ),
1106
+ yield* statement(
1107
+ database.insert(mapping.registration.table).values(
1108
+ driverValues(
1109
+ mapping.registration.encodeInsert(intent, {
1110
+ state: "registered",
1111
+ nativeSubjectId,
1112
+ retentionUntilMillis: transitionMillis + mapping.retentionMillis,
1113
+ }),
1114
+ [[mapping.registration.retentionUntil, retentionInstant]],
1115
+ ),
1116
+ ),
1117
+ ),
1118
+ );
1119
+ const sId = column(mapping.subject.table, mapping.subject.id);
1120
+ const sStatus = column(mapping.subject.table, mapping.subject.status);
1121
+ const sRevision = column(mapping.subject.table, mapping.subject.securityRevision);
1122
+ const iNamespace = column(mapping.identifier.table, mapping.identifier.namespace);
1123
+ const iValue = column(mapping.identifier.table, mapping.identifier.value);
1124
+ const iSubject = column(mapping.identifier.table, mapping.identifier.subjectId);
1125
+ const iVerifiedAt = column(mapping.identifier.table, mapping.identifier.verifiedAt);
1126
+ const iRevision = column(mapping.identifier.table, mapping.identifier.bindingRevision);
1127
+ const cModule = column(mapping.credential.table, mapping.credential.moduleId);
1128
+ const cSubject = column(mapping.credential.table, mapping.credential.subjectId);
1129
+ const cId = column(mapping.credential.table, mapping.credential.credentialId);
1130
+ const cNamespace = column(mapping.credential.table, mapping.credential.identifierNamespace);
1131
+ const cValue = column(mapping.credential.table, mapping.credential.identifierValue);
1132
+ const cRevision = column(mapping.credential.table, mapping.credential.credentialRevision);
1133
+
1134
+ const aSubject = column(
1135
+ mapping.authorityCredential.table,
1136
+ mapping.authorityCredential.subjectId,
1137
+ );
1138
+
1139
+ const aId = column(mapping.authorityCredential.table, mapping.authorityCredential.credentialId);
1140
+
1141
+ const aRevision = column(
1142
+ mapping.authorityCredential.table,
1143
+ mapping.authorityCredential.revision,
1144
+ );
1145
+
1146
+ applied = and(
1147
+ existsSql(
1148
+ sql`select 1 from ${mapping.subject.table} where ${sId}=${sql.param(nativeSubjectId, sId)} and ${sStatus}=${sql.param(mapping.subject.d1ActiveStatusValue, sStatus)} and ${sRevision}=${sql.param(allocated.securityRevision, sRevision)}`,
1149
+ ),
1150
+ existsSql(
1151
+ sql`select 1 from ${mapping.identifier.table} where ${iNamespace}=${sql.param(input.identifier.namespace, iNamespace)} and ${iValue}=${sql.param(input.identifier.value, iValue)} and ${iSubject}=${sql.param(nativeSubjectId, iSubject)} and ${iVerifiedAt}=${sql.param(mapping.encodeInstant(transitionMillis), iVerifiedAt)} and ${iRevision}=${sql.param(allocated.identifierRevision, iRevision)} and ${mapping.identifier.d1CurrentCondition!({ identifier: input.identifier, nativeSubjectId, bindingRevision: allocated.identifierRevision })}`,
1152
+ ),
1153
+ existsSql(
1154
+ sql`select 1 from ${mapping.credential.table} where ${cModule}=${sql.param(input.moduleId, cModule)} and ${cSubject}=${sql.param(nativeSubjectId, cSubject)} and ${cId}=${sql.param(allocated.credentialId, cId)} and ${cNamespace}=${sql.param(input.identifier.namespace, cNamespace)} and ${cValue}=${sql.param(input.identifier.value, cValue)} and ${cRevision}=${sql.param(allocated.credentialRevision, cRevision)} and ${column(mapping.credential.table, mapping.credential.status)}=${sql.param(mapping.credential.d1ActiveStatusValue, column(mapping.credential.table, mapping.credential.status))}`,
1155
+ ),
1156
+ existsSql(
1157
+ sql`select 1 from ${mapping.authorityCredential.table} where ${aSubject}=${sql.param(nativeSubjectId, aSubject)} and ${aId}=${sql.param(allocated.credentialId, aId)} and ${aRevision}=${sql.param(allocated.credentialRevision, aRevision)} and ${column(mapping.authorityCredential.table, mapping.authorityCredential.status)}=${sql.param(mapping.authorityCredential.d1ActiveStatusValue, column(mapping.authorityCredential.table, mapping.authorityCredential.status))}`,
1158
+ ),
1159
+ existsSql(
1160
+ sql`select 1 from ${mapping.registration.table} where ${registrationModule}=${sql.param(input.moduleId, registrationModule)} and ${registrationCommand}=${sql.param(input.commandId, registrationCommand)} and ${registrationFingerprint}=${sql.param(input.fingerprint, registrationFingerprint)} and ${registrationState}='registered' and ${column(mapping.registration.table, mapping.registration.subjectId)}=${sql.param(nativeSubjectId, column(mapping.registration.table, mapping.registration.subjectId))} and ${column(mapping.registration.table, mapping.registration.pendingReference)} is null and ${column(mapping.registration.table, mapping.registration.retentionUntil)}=${sql.param(retentionInstant, column(mapping.registration.table, mapping.registration.retentionUntil))}`,
1161
+ ),
1162
+ )!;
1163
+ }
1164
+ let receipt: PreparedCommit<A> | undefined;
1165
+
1166
+ const compiled = yield* compileD1ProofCompletionPlan(
1167
+ proofMapping,
1168
+ input.completion,
1169
+ { statements, appliedCondition: applied },
1170
+ (completion) => {
1171
+ receipt = prepare(completion === "completed" ? desired : { _tag: "Rejected" }, journal);
1172
+
1173
+ return completion;
1174
+ },
1175
+ );
1176
+
1177
+ const marker = `effect-auth-proof-guard:protected:${input.completion.input.continuationId}`;
1178
+
1179
+ return {
1180
+ receipt: receipt ?? prepare({ _tag: "Rejected" }, journal),
1181
+ statements: compiled.statements,
1182
+ retryable: (cause: unknown) =>
1183
+ guardFailure(cause, marker) ||
1184
+ isMappedConstraintConflict(mapping.isRequestConflict, cause) ||
1185
+ (mapping.mode === "atomic" &&
1186
+ (isMappedConstraintConflict(mapping.isIdentifierConflict, cause) ||
1187
+ isMappedConstraintConflict(mapping.isCredentialConflict, cause))),
1188
+ };
1189
+ });
1190
+
1191
+ const makeRegistrationPlans = <Registration>(
1192
+ mapping: RegistrationMapping<Registration> & { readonly d1: AddressMapping["d1"] },
1193
+ proofMapping: ProofMapping,
1194
+ ) => ({
1195
+ inspect: (input: Parameters<EmailRegistrationAuthority<Registration>["inspect"]>[0]) =>
1196
+ mapping.inspect(input),
1197
+ registerWithProof: <A>(
1198
+ uncaptured: Parameters<EmailRegistrationAuthority<Registration>["registerWithProof"]>[0],
1199
+ prepare: PlanPrepare<EmailRegistrationAuthority<Registration>["registerWithProof"], A>,
1200
+ ) =>
1201
+ Effect.gen(function* () {
1202
+ const registration = yield* mapping.snapshotRegistration(uncaptured.registration);
1203
+ const inspectionRegistration = yield* mapping.snapshotRegistration(uncaptured.registration);
1204
+
1205
+ const input = Object.freeze({
1206
+ ...uncaptured,
1207
+ identifier: Object.freeze({ ...uncaptured.identifier }),
1208
+ registration,
1209
+ });
1210
+
1211
+ const inspected = yield* mapping.inspect({
1212
+ identifier: input.identifier,
1213
+ registration: inspectionRegistration,
1214
+ });
1215
+
1216
+ const atomic = mapping.mode === "atomic";
1217
+
1218
+ const credentialId = atomic
1219
+ ? yield* allocate(mapping.allocateCredentialId, mapping.allocateCredentialIdSync)
1220
+ : undefined;
1221
+
1222
+ const securityRevision = atomic
1223
+ ? yield* allocate(mapping.allocateRevision, mapping.allocateRevisionSync)
1224
+ : undefined;
1225
+
1226
+ const identifierRevision = atomic
1227
+ ? yield* allocate(mapping.allocateRevision, mapping.allocateRevisionSync)
1228
+ : undefined;
1229
+
1230
+ const credentialRevision = atomic
1231
+ ? yield* allocate(mapping.allocateRevision, mapping.allocateRevisionSync)
1232
+ : undefined;
1233
+
1234
+ const nativeSubjectId =
1235
+ atomic && mapping.provisioning.idMode !== "generated"
1236
+ ? yield* allocate(
1237
+ mapping.provisioning.allocateSubjectId,
1238
+ mapping.provisioning.allocateSubjectIdSync,
1239
+ )
1240
+ : undefined;
1241
+
1242
+ const pendingReference =
1243
+ mapping.mode === "pending"
1244
+ ? yield* allocate(mapping.allocatePendingReference, mapping.allocatePendingReferenceSync)
1245
+ : undefined;
1246
+
1247
+ return yield* registrationPlan(
1248
+ mapping,
1249
+ proofMapping,
1250
+ input,
1251
+ inspected,
1252
+ {
1253
+ ...(credentialId === undefined ? {} : { credentialId }),
1254
+ ...(securityRevision === undefined ? {} : { securityRevision }),
1255
+ ...(identifierRevision === undefined ? {} : { identifierRevision }),
1256
+ ...(credentialRevision === undefined ? {} : { credentialRevision }),
1257
+ ...(nativeSubjectId === undefined ? {} : { nativeSubjectId }),
1258
+ ...(pendingReference === undefined ? {} : { pendingReference }),
1259
+ },
1260
+ prepare,
1261
+ );
1262
+ }),
1263
+ });
1264
+
1265
+ export const makeD1EmailSignInServices = <
1266
+ S extends AnySQLiteTable,
1267
+ I extends AnySQLiteTable,
1268
+ C extends AnySQLiteTable,
1269
+ NativeId,
1270
+ >(
1271
+ database: Database,
1272
+ mapping: EmailSignInMapping<S, I, C, NativeId>,
1273
+ ) =>
1274
+ Effect.succeed({
1275
+ emailSignInTargets: EmailSignInTargets.of({
1276
+ lookup: (input) => {
1277
+ if (!validEmailSignInConstraints(mapping as any)) return Effect.fail(unavailable());
1278
+
1279
+ return emailLookupRows(mapping as any, input.moduleId, input.identifier).pipe(
1280
+ Effect.flatMap((rows) => {
1281
+ const row = rows[0];
1282
+
1283
+ if (row === undefined) return Effect.succeed(Option.none());
1284
+
1285
+ return decodeEmailSnapshot(mapping as any, input.moduleId, input.identifier, row).pipe(
1286
+ Effect.map((snapshot) =>
1287
+ snapshot === undefined ? Option.none() : Option.some(snapshot),
1288
+ ),
1289
+ );
1290
+ }),
1291
+ Effect.provideService(CurrentD1PlanningDatabase, database),
1292
+ translateFailure,
1293
+ );
1294
+ },
1295
+ }),
1296
+ });
1297
+
1298
+ export const makeD1EmailAddressServices = <
1299
+ S extends AnySQLiteTable,
1300
+ I extends AnySQLiteTable,
1301
+ C extends AnySQLiteTable,
1302
+ AC extends AnySQLiteTable,
1303
+ M extends AnySQLiteTable,
1304
+ NativeId,
1305
+ PRq extends AnySQLiteTable,
1306
+ PS extends AnySQLiteTable,
1307
+ PG extends AnySQLiteTable,
1308
+ PCn extends AnySQLiteTable,
1309
+ PRs extends AnySQLiteTable,
1310
+ PAev extends AnySQLiteTable,
1311
+ PF extends AnySQLiteTable,
1312
+ PC extends AnySQLiteTable,
1313
+ PSub extends AnySQLiteTable,
1314
+ PI extends AnySQLiteTable,
1315
+ PCr extends AnySQLiteTable,
1316
+ PNativeId,
1317
+ >(
1318
+ database: Database,
1319
+ mapping: D1EmailAddressMapping<S, I, C, AC, M, NativeId>,
1320
+ proofMapping: D1ProofPersistenceMapping<
1321
+ PRq,
1322
+ PS,
1323
+ PG,
1324
+ PCn,
1325
+ PRs,
1326
+ PAev,
1327
+ PF,
1328
+ PC,
1329
+ PSub,
1330
+ PI,
1331
+ PCr,
1332
+ PNativeId
1333
+ >,
1334
+ ) =>
1335
+ Effect.gen(function* () {
1336
+ const hooks = yield* LifecycleHooks;
1337
+
1338
+ const plans = makeAddressPlans(
1339
+ mapping as unknown as AddressMapping,
1340
+ proofMapping as unknown as ProofMapping,
1341
+ );
1342
+
1343
+ const run = <Out, Err, Env>(plan: Effect.Effect<Planned<Out>, Err, Env>) =>
1344
+ Effect.gen(function* () {
1345
+ if (yield* hasCommitScope) return yield* unavailable();
1346
+
1347
+ return yield* executeStandalone(plan, 2);
1348
+ }).pipe(
1349
+ Effect.provideService(CurrentD1PlanningDatabase, database),
1350
+ Effect.provideService(CurrentEmailSql, database as unknown as EmailSqlDatabase),
1351
+ Effect.provideService(LifecycleHooks, hooks),
1352
+ );
1353
+
1354
+ const service: EmailAddressPersistence["Service"] = {
1355
+ target: (input) =>
1356
+ plans
1357
+ .target(input)
1358
+ .pipe(
1359
+ Effect.provideService(CurrentD1PlanningDatabase, database),
1360
+ Effect.provideService(CurrentEmailSql, database as unknown as EmailSqlDatabase),
1361
+ Effect.provideService(LifecycleHooks, hooks),
1362
+ translateFailure,
1363
+ ),
1364
+ checkCompletion: (input) =>
1365
+ plans
1366
+ .checkCompletion(input)
1367
+ .pipe(
1368
+ Effect.provideService(CurrentD1PlanningDatabase, database),
1369
+ Effect.provideService(CurrentEmailSql, database as unknown as EmailSqlDatabase),
1370
+ Effect.provideService(LifecycleHooks, hooks),
1371
+ translateFailure,
1372
+ ),
1373
+ verifyWithProof: (input, prepare) =>
1374
+ run(plans.verifyWithProof(input, prepare)).pipe(translateFailure),
1375
+ changeWithProof: (input, prepare) =>
1376
+ run(plans.changeWithProof(input, prepare)).pipe(translateFailure),
1377
+ cleanup: (input, prepare) => run(plans.cleanup(input, prepare)).pipe(translateFailure),
1378
+ };
1379
+
1380
+ return { emailAddressPersistence: service };
1381
+ });
1382
+
1383
+ export const makeD1EmailRegistrationServices = <
1384
+ Registration,
1385
+ S extends AnySQLiteTable,
1386
+ I extends AnySQLiteTable,
1387
+ C extends AnySQLiteTable,
1388
+ AC extends AnySQLiteTable,
1389
+ Rq extends AnySQLiteTable,
1390
+ NativeId,
1391
+ PRq extends AnySQLiteTable,
1392
+ PS extends AnySQLiteTable,
1393
+ PG extends AnySQLiteTable,
1394
+ PCn extends AnySQLiteTable,
1395
+ PRs extends AnySQLiteTable,
1396
+ PAev extends AnySQLiteTable,
1397
+ PF extends AnySQLiteTable,
1398
+ PC extends AnySQLiteTable,
1399
+ PSub extends AnySQLiteTable,
1400
+ PI extends AnySQLiteTable,
1401
+ PCr extends AnySQLiteTable,
1402
+ PNativeId,
1403
+ >(
1404
+ database: Database,
1405
+ mapping: D1EmailRegistrationMapping<Registration, S, I, C, AC, Rq, NativeId>,
1406
+ proofMapping: D1ProofPersistenceMapping<
1407
+ PRq,
1408
+ PS,
1409
+ PG,
1410
+ PCn,
1411
+ PRs,
1412
+ PAev,
1413
+ PF,
1414
+ PC,
1415
+ PSub,
1416
+ PI,
1417
+ PCr,
1418
+ PNativeId
1419
+ >,
1420
+ ) =>
1421
+ Effect.gen(function* () {
1422
+ const hooks = yield* LifecycleHooks;
1423
+
1424
+ const plans = makeRegistrationPlans(
1425
+ mapping as unknown as RegistrationMapping<Registration> & {
1426
+ readonly d1: AddressMapping["d1"];
1427
+ },
1428
+ proofMapping as unknown as ProofMapping,
1429
+ );
1430
+
1431
+ const run = <Out, Err, Env>(plan: Effect.Effect<Planned<Out>, Err, Env>) =>
1432
+ Effect.gen(function* () {
1433
+ if (yield* hasCommitScope) return yield* unavailable();
1434
+
1435
+ return yield* executeStandalone(plan, 2);
1436
+ }).pipe(
1437
+ Effect.provideService(CurrentD1PlanningDatabase, database),
1438
+ Effect.provideService(CurrentEmailSql, database as unknown as EmailSqlDatabase),
1439
+ Effect.provideService(LifecycleHooks, hooks),
1440
+ );
1441
+
1442
+ const service: EmailRegistrationAuthority<Registration> = {
1443
+ inspect: (input) =>
1444
+ plans
1445
+ .inspect(input)
1446
+ .pipe(
1447
+ Effect.provideService(CurrentD1PlanningDatabase, database),
1448
+ Effect.provideService(CurrentEmailSql, database as unknown as EmailSqlDatabase),
1449
+ Effect.provideService(LifecycleHooks, hooks),
1450
+ translateFailure,
1451
+ ),
1452
+ registerWithProof: (input, prepare) =>
1453
+ run(plans.registerWithProof(input, prepare)).pipe(translateFailure),
1454
+ };
1455
+
1456
+ return { registrationAuthority: service };
1457
+ });
1458
+
1459
+ type CoordinatorError<E> = E | EmailUnavailable | HookConfigurationError;
1460
+
1461
+ export function coordinateD1EmailAddress<
1462
+ S extends AnySQLiteTable,
1463
+ I extends AnySQLiteTable,
1464
+ C extends AnySQLiteTable,
1465
+ AC extends AnySQLiteTable,
1466
+ M extends AnySQLiteTable,
1467
+ NativeId,
1468
+ PRq extends AnySQLiteTable,
1469
+ PS extends AnySQLiteTable,
1470
+ PG extends AnySQLiteTable,
1471
+ PCn extends AnySQLiteTable,
1472
+ PRs extends AnySQLiteTable,
1473
+ PAev extends AnySQLiteTable,
1474
+ PF extends AnySQLiteTable,
1475
+ PC extends AnySQLiteTable,
1476
+ PSub extends AnySQLiteTable,
1477
+ PI extends AnySQLiteTable,
1478
+ PCr extends AnySQLiteTable,
1479
+ PNativeId,
1480
+ A,
1481
+ E,
1482
+ R,
1483
+ DatabaseError,
1484
+ DatabaseRequirements,
1485
+ >(
1486
+ acquire: Effect.Effect<Database, DatabaseError, DatabaseRequirements>,
1487
+ options: {
1488
+ readonly mapping: D1EmailAddressMapping<S, I, C, AC, M, NativeId>;
1489
+ readonly proofMapping: D1ProofPersistenceMapping<
1490
+ PRq,
1491
+ PS,
1492
+ PG,
1493
+ PCn,
1494
+ PRs,
1495
+ PAev,
1496
+ PF,
1497
+ PC,
1498
+ PSub,
1499
+ PI,
1500
+ PCr,
1501
+ PNativeId
1502
+ >;
1503
+ },
1504
+ body: Effect.Effect<A, E, R>,
1505
+ ): Effect.Effect<
1506
+ A,
1507
+ CoordinatorError<E> | DatabaseError,
1508
+ Exclude<R, EmailAddressPersistence | D1BatchStatements> | LifecycleHooks | DatabaseRequirements
1509
+ > {
1510
+ return Effect.flatMap(acquire, (database) =>
1511
+ Effect.gen(function* () {
1512
+ const hooks = yield* LifecycleHooks;
1513
+
1514
+ if (yield* hasCommitScope) return yield* unavailable();
1515
+
1516
+ const result = yield* coordinateCommit(
1517
+ () =>
1518
+ Effect.gen(function* () {
1519
+ const statements: Statement<any>[] = [];
1520
+ let mutation: Planned<any> | undefined;
1521
+
1522
+ const nativeCollector = D1BatchStatements.of({
1523
+ append: (statement) => Effect.sync(() => statements.push(statement)),
1524
+ });
1525
+
1526
+ const owner = yield* makeD1Owner(unavailable()).pipe(
1527
+ Effect.provideService(D1BatchStatements, nativeCollector),
1528
+ );
1529
+
1530
+ const plans = makeAddressPlans(
1531
+ options.mapping as unknown as AddressMapping,
1532
+ options.proofMapping as unknown as ProofMapping,
1533
+ );
1534
+
1535
+ const run = <Out, Err, Env>(plan: Effect.Effect<Planned<Out>, Err, Env>) =>
1536
+ coordinateCommit(
1537
+ () =>
1538
+ Effect.gen(function* () {
1539
+ const journal = yield* CurrentCommitJournal;
1540
+ const planned = yield* plan;
1541
+
1542
+ yield* owner.check;
1543
+ if (mutation !== undefined) return yield* unavailable();
1544
+ const guarded = { ...planned, journalGuard: journal.prepare(undefined) };
1545
+
1546
+ mutation = guarded;
1547
+ statements.push(...guarded.statements);
1548
+
1549
+ return guarded.receipt;
1550
+ }),
1551
+ { mode: "batch" },
1552
+ ).pipe(
1553
+ Effect.map((result) => result.value),
1554
+ Effect.provideService(CurrentD1PlanningDatabase, database),
1555
+ Effect.provideService(CurrentEmailSql, database as unknown as EmailSqlDatabase),
1556
+ Effect.provideService(LifecycleHooks, hooks),
1557
+ );
1558
+
1559
+ const service: EmailAddressPersistence["Service"] = {
1560
+ target: (input) =>
1561
+ owner.run(
1562
+ plans
1563
+ .target(input)
1564
+ .pipe(
1565
+ Effect.provideService(CurrentD1PlanningDatabase, database),
1566
+ Effect.provideService(
1567
+ CurrentEmailSql,
1568
+ database as unknown as EmailSqlDatabase,
1569
+ ),
1570
+ Effect.provideService(LifecycleHooks, hooks),
1571
+ translateFailure,
1572
+ ),
1573
+ ),
1574
+ checkCompletion: (input) =>
1575
+ owner.run(
1576
+ plans
1577
+ .checkCompletion(input)
1578
+ .pipe(
1579
+ Effect.provideService(CurrentD1PlanningDatabase, database),
1580
+ Effect.provideService(
1581
+ CurrentEmailSql,
1582
+ database as unknown as EmailSqlDatabase,
1583
+ ),
1584
+ Effect.provideService(LifecycleHooks, hooks),
1585
+ translateFailure,
1586
+ ),
1587
+ ),
1588
+ verifyWithProof: (input, prepare) =>
1589
+ owner.run(run(plans.verifyWithProof(input, prepare)).pipe(translateFailure)),
1590
+ changeWithProof: (input, prepare) =>
1591
+ owner.run(run(plans.changeWithProof(input, prepare)).pipe(translateFailure)),
1592
+ cleanup: (input, prepare) =>
1593
+ owner.run(run(plans.cleanup(input, prepare)).pipe(translateFailure)),
1594
+ };
1595
+
1596
+ const provided = Context.make(EmailAddressPersistence, service).pipe(
1597
+ Context.add(D1BatchStatements, owner.collector),
1598
+ );
1599
+
1600
+ const value = yield* owner.close(Effect.provideContext(body, provided));
1601
+
1602
+ if (mutation?.journalGuard !== undefined) {
1603
+ const status = yield* Effect.result(mutation.journalGuard.read);
1604
+
1605
+ if (status._tag === "Success" || status.failure._tag !== "CommitPending")
1606
+ return yield* unavailable();
1607
+ }
1608
+ yield* database.$client.batch(statements).pipe(
1609
+ Effect.catchCause((cause) =>
1610
+ Effect.failCause(
1611
+ Cause.map(cause, (error) =>
1612
+ everyFailureMatches(cause, mutation?.retryable) ? unavailable() : error,
1613
+ ),
1614
+ ),
1615
+ ),
1616
+ translateFailure,
1617
+ );
1618
+
1619
+ return value;
1620
+ }),
1621
+ { mode: "batch" },
1622
+ ).pipe(Effect.provideService(LifecycleHooks, hooks));
1623
+
1624
+ return result.value;
1625
+ }),
1626
+ );
1627
+ }
1628
+
1629
+ export function coordinateD1EmailRegistration<
1630
+ TargetId,
1631
+ Registration,
1632
+ S extends AnySQLiteTable,
1633
+ I extends AnySQLiteTable,
1634
+ C extends AnySQLiteTable,
1635
+ AC extends AnySQLiteTable,
1636
+ Rq extends AnySQLiteTable,
1637
+ NativeId,
1638
+ PRq extends AnySQLiteTable,
1639
+ PS extends AnySQLiteTable,
1640
+ PG extends AnySQLiteTable,
1641
+ PCn extends AnySQLiteTable,
1642
+ PRs extends AnySQLiteTable,
1643
+ PAev extends AnySQLiteTable,
1644
+ PF extends AnySQLiteTable,
1645
+ PC extends AnySQLiteTable,
1646
+ PSub extends AnySQLiteTable,
1647
+ PI extends AnySQLiteTable,
1648
+ PCr extends AnySQLiteTable,
1649
+ PNativeId,
1650
+ A,
1651
+ E,
1652
+ R,
1653
+ DatabaseError,
1654
+ DatabaseRequirements,
1655
+ >(
1656
+ acquire: Effect.Effect<Database, DatabaseError, DatabaseRequirements>,
1657
+ options: {
1658
+ readonly mapping: D1EmailRegistrationMapping<NoInfer<Registration>, S, I, C, AC, Rq, NativeId>;
1659
+ readonly proofMapping: D1ProofPersistenceMapping<
1660
+ PRq,
1661
+ PS,
1662
+ PG,
1663
+ PCn,
1664
+ PRs,
1665
+ PAev,
1666
+ PF,
1667
+ PC,
1668
+ PSub,
1669
+ PI,
1670
+ PCr,
1671
+ PNativeId
1672
+ >;
1673
+ readonly target: SuppliedService<TargetId, EmailRegistrationAuthority<Registration>>;
1674
+ },
1675
+ body: Effect.Effect<A, E, R>,
1676
+ ): Effect.Effect<
1677
+ A,
1678
+ CoordinatorError<E> | DatabaseError,
1679
+ Exclude<R, TargetId | D1BatchStatements> | LifecycleHooks | DatabaseRequirements
1680
+ > {
1681
+ return Effect.flatMap(acquire, (database) =>
1682
+ Effect.gen(function* () {
1683
+ const hooks = yield* LifecycleHooks;
1684
+
1685
+ if (yield* hasCommitScope) return yield* unavailable();
1686
+
1687
+ const result = yield* coordinateCommit(
1688
+ () =>
1689
+ Effect.gen(function* () {
1690
+ const statements: Statement<any>[] = [];
1691
+ let mutation: Planned<any> | undefined;
1692
+
1693
+ const nativeCollector = D1BatchStatements.of({
1694
+ append: (statement) => Effect.sync(() => statements.push(statement)),
1695
+ });
1696
+
1697
+ const owner = yield* makeD1Owner(unavailable()).pipe(
1698
+ Effect.provideService(D1BatchStatements, nativeCollector),
1699
+ );
1700
+
1701
+ const plans = makeRegistrationPlans(
1702
+ options.mapping as unknown as RegistrationMapping<Registration> & {
1703
+ readonly d1: AddressMapping["d1"];
1704
+ },
1705
+ options.proofMapping as unknown as ProofMapping,
1706
+ );
1707
+
1708
+ const run = <Out, Err, Env>(plan: Effect.Effect<Planned<Out>, Err, Env>) =>
1709
+ coordinateCommit(
1710
+ () =>
1711
+ Effect.gen(function* () {
1712
+ const journal = yield* CurrentCommitJournal;
1713
+ const planned = yield* plan;
1714
+
1715
+ yield* owner.check;
1716
+ if (mutation !== undefined) return yield* unavailable();
1717
+ const guarded = { ...planned, journalGuard: journal.prepare(undefined) };
1718
+
1719
+ mutation = guarded;
1720
+ statements.push(...guarded.statements);
1721
+
1722
+ return guarded.receipt;
1723
+ }),
1724
+ { mode: "batch" },
1725
+ ).pipe(
1726
+ Effect.map((result) => result.value),
1727
+ Effect.provideService(CurrentD1PlanningDatabase, database),
1728
+ Effect.provideService(CurrentEmailSql, database as unknown as EmailSqlDatabase),
1729
+ Effect.provideService(LifecycleHooks, hooks),
1730
+ );
1731
+
1732
+ const service: EmailRegistrationAuthority<Registration> = {
1733
+ inspect: (input) =>
1734
+ owner.run(
1735
+ plans
1736
+ .inspect(input)
1737
+ .pipe(
1738
+ Effect.provideService(CurrentD1PlanningDatabase, database),
1739
+ Effect.provideService(
1740
+ CurrentEmailSql,
1741
+ database as unknown as EmailSqlDatabase,
1742
+ ),
1743
+ Effect.provideService(LifecycleHooks, hooks),
1744
+ translateFailure,
1745
+ ),
1746
+ ),
1747
+ registerWithProof: (input, prepare) =>
1748
+ owner.run(run(plans.registerWithProof(input, prepare)).pipe(translateFailure)),
1749
+ };
1750
+
1751
+ const provided = Context.make(options.target, service).pipe(
1752
+ Context.add(D1BatchStatements, owner.collector),
1753
+ );
1754
+
1755
+ const value = yield* owner.close(Effect.provideContext(body, provided));
1756
+
1757
+ if (mutation?.journalGuard !== undefined) {
1758
+ const status = yield* Effect.result(mutation.journalGuard.read);
1759
+
1760
+ if (status._tag === "Success" || status.failure._tag !== "CommitPending")
1761
+ return yield* unavailable();
1762
+ }
1763
+ yield* database.$client.batch(statements).pipe(
1764
+ Effect.catchCause((cause) =>
1765
+ Effect.failCause(
1766
+ Cause.map(cause, (error) =>
1767
+ everyFailureMatches(cause, mutation?.retryable) ? unavailable() : error,
1768
+ ),
1769
+ ),
1770
+ ),
1771
+ translateFailure,
1772
+ );
1773
+
1774
+ return value;
1775
+ }),
1776
+ { mode: "batch" },
1777
+ ).pipe(Effect.provideService(LifecycleHooks, hooks));
1778
+
1779
+ return result.value;
1780
+ }),
1781
+ );
1782
+ }
1783
+
1784
+ const translateFailure = <A, E, R>(
1785
+ effect: Effect.Effect<A, E, R>,
1786
+ ): Effect.Effect<A, EmailUnavailable, R> =>
1787
+ reportPersistenceFailure(
1788
+ effect,
1789
+ (error) =>
1790
+ Schema.is(EmailUnavailable)(error) ||
1791
+ Schema.is(ProofUnavailable)(error) ||
1792
+ Schema.is(HookConfigurationError)(error),
1793
+ ).pipe(Effect.catchCause((cause) => Effect.failCause(Cause.map(cause, () => unavailable()))));
1794
+
1795
+ const executeStandalone = <A, E, R>(
1796
+ plan: Effect.Effect<Planned<A>, E, R>,
1797
+ retries = 2,
1798
+ ): Effect.Effect<
1799
+ A,
1800
+ | E
1801
+ | import("@yielded/auth/Hooks").HookConfigurationError
1802
+ | import("effect/unstable/sql/SqlError").SqlError,
1803
+ Exclude<R, CurrentCommitJournal> | CurrentD1PlanningDatabase | LifecycleHooks
1804
+ > =>
1805
+ Effect.suspend(() => {
1806
+ let retryable: ((cause: unknown) => boolean) | undefined;
1807
+
1808
+ const once = coordinateCommit(
1809
+ () =>
1810
+ Effect.gen(function* () {
1811
+ const database = yield* CurrentD1PlanningDatabase;
1812
+ const planned = yield* plan;
1813
+
1814
+ retryable = planned.retryable;
1815
+ yield* database.$client.batch(planned.statements);
1816
+
1817
+ return planned.receipt;
1818
+ }),
1819
+ { mode: "batch" },
1820
+ ).pipe(Effect.map((result) => result.value));
1821
+
1822
+ return once.pipe(
1823
+ Effect.catchCause((cause) =>
1824
+ retries > 0 && everyFailureMatches(cause, retryable)
1825
+ ? executeStandalone(plan, retries - 1)
1826
+ : Effect.failCause(cause),
1827
+ ),
1828
+ );
1829
+ });