@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,2354 +0,0 @@
1
- /* oxlint-disable no-explicit-any -- D1 planning bridges consumer Drizzle tables to Effect SQL statements. */
2
- import type { D1Client } from "@effect/sql-d1/D1Client";
3
- import { NativeDatabase } from "@yielded/auth-persistence/Adapter";
4
- import {
5
- coordinateCommit,
6
- CurrentCommitJournal,
7
- hasCommitScope,
8
- type CommitJournal,
9
- type PreparedCommit,
10
- LifecycleHooks,
11
- HookConfigurationError,
12
- } from "@yielded/auth/Hooks";
13
- import { reportPersistenceFailure } from "@yielded/auth/Persistence";
14
- import {
15
- type ProofCompletionPlan,
16
- ProofBinding,
17
- type ProofAttemptDecision,
18
- type ProofCleanupResult,
19
- type ProofCompletionDecision,
20
- type ProofDeliveryOutcome,
21
- type ProofVersion,
22
- type ProofBudget,
23
- type ProofPolicy,
24
- ProofPersistence,
25
- type PrepareProofCommit,
26
- type ProofDeliveryClaim,
27
- ProofRequestConflict,
28
- ProofUnavailable,
29
- } from "@yielded/auth/Proofs";
30
- import {
31
- and,
32
- count,
33
- eq,
34
- gte,
35
- inArray,
36
- isNull,
37
- lte,
38
- notExists,
39
- or,
40
- sql,
41
- type AnyRelations,
42
- type SQL,
43
- type Table,
44
- } from "drizzle-orm";
45
- import type { EffectDrizzleQueryError } from "drizzle-orm/effect-core";
46
- import type { EffectSQLiteD1Database } from "drizzle-orm/effect-d1";
47
- import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
48
- import { Cause, Effect, Schema, Context } from "effect";
49
- import type { Statement } from "effect/sql/Statement";
50
-
51
- import { Database as DatabaseService } from "./d1-database";
52
- import { balancedD1And } from "./d1-generated-statement";
53
- import { CurrentD1PlanningDatabase, makeD1Owner } from "./d1-planning";
54
- import { D1BatchStatements } from "./D1BatchStatements";
55
- import { column, PersistenceMappingError, isMappedConstraintConflict, updateValues } from "./model";
56
- import { nativeDatabase } from "./native-database";
57
- import {
58
- type D1ProofPersistenceMapping,
59
- requiredProofConstraints,
60
- type ProofAction,
61
- type ProofAuthorityInput,
62
- type ProofCommandDecision,
63
- type ProofContinuationRecord,
64
- type ProofPersistenceMapping,
65
- type ProofScopeKeys,
66
- type ProofScopeKind,
67
- } from "./proof-model";
68
- import { validateDrizzleStorage } from "./storage-validation";
69
-
70
- type PlanPrepare<Method extends (...args: any[]) => any, A> = (
71
- value: Parameters<Parameters<Method>[1]>[0],
72
- journal: CommitJournal,
73
- ) => PreparedCommit<A>;
74
-
75
- type Database = EffectSQLiteD1Database<AnyRelations> & { readonly $client: D1Client };
76
- type Mapping = D1ProofPersistenceMapping<
77
- Table,
78
- Table,
79
- Table,
80
- Table,
81
- Table,
82
- Table,
83
- Table,
84
- Table,
85
- Table,
86
- Table,
87
- Table,
88
- unknown
89
- >;
90
- interface Planned<A> {
91
- readonly receipt: A;
92
- readonly statements: ReadonlyArray<Statement<any>>;
93
- readonly retryable?: (cause: unknown) => boolean;
94
- readonly journalGuard?: PreparedCommit<void>;
95
- readonly postconditions?: ReadonlyArray<Statement<any>>;
96
- }
97
-
98
- const unavailable = () => ProofUnavailable.make({});
99
-
100
- const everyFailureMatches = (
101
- cause: Cause.Cause<unknown>,
102
- classify: ((cause: unknown) => boolean) | undefined,
103
- ): boolean =>
104
- classify !== undefined &&
105
- cause.reasons.length > 0 &&
106
- cause.reasons.every((reason) => Cause.isFailReason(reason) && classify(Cause.fail(reason.error)));
107
-
108
- const conflict = () => ProofRequestConflict.make({});
109
-
110
- const validConstraints = (mapping: Mapping) =>
111
- Object.entries(requiredProofConstraints).every(
112
- ([key, value]) => mapping.constraints[key as keyof typeof requiredProofConstraints] === value,
113
- );
114
-
115
- const canonicalBinding = Schema.encodeSync(Schema.fromJsonString(ProofBinding));
116
-
117
- const sameBinding = (left: ProofBinding, right: ProofBinding) =>
118
- canonicalBinding(left) === canonicalBinding(right);
119
-
120
- const containsFailure = (failure: unknown, predicate: (value: unknown) => boolean): boolean => {
121
- const seen = new Set<unknown>();
122
- const pending: Array<unknown> = [failure];
123
-
124
- for (let inspected = 0; inspected < 32 && pending.length > 0; inspected++) {
125
- const current = pending.shift();
126
-
127
- if (current === undefined || current === null || seen.has(current)) continue;
128
- seen.add(current);
129
- if (predicate(current)) return true;
130
- if (Cause.isCause(current)) {
131
- for (const reason of current.reasons) {
132
- if (Cause.isFailReason(reason)) pending.push(reason.error);
133
- else if (Cause.isDieReason(reason)) pending.push(reason.defect);
134
- }
135
- continue;
136
- }
137
- if (typeof current !== "object") continue;
138
-
139
- const wrapped = current as {
140
- readonly cause?: unknown;
141
- readonly reason?: unknown;
142
- readonly message?: unknown;
143
- };
144
-
145
- if (wrapped.cause !== undefined) pending.push(wrapped.cause);
146
- if (wrapped.reason !== undefined) pending.push(wrapped.reason);
147
- if (wrapped.message !== undefined) pending.push(wrapped.message);
148
- }
149
-
150
- return false;
151
- };
152
-
153
- // Match only the native workerd/D1 SQLite diagnostic. Query wrappers may echo
154
- // marker parameters, which is not evidence that the guard ran and rolled back.
155
- const isGuardFailure = (cause: unknown, marker: string) => {
156
- const path = `$[${marker.replaceAll("'", "''")}]`;
157
- const nativeMessage = `bad JSON path: '${path}': SQLITE_ERROR`;
158
-
159
- return containsFailure(
160
- cause,
161
- (value) => value === nativeMessage || value === `D1_ERROR: ${nativeMessage}`,
162
- );
163
- };
164
-
165
- type AdapterFailure = PersistenceMappingError | EffectDrizzleQueryError;
166
-
167
- const statement = Effect.fn(function* (query: {
168
- readonly toSQL: () => { readonly sql: string; readonly params: unknown[] };
169
- }) {
170
- const database = yield* CurrentD1PlanningDatabase;
171
- const rendered = query.toSQL();
172
-
173
- return database.$client.unsafe(rendered.sql, rendered.params);
174
- });
175
-
176
- const readEngineNowMillis = Effect.fn("Drizzle.readEngineNowMillis")(function* (mapping: Mapping) {
177
- const database = yield* CurrentD1PlanningDatabase;
178
-
179
- return yield* database
180
- .select({ value: mapping.d1.engineNowMillis.as("engine_now_millis") })
181
- .from(sql`(select 1)`)
182
- .pipe(
183
- Effect.flatMap((rows) => {
184
- const value = Number(rows[0]?.value);
185
-
186
- return Number.isFinite(value) ? Effect.succeed(value) : Effect.fail(unavailable());
187
- }),
188
- );
189
- });
190
-
191
- const driverValues = (values: object, entries: ReadonlyArray<readonly [string, unknown]>) => ({
192
- ...values,
193
- ...Object.fromEntries(entries),
194
- });
195
-
196
- const assertion = Effect.fn(function* (condition: SQL, marker: string) {
197
- const database = yield* CurrentD1PlanningDatabase;
198
-
199
- return yield* statement(
200
- database
201
- .select({
202
- ok: sql`case when ${condition} then 1 else json_extract('[]', ${`$[${marker}]`}) end`.as(
203
- "ok",
204
- ),
205
- })
206
- .from(sql`(select 1)`),
207
- );
208
- });
209
-
210
- const allocateVersion = (
211
- mapping: Mapping,
212
- ): Effect.Effect<ProofVersion, ProofUnavailable | PersistenceMappingError> =>
213
- mapping.allocateVersion !== undefined
214
- ? mapping.allocateVersion
215
- : mapping.allocateVersionSync === undefined
216
- ? Effect.fail(unavailable())
217
- : Effect.try({
218
- try: mapping.allocateVersionSync,
219
- catch: (cause) => new PersistenceMappingError({ operation: "mapping", cause }),
220
- });
221
-
222
- const requestColumns = (mapping: Mapping) => ({
223
- moduleId: column(mapping.request.table, mapping.request.moduleId),
224
- requestId: column(mapping.request.table, mapping.request.requestId),
225
- fingerprint: column(mapping.request.table, mapping.request.fingerprint),
226
- proofId: column(mapping.request.table, mapping.request.proofId),
227
- retentionUntil: column(mapping.request.table, mapping.request.retentionUntil),
228
- });
229
-
230
- const seriesColumns = (mapping: Mapping) => ({
231
- moduleId: column(mapping.series.table, mapping.series.moduleId),
232
- purpose: column(mapping.series.table, mapping.series.purpose),
233
- scopeKey: column(mapping.series.table, mapping.series.scopeKey),
234
- activeProofId: column(mapping.series.table, mapping.series.activeProofId),
235
- lastIssueAt: column(mapping.series.table, mapping.series.lastIssueAt),
236
- version: column(mapping.series.table, mapping.series.version),
237
- });
238
-
239
- const generationColumns = (mapping: Mapping) => ({
240
- moduleId: column(mapping.generation.table, mapping.generation.moduleId),
241
- purpose: column(mapping.generation.table, mapping.generation.purpose),
242
- proofId: column(mapping.generation.table, mapping.generation.proofId),
243
- seriesKey: column(mapping.generation.table, mapping.generation.seriesKey),
244
- deliveryId: column(mapping.generation.table, mapping.generation.deliveryId),
245
- verifierKeyId: column(mapping.generation.table, mapping.generation.verifierKeyId),
246
- verifierDigest: column(mapping.generation.table, mapping.generation.verifierDigest),
247
- expiresAt: column(mapping.generation.table, mapping.generation.expiresAt),
248
- version: column(mapping.generation.table, mapping.generation.version),
249
- state: column(mapping.generation.table, mapping.generation.state),
250
- sendCount: column(mapping.generation.table, mapping.generation.sendCount),
251
- deliveryState: column(mapping.generation.table, mapping.generation.deliveryState),
252
- claimVersion: column(mapping.generation.table, mapping.generation.claimVersion),
253
- claimDeadline: column(mapping.generation.table, mapping.generation.claimDeadline),
254
- retryAt: column(mapping.generation.table, mapping.generation.retryAt),
255
- deliveryRetryMillis: column(mapping.generation.table, mapping.generation.deliveryRetryMillis),
256
- retentionUntil: column(mapping.generation.table, mapping.generation.retentionUntil),
257
- });
258
-
259
- const continuationColumns = (mapping: Mapping) => ({
260
- moduleId: column(mapping.continuation.table, mapping.continuation.moduleId),
261
- continuationId: column(mapping.continuation.table, mapping.continuation.continuationId),
262
- digest: column(mapping.continuation.table, mapping.continuation.digest),
263
- consumed: column(mapping.continuation.table, mapping.continuation.consumed),
264
- retentionUntil: column(mapping.continuation.table, mapping.continuation.retentionUntil),
265
- });
266
-
267
- const scopeColumns = (mapping: Mapping) => ({
268
- moduleId: column(mapping.rateScope.table, mapping.rateScope.moduleId),
269
- purpose: column(mapping.rateScope.table, mapping.rateScope.purpose),
270
- action: column(mapping.rateScope.table, mapping.rateScope.action),
271
- scopeKind: column(mapping.rateScope.table, mapping.rateScope.scopeKind),
272
- scopeKey: column(mapping.rateScope.table, mapping.rateScope.scopeKey),
273
- });
274
-
275
- const abuseColumns = (mapping: Mapping) => ({
276
- moduleId: column(mapping.abuseEvent.table, mapping.abuseEvent.moduleId),
277
- purpose: column(mapping.abuseEvent.table, mapping.abuseEvent.purpose),
278
- action: column(mapping.abuseEvent.table, mapping.abuseEvent.action),
279
- scopeKind: column(mapping.abuseEvent.table, mapping.abuseEvent.scopeKind),
280
- scopeKey: column(mapping.abuseEvent.table, mapping.abuseEvent.scopeKey),
281
- commandId: column(mapping.abuseEvent.table, mapping.abuseEvent.commandId),
282
- occurredAt: column(mapping.abuseEvent.table, mapping.abuseEvent.occurredAt),
283
- retentionUntil: column(mapping.abuseEvent.table, mapping.abuseEvent.retentionUntil),
284
- });
285
-
286
- const failureColumns = (mapping: Mapping) => ({
287
- moduleId: column(mapping.failureEvent.table, mapping.failureEvent.moduleId),
288
- purpose: column(mapping.failureEvent.table, mapping.failureEvent.purpose),
289
- seriesKey: column(mapping.failureEvent.table, mapping.failureEvent.seriesKey),
290
- commandId: column(mapping.failureEvent.table, mapping.failureEvent.commandId),
291
- occurredAt: column(mapping.failureEvent.table, mapping.failureEvent.occurredAt),
292
- retentionUntil: column(mapping.failureEvent.table, mapping.failureEvent.retentionUntil),
293
- });
294
-
295
- const commandColumns = (mapping: Mapping) => ({
296
- moduleId: column(mapping.command.table, mapping.command.moduleId),
297
- commandId: column(mapping.command.table, mapping.command.commandId),
298
- decision: column(mapping.command.table, mapping.command.decision),
299
- retentionUntil: column(mapping.command.table, mapping.command.retentionUntil),
300
- });
301
-
302
- interface ScopeEntry {
303
- readonly kind: ProofScopeKind;
304
- readonly key: string;
305
- readonly budget: ProofBudget;
306
- }
307
-
308
- const scopeEntries = (
309
- keys: ProofScopeKeys,
310
- binding: ProofBinding,
311
- action: ProofAction,
312
- policy: ProofPolicy,
313
- ) => {
314
- const suffix = action === "issue" ? "Issues" : "Attempts";
315
- const simple = action === "issue" ? "issues" : "attempts";
316
-
317
- const entries: ScopeEntry[] = [
318
- { kind: "action", key: "*", budget: policy.abuse[`action${suffix}`] },
319
- { kind: "identifier", key: keys.identifier, budget: policy.abuse[simple] },
320
- ];
321
-
322
- if (binding._tag !== "Identifier")
323
- entries.push({ kind: "subject", key: keys.subject, budget: policy.abuse[`subject${suffix}`] });
324
-
325
- return entries;
326
- };
327
-
328
- const scopeCondition = (
329
- mapping: Mapping,
330
- moduleId: string,
331
- purpose: any,
332
- action: ProofAction,
333
- entry: ScopeEntry,
334
- ) => {
335
- const c = abuseColumns(mapping);
336
-
337
- return sql`(select count(*) from ${mapping.abuseEvent.table} where ${c.moduleId} = ${sql.param(moduleId, c.moduleId)} and ${c.purpose} = ${sql.param(purpose, c.purpose)} and ${c.action} = ${sql.param(action, c.action)} and ${c.scopeKind} = ${sql.param(entry.kind, c.scopeKind)} and ${c.scopeKey} = ${sql.param(entry.key, c.scopeKey)} and ${c.occurredAt} >= ${mapping.d1.engineInstantMinus(entry.budget.windowMillis)}) < ${entry.budget.limit}`;
338
- };
339
-
340
- const failureCondition = (
341
- mapping: Mapping,
342
- moduleId: string,
343
- purpose: any,
344
- seriesKey: string,
345
- policy: ProofPolicy,
346
- ) => {
347
- const c = failureColumns(mapping);
348
-
349
- return sql`(select count(*) from ${mapping.failureEvent.table} where ${c.moduleId} = ${sql.param(moduleId, c.moduleId)} and ${c.purpose} = ${sql.param(purpose, c.purpose)} and ${c.seriesKey} = ${sql.param(seriesKey, c.seriesKey)} and ${c.occurredAt} >= ${mapping.d1.engineInstantMinus(policy.abuse.attempts.windowMillis)}) < ${policy.maximumFailedAttempts}`;
350
- };
351
-
352
- const readScopeAdmission = Effect.fn("DrizzleD1Proof.readScopeAdmission")(function* (
353
- mapping: Mapping,
354
- moduleId: string,
355
- purpose: any,
356
- action: ProofAction,
357
- entries: ReadonlyArray<ScopeEntry>,
358
- now: number,
359
- ) {
360
- const database = yield* CurrentD1PlanningDatabase;
361
-
362
- const c = abuseColumns(mapping);
363
- const admitted: ScopeEntry[] = [];
364
-
365
- for (const entry of entries) {
366
- const rows = yield* database
367
- .select({ total: count() })
368
- .from(mapping.abuseEvent.table)
369
- .where(
370
- and(
371
- eq(c.moduleId, moduleId),
372
- eq(c.purpose, purpose),
373
- eq(c.action, action),
374
- eq(c.scopeKind, entry.kind),
375
- eq(c.scopeKey, entry.key),
376
- gte(c.occurredAt, mapping.encodeInstant(now - entry.budget.windowMillis)),
377
- ),
378
- );
379
-
380
- if (Number(rows[0]?.total ?? 0) < entry.budget.limit) admitted.push(entry);
381
- }
382
-
383
- return admitted;
384
- });
385
-
386
- const authority = Effect.fn("DrizzleD1Proof.authority")(function* (
387
- mapping: Mapping,
388
- moduleId: string,
389
- purpose: any,
390
- binding: ProofBinding,
391
- ) {
392
- const database = yield* CurrentD1PlanningDatabase;
393
-
394
- let nativeSubjectId: unknown | undefined;
395
- let subjectCurrent = true;
396
- const parts: SQL[] = [];
397
-
398
- if (binding._tag !== "Identifier") {
399
- if (
400
- mapping.authority.subject === undefined ||
401
- mapping.authority.credential === undefined ||
402
- mapping.authority.subjectId === undefined
403
- )
404
- return yield* unavailable();
405
- nativeSubjectId = yield* mapping.authority.subjectId.toNative(binding.revision.subjectId);
406
- const subject = mapping.authority.subject;
407
- const id = column(subject.table, subject.id);
408
- const status = column(subject.table, subject.status);
409
- const revision = column(subject.table, subject.securityRevision);
410
-
411
- const subjectRows = yield* database
412
- .select()
413
- .from(subject.table)
414
- .where(eq(id, nativeSubjectId))
415
- .limit(1);
416
-
417
- const subjectRow = subjectRows[0];
418
-
419
- subjectCurrent =
420
- subjectRow !== undefined &&
421
- subject.isActiveStatus(subjectRow[subject.status]) &&
422
- subjectRow[subject.securityRevision] === binding.revision.securityRevision;
423
- parts.push(
424
- sql`exists(select 1 from ${subject.table} where ${id} = ${sql.param(nativeSubjectId, id)} and ${status} = ${sql.param(subject.d1ActiveStatusValue, status)} and ${revision} = ${sql.param(binding.revision.securityRevision, revision)})`,
425
- );
426
- }
427
- const identifier = mapping.authority.identifier;
428
- const namespace = column(identifier.table, identifier.namespace);
429
- const value = column(identifier.table, identifier.value);
430
-
431
- const identifierRows = yield* database
432
- .select()
433
- .from(identifier.table)
434
- .where(and(eq(namespace, binding.identifier.namespace), eq(value, binding.identifier.value)));
435
-
436
- const input: ProofAuthorityInput<unknown> = {
437
- moduleId,
438
- purpose,
439
- binding,
440
- ...(nativeSubjectId === undefined ? {} : { nativeSubjectId }),
441
- };
442
-
443
- const identifierCurrent = identifier.isCurrent(input, identifierRows);
444
-
445
- parts.push(identifier.d1CurrentCondition(input));
446
- let credentialsCurrent = true;
447
-
448
- if (binding._tag !== "Identifier") {
449
- const credential = mapping.authority.credential!;
450
- const owner = column(credential.table, credential.subjectId);
451
- const id = column(credential.table, credential.credentialId);
452
- const revision = column(credential.table, credential.revision);
453
-
454
- const status =
455
- credential.status === undefined ? undefined : column(credential.table, credential.status);
456
-
457
- const expected = [...binding.revision.credentials].sort((a, b) =>
458
- a.credentialId.localeCompare(b.credentialId),
459
- );
460
-
461
- const rows =
462
- expected.length === 0
463
- ? []
464
- : yield* database
465
- .select()
466
- .from(credential.table)
467
- .where(
468
- and(
469
- eq(owner, nativeSubjectId),
470
- inArray(
471
- id,
472
- expected.map((item) => item.credentialId),
473
- ),
474
- ),
475
- );
476
-
477
- const actual = rows
478
- .map((row: any) => ({
479
- id: row[credential.credentialId],
480
- revision: row[credential.revision],
481
- active:
482
- credential.status === undefined ||
483
- credential.isActiveStatus?.(row[credential.status]) === true,
484
- }))
485
- .sort((a: any, b: any) => String(a.id).localeCompare(String(b.id)));
486
-
487
- credentialsCurrent =
488
- actual.length === expected.length &&
489
- actual.every(
490
- (row: any, index: number) =>
491
- row.active &&
492
- row.id === expected[index]?.credentialId &&
493
- row.revision === expected[index]?.revision,
494
- );
495
- for (const expectedCredential of expected)
496
- parts.push(
497
- sql`exists(select 1 from ${credential.table} where ${owner} = ${sql.param(nativeSubjectId, owner)} and ${id} = ${sql.param(expectedCredential.credentialId, id)} and ${revision} = ${sql.param(expectedCredential.revision, revision)}${status === undefined ? sql`` : sql` and ${status} = ${sql.param(credential.d1ActiveStatusValue, status)}`})`,
498
- );
499
- }
500
-
501
- return {
502
- current: subjectCurrent && identifierCurrent && credentialsCurrent,
503
- condition: balancedD1And(...parts) ?? sql`1 = 1`,
504
- };
505
- });
506
-
507
- const anchorStatements = Effect.fn(function* (
508
- mapping: Mapping,
509
- moduleId: string,
510
- purpose: any,
511
- action: ProofAction,
512
- entries: ReadonlyArray<ScopeEntry>,
513
- seriesKey: string,
514
- seriesVersion: ProofVersion,
515
- includeSeries: boolean,
516
- ) {
517
- const database = yield* CurrentD1PlanningDatabase;
518
-
519
- return [
520
- ...(yield* Effect.forEach(
521
- entries,
522
- Effect.fn(function* (entry) {
523
- return yield* statement(
524
- database
525
- .insert(mapping.rateScope.table)
526
- .values(
527
- mapping.rateScope.encodeInsert({
528
- moduleId,
529
- purpose,
530
- action,
531
- scopeKind: entry.kind,
532
- scopeKey: entry.key,
533
- }),
534
- )
535
- .onConflictDoNothing(),
536
- );
537
- }),
538
- )),
539
- ...(includeSeries
540
- ? [
541
- yield* statement(
542
- database
543
- .insert(mapping.series.table)
544
- .values(
545
- mapping.series.encodeInsert({
546
- moduleId,
547
- purpose,
548
- scopeKey: seriesKey,
549
- version: seriesVersion,
550
- }),
551
- )
552
- .onConflictDoNothing(),
553
- ),
554
- ]
555
- : []),
556
- ];
557
- });
558
-
559
- const insertScopeStatements = Effect.fn(function* (
560
- mapping: Mapping,
561
- moduleId: string,
562
- purpose: any,
563
- action: ProofAction,
564
- commandId: string,
565
- entries: ReadonlyArray<ScopeEntry>,
566
- now: number,
567
- retentionUntil: number,
568
- marker: string,
569
- ) {
570
- const database = yield* CurrentD1PlanningDatabase;
571
-
572
- return (yield* Effect.forEach(
573
- entries,
574
- Effect.fn(function* (entry) {
575
- return [
576
- yield* assertion(scopeCondition(mapping, moduleId, purpose, action, entry), marker),
577
- yield* statement(
578
- database.insert(mapping.abuseEvent.table).values(
579
- driverValues(
580
- mapping.abuseEvent.encodeInsert({
581
- moduleId,
582
- purpose,
583
- action,
584
- scopeKind: entry.kind,
585
- scopeKey: entry.key,
586
- commandId,
587
- occurredAtMillis: now,
588
- retentionUntilMillis: retentionUntil,
589
- }),
590
- [
591
- [mapping.abuseEvent.occurredAt, mapping.d1.engineNow],
592
- [
593
- mapping.abuseEvent.retentionUntil,
594
- mapping.d1.engineInstantPlus(retentionUntil - now),
595
- ],
596
- ],
597
- ),
598
- ),
599
- ),
600
- ];
601
- }),
602
- )).flat();
603
- });
604
-
605
- const readSeries = Effect.fn("Drizzle.readSeries")(function* (
606
- mapping: Mapping,
607
- moduleId: string,
608
- purpose: any,
609
- key: string,
610
- ) {
611
- const database = yield* CurrentD1PlanningDatabase;
612
-
613
- const c = seriesColumns(mapping);
614
-
615
- return yield* database
616
- .select()
617
- .from(mapping.series.table)
618
- .where(and(eq(c.moduleId, moduleId), eq(c.purpose, purpose), eq(c.scopeKey, key)))
619
- .limit(1);
620
- });
621
-
622
- const readGeneration = Effect.fn("Drizzle.readGeneration")(function* (
623
- mapping: Mapping,
624
- moduleId: string,
625
- proofId: string,
626
- ) {
627
- const database = yield* CurrentD1PlanningDatabase;
628
-
629
- const c = generationColumns(mapping);
630
-
631
- return yield* database
632
- .select()
633
- .from(mapping.generation.table)
634
- .where(and(eq(c.moduleId, moduleId), eq(c.proofId, proofId)))
635
- .limit(1);
636
- });
637
-
638
- const commandInsert = Effect.fn(function* (
639
- mapping: Mapping,
640
- moduleId: string,
641
- commandId: string,
642
- kind: "attempt" | "complete",
643
- decision: ProofCommandDecision,
644
- retentionUntil: number,
645
- nativeRetention?: SQL,
646
- ) {
647
- const database = yield* CurrentD1PlanningDatabase;
648
-
649
- return yield* statement(
650
- database.insert(mapping.command.table).values(
651
- driverValues(
652
- mapping.command.encodeInsert({
653
- moduleId,
654
- commandId,
655
- kind,
656
- decision,
657
- retentionUntilMillis: retentionUntil,
658
- }),
659
- nativeRetention === undefined ? [] : [[mapping.command.retentionUntil, nativeRetention]],
660
- ),
661
- ),
662
- );
663
- });
664
-
665
- const issuePlan = Effect.fn("Drizzle.issuePlan")(function* <A>(
666
- mapping: Mapping,
667
- input: Parameters<ProofPersistence["Service"]["issue"]>[0],
668
- prepare: PlanPrepare<ProofPersistence["Service"]["issue"], A>,
669
- ) {
670
- const database = yield* CurrentD1PlanningDatabase;
671
- const journal = yield* CurrentCommitJournal;
672
-
673
- const keys = mapping.scopeKeys({
674
- moduleId: input.record.moduleId,
675
- purpose: input.record.purpose,
676
- binding: input.record.binding,
677
- });
678
-
679
- const current = yield* authority(
680
- mapping,
681
- input.record.moduleId,
682
- input.record.purpose,
683
- input.record.binding,
684
- );
685
-
686
- const now = yield* readEngineNowMillis(mapping);
687
- const retentionUntil = now + input.policy.requestRetentionMillis;
688
- const rc = requestColumns(mapping);
689
-
690
- const existingRequest = (yield* database
691
- .select()
692
- .from(mapping.request.table)
693
- .where(and(eq(rc.moduleId, input.record.moduleId), eq(rc.requestId, input.record.requestId)))
694
- .limit(1))[0];
695
-
696
- if (existingRequest !== undefined) {
697
- const retained = yield* mapping.decodeInstant(existingRequest[mapping.request.retentionUntil]);
698
-
699
- if (retained > now) {
700
- if (existingRequest[mapping.request.fingerprint] !== input.record.fingerprint)
701
- return yield* conflict();
702
- const receipt = yield* mapping.request.decodeReceipt(existingRequest);
703
-
704
- return {
705
- receipt: prepare({ _tag: "Existing", receipt }, journal) as PreparedCommit<A>,
706
- statements: [],
707
- };
708
- }
709
- }
710
- const scopes = scopeEntries(keys, input.record.binding, "issue", input.policy);
711
-
712
- const initiallyAdmitted = yield* readScopeAdmission(
713
- mapping,
714
- input.record.moduleId,
715
- input.record.purpose,
716
- "issue",
717
- scopes,
718
- now,
719
- );
720
-
721
- const actionOpen = initiallyAdmitted.some((entry) => entry.kind === "action");
722
- const admitted = actionOpen ? initiallyAdmitted : [];
723
-
724
- const existingSeries = actionOpen
725
- ? (yield* readSeries(mapping, input.record.moduleId, input.record.purpose, keys.series))[0]
726
- : undefined;
727
-
728
- const seriesVersion = yield* allocateVersion(mapping);
729
- const active = existingSeries?.[mapping.series.activeProofId] as string | null | undefined;
730
-
731
- const activeGeneration =
732
- active === null || active === undefined
733
- ? undefined
734
- : (yield* readGeneration(mapping, input.record.moduleId, active))[0];
735
-
736
- if (
737
- active !== null &&
738
- active !== undefined &&
739
- (activeGeneration === undefined ||
740
- activeGeneration[mapping.generation.purpose] !== input.record.purpose)
741
- )
742
- return yield* unavailable();
743
-
744
- const matchesActiveBinding =
745
- activeGeneration !== undefined &&
746
- sameBinding(yield* mapping.generation.decodeBinding(activeGeneration), input.record.binding);
747
-
748
- const replacesLiveProof =
749
- activeGeneration !== undefined &&
750
- activeGeneration[mapping.generation.state] === "active" &&
751
- (yield* mapping.decodeInstant(activeGeneration[mapping.generation.expiresAt])) > now;
752
-
753
- const lastIssue =
754
- existingSeries?.[mapping.series.lastIssueAt] === null ||
755
- existingSeries?.[mapping.series.lastIssueAt] === undefined
756
- ? undefined
757
- : yield* mapping.decodeInstant(existingSeries[mapping.series.lastIssueAt]);
758
-
759
- const allowed =
760
- current.current &&
761
- input.eligible &&
762
- admitted.length === scopes.length &&
763
- input.record.issuedAtMillis <= now &&
764
- input.record.expiresAtMillis > now &&
765
- (!replacesLiveProof || matchesActiveBinding) &&
766
- (input.supersedes === undefined || input.supersedes === active) &&
767
- (lastIssue === undefined || now - lastIssue >= input.policy.abuse.resendCooldownMillis);
768
-
769
- const receipt = {
770
- requestId: input.record.requestId,
771
- reference: {
772
- proofId: input.record.proofId,
773
- purpose: input.record.purpose,
774
- keyId: input.record.verifier.keyId,
775
- },
776
- };
777
-
778
- const prepared = prepare(
779
- allowed ? { _tag: "Issued", record: input.record } : { _tag: "Suppressed", receipt },
780
- journal,
781
- );
782
-
783
- const statements: Statement<any>[] = [];
784
- const marker = `effect-auth-proof-guard:issue:${input.record.requestId}`;
785
-
786
- if (existingRequest !== undefined)
787
- statements.push(
788
- yield* statement(
789
- database
790
- .delete(mapping.request.table)
791
- .where(
792
- and(
793
- eq(rc.moduleId, input.record.moduleId),
794
- eq(rc.requestId, input.record.requestId),
795
- lte(rc.retentionUntil, mapping.d1.engineNow),
796
- ),
797
- ),
798
- ),
799
- );
800
- statements.push(
801
- yield* statement(
802
- database.insert(mapping.request.table).values(
803
- driverValues(
804
- mapping.request.encodeInsert({
805
- record: input.record,
806
- createdAtMillis: now,
807
- retentionUntilMillis: retentionUntil,
808
- }),
809
- [
810
- [mapping.request.createdAt, mapping.d1.engineNow],
811
- [
812
- mapping.request.retentionUntil,
813
- mapping.d1.engineInstantPlus(input.policy.requestRetentionMillis),
814
- ],
815
- ],
816
- ),
817
- ),
818
- ),
819
- ...(yield* anchorStatements(
820
- mapping,
821
- input.record.moduleId,
822
- input.record.purpose,
823
- "issue",
824
- actionOpen ? scopes : scopes.slice(0, 1),
825
- keys.series,
826
- seriesVersion,
827
- actionOpen,
828
- )),
829
- ...(yield* insertScopeStatements(
830
- mapping,
831
- input.record.moduleId,
832
- input.record.purpose,
833
- "issue",
834
- input.record.requestId,
835
- allowed ? scopes : [],
836
- now,
837
- retentionUntil,
838
- marker,
839
- )),
840
- );
841
- if (allowed) {
842
- const sc = seriesColumns(mapping);
843
- const gc = generationColumns(mapping);
844
-
845
- // Recheck replacement authority in the submitted batch. Generation bindings
846
- // are immutable; the version and stored binding also fence a changed row.
847
- const replacementCondition =
848
- activeGeneration === undefined
849
- ? sql`1 = 1`
850
- : sql`exists(select 1 from ${mapping.generation.table} where ${and(
851
- eq(gc.moduleId, input.record.moduleId),
852
- eq(gc.proofId, activeGeneration[mapping.generation.proofId]),
853
- eq(gc.version, activeGeneration[mapping.generation.version]),
854
- eq(
855
- column(mapping.generation.table, mapping.generation.binding),
856
- activeGeneration[mapping.generation.binding],
857
- ),
858
- matchesActiveBinding
859
- ? sql`1 = 1`
860
- : sql`(${gc.state} <> 'active' or ${gc.expiresAt} <= ${mapping.d1.engineNow})`,
861
- )})`;
862
-
863
- const seriesCondition = and(
864
- eq(sc.moduleId, input.record.moduleId),
865
- eq(sc.purpose, input.record.purpose),
866
- eq(sc.scopeKey, keys.series),
867
- active === null || active === undefined
868
- ? or(eq(sc.activeProofId, null), sql`${sc.activeProofId} is null`)
869
- : eq(sc.activeProofId, active),
870
- lastIssue === undefined
871
- ? sql`1 = 1`
872
- : lte(
873
- sc.lastIssueAt,
874
- mapping.d1.engineInstantMinus(input.policy.abuse.resendCooldownMillis),
875
- ),
876
- );
877
-
878
- statements.push(
879
- yield* assertion(
880
- and(
881
- current.condition,
882
- replacementCondition,
883
- sql`${mapping.d1.engineNowMillis} >= ${input.record.issuedAtMillis}`,
884
- sql`${mapping.d1.engineNow} < ${sql.param(mapping.encodeInstant(input.record.expiresAtMillis), generationColumns(mapping).expiresAt)}`,
885
- sql`exists(select 1 from ${mapping.series.table} where ${seriesCondition})`,
886
- )!,
887
- marker,
888
- ),
889
- );
890
- if (active !== null && active !== undefined)
891
- statements.push(
892
- yield* statement(
893
- database
894
- .update(mapping.generation.table)
895
- .set(updateValues([[mapping.generation.state, "superseded"]]))
896
- .where(
897
- and(
898
- eq(generationColumns(mapping).moduleId, input.record.moduleId),
899
- eq(generationColumns(mapping).proofId, active),
900
- eq(generationColumns(mapping).state, "active"),
901
- ),
902
- ),
903
- ),
904
- );
905
- statements.push(
906
- yield* statement(
907
- database.insert(mapping.generation.table).values(
908
- driverValues(
909
- mapping.generation.encodeInsert({
910
- record: input.record,
911
- seriesKey: keys.series,
912
- retentionUntilMillis: retentionUntil,
913
- state: "active",
914
- deliveryState: "new",
915
- policy: input.policy,
916
- }),
917
- [
918
- [
919
- mapping.generation.retentionUntil,
920
- mapping.d1.engineInstantPlus(input.policy.requestRetentionMillis),
921
- ],
922
- ],
923
- ),
924
- ),
925
- ),
926
- yield* statement(
927
- database
928
- .update(mapping.series.table)
929
- .set(
930
- updateValues([
931
- [mapping.series.activeProofId, input.record.proofId],
932
- [mapping.series.lastIssueAt, mapping.d1.engineNow],
933
- [mapping.series.version, seriesVersion],
934
- ]),
935
- )
936
- .where(
937
- and(
938
- eq(sc.moduleId, input.record.moduleId),
939
- eq(sc.purpose, input.record.purpose),
940
- eq(sc.scopeKey, keys.series),
941
- ),
942
- ),
943
- ),
944
- );
945
- }
946
-
947
- return {
948
- receipt: prepared as PreparedCommit<A>,
949
- statements,
950
- retryable: (cause: unknown) =>
951
- isMappedConstraintConflict(mapping.isRequestConflict, cause) ||
952
- isMappedConstraintConflict(mapping.isSeriesConflict, cause) ||
953
- isGuardFailure(cause, marker),
954
- };
955
- });
956
-
957
- const attemptPlan = Effect.fn("Drizzle.attemptPlan")(function* <A>(
958
- mapping: Mapping,
959
- input: Parameters<ProofPersistence["Service"]["attempt"]>[0],
960
- prepare: PlanPrepare<ProofPersistence["Service"]["attempt"], A>,
961
- ) {
962
- const database = yield* CurrentD1PlanningDatabase;
963
- const journal = yield* CurrentCommitJournal;
964
-
965
- const keys = mapping.scopeKeys({
966
- moduleId: input.moduleId,
967
- purpose: input.purpose,
968
- binding: input.binding,
969
- });
970
-
971
- const current = yield* authority(mapping, input.moduleId, input.purpose, input.binding);
972
- const command = commandColumns(mapping);
973
-
974
- const existingCommand = (yield* database
975
- .select()
976
- .from(mapping.command.table)
977
- .where(and(eq(command.moduleId, input.moduleId), eq(command.commandId, input.continuationId)))
978
- .limit(1))[0];
979
-
980
- if (existingCommand !== undefined) {
981
- if (existingCommand[mapping.command.decision] === "rejected")
982
- return {
983
- receipt: prepare({ _tag: "Rejected" }, journal) as PreparedCommit<A>,
984
- statements: [],
985
- };
986
-
987
- return yield* unavailable();
988
- }
989
- const now = yield* readEngineNowMillis(mapping);
990
- const scopes = scopeEntries(keys, input.binding, "attempt", input.policy);
991
-
992
- const initiallyAdmitted = yield* readScopeAdmission(
993
- mapping,
994
- input.moduleId,
995
- input.purpose,
996
- "attempt",
997
- scopes,
998
- now,
999
- );
1000
-
1001
- const actionOpen = initiallyAdmitted.some((entry) => entry.kind === "action");
1002
- const admitted = actionOpen ? initiallyAdmitted : [];
1003
-
1004
- const series = actionOpen
1005
- ? (yield* readSeries(mapping, input.moduleId, input.purpose, keys.series))[0]
1006
- : undefined;
1007
-
1008
- const generation = (yield* readGeneration(mapping, input.moduleId, input.proofId))[0];
1009
- const fc = failureColumns(mapping);
1010
-
1011
- const failureRows = yield* database
1012
- .select({ total: count() })
1013
- .from(mapping.failureEvent.table)
1014
- .where(
1015
- and(
1016
- eq(fc.moduleId, input.moduleId),
1017
- eq(fc.purpose, input.purpose),
1018
- eq(fc.seriesKey, keys.series),
1019
- gte(fc.occurredAt, mapping.encodeInstant(now - input.policy.abuse.attempts.windowMillis)),
1020
- ),
1021
- );
1022
-
1023
- const failures = Number(failureRows[0]?.total ?? 0);
1024
-
1025
- const storedBinding =
1026
- generation === undefined ? undefined : yield* mapping.generation.decodeBinding(generation);
1027
-
1028
- const expires =
1029
- generation === undefined
1030
- ? undefined
1031
- : yield* mapping.decodeInstant(generation[mapping.generation.expiresAt]);
1032
-
1033
- const currentGeneration =
1034
- generation !== undefined &&
1035
- generation[mapping.generation.purpose] === input.purpose &&
1036
- generation[mapping.generation.state] === "active" &&
1037
- series?.[mapping.series.activeProofId] === input.proofId &&
1038
- storedBinding !== undefined &&
1039
- sameBinding(storedBinding, input.binding) &&
1040
- expires !== undefined &&
1041
- expires > now;
1042
-
1043
- const candidateMatches =
1044
- generation !== undefined &&
1045
- input.candidate !== undefined &&
1046
- generation[mapping.generation.verifierKeyId] === input.candidate.keyId &&
1047
- generation[mapping.generation.verifierDigest] === input.candidate.digest;
1048
-
1049
- const accepted =
1050
- current.current &&
1051
- currentGeneration &&
1052
- candidateMatches &&
1053
- admitted.length === scopes.length &&
1054
- failures < input.policy.maximumFailedAttempts;
1055
-
1056
- const retentionUntil = now + input.policy.requestRetentionMillis;
1057
- const seriesVersion = yield* allocateVersion(mapping);
1058
-
1059
- const statements: Statement<any>[] = [
1060
- ...(yield* anchorStatements(
1061
- mapping,
1062
- input.moduleId,
1063
- input.purpose,
1064
- "attempt",
1065
- actionOpen ? scopes : scopes.slice(0, 1),
1066
- keys.series,
1067
- seriesVersion,
1068
- actionOpen,
1069
- )),
1070
- ...(yield* insertScopeStatements(
1071
- mapping,
1072
- input.moduleId,
1073
- input.purpose,
1074
- "attempt",
1075
- input.continuationId,
1076
- admitted,
1077
- now,
1078
- retentionUntil,
1079
- `effect-auth-proof-guard:attempt:${input.continuationId}`,
1080
- )),
1081
- ];
1082
-
1083
- const marker = `effect-auth-proof-guard:attempt:${input.continuationId}`;
1084
-
1085
- if (!accepted) {
1086
- const prepared = prepare({ _tag: "Rejected" }, journal) as PreparedCommit<A>;
1087
-
1088
- if (
1089
- admitted.length === scopes.length &&
1090
- current.current &&
1091
- currentGeneration &&
1092
- !candidateMatches &&
1093
- failures < input.policy.maximumFailedAttempts
1094
- ) {
1095
- const gc = generationColumns(mapping);
1096
- const sc = seriesColumns(mapping);
1097
-
1098
- statements.push(
1099
- yield* assertion(
1100
- and(
1101
- current.condition,
1102
- sql`exists(select 1 from ${mapping.series.table} where ${sc.moduleId} = ${sql.param(input.moduleId, sc.moduleId)} and ${sc.purpose} = ${sql.param(input.purpose, sc.purpose)} and ${sc.scopeKey} = ${sql.param(keys.series, sc.scopeKey)} and ${sc.activeProofId} = ${sql.param(input.proofId, sc.activeProofId)})`,
1103
- sql`exists(select 1 from ${mapping.generation.table} where ${gc.moduleId} = ${sql.param(input.moduleId, gc.moduleId)} and ${gc.proofId} = ${sql.param(input.proofId, gc.proofId)} and ${gc.state} = 'active' and ${gc.expiresAt} > ${mapping.d1.engineNow})`,
1104
- failureCondition(mapping, input.moduleId, input.purpose, keys.series, input.policy),
1105
- )!,
1106
- marker,
1107
- ),
1108
- yield* statement(
1109
- database.insert(mapping.failureEvent.table).values(
1110
- driverValues(
1111
- mapping.failureEvent.encodeInsert({
1112
- moduleId: input.moduleId,
1113
- purpose: input.purpose,
1114
- seriesKey: keys.series,
1115
- commandId: input.continuationId,
1116
- occurredAtMillis: now,
1117
- retentionUntilMillis: retentionUntil,
1118
- }),
1119
- [
1120
- [mapping.failureEvent.occurredAt, mapping.d1.engineNow],
1121
- [
1122
- mapping.failureEvent.retentionUntil,
1123
- mapping.d1.engineInstantPlus(input.policy.requestRetentionMillis),
1124
- ],
1125
- ],
1126
- ),
1127
- ),
1128
- ),
1129
- );
1130
- }
1131
- statements.push(
1132
- yield* commandInsert(
1133
- mapping,
1134
- input.moduleId,
1135
- input.continuationId,
1136
- "attempt",
1137
- "rejected",
1138
- retentionUntil,
1139
- mapping.d1.engineInstantPlus(input.policy.requestRetentionMillis),
1140
- ),
1141
- );
1142
-
1143
- return {
1144
- receipt: prepared,
1145
- statements,
1146
- retryable: (cause: unknown) =>
1147
- isMappedConstraintConflict(mapping.isCommandConflict, cause) ||
1148
- isMappedConstraintConflict(mapping.isSeriesConflict, cause) ||
1149
- isGuardFailure(cause, marker),
1150
- };
1151
- }
1152
- const version = yield* allocateVersion(mapping);
1153
- const continuationExpires = Math.min(expires!, now + input.policy.continuationLifetimeMillis);
1154
-
1155
- const continuation: ProofContinuationRecord = {
1156
- moduleId: input.moduleId,
1157
- purpose: input.purpose,
1158
- continuationId: input.continuationId,
1159
- digest: input.continuationDigest,
1160
- proofId: input.proofId,
1161
- seriesKey: keys.series,
1162
- binding: input.binding,
1163
- expiresAtMillis: continuationExpires,
1164
- version,
1165
- };
1166
-
1167
- const decision: ProofAttemptDecision = {
1168
- _tag: "Accepted",
1169
- continuation: {
1170
- continuationId: input.continuationId,
1171
- purpose: input.purpose,
1172
- expiresAtMillis: continuationExpires,
1173
- },
1174
- };
1175
-
1176
- const prepared = prepare(decision, journal) as PreparedCommit<A>;
1177
- const gc = generationColumns(mapping);
1178
- const sc = seriesColumns(mapping);
1179
-
1180
- statements.push(
1181
- yield* assertion(
1182
- and(
1183
- current.condition,
1184
- failureCondition(mapping, input.moduleId, input.purpose, keys.series, input.policy),
1185
- sql`exists(select 1 from ${mapping.series.table} where ${sc.moduleId} = ${sql.param(input.moduleId, sc.moduleId)} and ${sc.purpose} = ${sql.param(input.purpose, sc.purpose)} and ${sc.scopeKey} = ${sql.param(keys.series, sc.scopeKey)} and ${sc.activeProofId} = ${sql.param(input.proofId, sc.activeProofId)})`,
1186
- sql`exists(select 1 from ${mapping.generation.table} where ${gc.moduleId} = ${sql.param(input.moduleId, gc.moduleId)} and ${gc.proofId} = ${sql.param(input.proofId, gc.proofId)} and ${gc.purpose} = ${sql.param(input.purpose, gc.purpose)} and ${gc.state} = 'active' and ${gc.verifierKeyId} = ${sql.param(input.candidate!.keyId, gc.verifierKeyId)} and ${gc.verifierDigest} = ${sql.param(input.candidate!.digest, gc.verifierDigest)} and ${gc.expiresAt} > ${mapping.d1.engineNow})`,
1187
- )!,
1188
- marker,
1189
- ),
1190
- yield* commandInsert(
1191
- mapping,
1192
- input.moduleId,
1193
- input.continuationId,
1194
- "attempt",
1195
- "accepted",
1196
- retentionUntil,
1197
- mapping.d1.engineInstantPlus(input.policy.requestRetentionMillis),
1198
- ),
1199
- yield* statement(
1200
- database
1201
- .update(mapping.generation.table)
1202
- .set(updateValues([[mapping.generation.state, "consumed"]]))
1203
- .where(
1204
- and(
1205
- eq(gc.moduleId, input.moduleId),
1206
- eq(gc.proofId, input.proofId),
1207
- eq(gc.state, "active"),
1208
- ),
1209
- ),
1210
- ),
1211
- yield* statement(
1212
- database.insert(mapping.continuation.table).values(
1213
- driverValues(
1214
- mapping.continuation.encodeInsert({
1215
- ...continuation,
1216
- retentionUntilMillis: retentionUntil,
1217
- }),
1218
- [
1219
- [
1220
- mapping.continuation.retentionUntil,
1221
- mapping.d1.engineInstantPlus(input.policy.requestRetentionMillis),
1222
- ],
1223
- ],
1224
- ),
1225
- ),
1226
- ),
1227
- );
1228
-
1229
- return {
1230
- receipt: prepared,
1231
- statements,
1232
- retryable: (cause: unknown) =>
1233
- isMappedConstraintConflict(mapping.isCommandConflict, cause) ||
1234
- isMappedConstraintConflict(mapping.isSeriesConflict, cause) ||
1235
- isGuardFailure(cause, marker),
1236
- };
1237
- });
1238
-
1239
- const inspectD1Completion = Effect.fn("DrizzleD1Proof.inspectCompletion")(function* (
1240
- mapping: Mapping,
1241
- input: ProofCompletionPlan["input"],
1242
- ) {
1243
- const database = yield* CurrentD1PlanningDatabase;
1244
-
1245
- const keys = mapping.scopeKeys({
1246
- moduleId: input.moduleId,
1247
- purpose: input.purpose,
1248
- binding: input.binding,
1249
- });
1250
-
1251
- const current = yield* authority(mapping, input.moduleId, input.purpose, input.binding);
1252
- const c = continuationColumns(mapping);
1253
-
1254
- const row = (yield* database
1255
- .select()
1256
- .from(mapping.continuation.table)
1257
- .where(and(eq(c.moduleId, input.moduleId), eq(c.continuationId, input.continuationId)))
1258
- .limit(1))[0];
1259
-
1260
- const now = yield* readEngineNowMillis(mapping);
1261
- const record = row === undefined ? undefined : yield* mapping.continuation.decode(row);
1262
-
1263
- const accepted =
1264
- current.current &&
1265
- record !== undefined &&
1266
- record.moduleId === input.moduleId &&
1267
- record.continuationId === input.continuationId &&
1268
- record.purpose === input.purpose &&
1269
- record.digest === input.continuationDigest &&
1270
- record.seriesKey === keys.series &&
1271
- sameBinding(record.binding, input.binding) &&
1272
- row[mapping.continuation.consumed] === false &&
1273
- record.expiresAtMillis > now;
1274
-
1275
- const immutable = [
1276
- mapping.continuation.moduleId,
1277
- mapping.continuation.purpose,
1278
- mapping.continuation.continuationId,
1279
- mapping.continuation.digest,
1280
- mapping.continuation.proofId,
1281
- mapping.continuation.seriesKey,
1282
- mapping.continuation.binding,
1283
- mapping.continuation.expiresAt,
1284
- mapping.continuation.version,
1285
- mapping.continuation.retentionUntil,
1286
- ];
1287
-
1288
- const exact =
1289
- row === undefined
1290
- ? sql`false`
1291
- : and(...immutable.map((key) => eq(column(mapping.continuation.table, key), row[key])))!;
1292
-
1293
- return {
1294
- accepted,
1295
- record,
1296
- row,
1297
- columns: c,
1298
- condition: and(
1299
- current.condition,
1300
- sql`exists(select 1 from ${mapping.continuation.table} where ${exact} and ${c.consumed} = ${sql.param(false, c.consumed)} and ${column(mapping.continuation.table, mapping.continuation.expiresAt)} > ${mapping.d1.engineNow})`,
1301
- )!,
1302
- appliedCondition:
1303
- row === undefined
1304
- ? sql`false`
1305
- : sql`exists(select 1 from ${mapping.continuation.table} where ${exact} and ${c.consumed} = ${sql.param(true, c.consumed)} and ${column(mapping.continuation.table, mapping.continuation.expiresAt)} > ${mapping.d1.engineNow})`,
1306
- };
1307
- });
1308
-
1309
- /** Read-only preflight plus its exact final-engine-time batch predicate. */
1310
- export const checkD1ProofCompletion = Effect.fn("Drizzle.checkD1ProofCompletion")(function* (
1311
- mapping: Mapping,
1312
- input: ProofCompletionPlan["input"],
1313
- ) {
1314
- return yield* inspectD1Completion(mapping, input);
1315
- });
1316
-
1317
- const completionPlan = Effect.fn("Drizzle.completionPlan")(function* <A>(
1318
- mapping: Mapping,
1319
- input: Parameters<ProofPersistence["Service"]["complete"]>[0],
1320
- prepare: PrepareProofCommit<ProofCompletionDecision, A>,
1321
- ) {
1322
- const database = yield* CurrentD1PlanningDatabase;
1323
- const journal = yield* CurrentCommitJournal;
1324
-
1325
- const inspected = yield* inspectD1Completion(mapping, input);
1326
- const { accepted, record, columns: c } = inspected;
1327
- const decision: ProofCompletionDecision = accepted ? "completed" : "rejected";
1328
- const receipt = prepare(decision, journal) as PreparedCommit<A>;
1329
-
1330
- if (!accepted) return { receipt, statements: [] };
1331
-
1332
- const postconditions = [
1333
- yield* assertion(
1334
- and(
1335
- inspected.appliedCondition,
1336
- sql`exists(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.continuationDigest, column(mapping.command.table, mapping.command.commandId))} and ${column(mapping.command.table, mapping.command.kind)} = ${sql.param("complete", column(mapping.command.table, mapping.command.kind))} and ${column(mapping.command.table, mapping.command.decision)} = ${sql.param("completed", column(mapping.command.table, mapping.command.decision))} and ${column(mapping.command.table, mapping.command.retentionUntil)} = ${sql.param(mapping.encodeInstant(record!.expiresAtMillis), column(mapping.command.table, mapping.command.retentionUntil))})`,
1337
- )!,
1338
- `effect-auth-proof-guard:complete:${input.continuationId}`,
1339
- ),
1340
- ];
1341
-
1342
- const statements = [
1343
- yield* assertion(
1344
- inspected.condition,
1345
- `effect-auth-proof-guard:complete:${input.continuationId}`,
1346
- ),
1347
- yield* statement(
1348
- database
1349
- .update(mapping.continuation.table)
1350
- .set(updateValues([[mapping.continuation.consumed, true]]))
1351
- .where(
1352
- and(
1353
- eq(c.moduleId, input.moduleId),
1354
- eq(c.continuationId, input.continuationId),
1355
- eq(c.digest, input.continuationDigest),
1356
- eq(c.consumed, false),
1357
- ),
1358
- ),
1359
- ),
1360
- yield* commandInsert(
1361
- mapping,
1362
- input.moduleId,
1363
- input.continuationDigest,
1364
- "complete",
1365
- "completed",
1366
- record!.expiresAtMillis,
1367
- ),
1368
- ...postconditions,
1369
- ];
1370
-
1371
- return {
1372
- receipt,
1373
- statements,
1374
- postconditions,
1375
- retryable: (cause: unknown) =>
1376
- isMappedConstraintConflict(mapping.isCommandConflict, cause) ||
1377
- isGuardFailure(cause, `effect-auth-proof-guard:complete:${input.continuationId}`),
1378
- };
1379
- });
1380
-
1381
- const makeProofPlans = (mapping: Mapping) => {
1382
- const run = <A, E, R>(plan: Effect.Effect<Planned<A>, E, R>) =>
1383
- Effect.gen(function* () {
1384
- if (!validConstraints(mapping)) return yield* unavailable();
1385
-
1386
- return yield* plan;
1387
- });
1388
-
1389
- return {
1390
- issue: <A>(
1391
- input: Parameters<ProofPersistence["Service"]["issue"]>[0],
1392
- prepare: PlanPrepare<ProofPersistence["Service"]["issue"], A>,
1393
- ) =>
1394
- run(
1395
- Effect.gen(function* () {
1396
- return yield* issuePlan(mapping, input, prepare);
1397
- }),
1398
- ),
1399
- attempt: <A>(
1400
- input: Parameters<ProofPersistence["Service"]["attempt"]>[0],
1401
- prepare: PlanPrepare<ProofPersistence["Service"]["attempt"], A>,
1402
- ) =>
1403
- run(
1404
- Effect.gen(function* () {
1405
- return yield* attemptPlan(mapping, input, prepare);
1406
- }),
1407
- ),
1408
- complete: <A>(
1409
- input: Parameters<ProofPersistence["Service"]["complete"]>[0],
1410
- prepare: PlanPrepare<ProofPersistence["Service"]["complete"], A>,
1411
- ) =>
1412
- run(
1413
- Effect.gen(function* () {
1414
- return yield* completionPlan(mapping, input, prepare);
1415
- }),
1416
- ),
1417
- claimDelivery: <A>(
1418
- input: Parameters<ProofPersistence["Service"]["claimDelivery"]>[0],
1419
- prepare: PlanPrepare<ProofPersistence["Service"]["claimDelivery"], A>,
1420
- ) =>
1421
- Effect.gen(function* () {
1422
- const database = yield* CurrentD1PlanningDatabase;
1423
-
1424
- return yield* run(
1425
- Effect.gen(function* () {
1426
- const journal = yield* CurrentCommitJournal;
1427
-
1428
- const row = (yield* readGeneration(mapping, input.moduleId, input.proofId))[0];
1429
- const now = yield* readEngineNowMillis(mapping);
1430
- const c = generationColumns(mapping);
1431
-
1432
- if (row === undefined)
1433
- return { receipt: prepare({ _tag: "Declined" }, journal), statements: [] };
1434
- const expires = yield* mapping.decodeInstant(row[mapping.generation.expiresAt]);
1435
-
1436
- const claimDeadline =
1437
- row[mapping.generation.claimDeadline] === null ||
1438
- row[mapping.generation.claimDeadline] === undefined
1439
- ? undefined
1440
- : yield* mapping.decodeInstant(row[mapping.generation.claimDeadline]);
1441
-
1442
- const retryAt =
1443
- row[mapping.generation.retryAt] === null ||
1444
- row[mapping.generation.retryAt] === undefined
1445
- ? undefined
1446
- : yield* mapping.decodeInstant(row[mapping.generation.retryAt]);
1447
-
1448
- const state = row[mapping.generation.deliveryState] as string;
1449
-
1450
- const expiredClaim =
1451
- state === "claimed" && claimDeadline !== undefined && claimDeadline <= now;
1452
-
1453
- const effectiveState = expiredClaim ? "ambiguous" : state;
1454
-
1455
- const canClaim =
1456
- row[mapping.generation.version] === input.version &&
1457
- row[mapping.generation.deliveryId] === input.deliveryId &&
1458
- row[mapping.generation.state] === "active" &&
1459
- expires > now &&
1460
- Number(row[mapping.generation.sendCount]) < input.policy.maximumDeliveryAttempts &&
1461
- (effectiveState === "new" ||
1462
- (effectiveState === "ambiguous" &&
1463
- input.allowAmbiguousRetry &&
1464
- (retryAt === undefined || retryAt <= now)));
1465
-
1466
- if (!canClaim) {
1467
- const statements = expiredClaim
1468
- ? [
1469
- yield* statement(
1470
- database
1471
- .update(mapping.generation.table)
1472
- .set(updateValues([[mapping.generation.deliveryState, "ambiguous"]]))
1473
- .where(
1474
- and(
1475
- eq(c.moduleId, input.moduleId),
1476
- eq(c.proofId, input.proofId),
1477
- eq(c.deliveryState, "claimed"),
1478
- lte(c.claimDeadline, mapping.d1.engineNow),
1479
- ),
1480
- ),
1481
- ),
1482
- ]
1483
- : [];
1484
-
1485
- return { receipt: prepare({ _tag: "Declined" }, journal), statements };
1486
- }
1487
- const claimVersion = yield* allocateVersion(mapping);
1488
- const decision: ProofDeliveryClaim = { _tag: "Claimed", claimVersion };
1489
-
1490
- const condition = and(
1491
- eq(c.moduleId, input.moduleId),
1492
- eq(c.proofId, input.proofId),
1493
- eq(c.version, input.version),
1494
- eq(c.deliveryId, input.deliveryId),
1495
- eq(c.state, "active"),
1496
- sql`${c.expiresAt} > ${mapping.d1.engineNow}`,
1497
- sql`${c.sendCount} < ${input.policy.maximumDeliveryAttempts}`,
1498
- effectiveState === "new"
1499
- ? eq(c.deliveryState, "new")
1500
- : and(
1501
- eq(c.deliveryState, "ambiguous"),
1502
- or(sql`${c.retryAt} is null`, lte(c.retryAt, mapping.d1.engineNow)),
1503
- ),
1504
- )!;
1505
-
1506
- return {
1507
- receipt: prepare(decision, journal),
1508
- statements: [
1509
- yield* assertion(
1510
- sql`exists(select 1 from ${mapping.generation.table} where ${condition})`,
1511
- `effect-auth-proof-guard:claim:${input.proofId}`,
1512
- ),
1513
- yield* statement(
1514
- database
1515
- .update(mapping.generation.table)
1516
- .set(
1517
- updateValues([
1518
- [
1519
- mapping.generation.sendCount,
1520
- Number(row[mapping.generation.sendCount]) + 1,
1521
- ],
1522
- [mapping.generation.deliveryState, "claimed"],
1523
- [mapping.generation.claimVersion, claimVersion],
1524
- [
1525
- mapping.generation.claimDeadline,
1526
- mapping.d1.engineInstantPlus(input.policy.deliveryClaimMillis),
1527
- ],
1528
- [
1529
- mapping.generation.retryAt,
1530
- mapping.d1.engineInstantPlus(input.policy.deliveryRetryMillis),
1531
- ],
1532
- ]),
1533
- )
1534
- .where(condition),
1535
- ),
1536
- ],
1537
- retryable: (cause: unknown) =>
1538
- isGuardFailure(cause, `effect-auth-proof-guard:claim:${input.proofId}`),
1539
- };
1540
- }),
1541
- );
1542
- }),
1543
- settleDelivery: <A>(
1544
- input: Parameters<ProofPersistence["Service"]["settleDelivery"]>[0],
1545
- prepare: PlanPrepare<ProofPersistence["Service"]["settleDelivery"], A>,
1546
- ) =>
1547
- Effect.gen(function* () {
1548
- const database = yield* CurrentD1PlanningDatabase;
1549
-
1550
- return yield* run(
1551
- Effect.gen(function* () {
1552
- const journal = yield* CurrentCommitJournal;
1553
-
1554
- const row = (yield* readGeneration(mapping, input.moduleId, input.proofId))[0];
1555
- const receipt = prepare(undefined, journal);
1556
-
1557
- if (
1558
- row === undefined ||
1559
- row[mapping.generation.version] !== input.version ||
1560
- row[mapping.generation.deliveryId] !== input.deliveryId ||
1561
- row[mapping.generation.claimVersion] !== input.claimVersion ||
1562
- row[mapping.generation.deliveryState] !== "claimed"
1563
- )
1564
- return { receipt, statements: [] };
1565
- const c = generationColumns(mapping);
1566
- const outcome: ProofDeliveryOutcome = input.outcome;
1567
-
1568
- const state =
1569
- outcome._tag === "Accepted"
1570
- ? "accepted"
1571
- : outcome._tag === "DefiniteFailure"
1572
- ? "failed"
1573
- : "ambiguous";
1574
-
1575
- const values: Array<readonly [string, unknown]> = [
1576
- [mapping.generation.deliveryState, state],
1577
- [mapping.generation.claimDeadline, null],
1578
- ];
1579
-
1580
- if (outcome._tag === "Ambiguous")
1581
- values.push([
1582
- mapping.generation.retryAt,
1583
- mapping.d1.engineInstantPlus(Number(row[mapping.generation.deliveryRetryMillis])),
1584
- ]);
1585
- if (outcome._tag === "DefiniteFailure")
1586
- values.push([mapping.generation.state, "cancelled"]);
1587
-
1588
- const condition = and(
1589
- eq(c.moduleId, input.moduleId),
1590
- eq(c.proofId, input.proofId),
1591
- eq(c.version, input.version),
1592
- eq(c.deliveryId, input.deliveryId),
1593
- eq(c.claimVersion, input.claimVersion),
1594
- eq(c.deliveryState, "claimed"),
1595
- );
1596
-
1597
- const statements: Statement<any>[] = [
1598
- yield* statement(
1599
- database
1600
- .update(mapping.generation.table)
1601
- .set(updateValues(values))
1602
- .where(condition),
1603
- ),
1604
- ];
1605
-
1606
- if (outcome._tag === "DefiniteFailure") {
1607
- const sc = seriesColumns(mapping);
1608
-
1609
- statements.push(
1610
- yield* statement(
1611
- database
1612
- .update(mapping.series.table)
1613
- .set(updateValues([[mapping.series.activeProofId, null]]))
1614
- .where(
1615
- and(
1616
- eq(sc.moduleId, input.moduleId),
1617
- eq(sc.purpose, row[mapping.generation.purpose]),
1618
- eq(sc.scopeKey, row[mapping.generation.seriesKey]),
1619
- eq(sc.activeProofId, input.proofId),
1620
- sql`exists(select 1 from ${mapping.generation.table} where ${c.moduleId} = ${sql.param(input.moduleId, c.moduleId)} and ${c.proofId} = ${sql.param(input.proofId, c.proofId)} and ${c.version} = ${sql.param(input.version, c.version)} and ${c.deliveryId} = ${sql.param(input.deliveryId, c.deliveryId)} and ${c.claimVersion} = ${sql.param(input.claimVersion, c.claimVersion)} and ${c.deliveryState} = 'failed' and ${c.state} = 'cancelled')`,
1621
- ),
1622
- ),
1623
- ),
1624
- );
1625
- }
1626
-
1627
- return { receipt, statements };
1628
- }),
1629
- );
1630
- }),
1631
- cancel: <A>(
1632
- input: Parameters<ProofPersistence["Service"]["cancel"]>[0],
1633
- prepare: PlanPrepare<ProofPersistence["Service"]["cancel"], A>,
1634
- ) =>
1635
- Effect.gen(function* () {
1636
- const database = yield* CurrentD1PlanningDatabase;
1637
-
1638
- return yield* run(
1639
- Effect.gen(function* () {
1640
- const journal = yield* CurrentCommitJournal;
1641
-
1642
- const keys = mapping.scopeKeys({
1643
- moduleId: input.moduleId,
1644
- purpose: input.purpose,
1645
- binding: input.binding,
1646
- });
1647
-
1648
- const current = yield* authority(mapping, input.moduleId, input.purpose, input.binding);
1649
-
1650
- const series = (yield* readSeries(
1651
- mapping,
1652
- input.moduleId,
1653
- input.purpose,
1654
- keys.series,
1655
- ))[0];
1656
-
1657
- const receipt = prepare(undefined, journal);
1658
- const active = series?.[mapping.series.activeProofId] as string | null | undefined;
1659
-
1660
- if (!current.current || active === null || active === undefined)
1661
- return { receipt, statements: [] };
1662
- const generation = (yield* readGeneration(mapping, input.moduleId, active))[0];
1663
-
1664
- const storedBinding =
1665
- generation === undefined
1666
- ? undefined
1667
- : yield* mapping.generation.decodeBinding(generation);
1668
-
1669
- if (storedBinding === undefined || !sameBinding(storedBinding, input.binding))
1670
- return { receipt, statements: [] };
1671
- const gc = generationColumns(mapping);
1672
- const sc = seriesColumns(mapping);
1673
- const marker = `effect-auth-proof-guard:cancel:${active}`;
1674
-
1675
- return {
1676
- receipt,
1677
- statements: [
1678
- yield* assertion(
1679
- and(
1680
- current.condition,
1681
- sql`exists(select 1 from ${mapping.series.table} where ${sc.moduleId} = ${sql.param(input.moduleId, sc.moduleId)} and ${sc.purpose} = ${sql.param(input.purpose, sc.purpose)} and ${sc.scopeKey} = ${sql.param(keys.series, sc.scopeKey)} and ${sc.activeProofId} = ${sql.param(active, sc.activeProofId)})`,
1682
- sql`exists(select 1 from ${mapping.generation.table} where ${gc.moduleId} = ${sql.param(input.moduleId, gc.moduleId)} and ${gc.proofId} = ${sql.param(active, gc.proofId)} and ${gc.state} = 'active')`,
1683
- )!,
1684
- marker,
1685
- ),
1686
- yield* statement(
1687
- database
1688
- .update(mapping.generation.table)
1689
- .set(updateValues([[mapping.generation.state, "cancelled"]]))
1690
- .where(
1691
- and(
1692
- eq(gc.moduleId, input.moduleId),
1693
- eq(gc.proofId, active),
1694
- eq(gc.state, "active"),
1695
- ),
1696
- ),
1697
- ),
1698
- yield* statement(
1699
- database
1700
- .update(mapping.series.table)
1701
- .set(updateValues([[mapping.series.activeProofId, null]]))
1702
- .where(
1703
- and(
1704
- eq(sc.moduleId, input.moduleId),
1705
- eq(sc.purpose, input.purpose),
1706
- eq(sc.scopeKey, keys.series),
1707
- eq(sc.activeProofId, active),
1708
- ),
1709
- ),
1710
- ),
1711
- ],
1712
- retryable: (cause: unknown) => isGuardFailure(cause, marker),
1713
- };
1714
- }),
1715
- );
1716
- }),
1717
- cleanup: <A>(
1718
- input: Parameters<ProofPersistence["Service"]["cleanup"]>[0],
1719
- prepare: PlanPrepare<ProofPersistence["Service"]["cleanup"], A>,
1720
- ) =>
1721
- Effect.gen(function* () {
1722
- const database = yield* CurrentD1PlanningDatabase;
1723
-
1724
- return yield* run(
1725
- Effect.gen(function* () {
1726
- const journal = yield* CurrentCommitJournal;
1727
-
1728
- const now = yield* readEngineNowMillis(mapping);
1729
- const nativeNow = mapping.encodeInstant(now);
1730
- const gc = generationColumns(mapping);
1731
- const cc = continuationColumns(mapping);
1732
- const rc = requestColumns(mapping);
1733
- const ac = abuseColumns(mapping);
1734
- const fc = failureColumns(mapping);
1735
- const mc = commandColumns(mapping);
1736
- const sc = seriesColumns(mapping);
1737
- const rsc = scopeColumns(mapping);
1738
- let remaining = input.limit;
1739
- let hasMore = false;
1740
-
1741
- const take = <A>(effect: Effect.Effect<ReadonlyArray<A>, AdapterFailure>) =>
1742
- effect.pipe(
1743
- Effect.map((rows) => {
1744
- if (rows.length > remaining) hasMore = true;
1745
- const selected = rows.slice(0, remaining);
1746
-
1747
- remaining -= selected.length;
1748
-
1749
- return selected;
1750
- }),
1751
- );
1752
-
1753
- const continuations = yield* take(
1754
- database
1755
- .select({ continuationId: cc.continuationId })
1756
- .from(mapping.continuation.table)
1757
- .where(and(eq(cc.moduleId, input.moduleId), lte(cc.retentionUntil, nativeNow)))
1758
- .limit(remaining + 1),
1759
- );
1760
-
1761
- const generations = yield* take(
1762
- database
1763
- .select({ proofId: gc.proofId })
1764
- .from(mapping.generation.table)
1765
- .where(and(eq(gc.moduleId, input.moduleId), lte(gc.retentionUntil, nativeNow)))
1766
- .limit(remaining + 1),
1767
- );
1768
-
1769
- const requests = yield* take(
1770
- database
1771
- .select({ requestId: rc.requestId })
1772
- .from(mapping.request.table)
1773
- .where(and(eq(rc.moduleId, input.moduleId), lte(rc.retentionUntil, nativeNow)))
1774
- .limit(remaining + 1),
1775
- );
1776
-
1777
- const abuse = yield* take(
1778
- database
1779
- .select({
1780
- action: ac.action,
1781
- scopeKind: ac.scopeKind,
1782
- scopeKey: ac.scopeKey,
1783
- commandId: ac.commandId,
1784
- })
1785
- .from(mapping.abuseEvent.table)
1786
- .where(and(eq(ac.moduleId, input.moduleId), lte(ac.retentionUntil, nativeNow)))
1787
- .limit(remaining + 1),
1788
- );
1789
-
1790
- const failures = yield* take(
1791
- database
1792
- .select({ seriesKey: fc.seriesKey, commandId: fc.commandId })
1793
- .from(mapping.failureEvent.table)
1794
- .where(and(eq(fc.moduleId, input.moduleId), lte(fc.retentionUntil, nativeNow)))
1795
- .limit(remaining + 1),
1796
- );
1797
-
1798
- const commands = yield* take(
1799
- database
1800
- .select({ commandId: mc.commandId })
1801
- .from(mapping.command.table)
1802
- .where(and(eq(mc.moduleId, input.moduleId), lte(mc.retentionUntil, nativeNow)))
1803
- .limit(remaining + 1),
1804
- );
1805
-
1806
- const series = yield* take(
1807
- database
1808
- .select({ purpose: sc.purpose, scopeKey: sc.scopeKey })
1809
- .from(mapping.series.table)
1810
- .where(
1811
- and(
1812
- eq(sc.moduleId, input.moduleId),
1813
- isNull(sc.activeProofId),
1814
- notExists(
1815
- database
1816
- .select({ one: sql`1` })
1817
- .from(mapping.generation.table)
1818
- .where(
1819
- and(
1820
- eq(gc.moduleId, sc.moduleId),
1821
- eq(gc.purpose, sc.purpose),
1822
- eq(gc.seriesKey, sc.scopeKey),
1823
- ),
1824
- ),
1825
- ),
1826
- notExists(
1827
- database
1828
- .select({ one: sql`1` })
1829
- .from(mapping.failureEvent.table)
1830
- .where(
1831
- and(
1832
- eq(fc.moduleId, sc.moduleId),
1833
- eq(fc.purpose, sc.purpose),
1834
- eq(fc.seriesKey, sc.scopeKey),
1835
- ),
1836
- ),
1837
- ),
1838
- ),
1839
- )
1840
- .limit(remaining + 1),
1841
- );
1842
-
1843
- const rateScopes = yield* take(
1844
- database
1845
- .select({
1846
- purpose: rsc.purpose,
1847
- action: rsc.action,
1848
- scopeKind: rsc.scopeKind,
1849
- scopeKey: rsc.scopeKey,
1850
- })
1851
- .from(mapping.rateScope.table)
1852
- .where(
1853
- and(
1854
- eq(rsc.moduleId, input.moduleId),
1855
- notExists(
1856
- database
1857
- .select({ one: sql`1` })
1858
- .from(mapping.abuseEvent.table)
1859
- .where(
1860
- and(
1861
- eq(ac.moduleId, rsc.moduleId),
1862
- eq(ac.purpose, rsc.purpose),
1863
- eq(ac.action, rsc.action),
1864
- eq(ac.scopeKind, rsc.scopeKind),
1865
- eq(ac.scopeKey, rsc.scopeKey),
1866
- ),
1867
- ),
1868
- ),
1869
- ),
1870
- )
1871
- .limit(remaining + 1),
1872
- );
1873
-
1874
- const marker = `effect-auth-proof-guard:cleanup:${input.moduleId}:${now}`;
1875
- const statements: Statement<any>[] = [];
1876
-
1877
- const guardedDelete = Effect.fn(function* (
1878
- condition: SQL,
1879
- query: { readonly toSQL: () => { sql: string; params: unknown[] } },
1880
- ) {
1881
- statements.push(yield* assertion(condition, marker), yield* statement(query));
1882
- });
1883
-
1884
- for (const row of continuations) {
1885
- const condition = and(
1886
- eq(cc.moduleId, input.moduleId),
1887
- eq(cc.continuationId, row.continuationId),
1888
- lte(cc.retentionUntil, mapping.d1.engineNow),
1889
- )!;
1890
-
1891
- yield* guardedDelete(
1892
- sql`exists(select 1 from ${mapping.continuation.table} where ${condition})`,
1893
- database.delete(mapping.continuation.table).where(condition),
1894
- );
1895
- }
1896
- for (const row of generations) {
1897
- const condition = and(
1898
- eq(gc.moduleId, input.moduleId),
1899
- eq(gc.proofId, row.proofId),
1900
- lte(gc.retentionUntil, mapping.d1.engineNow),
1901
- )!;
1902
-
1903
- yield* guardedDelete(
1904
- sql`exists(select 1 from ${mapping.generation.table} where ${condition})`,
1905
- database
1906
- .update(mapping.series.table)
1907
- .set(updateValues([[mapping.series.activeProofId, null]]))
1908
- .where(and(eq(sc.moduleId, input.moduleId), eq(sc.activeProofId, row.proofId))),
1909
- );
1910
- statements.push(
1911
- yield* statement(database.delete(mapping.generation.table).where(condition)),
1912
- );
1913
- }
1914
- for (const row of requests) {
1915
- const condition = and(
1916
- eq(rc.moduleId, input.moduleId),
1917
- eq(rc.requestId, row.requestId),
1918
- lte(rc.retentionUntil, mapping.d1.engineNow),
1919
- )!;
1920
-
1921
- yield* guardedDelete(
1922
- sql`exists(select 1 from ${mapping.request.table} where ${condition})`,
1923
- database.delete(mapping.request.table).where(condition),
1924
- );
1925
- }
1926
- for (const row of abuse) {
1927
- const condition = and(
1928
- eq(ac.moduleId, input.moduleId),
1929
- eq(ac.action, row.action),
1930
- eq(ac.scopeKind, row.scopeKind),
1931
- eq(ac.scopeKey, row.scopeKey),
1932
- eq(ac.commandId, row.commandId),
1933
- lte(ac.retentionUntil, mapping.d1.engineNow),
1934
- )!;
1935
-
1936
- yield* guardedDelete(
1937
- sql`exists(select 1 from ${mapping.abuseEvent.table} where ${condition})`,
1938
- database.delete(mapping.abuseEvent.table).where(condition),
1939
- );
1940
- }
1941
- for (const row of failures) {
1942
- const condition = and(
1943
- eq(fc.moduleId, input.moduleId),
1944
- eq(fc.seriesKey, row.seriesKey),
1945
- eq(fc.commandId, row.commandId),
1946
- lte(fc.retentionUntil, mapping.d1.engineNow),
1947
- )!;
1948
-
1949
- yield* guardedDelete(
1950
- sql`exists(select 1 from ${mapping.failureEvent.table} where ${condition})`,
1951
- database.delete(mapping.failureEvent.table).where(condition),
1952
- );
1953
- }
1954
- for (const row of commands) {
1955
- const condition = and(
1956
- eq(mc.moduleId, input.moduleId),
1957
- eq(mc.commandId, row.commandId),
1958
- lte(mc.retentionUntil, mapping.d1.engineNow),
1959
- )!;
1960
-
1961
- yield* guardedDelete(
1962
- sql`exists(select 1 from ${mapping.command.table} where ${condition})`,
1963
- database.delete(mapping.command.table).where(condition),
1964
- );
1965
- }
1966
- for (const row of series) {
1967
- const condition = and(
1968
- eq(sc.moduleId, input.moduleId),
1969
- eq(sc.purpose, row.purpose),
1970
- eq(sc.scopeKey, row.scopeKey),
1971
- isNull(sc.activeProofId),
1972
- notExists(
1973
- database
1974
- .select({ one: sql`1` })
1975
- .from(mapping.generation.table)
1976
- .where(
1977
- and(
1978
- eq(gc.moduleId, sc.moduleId),
1979
- eq(gc.purpose, sc.purpose),
1980
- eq(gc.seriesKey, sc.scopeKey),
1981
- ),
1982
- ),
1983
- ),
1984
- )!;
1985
-
1986
- yield* guardedDelete(
1987
- sql`exists(select 1 from ${mapping.series.table} where ${condition})`,
1988
- database.delete(mapping.series.table).where(condition),
1989
- );
1990
- }
1991
- for (const row of rateScopes) {
1992
- const condition = and(
1993
- eq(rsc.moduleId, input.moduleId),
1994
- eq(rsc.purpose, row.purpose),
1995
- eq(rsc.action, row.action),
1996
- eq(rsc.scopeKind, row.scopeKind),
1997
- eq(rsc.scopeKey, row.scopeKey),
1998
- notExists(
1999
- database
2000
- .select({ one: sql`1` })
2001
- .from(mapping.abuseEvent.table)
2002
- .where(
2003
- and(
2004
- eq(ac.moduleId, rsc.moduleId),
2005
- eq(ac.purpose, rsc.purpose),
2006
- eq(ac.action, rsc.action),
2007
- eq(ac.scopeKind, rsc.scopeKind),
2008
- eq(ac.scopeKey, rsc.scopeKey),
2009
- ),
2010
- ),
2011
- ),
2012
- )!;
2013
-
2014
- yield* guardedDelete(
2015
- sql`exists(select 1 from ${mapping.rateScope.table} where ${condition})`,
2016
- database.delete(mapping.rateScope.table).where(condition),
2017
- );
2018
- }
2019
- // Removing the final history row can make an anchor eligible only after this
2020
- // transaction/batch commits. One extra page lets the caller reclaim it.
2021
- hasMore ||= generations.length > 0 || failures.length > 0 || abuse.length > 0;
2022
- const result: ProofCleanupResult = { removed: input.limit - remaining, hasMore };
2023
-
2024
- return {
2025
- receipt: prepare(result, journal),
2026
- statements,
2027
- retryable: (cause: unknown) => isGuardFailure(cause, marker),
2028
- };
2029
- }),
2030
- );
2031
- }),
2032
- };
2033
- };
2034
-
2035
- export const makeD1ProofPersistenceServices = Effect.fnUntraced(function* <
2036
- Rq extends AnySQLiteTable,
2037
- S extends AnySQLiteTable,
2038
- G extends AnySQLiteTable,
2039
- Cn extends AnySQLiteTable,
2040
- Rs extends AnySQLiteTable,
2041
- A extends AnySQLiteTable,
2042
- F extends AnySQLiteTable,
2043
- C extends AnySQLiteTable,
2044
- Sub extends AnySQLiteTable,
2045
- I extends AnySQLiteTable,
2046
- Cr extends AnySQLiteTable,
2047
- NativeId,
2048
- >(mapping: D1ProofPersistenceMapping<Rq, S, G, Cn, Rs, A, F, C, Sub, I, Cr, NativeId>) {
2049
- const database = yield* DatabaseService;
2050
-
2051
- yield* validateDrizzleStorage(mapping).pipe(
2052
- Effect.provideServiceEffect(NativeDatabase, nativeDatabase(DatabaseService)),
2053
- Effect.mapError(unavailable),
2054
- );
2055
-
2056
- const hooks = yield* LifecycleHooks;
2057
- const plans = makeProofPlans(mapping as unknown as Mapping);
2058
-
2059
- const run = <Out, Err, Env>(plan: Effect.Effect<Planned<Out>, Err, Env>) =>
2060
- Effect.gen(function* () {
2061
- if (yield* hasCommitScope) return yield* unavailable();
2062
-
2063
- return yield* executeStandalone(plan, 2);
2064
- }).pipe(
2065
- Effect.provideService(CurrentD1PlanningDatabase, database),
2066
- Effect.provideService(LifecycleHooks, hooks),
2067
- );
2068
-
2069
- const service: ProofPersistence["Service"] = {
2070
- issue: (input, prepare) => run(plans.issue(input, prepare)).pipe(translateIssueFailure),
2071
- attempt: (input, prepare) => run(plans.attempt(input, prepare)).pipe(translateFailure),
2072
- complete: (input, prepare) => run(plans.complete(input, prepare)).pipe(translateFailure),
2073
- claimDelivery: (input, prepare) =>
2074
- run(plans.claimDelivery(input, prepare)).pipe(translateFailure),
2075
- settleDelivery: (input, prepare) =>
2076
- run(plans.settleDelivery(input, prepare)).pipe(translateFailure),
2077
- cancel: (input, prepare) => run(plans.cancel(input, prepare)).pipe(translateFailure),
2078
- cleanup: (input, prepare) => run(plans.cleanup(input, prepare)).pipe(translateFailure),
2079
- };
2080
-
2081
- return { proofPersistence: service };
2082
- });
2083
-
2084
- type CoordinatorError<E> = E | ProofUnavailable | HookConfigurationError;
2085
-
2086
- /**
2087
- * Owns one proof transition plus application statements in one D1 batch. It
2088
- * never replays the owner effect. A reserved guard or named race becomes
2089
- * ProofUnavailable so the caller can retry the entire command with fresh secrets.
2090
- */
2091
- export function coordinateD1ProofPersistence<
2092
- Rq extends AnySQLiteTable,
2093
- S extends AnySQLiteTable,
2094
- G extends AnySQLiteTable,
2095
- Cn extends AnySQLiteTable,
2096
- Rs extends AnySQLiteTable,
2097
- Aev extends AnySQLiteTable,
2098
- F extends AnySQLiteTable,
2099
- C extends AnySQLiteTable,
2100
- Sub extends AnySQLiteTable,
2101
- I extends AnySQLiteTable,
2102
- Cr extends AnySQLiteTable,
2103
- NativeId,
2104
- A,
2105
- E,
2106
- R,
2107
- DatabaseError,
2108
- DatabaseRequirements,
2109
- >(
2110
- acquire: Effect.Effect<Database, DatabaseError, DatabaseRequirements>,
2111
- options: {
2112
- readonly mapping: D1ProofPersistenceMapping<Rq, S, G, Cn, Rs, Aev, F, C, Sub, I, Cr, NativeId>;
2113
- },
2114
- body: Effect.Effect<A, E, R>,
2115
- ): Effect.Effect<
2116
- A,
2117
- CoordinatorError<E> | DatabaseError,
2118
- Exclude<R, ProofPersistence | D1BatchStatements> | LifecycleHooks | DatabaseRequirements
2119
- > {
2120
- return Effect.flatMap(nativeDatabase(acquire), (database) =>
2121
- Effect.gen(function* () {
2122
- yield* validateDrizzleStorage(options.mapping).pipe(Effect.mapError(unavailable));
2123
- const hooks = yield* LifecycleHooks;
2124
-
2125
- if (yield* hasCommitScope) return yield* unavailable();
2126
-
2127
- const result = yield* coordinateCommit(
2128
- () =>
2129
- Effect.gen(function* () {
2130
- const statements: Statement<any>[] = [];
2131
- let mutation: Planned<any> | undefined;
2132
-
2133
- const nativeCollector = D1BatchStatements.of({
2134
- append: (statement) => Effect.sync(() => statements.push(statement)),
2135
- });
2136
-
2137
- const owner = yield* makeD1Owner(unavailable()).pipe(
2138
- Effect.provideService(D1BatchStatements, nativeCollector),
2139
- );
2140
-
2141
- const plans = makeProofPlans(options.mapping as unknown as Mapping);
2142
-
2143
- const run = <Out, Err, Env>(plan: Effect.Effect<Planned<Out>, Err, Env>) =>
2144
- coordinateCommit(
2145
- () =>
2146
- Effect.gen(function* () {
2147
- const journal = yield* CurrentCommitJournal;
2148
- const planned = yield* plan;
2149
-
2150
- yield* owner.check;
2151
- if (mutation !== undefined) return yield* unavailable();
2152
- const guarded = { ...planned, journalGuard: journal.prepare(undefined) };
2153
-
2154
- mutation = guarded;
2155
- statements.push(...guarded.statements);
2156
-
2157
- return guarded.receipt;
2158
- }),
2159
- { mode: "batch" },
2160
- ).pipe(
2161
- Effect.map((result) => result.value),
2162
- Effect.provideService(CurrentD1PlanningDatabase, database),
2163
- Effect.provideService(LifecycleHooks, hooks),
2164
- );
2165
-
2166
- const service: ProofPersistence["Service"] = {
2167
- issue: (input, prepare) =>
2168
- owner.run(run(plans.issue(input, prepare)).pipe(translateIssueFailure)),
2169
- attempt: (input, prepare) =>
2170
- owner.run(run(plans.attempt(input, prepare)).pipe(translateFailure)),
2171
- complete: (input, prepare) =>
2172
- owner.run(run(plans.complete(input, prepare)).pipe(translateFailure)),
2173
- claimDelivery: (input, prepare) =>
2174
- owner.run(run(plans.claimDelivery(input, prepare)).pipe(translateFailure)),
2175
- settleDelivery: (input, prepare) =>
2176
- owner.run(run(plans.settleDelivery(input, prepare)).pipe(translateFailure)),
2177
- cancel: (input, prepare) =>
2178
- owner.run(run(plans.cancel(input, prepare)).pipe(translateFailure)),
2179
- cleanup: (input, prepare) =>
2180
- owner.run(run(plans.cleanup(input, prepare)).pipe(translateFailure)),
2181
- };
2182
-
2183
- const provided = Context.make(ProofPersistence, service).pipe(
2184
- Context.add(D1BatchStatements, owner.collector),
2185
- );
2186
-
2187
- const value = yield* owner.close(Effect.provideContext(body, provided));
2188
-
2189
- if (mutation?.journalGuard !== undefined) {
2190
- const status = yield* Effect.result(mutation.journalGuard.read);
2191
-
2192
- if (status._tag === "Success" || status.failure._tag !== "CommitPending")
2193
- return yield* unavailable();
2194
- }
2195
- yield* database.$client.batch(statements).pipe(
2196
- Effect.catchCause((cause) =>
2197
- Effect.failCause(
2198
- Cause.map(cause, (error) =>
2199
- everyFailureMatches(cause, mutation?.retryable) ? unavailable() : error,
2200
- ),
2201
- ),
2202
- ),
2203
- translateFailure,
2204
- );
2205
-
2206
- return value;
2207
- }),
2208
- { mode: "batch" },
2209
- ).pipe(Effect.provideService(LifecycleHooks, hooks));
2210
-
2211
- return result.value;
2212
- }).pipe(Effect.provideService(NativeDatabase, database)),
2213
- );
2214
- }
2215
-
2216
- export interface D1ProtectedProofMutation {
2217
- /** Conditional statements compiled by the owning method adapter. */
2218
- readonly statements: ReadonlyArray<Statement<any>>;
2219
- /** Postcondition proving the protected CAS and its invalidations all applied. */
2220
- readonly appliedCondition: SQL;
2221
- }
2222
-
2223
- export interface D1CompiledProofCompletion<A> {
2224
- readonly receipt: PreparedCommit<A>;
2225
- readonly statements: ReadonlyArray<Statement<any>>;
2226
- readonly postconditions?: ReadonlyArray<Statement<any>>;
2227
- }
2228
-
2229
- /** Private compiler seam for a future same-owner D1 password-reset batch. */
2230
- export const compileD1ProofCompletionPlan = Effect.fn("Drizzle.compileD1ProofCompletionPlan")(
2231
- function* <
2232
- Rq extends AnySQLiteTable,
2233
- S extends AnySQLiteTable,
2234
- G extends AnySQLiteTable,
2235
- Cn extends AnySQLiteTable,
2236
- Rs extends AnySQLiteTable,
2237
- Aev extends AnySQLiteTable,
2238
- F extends AnySQLiteTable,
2239
- C extends AnySQLiteTable,
2240
- Sub extends AnySQLiteTable,
2241
- I extends AnySQLiteTable,
2242
- Cr extends AnySQLiteTable,
2243
- NativeId,
2244
- A,
2245
- >(
2246
- mapping: D1ProofPersistenceMapping<Rq, S, G, Cn, Rs, Aev, F, C, Sub, I, Cr, NativeId>,
2247
- plan: ProofCompletionPlan,
2248
- protectedMutation: D1ProtectedProofMutation,
2249
- project: (decision: ProofCompletionDecision) => A,
2250
- ) {
2251
- const planned = yield* completionPlan<A>(
2252
- mapping as unknown as Mapping,
2253
- plan.input,
2254
- (decision, activeJournal) => plan.prepare(decision, activeJournal, project),
2255
- );
2256
-
2257
- // A rejected completion has no protected mutation. For a predicted success,
2258
- // preserve the fresh continuation/authority guard first, then prove the
2259
- // method CAS before consuming the continuation and inserting its marker.
2260
- if (planned.statements.length === 0) return planned;
2261
- const [freshGuard, ...completionStatements] = planned.statements;
2262
- const marker = `effect-auth-proof-guard:protected:${plan.input.continuationId}`;
2263
-
2264
- return {
2265
- receipt: planned.receipt,
2266
- statements: [
2267
- freshGuard!,
2268
- ...protectedMutation.statements,
2269
- yield* assertion(protectedMutation.appliedCondition, marker),
2270
- ...completionStatements,
2271
- ],
2272
- postconditions: [
2273
- yield* assertion(protectedMutation.appliedCondition, marker),
2274
- ...(planned.postconditions ?? []),
2275
- ],
2276
- };
2277
- },
2278
- );
2279
-
2280
- export type AnyD1ProofMapping = ProofPersistenceMapping<
2281
- Table,
2282
- Table,
2283
- Table,
2284
- Table,
2285
- Table,
2286
- Table,
2287
- Table,
2288
- Table,
2289
- Table,
2290
- Table,
2291
- Table,
2292
- unknown
2293
- >;
2294
-
2295
- const translateFailure = <A, E, R>(
2296
- effect: Effect.Effect<A, E, R>,
2297
- ): Effect.Effect<A, ProofUnavailable, R> =>
2298
- reportPersistenceFailure(
2299
- effect,
2300
- (error) => Schema.is(ProofUnavailable)(error) || Schema.is(HookConfigurationError)(error),
2301
- ).pipe(Effect.catchCause((cause) => Effect.failCause(Cause.map(cause, () => unavailable()))));
2302
-
2303
- const translateIssueFailure = <A, E, R>(
2304
- effect: Effect.Effect<A, E, R>,
2305
- ): Effect.Effect<A, ProofUnavailable | ProofRequestConflict, R> =>
2306
- reportPersistenceFailure(
2307
- effect,
2308
- (error) =>
2309
- Schema.is(ProofUnavailable)(error) ||
2310
- Schema.is(ProofRequestConflict)(error) ||
2311
- Schema.is(HookConfigurationError)(error),
2312
- ).pipe(
2313
- Effect.catchCause((cause) =>
2314
- Effect.failCause(
2315
- Cause.map(cause, (error) =>
2316
- Schema.is(ProofRequestConflict)(error) ? error : unavailable(),
2317
- ),
2318
- ),
2319
- ),
2320
- );
2321
-
2322
- const executeStandalone = <A, E, R>(
2323
- plan: Effect.Effect<Planned<A>, E, R>,
2324
- retries = 2,
2325
- ): Effect.Effect<
2326
- A,
2327
- E | import("@yielded/auth/Hooks").HookConfigurationError | import("effect/sql/SqlError").SqlError,
2328
- Exclude<R, CurrentCommitJournal> | CurrentD1PlanningDatabase | LifecycleHooks
2329
- > =>
2330
- Effect.suspend(() => {
2331
- let retryable: ((cause: unknown) => boolean) | undefined;
2332
-
2333
- const once = coordinateCommit(
2334
- () =>
2335
- Effect.gen(function* () {
2336
- const database = yield* CurrentD1PlanningDatabase;
2337
- const planned = yield* plan;
2338
-
2339
- retryable = planned.retryable;
2340
- yield* database.$client.batch(planned.statements);
2341
-
2342
- return planned.receipt;
2343
- }),
2344
- { mode: "batch" },
2345
- ).pipe(Effect.map((result) => result.value));
2346
-
2347
- return once.pipe(
2348
- Effect.catchCause((cause) =>
2349
- retries > 0 && everyFailureMatches(cause, retryable)
2350
- ? executeStandalone(plan, retries - 1)
2351
- : Effect.failCause(cause),
2352
- ),
2353
- );
2354
- });