@yielded/auth-persistence-drizzle 0.1.0-beta.25 → 0.1.0-beta.26

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