@yielded/auth-persistence-drizzle 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (401) hide show
  1. package/README.md +17 -0
  2. package/dist/D1.d.mts +963 -0
  3. package/dist/D1.mjs +29 -0
  4. package/dist/D1.mjs.map +1 -0
  5. package/dist/Libsql.d.mts +2134 -0
  6. package/dist/Libsql.mjs +77 -0
  7. package/dist/Libsql.mjs.map +1 -0
  8. package/dist/Mysql2.d.mts +1817 -0
  9. package/dist/Mysql2.mjs +55 -0
  10. package/dist/Mysql2.mjs.map +1 -0
  11. package/dist/Pglite.d.mts +1821 -0
  12. package/dist/Pglite.mjs +53 -0
  13. package/dist/Pglite.mjs.map +1 -0
  14. package/dist/Postgres.d.mts +1821 -0
  15. package/dist/Postgres.mjs +53 -0
  16. package/dist/Postgres.mjs.map +1 -0
  17. package/dist/SqliteBun.d.mts +2129 -0
  18. package/dist/SqliteBun.mjs +70 -0
  19. package/dist/SqliteBun.mjs.map +1 -0
  20. package/dist/SqliteDo.d.mts +2181 -0
  21. package/dist/SqliteDo.mjs +105 -0
  22. package/dist/SqliteDo.mjs.map +1 -0
  23. package/dist/SqliteNode.d.mts +2129 -0
  24. package/dist/SqliteNode.mjs +70 -0
  25. package/dist/SqliteNode.mjs.map +1 -0
  26. package/dist/SqliteWasm.d.mts +2132 -0
  27. package/dist/SqliteWasm.mjs +70 -0
  28. package/dist/SqliteWasm.mjs.map +1 -0
  29. package/dist/_virtual/_rolldown/runtime.mjs +13 -0
  30. package/dist/drizzle/D1BatchStatements.d.mts +10 -0
  31. package/dist/drizzle/D1BatchStatements.mjs +8 -0
  32. package/dist/drizzle/D1BatchStatements.mjs.map +1 -0
  33. package/dist/drizzle/SuppliedService.d.mts +9 -0
  34. package/dist/drizzle/d1-emails.d.mts +61 -0
  35. package/dist/drizzle/d1-emails.mjs +578 -0
  36. package/dist/drizzle/d1-emails.mjs.map +1 -0
  37. package/dist/drizzle/d1-generated-statement.mjs +79 -0
  38. package/dist/drizzle/d1-generated-statement.mjs.map +1 -0
  39. package/dist/drizzle/d1-identity.d.mts +30 -0
  40. package/dist/drizzle/d1-identity.mjs +118 -0
  41. package/dist/drizzle/d1-identity.mjs.map +1 -0
  42. package/dist/drizzle/d1-oauth.d.mts +300 -0
  43. package/dist/drizzle/d1-oauth.mjs +76 -0
  44. package/dist/drizzle/d1-oauth.mjs.map +1 -0
  45. package/dist/drizzle/d1-passkey.d.mts +47 -0
  46. package/dist/drizzle/d1-passkey.mjs +95 -0
  47. package/dist/drizzle/d1-passkey.mjs.map +1 -0
  48. package/dist/drizzle/d1-password-prepared.d.mts +24 -0
  49. package/dist/drizzle/d1-password-prepared.mjs +460 -0
  50. package/dist/drizzle/d1-password-prepared.mjs.map +1 -0
  51. package/dist/drizzle/d1-passwords.d.mts +86 -0
  52. package/dist/drizzle/d1-passwords.mjs +1003 -0
  53. package/dist/drizzle/d1-passwords.mjs.map +1 -0
  54. package/dist/drizzle/d1-phone.d.mts +23 -0
  55. package/dist/drizzle/d1-phone.mjs +30 -0
  56. package/dist/drizzle/d1-phone.mjs.map +1 -0
  57. package/dist/drizzle/d1-planning.d.mts +11 -0
  58. package/dist/drizzle/d1-planning.mjs +43 -0
  59. package/dist/drizzle/d1-planning.mjs.map +1 -0
  60. package/dist/drizzle/d1-proofs.d.mts +79 -0
  61. package/dist/drizzle/d1-proofs.mjs +829 -0
  62. package/dist/drizzle/d1-proofs.mjs.map +1 -0
  63. package/dist/drizzle/d1-sessions.d.mts +66 -0
  64. package/dist/drizzle/d1-sessions.mjs +965 -0
  65. package/dist/drizzle/d1-sessions.mjs.map +1 -0
  66. package/dist/drizzle/d1-totp.d.mts +23 -0
  67. package/dist/drizzle/d1-totp.mjs +27 -0
  68. package/dist/drizzle/d1-totp.mjs.map +1 -0
  69. package/dist/drizzle/email-model.d.mts +30 -0
  70. package/dist/drizzle/email-model.mjs +2 -0
  71. package/dist/drizzle/email-registration.d.mts +29 -0
  72. package/dist/drizzle/email-registration.mjs +236 -0
  73. package/dist/drizzle/email-registration.mjs.map +1 -0
  74. package/dist/drizzle/email-sql.d.mts +1 -0
  75. package/dist/drizzle/email-sql.mjs +9 -0
  76. package/dist/drizzle/email-sql.mjs.map +1 -0
  77. package/dist/drizzle/email-target.d.mts +19 -0
  78. package/dist/drizzle/email-target.mjs +72 -0
  79. package/dist/drizzle/email-target.mjs.map +1 -0
  80. package/dist/drizzle/model.d.mts +245 -0
  81. package/dist/drizzle/model.mjs +55 -0
  82. package/dist/drizzle/model.mjs.map +1 -0
  83. package/dist/drizzle/mysql-emails.d.mts +70 -0
  84. package/dist/drizzle/mysql-emails.mjs +38 -0
  85. package/dist/drizzle/mysql-emails.mjs.map +1 -0
  86. package/dist/drizzle/mysql-identity.d.mts +21 -0
  87. package/dist/drizzle/mysql-identity.mjs +93 -0
  88. package/dist/drizzle/mysql-identity.mjs.map +1 -0
  89. package/dist/drizzle/mysql-password-prepared.d.mts +29 -0
  90. package/dist/drizzle/mysql-password-prepared.mjs +16 -0
  91. package/dist/drizzle/mysql-password-prepared.mjs.map +1 -0
  92. package/dist/drizzle/mysql-passwords.d.mts +85 -0
  93. package/dist/drizzle/mysql-passwords.mjs +38 -0
  94. package/dist/drizzle/mysql-passwords.mjs.map +1 -0
  95. package/dist/drizzle/mysql-proofs.d.mts +74 -0
  96. package/dist/drizzle/mysql-proofs.mjs +23 -0
  97. package/dist/drizzle/mysql-proofs.mjs.map +1 -0
  98. package/dist/drizzle/mysql-sessions.d.mts +91 -0
  99. package/dist/drizzle/mysql-sessions.mjs +53 -0
  100. package/dist/drizzle/mysql-sessions.mjs.map +1 -0
  101. package/dist/drizzle/mysql.d.mts +69 -0
  102. package/dist/drizzle/mysql.mjs +218 -0
  103. package/dist/drizzle/mysql.mjs.map +1 -0
  104. package/dist/drizzle/oauth-accounts.mjs +358 -0
  105. package/dist/drizzle/oauth-accounts.mjs.map +1 -0
  106. package/dist/drizzle/oauth-connected-access.mjs +210 -0
  107. package/dist/drizzle/oauth-connected-access.mjs.map +1 -0
  108. package/dist/drizzle/oauth-connected-collection.mjs +202 -0
  109. package/dist/drizzle/oauth-connected-collection.mjs.map +1 -0
  110. package/dist/drizzle/oauth-connected-custody.mjs +146 -0
  111. package/dist/drizzle/oauth-connected-custody.mjs.map +1 -0
  112. package/dist/drizzle/oauth-connected-drivers.mjs +76 -0
  113. package/dist/drizzle/oauth-connected-drivers.mjs.map +1 -0
  114. package/dist/drizzle/oauth-connected-flow.mjs +247 -0
  115. package/dist/drizzle/oauth-connected-flow.mjs.map +1 -0
  116. package/dist/drizzle/oauth-connected-input.mjs +103 -0
  117. package/dist/drizzle/oauth-connected-input.mjs.map +1 -0
  118. package/dist/drizzle/oauth-connected-maintenance.mjs +214 -0
  119. package/dist/drizzle/oauth-connected-maintenance.mjs.map +1 -0
  120. package/dist/drizzle/oauth-connected-management.mjs +228 -0
  121. package/dist/drizzle/oauth-connected-management.mjs.map +1 -0
  122. package/dist/drizzle/oauth-connected-model.d.mts +277 -0
  123. package/dist/drizzle/oauth-connected-model.mjs +22 -0
  124. package/dist/drizzle/oauth-connected-model.mjs.map +1 -0
  125. package/dist/drizzle/oauth-connected-reference.d.mts +16 -0
  126. package/dist/drizzle/oauth-connected-reference.mjs +53 -0
  127. package/dist/drizzle/oauth-connected-reference.mjs.map +1 -0
  128. package/dist/drizzle/oauth-connected-settlement.mjs +117 -0
  129. package/dist/drizzle/oauth-connected-settlement.mjs.map +1 -0
  130. package/dist/drizzle/oauth-connected-state.d.mts +1954 -0
  131. package/dist/drizzle/oauth-connected-state.mjs +422 -0
  132. package/dist/drizzle/oauth-connected-state.mjs.map +1 -0
  133. package/dist/drizzle/oauth-connected-target.d.mts +752 -0
  134. package/dist/drizzle/oauth-connected-target.mjs +91 -0
  135. package/dist/drizzle/oauth-connected-target.mjs.map +1 -0
  136. package/dist/drizzle/oauth-drivers.mjs +51 -0
  137. package/dist/drizzle/oauth-drivers.mjs.map +1 -0
  138. package/dist/drizzle/oauth-flow.d.mts +22 -0
  139. package/dist/drizzle/oauth-flow.mjs +253 -0
  140. package/dist/drizzle/oauth-flow.mjs.map +1 -0
  141. package/dist/drizzle/oauth-input.mjs +127 -0
  142. package/dist/drizzle/oauth-input.mjs.map +1 -0
  143. package/dist/drizzle/oauth-model.d.mts +461 -0
  144. package/dist/drizzle/oauth-model.mjs +49 -0
  145. package/dist/drizzle/oauth-model.mjs.map +1 -0
  146. package/dist/drizzle/oauth-mysql.mjs +8 -0
  147. package/dist/drizzle/oauth-mysql.mjs.map +1 -0
  148. package/dist/drizzle/oauth-owner.d.mts +10 -0
  149. package/dist/drizzle/oauth-owner.mjs +10 -0
  150. package/dist/drizzle/oauth-owner.mjs.map +1 -0
  151. package/dist/drizzle/oauth-registration.d.mts +5 -0
  152. package/dist/drizzle/oauth-registration.mjs +374 -0
  153. package/dist/drizzle/oauth-registration.mjs.map +1 -0
  154. package/dist/drizzle/oauth-state.mjs +107 -0
  155. package/dist/drizzle/oauth-state.mjs.map +1 -0
  156. package/dist/drizzle/oauth-target.d.mts +109 -0
  157. package/dist/drizzle/oauth-target.mjs +246 -0
  158. package/dist/drizzle/oauth-target.mjs.map +1 -0
  159. package/dist/drizzle/passkey-drivers.mjs +34 -0
  160. package/dist/drizzle/passkey-drivers.mjs.map +1 -0
  161. package/dist/drizzle/passkey-kernel.mjs +8 -0
  162. package/dist/drizzle/passkey-kernel.mjs.map +1 -0
  163. package/dist/drizzle/passkey-model.d.mts +43 -0
  164. package/dist/drizzle/passkey-model.mjs +8 -0
  165. package/dist/drizzle/passkey-model.mjs.map +1 -0
  166. package/dist/drizzle/passkey-registration-ceremony-model.d.mts +12 -0
  167. package/dist/drizzle/passkey-registration-ceremony-model.mjs +2 -0
  168. package/dist/drizzle/passkey-state.mjs +7 -0
  169. package/dist/drizzle/passkey-state.mjs.map +1 -0
  170. package/dist/drizzle/passkey-target.d.mts +2 -0
  171. package/dist/drizzle/passkey-target.mjs +7 -0
  172. package/dist/drizzle/passkey-target.mjs.map +1 -0
  173. package/dist/drizzle/passkey-write-drivers.mjs +32 -0
  174. package/dist/drizzle/passkey-write-drivers.mjs.map +1 -0
  175. package/dist/drizzle/passkey-write-model.d.mts +20 -0
  176. package/dist/drizzle/passkey-write-model.mjs +9 -0
  177. package/dist/drizzle/passkey-write-model.mjs.map +1 -0
  178. package/dist/drizzle/passkey-write-target.d.mts +5 -0
  179. package/dist/drizzle/passkey-write-target.mjs +7 -0
  180. package/dist/drizzle/passkey-write-target.mjs.map +1 -0
  181. package/dist/drizzle/password-model.d.mts +24 -0
  182. package/dist/drizzle/password-model.mjs +2 -0
  183. package/dist/drizzle/password-prepared-model.d.mts +92 -0
  184. package/dist/drizzle/password-prepared-model.mjs +11 -0
  185. package/dist/drizzle/password-prepared-model.mjs.map +1 -0
  186. package/dist/drizzle/password-prepared-sql.mjs +389 -0
  187. package/dist/drizzle/password-prepared-sql.mjs.map +1 -0
  188. package/dist/drizzle/password-prepared-state.mjs +104 -0
  189. package/dist/drizzle/password-prepared-state.mjs.map +1 -0
  190. package/dist/drizzle/password-prepared-target.d.mts +11 -0
  191. package/dist/drizzle/password-prepared-target.mjs +56 -0
  192. package/dist/drizzle/password-prepared-target.mjs.map +1 -0
  193. package/dist/drizzle/password-registration.d.mts +7 -0
  194. package/dist/drizzle/password-registration.mjs +134 -0
  195. package/dist/drizzle/password-registration.mjs.map +1 -0
  196. package/dist/drizzle/password-sql.d.mts +1 -0
  197. package/dist/drizzle/password-sql.mjs +9 -0
  198. package/dist/drizzle/password-sql.mjs.map +1 -0
  199. package/dist/drizzle/password-target.d.mts +16 -0
  200. package/dist/drizzle/password-target.mjs +61 -0
  201. package/dist/drizzle/password-target.mjs.map +1 -0
  202. package/dist/drizzle/pg-emails.d.mts +71 -0
  203. package/dist/drizzle/pg-emails.mjs +38 -0
  204. package/dist/drizzle/pg-emails.mjs.map +1 -0
  205. package/dist/drizzle/pg-password-prepared.d.mts +30 -0
  206. package/dist/drizzle/pg-password-prepared.mjs +16 -0
  207. package/dist/drizzle/pg-password-prepared.mjs.map +1 -0
  208. package/dist/drizzle/pg-passwords.d.mts +86 -0
  209. package/dist/drizzle/pg-passwords.mjs +38 -0
  210. package/dist/drizzle/pg-passwords.mjs.map +1 -0
  211. package/dist/drizzle/pg-proofs.d.mts +75 -0
  212. package/dist/drizzle/pg-proofs.mjs +22 -0
  213. package/dist/drizzle/pg-proofs.mjs.map +1 -0
  214. package/dist/drizzle/pg-sessions.d.mts +92 -0
  215. package/dist/drizzle/pg-sessions.mjs +53 -0
  216. package/dist/drizzle/pg-sessions.mjs.map +1 -0
  217. package/dist/drizzle/pg.d.mts +80 -0
  218. package/dist/drizzle/pg.mjs +289 -0
  219. package/dist/drizzle/pg.mjs.map +1 -0
  220. package/dist/drizzle/phone-model.d.mts +113 -0
  221. package/dist/drizzle/phone-model.mjs +39 -0
  222. package/dist/drizzle/phone-model.mjs.map +1 -0
  223. package/dist/drizzle/phone-state.mjs +14 -0
  224. package/dist/drizzle/phone-state.mjs.map +1 -0
  225. package/dist/drizzle/phone-target.d.mts +14 -0
  226. package/dist/drizzle/phone-target.mjs +168 -0
  227. package/dist/drizzle/phone-target.mjs.map +1 -0
  228. package/dist/drizzle/proof-model.d.mts +21 -0
  229. package/dist/drizzle/proof-model.mjs +2 -0
  230. package/dist/drizzle/proof-sql.d.mts +1 -0
  231. package/dist/drizzle/proof-sql.mjs +8 -0
  232. package/dist/drizzle/proof-sql.mjs.map +1 -0
  233. package/dist/drizzle/proof-target.d.mts +11 -0
  234. package/dist/drizzle/proof-target.mjs +36 -0
  235. package/dist/drizzle/proof-target.mjs.map +1 -0
  236. package/dist/drizzle/query-operations.mjs +27 -0
  237. package/dist/drizzle/query-operations.mjs.map +1 -0
  238. package/dist/drizzle/session-model.d.mts +31 -0
  239. package/dist/drizzle/session-model.mjs +2 -0
  240. package/dist/drizzle/session-sql.mjs +8 -0
  241. package/dist/drizzle/session-sql.mjs.map +1 -0
  242. package/dist/drizzle/session-target.d.mts +27 -0
  243. package/dist/drizzle/session-target.mjs +46 -0
  244. package/dist/drizzle/session-target.mjs.map +1 -0
  245. package/dist/drizzle/sqlite-emails.mjs +45 -0
  246. package/dist/drizzle/sqlite-emails.mjs.map +1 -0
  247. package/dist/drizzle/sqlite-identity.mjs +90 -0
  248. package/dist/drizzle/sqlite-identity.mjs.map +1 -0
  249. package/dist/drizzle/sqlite-password-prepared.mjs +21 -0
  250. package/dist/drizzle/sqlite-password-prepared.mjs.map +1 -0
  251. package/dist/drizzle/sqlite-passwords.mjs +49 -0
  252. package/dist/drizzle/sqlite-passwords.mjs.map +1 -0
  253. package/dist/drizzle/sqlite-proofs.mjs +29 -0
  254. package/dist/drizzle/sqlite-proofs.mjs.map +1 -0
  255. package/dist/drizzle/sqlite-sessions.mjs +74 -0
  256. package/dist/drizzle/sqlite-sessions.mjs.map +1 -0
  257. package/dist/drizzle/sqlite.d.mts +77 -0
  258. package/dist/drizzle/sqlite.mjs +217 -0
  259. package/dist/drizzle/sqlite.mjs.map +1 -0
  260. package/dist/drizzle/standalone-guard.mjs +17 -0
  261. package/dist/drizzle/standalone-guard.mjs.map +1 -0
  262. package/dist/drizzle/step-up-model.d.mts +16 -0
  263. package/dist/drizzle/step-up-model.mjs +2 -0
  264. package/dist/drizzle/table-model.d.mts +11 -0
  265. package/dist/drizzle/totp-model.d.mts +82 -0
  266. package/dist/drizzle/totp-model.mjs +13 -0
  267. package/dist/drizzle/totp-model.mjs.map +1 -0
  268. package/dist/drizzle/totp-state.d.mts +37 -0
  269. package/dist/drizzle/totp-state.mjs +287 -0
  270. package/dist/drizzle/totp-state.mjs.map +1 -0
  271. package/dist/drizzle/totp-target.d.mts +14 -0
  272. package/dist/drizzle/totp-target.mjs +81 -0
  273. package/dist/drizzle/totp-target.mjs.map +1 -0
  274. package/dist/drizzle/transaction-execution.d.mts +2 -0
  275. package/dist/drizzle/transaction-execution.mjs +11 -0
  276. package/dist/drizzle/transaction-execution.mjs.map +1 -0
  277. package/dist/drizzle/transaction-mysql.mjs +47 -0
  278. package/dist/drizzle/transaction-mysql.mjs.map +1 -0
  279. package/dist/drizzle/transaction-owner.d.mts +2 -0
  280. package/dist/drizzle/transaction-owner.mjs +8 -0
  281. package/dist/drizzle/transaction-owner.mjs.map +1 -0
  282. package/dist/index.d.mts +27 -0
  283. package/dist/index.mjs +27 -0
  284. package/dist/internal/drizzle-migrations.mjs +19 -0
  285. package/dist/internal/drizzle-migrations.mjs.map +1 -0
  286. package/dist/internal/drizzle-postgres.mjs +37 -0
  287. package/dist/internal/drizzle-postgres.mjs.map +1 -0
  288. package/dist/internal/drizzle-sqlite.mjs +35 -0
  289. package/dist/internal/drizzle-sqlite.mjs.map +1 -0
  290. package/package.json +1 -0
  291. package/src/D1.ts +136 -0
  292. package/src/Libsql.ts +257 -0
  293. package/src/Mysql2.ts +184 -0
  294. package/src/Pglite.ts +174 -0
  295. package/src/Postgres.ts +174 -0
  296. package/src/SqliteBun.ts +258 -0
  297. package/src/SqliteDo.ts +473 -0
  298. package/src/SqliteNode.ts +259 -0
  299. package/src/SqliteWasm.ts +263 -0
  300. package/src/drizzle/D1BatchStatements.ts +10 -0
  301. package/src/drizzle/SuppliedService.ts +10 -0
  302. package/src/drizzle/d1-emails.ts +1829 -0
  303. package/src/drizzle/d1-generated-statement.ts +129 -0
  304. package/src/drizzle/d1-identity.ts +375 -0
  305. package/src/drizzle/d1-oauth.ts +340 -0
  306. package/src/drizzle/d1-passkey.ts +373 -0
  307. package/src/drizzle/d1-password-prepared.ts +1270 -0
  308. package/src/drizzle/d1-passwords.ts +2656 -0
  309. package/src/drizzle/d1-phone.ts +96 -0
  310. package/src/drizzle/d1-planning.ts +104 -0
  311. package/src/drizzle/d1-proofs.ts +2305 -0
  312. package/src/drizzle/d1-sessions.ts +2857 -0
  313. package/src/drizzle/d1-totp.ts +78 -0
  314. package/src/drizzle/email-model.ts +202 -0
  315. package/src/drizzle/email-registration.ts +698 -0
  316. package/src/drizzle/email-sql.ts +25 -0
  317. package/src/drizzle/email-target.ts +243 -0
  318. package/src/drizzle/model.ts +459 -0
  319. package/src/drizzle/mysql-emails.ts +321 -0
  320. package/src/drizzle/mysql-identity.ts +324 -0
  321. package/src/drizzle/mysql-password-prepared.ts +204 -0
  322. package/src/drizzle/mysql-passwords.ts +339 -0
  323. package/src/drizzle/mysql-proofs.ts +181 -0
  324. package/src/drizzle/mysql-sessions.ts +741 -0
  325. package/src/drizzle/mysql.ts +687 -0
  326. package/src/drizzle/oauth-accounts.ts +804 -0
  327. package/src/drizzle/oauth-connected-access.ts +354 -0
  328. package/src/drizzle/oauth-connected-collection.ts +324 -0
  329. package/src/drizzle/oauth-connected-custody.ts +277 -0
  330. package/src/drizzle/oauth-connected-drivers.ts +465 -0
  331. package/src/drizzle/oauth-connected-flow.ts +441 -0
  332. package/src/drizzle/oauth-connected-input.ts +127 -0
  333. package/src/drizzle/oauth-connected-maintenance.ts +345 -0
  334. package/src/drizzle/oauth-connected-management.ts +365 -0
  335. package/src/drizzle/oauth-connected-model.ts +347 -0
  336. package/src/drizzle/oauth-connected-reference.ts +112 -0
  337. package/src/drizzle/oauth-connected-settlement.ts +226 -0
  338. package/src/drizzle/oauth-connected-state.ts +845 -0
  339. package/src/drizzle/oauth-connected-target.ts +264 -0
  340. package/src/drizzle/oauth-drivers.ts +569 -0
  341. package/src/drizzle/oauth-flow.ts +462 -0
  342. package/src/drizzle/oauth-input.ts +201 -0
  343. package/src/drizzle/oauth-model.ts +617 -0
  344. package/src/drizzle/oauth-mysql.ts +10 -0
  345. package/src/drizzle/oauth-owner.ts +29 -0
  346. package/src/drizzle/oauth-registration.ts +640 -0
  347. package/src/drizzle/oauth-state.ts +215 -0
  348. package/src/drizzle/oauth-target.ts +750 -0
  349. package/src/drizzle/passkey-drivers.ts +385 -0
  350. package/src/drizzle/passkey-kernel.ts +4 -0
  351. package/src/drizzle/passkey-model.ts +153 -0
  352. package/src/drizzle/passkey-registration-ceremony-model.ts +32 -0
  353. package/src/drizzle/passkey-state.ts +3 -0
  354. package/src/drizzle/passkey-target.ts +23 -0
  355. package/src/drizzle/passkey-write-drivers.ts +395 -0
  356. package/src/drizzle/passkey-write-model.ts +103 -0
  357. package/src/drizzle/passkey-write-target.ts +9 -0
  358. package/src/drizzle/password-model.ts +169 -0
  359. package/src/drizzle/password-prepared-model.ts +259 -0
  360. package/src/drizzle/password-prepared-sql.ts +1191 -0
  361. package/src/drizzle/password-prepared-state.ts +231 -0
  362. package/src/drizzle/password-prepared-target.ts +160 -0
  363. package/src/drizzle/password-registration.ts +363 -0
  364. package/src/drizzle/password-sql.ts +13 -0
  365. package/src/drizzle/password-target.ts +224 -0
  366. package/src/drizzle/pg-emails.ts +321 -0
  367. package/src/drizzle/pg-password-prepared.ts +205 -0
  368. package/src/drizzle/pg-passwords.ts +338 -0
  369. package/src/drizzle/pg-proofs.ts +191 -0
  370. package/src/drizzle/pg-sessions.ts +750 -0
  371. package/src/drizzle/pg.ts +984 -0
  372. package/src/drizzle/phone-model.ts +190 -0
  373. package/src/drizzle/phone-state.ts +21 -0
  374. package/src/drizzle/phone-target.ts +472 -0
  375. package/src/drizzle/proof-model.ts +139 -0
  376. package/src/drizzle/proof-sql.ts +10 -0
  377. package/src/drizzle/proof-target.ts +122 -0
  378. package/src/drizzle/query-operations.ts +43 -0
  379. package/src/drizzle/session-model.ts +215 -0
  380. package/src/drizzle/session-sql.ts +14 -0
  381. package/src/drizzle/session-target.ts +338 -0
  382. package/src/drizzle/sqlite-emails.ts +366 -0
  383. package/src/drizzle/sqlite-identity.ts +344 -0
  384. package/src/drizzle/sqlite-password-prepared.ts +260 -0
  385. package/src/drizzle/sqlite-passwords.ts +497 -0
  386. package/src/drizzle/sqlite-proofs.ts +202 -0
  387. package/src/drizzle/sqlite-sessions.ts +971 -0
  388. package/src/drizzle/sqlite.ts +705 -0
  389. package/src/drizzle/standalone-guard.ts +32 -0
  390. package/src/drizzle/step-up-model.ts +73 -0
  391. package/src/drizzle/table-model.ts +9 -0
  392. package/src/drizzle/totp-model.ts +92 -0
  393. package/src/drizzle/totp-state.ts +525 -0
  394. package/src/drizzle/totp-target.ts +308 -0
  395. package/src/drizzle/transaction-execution.ts +13 -0
  396. package/src/drizzle/transaction-mysql.ts +106 -0
  397. package/src/drizzle/transaction-owner.ts +11 -0
  398. package/src/index.ts +378 -0
  399. package/src/internal/drizzle-migrations.ts +29 -0
  400. package/src/internal/drizzle-postgres.ts +83 -0
  401. package/src/internal/drizzle-sqlite.ts +81 -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
+ });