@yielded/auth-persistence 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 (481) hide show
  1. package/README.md +6 -8
  2. package/dist/Adapter.d.mts +47 -33
  3. package/dist/Adapter.mjs +36 -23
  4. package/dist/OAuthPersistence.d.mts +35 -51
  5. package/dist/OAuthPersistence.mjs +60 -68
  6. package/dist/OAuthPersistence.mjs.map +1 -1
  7. package/dist/index.d.mts +2 -2
  8. package/dist/index.mjs +2 -2
  9. package/dist/internal/browser-login.d.mts +1 -1
  10. package/dist/internal/browser-login.mjs +29 -9
  11. package/dist/internal/browser-login.mjs.map +1 -1
  12. package/dist/internal/configuration.d.mts +13 -8
  13. package/dist/internal/configuration.mjs.map +1 -1
  14. package/dist/internal/crypto.d.mts +1 -0
  15. package/dist/internal/crypto.mjs +1 -1
  16. package/dist/internal/d1-planning.mjs +8 -0
  17. package/dist/internal/d1-planning.mjs.map +1 -0
  18. package/dist/internal/email-native-state.mjs +135 -0
  19. package/dist/internal/email-native-state.mjs.map +1 -0
  20. package/dist/internal/email-native.d.mts +17 -0
  21. package/dist/internal/email-native.mjs +245 -0
  22. package/dist/internal/email-native.mjs.map +1 -0
  23. package/dist/internal/email-policy.mjs +92 -0
  24. package/dist/internal/email-policy.mjs.map +1 -0
  25. package/dist/internal/email-registration-native.d.mts +38 -0
  26. package/dist/internal/email-registration-native.mjs +171 -0
  27. package/dist/internal/email-registration-native.mjs.map +1 -0
  28. package/dist/internal/models/email-model.d.mts +40 -104
  29. package/dist/internal/models/email-model.mjs +3 -5
  30. package/dist/internal/models/email-model.mjs.map +1 -1
  31. package/dist/internal/models/oauth-connected-model.d.mts +44 -213
  32. package/dist/internal/models/oauth-connected-model.mjs +6 -14
  33. package/dist/internal/models/oauth-connected-model.mjs.map +1 -1
  34. package/dist/internal/models/oauth-model.d.mts +95 -244
  35. package/dist/internal/models/oauth-model.mjs +28 -22
  36. package/dist/internal/models/oauth-model.mjs.map +1 -1
  37. package/dist/internal/models/passkey-model.d.mts +11 -147
  38. package/dist/internal/models/passkey-model.mjs +5 -24
  39. package/dist/internal/models/passkey-model.mjs.map +1 -1
  40. package/dist/internal/models/passkey-registration-ceremony-model.d.mts +5 -39
  41. package/dist/internal/models/passkey-write-model.d.mts +17 -75
  42. package/dist/internal/models/passkey-write-model.mjs +1 -7
  43. package/dist/internal/models/passkey-write-model.mjs.map +1 -1
  44. package/dist/internal/models/password-model.d.mts +14 -81
  45. package/dist/internal/models/password-model.mjs +1 -4
  46. package/dist/internal/models/password-model.mjs.map +1 -1
  47. package/dist/internal/models/phone-model.d.mts +86 -0
  48. package/dist/internal/models/phone-model.mjs +10 -0
  49. package/dist/internal/models/phone-model.mjs.map +1 -0
  50. package/dist/internal/models/proof-model.d.mts +43 -272
  51. package/dist/internal/models/proof-model.mjs +2 -10
  52. package/dist/internal/models/proof-model.mjs.map +1 -1
  53. package/dist/internal/models/session-model.d.mts +74 -93
  54. package/dist/internal/models/session-model.mjs +3 -11
  55. package/dist/internal/models/session-model.mjs.map +1 -1
  56. package/dist/internal/models/step-up-model.d.mts +12 -50
  57. package/dist/internal/models/step-up-model.mjs +2 -4
  58. package/dist/internal/models/step-up-model.mjs.map +1 -1
  59. package/dist/internal/models/totp-model.d.mts +76 -0
  60. package/dist/internal/models/totp-model.mjs +10 -0
  61. package/dist/internal/models/totp-model.mjs.map +1 -0
  62. package/dist/internal/native-sql-table.d.mts +21 -4
  63. package/dist/internal/native-sql-table.mjs +9 -5
  64. package/dist/internal/native-sql-table.mjs.map +1 -1
  65. package/dist/internal/oauth/native-accounts.d.mts +17 -0
  66. package/dist/internal/oauth/native-accounts.mjs +290 -0
  67. package/dist/internal/oauth/native-accounts.mjs.map +1 -0
  68. package/dist/internal/oauth/native-connected-state.d.mts +11 -0
  69. package/dist/internal/oauth/native-connected-state.mjs +146 -0
  70. package/dist/internal/oauth/native-connected-state.mjs.map +1 -0
  71. package/dist/internal/oauth/native-connected.d.mts +15 -0
  72. package/dist/internal/oauth/native-connected.mjs +462 -0
  73. package/dist/internal/oauth/native-connected.mjs.map +1 -0
  74. package/dist/internal/oauth/native-flow.mjs +138 -0
  75. package/dist/internal/oauth/native-flow.mjs.map +1 -0
  76. package/dist/internal/oauth/native-layers.d.mts +24 -0
  77. package/dist/internal/oauth/native-layers.mjs +13 -0
  78. package/dist/internal/oauth/native-layers.mjs.map +1 -0
  79. package/dist/internal/oauth/native-mutation.mjs +122 -0
  80. package/dist/internal/oauth/native-mutation.mjs.map +1 -0
  81. package/dist/internal/oauth/native-registration.d.mts +24 -0
  82. package/dist/internal/oauth/native-registration.mjs +328 -0
  83. package/dist/internal/oauth/native-registration.mjs.map +1 -0
  84. package/dist/internal/oauth/native-revocations.d.mts +17 -0
  85. package/dist/internal/oauth/native-revocations.mjs +106 -0
  86. package/dist/internal/oauth/native-revocations.mjs.map +1 -0
  87. package/dist/internal/oauth/native-sign-in.d.mts +17 -0
  88. package/dist/internal/oauth/native-sign-in.mjs +50 -0
  89. package/dist/internal/oauth/native-sign-in.mjs.map +1 -0
  90. package/dist/internal/oauth/native-state.d.mts +10 -0
  91. package/dist/internal/oauth/native-state.mjs +214 -0
  92. package/dist/internal/oauth/native-state.mjs.map +1 -0
  93. package/dist/internal/oauth/state.d.mts +10 -0
  94. package/dist/internal/oauth/state.mjs +3 -27
  95. package/dist/internal/oauth/state.mjs.map +1 -1
  96. package/dist/internal/passkey-actions.mjs +55 -0
  97. package/dist/internal/passkey-actions.mjs.map +1 -0
  98. package/dist/internal/passkey-inputs.mjs +73 -0
  99. package/dist/internal/passkey-inputs.mjs.map +1 -0
  100. package/dist/internal/passkey-managed.mjs +224 -0
  101. package/dist/internal/passkey-managed.mjs.map +1 -0
  102. package/dist/internal/passkey-native-ceremony.d.mts +35 -0
  103. package/dist/internal/passkey-native-ceremony.mjs +38 -0
  104. package/dist/internal/passkey-native-ceremony.mjs.map +1 -0
  105. package/dist/internal/passkey-native-flow.mjs +91 -0
  106. package/dist/internal/passkey-native-flow.mjs.map +1 -0
  107. package/dist/internal/passkey-native-registration.d.mts +10 -0
  108. package/dist/internal/passkey-native-registration.mjs +114 -0
  109. package/dist/internal/passkey-native-registration.mjs.map +1 -0
  110. package/dist/internal/passkey-native-state.d.mts +15 -0
  111. package/dist/internal/passkey-native-state.mjs +186 -0
  112. package/dist/internal/passkey-native-state.mjs.map +1 -0
  113. package/dist/internal/passkey-native-write.d.mts +82 -0
  114. package/dist/internal/passkey-native-write.mjs +330 -0
  115. package/dist/internal/passkey-native-write.mjs.map +1 -0
  116. package/dist/internal/passkey-native.d.mts +835 -0
  117. package/dist/internal/passkey-native.mjs +175 -0
  118. package/dist/internal/passkey-native.mjs.map +1 -0
  119. package/dist/internal/passkey-policy.mjs +115 -0
  120. package/dist/internal/passkey-policy.mjs.map +1 -0
  121. package/dist/internal/password-credentials.mjs +38 -9
  122. package/dist/internal/password-credentials.mjs.map +1 -1
  123. package/dist/internal/password-native.d.mts +14 -0
  124. package/dist/internal/password-native.mjs +292 -0
  125. package/dist/internal/password-native.mjs.map +1 -0
  126. package/dist/internal/password-policy.mjs +93 -0
  127. package/dist/internal/password-policy.mjs.map +1 -0
  128. package/dist/internal/password-registration-native.d.mts +22 -0
  129. package/dist/internal/password-registration-native.mjs +142 -0
  130. package/dist/internal/password-registration-native.mjs.map +1 -0
  131. package/dist/internal/persistence.d.mts +6 -19
  132. package/dist/internal/persistence.mjs +199 -166
  133. package/dist/internal/persistence.mjs.map +1 -1
  134. package/dist/internal/phone-composed.mjs +79 -0
  135. package/dist/internal/phone-composed.mjs.map +1 -0
  136. package/dist/internal/phone-native.d.mts +15 -0
  137. package/dist/internal/phone-native.mjs +354 -0
  138. package/dist/internal/phone-native.mjs.map +1 -0
  139. package/dist/internal/proof-native.d.mts +15 -0
  140. package/dist/internal/proof-native.mjs +289 -0
  141. package/dist/internal/proof-native.mjs.map +1 -0
  142. package/dist/internal/registration.mjs +50 -60
  143. package/dist/internal/registration.mjs.map +1 -1
  144. package/dist/internal/session-native-authority.d.mts +15 -0
  145. package/dist/internal/session-native-authority.mjs +91 -0
  146. package/dist/internal/session-native-authority.mjs.map +1 -0
  147. package/dist/internal/session-native-cleanup.d.mts +14 -0
  148. package/dist/internal/session-native-cleanup.mjs +48 -0
  149. package/dist/internal/session-native-cleanup.mjs.map +1 -0
  150. package/dist/internal/session-native-login.d.mts +15 -0
  151. package/dist/internal/session-native-login.mjs +87 -0
  152. package/dist/internal/session-native-login.mjs.map +1 -0
  153. package/dist/internal/session-native-pending-read.mjs +42 -0
  154. package/dist/internal/session-native-pending-read.mjs.map +1 -0
  155. package/dist/internal/session-native-pending.mjs +134 -0
  156. package/dist/internal/session-native-pending.mjs.map +1 -0
  157. package/dist/internal/session-native-record.mjs +79 -0
  158. package/dist/internal/session-native-record.mjs.map +1 -0
  159. package/dist/internal/session-native-state.mjs +130 -0
  160. package/dist/internal/session-native-state.mjs.map +1 -0
  161. package/dist/internal/session-native-stateful.d.mts +16 -0
  162. package/dist/internal/session-native-stateful.mjs +225 -0
  163. package/dist/internal/session-native-stateful.mjs.map +1 -0
  164. package/dist/internal/session-native-step-up.d.mts +15 -0
  165. package/dist/internal/session-native-step-up.mjs +212 -0
  166. package/dist/internal/session-native-step-up.mjs.map +1 -0
  167. package/dist/internal/session-native-validity.d.mts +15 -0
  168. package/dist/internal/session-native-validity.mjs +97 -0
  169. package/dist/internal/session-native-validity.mjs.map +1 -0
  170. package/dist/internal/session-policy.mjs +29 -0
  171. package/dist/internal/session-policy.mjs.map +1 -0
  172. package/dist/internal/sql-authority.d.mts +16 -0
  173. package/dist/internal/sql-authority.mjs +32 -0
  174. package/dist/internal/sql-authority.mjs.map +1 -0
  175. package/dist/internal/sql-change.mjs +21 -0
  176. package/dist/internal/sql-change.mjs.map +1 -0
  177. package/dist/internal/sql-cleanup.mjs +35 -0
  178. package/dist/internal/sql-cleanup.mjs.map +1 -0
  179. package/dist/internal/sql-commit.d.mts +72 -0
  180. package/dist/internal/sql-commit.mjs +176 -0
  181. package/dist/internal/sql-commit.mjs.map +1 -0
  182. package/dist/internal/sql-expression.d.mts +17 -0
  183. package/dist/internal/sql-expression.mjs +49 -0
  184. package/dist/internal/sql-expression.mjs.map +1 -0
  185. package/dist/internal/sql-oauth-connected-model.d.mts +7 -23
  186. package/dist/internal/sql-oauth-model.d.mts +27 -52
  187. package/dist/internal/sql-oauth-model.mjs +3 -4
  188. package/dist/internal/sql-oauth-model.mjs.map +1 -1
  189. package/dist/internal/sql-persistence.mjs +5 -9
  190. package/dist/internal/sql-persistence.mjs.map +1 -1
  191. package/dist/internal/sql-storage-validation.mjs +1 -2
  192. package/dist/internal/sql-storage-validation.mjs.map +1 -1
  193. package/dist/internal/sql-table.d.mts +3 -16
  194. package/dist/internal/sql-table.mjs +21 -42
  195. package/dist/internal/sql-table.mjs.map +1 -1
  196. package/dist/internal/standalone.mjs +2 -3
  197. package/dist/internal/standalone.mjs.map +1 -1
  198. package/dist/internal/step-up-state.mjs +5 -8
  199. package/dist/internal/step-up-state.mjs.map +1 -1
  200. package/dist/internal/storage-clock.mjs +44 -0
  201. package/dist/internal/storage-clock.mjs.map +1 -0
  202. package/dist/internal/storage-mapping.d.mts +11 -8
  203. package/dist/internal/storage-mapping.mjs +88 -302
  204. package/dist/internal/storage-mapping.mjs.map +1 -1
  205. package/dist/internal/storage-plans.mjs +15 -73
  206. package/dist/internal/storage-plans.mjs.map +1 -1
  207. package/dist/internal/storage-tables.d.mts +37 -520
  208. package/dist/internal/storage-tables.mjs +31 -211
  209. package/dist/internal/storage-tables.mjs.map +1 -1
  210. package/dist/internal/storage-validation.d.mts +1 -6
  211. package/dist/internal/storage-validation.mjs +1 -42
  212. package/dist/internal/storage-validation.mjs.map +1 -1
  213. package/dist/internal/table-model.d.mts +21 -0
  214. package/dist/internal/totp-native.d.mts +15 -0
  215. package/dist/internal/totp-native.mjs +401 -0
  216. package/dist/internal/totp-native.mjs.map +1 -0
  217. package/package.json +1 -1
  218. package/src/Adapter.ts +143 -199
  219. package/src/OAuthPersistence.ts +128 -271
  220. package/src/index.ts +1 -1
  221. package/src/internal/browser-login.ts +54 -24
  222. package/src/internal/configuration.ts +16 -31
  223. package/src/internal/d1-planning.ts +7 -0
  224. package/src/internal/email-native-state.ts +247 -0
  225. package/src/internal/email-native.ts +514 -0
  226. package/src/internal/email-policy.ts +235 -0
  227. package/src/internal/email-registration-native.ts +386 -0
  228. package/src/internal/models/email-model.ts +76 -214
  229. package/src/internal/models/oauth-connected-model.ts +50 -288
  230. package/src/internal/models/oauth-model.ts +147 -368
  231. package/src/internal/models/passkey-model.ts +11 -208
  232. package/src/internal/models/passkey-registration-ceremony-model.ts +6 -60
  233. package/src/internal/models/passkey-write-model.ts +14 -95
  234. package/src/internal/models/password-model.ts +26 -147
  235. package/src/internal/models/phone-model.ts +110 -0
  236. package/src/internal/models/proof-model.ts +48 -404
  237. package/src/internal/models/session-model.ts +102 -200
  238. package/src/internal/models/step-up-model.ts +17 -96
  239. package/src/internal/models/totp-model.ts +82 -0
  240. package/src/internal/native-sql-table.ts +32 -11
  241. package/src/internal/oauth/native-accounts.ts +576 -0
  242. package/src/internal/oauth/native-connected-state.ts +288 -0
  243. package/src/internal/oauth/native-connected.ts +888 -0
  244. package/src/internal/oauth/native-flow.ts +253 -0
  245. package/src/internal/oauth/native-layers.ts +83 -0
  246. package/src/internal/oauth/native-mutation.ts +222 -0
  247. package/src/internal/oauth/native-registration.ts +545 -0
  248. package/src/internal/oauth/native-revocations.ts +190 -0
  249. package/src/internal/oauth/native-sign-in.ts +111 -0
  250. package/src/internal/oauth/native-state.ts +346 -0
  251. package/src/internal/oauth/state.ts +2 -70
  252. package/src/internal/passkey-actions.ts +70 -0
  253. package/src/internal/passkey-inputs.ts +56 -0
  254. package/src/internal/passkey-managed.ts +360 -0
  255. package/src/internal/passkey-native-ceremony.ts +66 -0
  256. package/src/internal/passkey-native-flow.ts +140 -0
  257. package/src/internal/passkey-native-registration.ts +221 -0
  258. package/src/internal/passkey-native-state.ts +335 -0
  259. package/src/internal/passkey-native-write.ts +611 -0
  260. package/src/internal/passkey-native.ts +372 -0
  261. package/src/internal/passkey-policy.ts +246 -0
  262. package/src/internal/password-credentials.ts +52 -10
  263. package/src/internal/password-native.ts +571 -0
  264. package/src/internal/password-policy.ts +307 -0
  265. package/src/internal/password-registration-native.ts +322 -0
  266. package/src/internal/persistence.ts +336 -290
  267. package/src/internal/phone-composed.ts +86 -0
  268. package/src/internal/phone-native.ts +659 -0
  269. package/src/internal/proof-native.ts +448 -0
  270. package/src/internal/registration.ts +96 -143
  271. package/src/internal/session-native-authority.ts +247 -0
  272. package/src/internal/session-native-cleanup.ts +65 -0
  273. package/src/internal/session-native-login.ts +205 -0
  274. package/src/internal/session-native-pending-read.ts +68 -0
  275. package/src/internal/session-native-pending.ts +223 -0
  276. package/src/internal/session-native-record.ts +141 -0
  277. package/src/internal/session-native-state.ts +259 -0
  278. package/src/internal/session-native-stateful.ts +512 -0
  279. package/src/internal/session-native-step-up.ts +450 -0
  280. package/src/internal/session-native-validity.ts +208 -0
  281. package/src/internal/session-policy.ts +59 -0
  282. package/src/internal/sql-authority.ts +56 -0
  283. package/src/internal/sql-change.ts +38 -0
  284. package/src/internal/sql-cleanup.ts +56 -0
  285. package/src/internal/sql-commit.ts +435 -0
  286. package/src/internal/sql-expression.ts +94 -0
  287. package/src/internal/sql-metadata.ts +100 -0
  288. package/src/internal/sql-oauth-connected-model.ts +6 -95
  289. package/src/internal/sql-oauth-model.ts +31 -134
  290. package/src/internal/sql-persistence.ts +2 -6
  291. package/src/internal/sql-storage-validation.ts +1 -2
  292. package/src/internal/sql-table.ts +19 -52
  293. package/src/internal/standalone.ts +2 -11
  294. package/src/internal/step-up-state.ts +4 -17
  295. package/src/internal/storage-clock.ts +58 -0
  296. package/src/internal/storage-mapping.ts +125 -340
  297. package/src/internal/storage-plans.ts +14 -80
  298. package/src/internal/storage-tables.ts +28 -212
  299. package/src/internal/storage-validation.ts +0 -58
  300. package/src/internal/table-model.ts +24 -0
  301. package/src/internal/totp-native.ts +701 -0
  302. package/dist/internal/PasswordPreparedPostconditions.d.mts +0 -22
  303. package/dist/internal/PasswordPreparedPostconditions.mjs +0 -12
  304. package/dist/internal/PasswordPreparedPostconditions.mjs.map +0 -1
  305. package/dist/internal/authority-snapshot.mjs +0 -53
  306. package/dist/internal/authority-snapshot.mjs.map +0 -1
  307. package/dist/internal/email-kernel.d.mts +0 -276
  308. package/dist/internal/email-kernel.mjs +0 -497
  309. package/dist/internal/email-kernel.mjs.map +0 -1
  310. package/dist/internal/models/passkey-registration-ceremony-model.mjs +0 -18
  311. package/dist/internal/models/passkey-registration-ceremony-model.mjs.map +0 -1
  312. package/dist/internal/oauth/accounts.d.mts +0 -8
  313. package/dist/internal/oauth/accounts.mjs +0 -379
  314. package/dist/internal/oauth/accounts.mjs.map +0 -1
  315. package/dist/internal/oauth/connected-access.d.mts +0 -9
  316. package/dist/internal/oauth/connected-access.mjs +0 -229
  317. package/dist/internal/oauth/connected-access.mjs.map +0 -1
  318. package/dist/internal/oauth/connected-collection.d.mts +0 -7
  319. package/dist/internal/oauth/connected-collection.mjs +0 -214
  320. package/dist/internal/oauth/connected-collection.mjs.map +0 -1
  321. package/dist/internal/oauth/connected-custody.d.mts +0 -6
  322. package/dist/internal/oauth/connected-custody.mjs +0 -165
  323. package/dist/internal/oauth/connected-custody.mjs.map +0 -1
  324. package/dist/internal/oauth/connected-flow.d.mts +0 -7
  325. package/dist/internal/oauth/connected-flow.mjs +0 -324
  326. package/dist/internal/oauth/connected-flow.mjs.map +0 -1
  327. package/dist/internal/oauth/connected-input.mjs +0 -122
  328. package/dist/internal/oauth/connected-input.mjs.map +0 -1
  329. package/dist/internal/oauth/connected-keys.d.mts +0 -2
  330. package/dist/internal/oauth/connected-keys.mjs +0 -22
  331. package/dist/internal/oauth/connected-keys.mjs.map +0 -1
  332. package/dist/internal/oauth/connected-maintenance.d.mts +0 -9
  333. package/dist/internal/oauth/connected-maintenance.mjs +0 -226
  334. package/dist/internal/oauth/connected-maintenance.mjs.map +0 -1
  335. package/dist/internal/oauth/connected-management.d.mts +0 -9
  336. package/dist/internal/oauth/connected-management.mjs +0 -243
  337. package/dist/internal/oauth/connected-management.mjs.map +0 -1
  338. package/dist/internal/oauth/connected-reference.mjs +0 -63
  339. package/dist/internal/oauth/connected-reference.mjs.map +0 -1
  340. package/dist/internal/oauth/connected-settlement.d.mts +0 -8
  341. package/dist/internal/oauth/connected-settlement.mjs +0 -141
  342. package/dist/internal/oauth/connected-settlement.mjs.map +0 -1
  343. package/dist/internal/oauth/connected-sign-in.d.mts +0 -10
  344. package/dist/internal/oauth/connected-sign-in.mjs +0 -281
  345. package/dist/internal/oauth/connected-sign-in.mjs.map +0 -1
  346. package/dist/internal/oauth/connected-state.d.mts +0 -14
  347. package/dist/internal/oauth/connected-state.mjs +0 -469
  348. package/dist/internal/oauth/connected-state.mjs.map +0 -1
  349. package/dist/internal/oauth/connected-target.d.mts +0 -21
  350. package/dist/internal/oauth/connected-target.mjs +0 -117
  351. package/dist/internal/oauth/connected-target.mjs.map +0 -1
  352. package/dist/internal/oauth/flow.d.mts +0 -5
  353. package/dist/internal/oauth/flow.mjs +0 -279
  354. package/dist/internal/oauth/flow.mjs.map +0 -1
  355. package/dist/internal/oauth/input.mjs +0 -127
  356. package/dist/internal/oauth/input.mjs.map +0 -1
  357. package/dist/internal/oauth/owner.d.mts +0 -10
  358. package/dist/internal/oauth/owner.mjs +0 -10
  359. package/dist/internal/oauth/owner.mjs.map +0 -1
  360. package/dist/internal/oauth/query-compiler.d.mts +0 -7
  361. package/dist/internal/oauth/query-compiler.mjs +0 -9
  362. package/dist/internal/oauth/query-compiler.mjs.map +0 -1
  363. package/dist/internal/oauth/registration.d.mts +0 -7
  364. package/dist/internal/oauth/registration.mjs +0 -393
  365. package/dist/internal/oauth/registration.mjs.map +0 -1
  366. package/dist/internal/oauth/target.d.mts +0 -31
  367. package/dist/internal/oauth/target.mjs +0 -273
  368. package/dist/internal/oauth/target.mjs.map +0 -1
  369. package/dist/internal/oauth-kernel.d.mts +0 -1423
  370. package/dist/internal/oauth-kernel.mjs +0 -187
  371. package/dist/internal/oauth-kernel.mjs.map +0 -1
  372. package/dist/internal/passkey/admission.d.mts +0 -6
  373. package/dist/internal/passkey/admission.mjs +0 -220
  374. package/dist/internal/passkey/admission.mjs.map +0 -1
  375. package/dist/internal/passkey/credentials.d.mts +0 -14
  376. package/dist/internal/passkey/credentials.mjs +0 -249
  377. package/dist/internal/passkey/credentials.mjs.map +0 -1
  378. package/dist/internal/passkey/enrollment.mjs +0 -138
  379. package/dist/internal/passkey/enrollment.mjs.map +0 -1
  380. package/dist/internal/passkey/flow.d.mts +0 -21
  381. package/dist/internal/passkey/flow.mjs +0 -452
  382. package/dist/internal/passkey/flow.mjs.map +0 -1
  383. package/dist/internal/passkey/management.mjs +0 -231
  384. package/dist/internal/passkey/management.mjs.map +0 -1
  385. package/dist/internal/passkey/registration-ceremony.d.mts +0 -9
  386. package/dist/internal/passkey/registration-ceremony.mjs +0 -129
  387. package/dist/internal/passkey/registration-ceremony.mjs.map +0 -1
  388. package/dist/internal/passkey/registration-custody.d.mts +0 -2
  389. package/dist/internal/passkey/registration-custody.mjs +0 -52
  390. package/dist/internal/passkey/registration-custody.mjs.map +0 -1
  391. package/dist/internal/passkey/registration-write.mjs +0 -211
  392. package/dist/internal/passkey/registration-write.mjs.map +0 -1
  393. package/dist/internal/passkey/state.d.mts +0 -10
  394. package/dist/internal/passkey/state.mjs +0 -234
  395. package/dist/internal/passkey/state.mjs.map +0 -1
  396. package/dist/internal/passkey/target.d.mts +0 -20
  397. package/dist/internal/passkey/target.mjs +0 -200
  398. package/dist/internal/passkey/target.mjs.map +0 -1
  399. package/dist/internal/passkey/write-state.d.mts +0 -17
  400. package/dist/internal/passkey/write-state.mjs +0 -354
  401. package/dist/internal/passkey/write-state.mjs.map +0 -1
  402. package/dist/internal/passkey/write-target.mjs +0 -192
  403. package/dist/internal/passkey/write-target.mjs.map +0 -1
  404. package/dist/internal/passkey-kernel.d.mts +0 -6045
  405. package/dist/internal/passkey-kernel.mjs +0 -49
  406. package/dist/internal/passkey-kernel.mjs.map +0 -1
  407. package/dist/internal/passkeys.d.mts +0 -7
  408. package/dist/internal/passkeys.mjs +0 -387
  409. package/dist/internal/passkeys.mjs.map +0 -1
  410. package/dist/internal/password-credentials.d.mts +0 -11
  411. package/dist/internal/password-kernel.d.mts +0 -278
  412. package/dist/internal/password-kernel.mjs +0 -508
  413. package/dist/internal/password-kernel.mjs.map +0 -1
  414. package/dist/internal/phone-kernel.d.mts +0 -162
  415. package/dist/internal/phone-kernel.mjs +0 -437
  416. package/dist/internal/phone-kernel.mjs.map +0 -1
  417. package/dist/internal/proof-kernel.d.mts +0 -94
  418. package/dist/internal/proof-kernel.mjs +0 -624
  419. package/dist/internal/proof-kernel.mjs.map +0 -1
  420. package/dist/internal/query-operations.d.mts +0 -68
  421. package/dist/internal/session-kernel.d.mts +0 -62
  422. package/dist/internal/session-kernel.mjs +0 -820
  423. package/dist/internal/session-kernel.mjs.map +0 -1
  424. package/dist/internal/sql-oauth-connected-model.mjs +0 -7
  425. package/dist/internal/sql-oauth-connected-model.mjs.map +0 -1
  426. package/dist/internal/sql-query.d.mts +0 -12
  427. package/dist/internal/sql-query.mjs +0 -206
  428. package/dist/internal/sql-query.mjs.map +0 -1
  429. package/dist/internal/step-up-state.d.mts +0 -49
  430. package/dist/internal/transaction-execution-kernel.d.mts +0 -37
  431. package/dist/internal/transaction-execution-kernel.mjs +0 -120
  432. package/dist/internal/transaction-execution-kernel.mjs.map +0 -1
  433. package/dist/internal/transaction-kernel.d.mts +0 -81
  434. package/dist/internal/transaction-kernel.mjs +0 -264
  435. package/dist/internal/transaction-kernel.mjs.map +0 -1
  436. package/src/internal/PasswordPreparedPostconditions.ts +0 -30
  437. package/src/internal/authority-snapshot.ts +0 -91
  438. package/src/internal/email-kernel.ts +0 -1556
  439. package/src/internal/oauth/accounts.ts +0 -839
  440. package/src/internal/oauth/connected-access.ts +0 -388
  441. package/src/internal/oauth/connected-collection.ts +0 -345
  442. package/src/internal/oauth/connected-custody.ts +0 -308
  443. package/src/internal/oauth/connected-flow.ts +0 -562
  444. package/src/internal/oauth/connected-input.ts +0 -147
  445. package/src/internal/oauth/connected-keys.ts +0 -31
  446. package/src/internal/oauth/connected-maintenance.ts +0 -379
  447. package/src/internal/oauth/connected-management.ts +0 -391
  448. package/src/internal/oauth/connected-reference.ts +0 -135
  449. package/src/internal/oauth/connected-settlement.ts +0 -278
  450. package/src/internal/oauth/connected-sign-in.ts +0 -472
  451. package/src/internal/oauth/connected-state.ts +0 -924
  452. package/src/internal/oauth/connected-target.ts +0 -307
  453. package/src/internal/oauth/flow.ts +0 -505
  454. package/src/internal/oauth/input.ts +0 -201
  455. package/src/internal/oauth/owner.ts +0 -22
  456. package/src/internal/oauth/query-compiler.ts +0 -15
  457. package/src/internal/oauth/registration.ts +0 -676
  458. package/src/internal/oauth/target.ts +0 -803
  459. package/src/internal/oauth-kernel.ts +0 -294
  460. package/src/internal/passkey/admission.ts +0 -368
  461. package/src/internal/passkey/credentials.ts +0 -511
  462. package/src/internal/passkey/enrollment.ts +0 -326
  463. package/src/internal/passkey/flow.ts +0 -1062
  464. package/src/internal/passkey/management.ts +0 -426
  465. package/src/internal/passkey/registration-ceremony.ts +0 -283
  466. package/src/internal/passkey/registration-custody.ts +0 -91
  467. package/src/internal/passkey/registration-write.ts +0 -426
  468. package/src/internal/passkey/state.ts +0 -351
  469. package/src/internal/passkey/target.ts +0 -598
  470. package/src/internal/passkey/write-state.ts +0 -692
  471. package/src/internal/passkey/write-target.ts +0 -450
  472. package/src/internal/passkey-kernel.ts +0 -112
  473. package/src/internal/passkeys.ts +0 -652
  474. package/src/internal/password-kernel.ts +0 -1523
  475. package/src/internal/phone-kernel.ts +0 -872
  476. package/src/internal/proof-kernel.ts +0 -1992
  477. package/src/internal/query-operations.ts +0 -91
  478. package/src/internal/session-kernel.ts +0 -2406
  479. package/src/internal/sql-query.ts +0 -304
  480. package/src/internal/transaction-execution-kernel.ts +0 -335
  481. package/src/internal/transaction-kernel.ts +0 -603
@@ -3,8 +3,13 @@ import type { SqlClient } from "effect/sql/SqlClient";
3
3
  import * as Statement from "effect/sql/Statement";
4
4
 
5
5
  import { PersistenceMappingError } from "./mapping-error";
6
+ import { compileSqlExpression } from "./sql-expression";
6
7
  import { Table, identifier } from "./sql-table";
7
- import type { PhysicalTextColumn } from "./storage-validation";
8
+
9
+ export interface PhysicalTextColumn {
10
+ readonly table: { readonly name: string; readonly schema?: string };
11
+ readonly name: string;
12
+ }
8
13
 
9
14
  /** Capture synchronous mapping callbacks without changing SQL execution errors. */
10
15
  export const sqlMapping = <A>(thunk: () => A): Effect.Effect<A, PersistenceMappingError> =>
@@ -16,10 +21,15 @@ export const sqlMapping = <A>(thunk: () => A): Effect.Effect<A, PersistenceMappi
16
21
  /** Physical SQL representation only. Semantic row codecs and policy remain in
17
22
  * the owning mapping. Execute assembled statements without name/result transforms. */
18
23
  export interface SqlTable {
24
+ readonly keys: ReadonlyArray<string>;
19
25
  readonly name: Statement.Fragment;
20
26
  /** Alias reads only; writes continue to target the original physical table. */
21
27
  readonly as: (alias: string) => SqlTable;
22
28
  readonly column: (key: string) => Statement.Fragment;
29
+ /** Unqualified physical name for conflict targets and assignment lists. */
30
+ readonly columnName: (key: string) => Statement.Fragment;
31
+ /** Result projection, including the physical driver's result codec cast. */
32
+ readonly selectedColumn: (key: string) => Statement.Fragment;
23
33
  /** Optional codec-free text candidate. Physical type/collation compatibility
24
34
  * must still be checked against the database before comparing columns. */
25
35
  readonly unencodedTextColumn?: (key: string) => PhysicalTextColumn | undefined;
@@ -36,7 +46,11 @@ export interface SqlTable {
36
46
  readonly update: (values: Readonly<Record<string, unknown>>) => Statement.Fragment;
37
47
  }
38
48
 
39
- export type NativeSqlTables = (table: object) => SqlTable;
49
+ export interface NativeSqlTables {
50
+ (table: object): SqlTable;
51
+ /** Compile a mapped predicate without executing it or acquiring resources. */
52
+ readonly expression: (value: unknown) => Statement.Fragment;
53
+ }
40
54
 
41
55
  /** Prefixes are short ASCII identifiers so ordinal aliases cannot be truncated
42
56
  * by PostgreSQL. Callers distinguish tables by prefix, never by physical names. */
@@ -75,10 +89,10 @@ const directTable = (client: SqlClient, physical: object): SqlTable => {
75
89
  };
76
90
 
77
91
  const value = (key: string, input: unknown): Statement.Fragment => {
78
- const mapped = getColumn(key);
92
+ getColumn(key);
79
93
 
80
94
  if (Statement.isFragment(input)) return input;
81
- const encoded = input === null ? null : mapped.mapToDriverValue(input);
95
+ const encoded = input;
82
96
 
83
97
  return Statement.fragment([
84
98
  Statement.parameter(
@@ -99,9 +113,12 @@ const directTable = (client: SqlClient, physical: object): SqlTable => {
99
113
  const column = (key: string) => client`${reference}.${quote(getColumn(key).options.name)}`;
100
114
 
101
115
  return {
116
+ keys: columns.map(([key]) => key),
102
117
  name: alias === undefined ? name : client`${name} AS ${reference}`,
103
118
  as: bind,
104
119
  column,
120
+ columnName: (key) => quote(getColumn(key).options.name),
121
+ selectedColumn: column,
105
122
  unencodedTextColumn: (key) =>
106
123
  getColumn(key).options.type === "text"
107
124
  ? {
@@ -162,17 +179,21 @@ const directTable = (client: SqlClient, physical: object): SqlTable => {
162
179
  export const makeNativeSqlTables = (
163
180
  client: SqlClient,
164
181
  convert: (client: SqlClient, table: object) => SqlTable = directTable,
182
+ expression: NativeSqlTables["expression"] = (value) => compileSqlExpression(client, value),
165
183
  ): NativeSqlTables => {
166
184
  const tables = new WeakMap<object, SqlTable>();
167
185
 
168
- return (table) => {
169
- const existing = tables.get(table);
186
+ return Object.assign(
187
+ (table: object) => {
188
+ const existing = tables.get(table);
170
189
 
171
- if (existing !== undefined) return existing;
172
- const mapped = convert(client, table);
190
+ if (existing !== undefined) return existing;
191
+ const mapped = convert(client, table);
173
192
 
174
- tables.set(table, mapped);
193
+ tables.set(table, mapped);
175
194
 
176
- return mapped;
177
- };
195
+ return mapped;
196
+ },
197
+ { expression },
198
+ );
178
199
  };
@@ -0,0 +1,576 @@
1
+ import type { LifecycleHooks } from "@yielded/auth/Hooks";
2
+ import * as M from "@yielded/auth/OAuth";
3
+ import { SessionInvalidationWindow } from "@yielded/auth/Sessions";
4
+ import { Crypto, Effect, Option, Schema } from "effect";
5
+ import type { SqlClient } from "effect/sql/SqlClient";
6
+ import type { Fragment } from "effect/sql/Statement";
7
+
8
+ import { type OAuthAccountsMapping, OAuthEligibilityFact } from "../models/oauth-model";
9
+ import type { NativeSqlTables } from "../native-sql-table";
10
+ import { makeConditionalSqlInsert } from "../session-native-record";
11
+ import { anySqlCondition, exactSqlText } from "../sql-change";
12
+ import { makeSqlCommitExecutor, SqlBatchCommit, CurrentSqlCommit } from "../sql-commit";
13
+ import type { SqlExpression, TableModel } from "../table-model";
14
+ import { makeOAuthNativeFlow } from "./native-flow";
15
+ import { makeOAuthNativeMutation } from "./native-mutation";
16
+ import { prepareOAuthNative } from "./native-sign-in";
17
+ import { makeOAuthNativeState } from "./native-state";
18
+ import { invariant, oauthIdentityKey, sameRevision, satisfies, unavailable } from "./state";
19
+
20
+ // Physical metadata is validated by the adapter; schema values remain typed.
21
+ export type OAuthNativeAccountsMapping = OAuthAccountsMapping<
22
+ TableModel,
23
+ TableModel,
24
+ TableModel,
25
+ TableModel,
26
+ TableModel,
27
+ unknown,
28
+ SqlExpression
29
+ >;
30
+
31
+ export const makeNativeOAuthAccountsServices = Effect.fnUntraced(function* (
32
+ tables: NativeSqlTables,
33
+ mapping: OAuthNativeAccountsMapping,
34
+ batch?: SqlBatchCommit["Service"],
35
+ ): Effect.fn.Return<
36
+ { readonly oauthAccountsPersistence: M.OAuthAccountsPersistence["Service"] },
37
+ M.OAuthUnavailable,
38
+ SqlClient | LifecycleHooks | Crypto.Crypto
39
+ > {
40
+ const conditionalInsert = yield* makeConditionalSqlInsert();
41
+ const executor = yield* makeSqlCommitExecutor(unavailable);
42
+ const crypto = yield* Crypto.Crypto;
43
+ const external = yield* Effect.serviceOption(CurrentSqlCommit);
44
+ const state = yield* makeOAuthNativeState(tables, mapping);
45
+ const mutation = yield* makeOAuthNativeMutation(tables, mapping, batch !== undefined);
46
+
47
+ const flow = yield* makeOAuthNativeFlow(
48
+ tables,
49
+ mapping,
50
+ M.OAuthLinkFlow,
51
+ "link",
52
+ (value) => value.context.revision.subjectId,
53
+ batch !== undefined,
54
+ );
55
+
56
+ const { sql, now, credential, authority } = state;
57
+
58
+ const c = mapping.credential;
59
+ const a = mapping.authority;
60
+
61
+ const run = <A, E, R>(
62
+ effect: Effect.Effect<A, E, R>,
63
+ mode: "transaction" | "statement" = "transaction",
64
+ ) => {
65
+ const work = Effect.provideService(effect, Crypto.Crypto, crypto);
66
+
67
+ return batch === undefined
68
+ ? executor.run(work, mode)
69
+ : executor.batch(work).pipe(Effect.provideService(SqlBatchCommit, batch));
70
+ };
71
+
72
+ const read = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
73
+ executor.read(Effect.provideService(effect, Crypto.Crypto, crypto));
74
+
75
+ const credentialCondition = (nativeId: unknown, key: string, id: string, revision: unknown) =>
76
+ sql.and([
77
+ sql`${credential.column(c.subjectId)} = ${credential.value(c.subjectId, nativeId)}`,
78
+ exactSqlText(sql, credential.column(c.identityKey), credential.value(c.identityKey, key)),
79
+ exactSqlText(sql, credential.column(c.credentialId), credential.value(c.credentialId, id)),
80
+ exactSqlText(
81
+ sql,
82
+ credential.column(c.credentialRevision),
83
+ credential.value(c.credentialRevision, revision),
84
+ ),
85
+ tables.expression(c.activeCondition),
86
+ ]);
87
+
88
+ const service: M.OAuthAccountsPersistence["Service"] = {
89
+ capture: (input) =>
90
+ read(Effect.map(state.capture(input.subjectId, false), (current) => current?.revision)),
91
+ issue: (original, prepare) =>
92
+ run(
93
+ Effect.gen(function* () {
94
+ const input = M.snapshotOAuthSync(M.OAuthLinkFlow, original);
95
+ // Flow issue has no protected authority mutation; completion owns the one
96
+ // subject-first recheck of this authenticated begin authorization.
97
+ const issued = yield* flow.issue(input);
98
+
99
+ return yield* prepareOAuthNative(
100
+ issued ? { _tag: "Issued", flow: input } : { _tag: "Rejected" },
101
+ prepare,
102
+ );
103
+ }),
104
+ flow.mysql ? "transaction" : "statement",
105
+ ),
106
+ consume: (original, prepare) =>
107
+ run(
108
+ Effect.gen(function* () {
109
+ const input = M.snapshotOAuthSync(M.OAuthLinkAccess, original);
110
+ const consumed = yield* flow.consume(input);
111
+
112
+ return yield* prepareOAuthNative(
113
+ consumed === undefined ? { _tag: "Rejected" } : { _tag: "Consumed", flow: consumed },
114
+ prepare,
115
+ );
116
+ }),
117
+ flow.mysql ? "transaction" : "statement",
118
+ ),
119
+ link: (original, prepare) =>
120
+ run(
121
+ Effect.gen(function* () {
122
+ const flow = M.snapshotOAuthSync(M.OAuthLinkFlow, original.flow);
123
+ const verified = M.snapshotOAuthSync(M.OAuthVerifiedExternalIdentity, original.identity);
124
+ const context = flow.context;
125
+
126
+ const current = yield* mutation.capture(context.revision.subjectId, batch === undefined);
127
+ const rejected = () => prepareOAuthNative({ _tag: "Rejected" } as const, prepare);
128
+
129
+ if (
130
+ current === undefined ||
131
+ !sameRevision(context.revision, current.revision) ||
132
+ context.provider !== verified.identity.provider ||
133
+ context.issuer !== verified.identity.issuer ||
134
+ current.now >= context.expiresAtMillis
135
+ )
136
+ return yield* rejected();
137
+
138
+ const deadline = Math.min(
139
+ context.expiresAtMillis,
140
+ context.authorization.validUntilMillis,
141
+ );
142
+
143
+ const before = sql.and([
144
+ mutation.authorityCondition(current.nativeId, current.revision),
145
+ sql`${now} < ${deadline}`,
146
+ ]);
147
+
148
+ if (batch !== undefined) yield* mutation.assert(before);
149
+ const identityKey = yield* mutation.ensureOwnership(verified.identity, current.nativeId);
150
+
151
+ if (identityKey === undefined)
152
+ return yield* prepareOAuthNative({ _tag: "Conflict" }, prepare);
153
+
154
+ const identityCondition = exactSqlText(
155
+ sql,
156
+ credential.column(c.identityKey),
157
+ credential.value(c.identityKey, identityKey),
158
+ );
159
+
160
+ // Fixed D1 plans classify an existing login before staging writes. Native
161
+ // owners attempt the unique insert and read only its conflict outcome.
162
+ const existing =
163
+ batch === undefined
164
+ ? []
165
+ : yield* sql`select ${credential.fields("login_")} from ${credential.name} where ${identityCondition} and ${before}`;
166
+
167
+ invariant(existing.length <= 1);
168
+ let credentialId = yield* mapping.allocateCredentialId;
169
+ let credentialRevision = yield* mapping.allocateRevision;
170
+ let changed = false;
171
+ let revision = current.revision;
172
+
173
+ if (existing.length === 0) {
174
+ const values = {
175
+ ...c.encodeInsert({
176
+ moduleId: context.moduleId,
177
+ subjectId: current.nativeId,
178
+ identityKey,
179
+ credentialId,
180
+ credentialRevision,
181
+ }),
182
+ [c.moduleId]: context.moduleId,
183
+ [c.subjectId]: current.nativeId,
184
+ [c.identityKey]: identityKey,
185
+ [c.credentialId]: credentialId,
186
+ [c.credentialRevision]: credentialRevision,
187
+ };
188
+
189
+ invariant(c.isActiveStatus(values[c.status]));
190
+
191
+ const inserted = yield* mutation.insertUnique(
192
+ sql`${conditionalInsert(credential, values, before)} ${sql.onDialectOrElse({ mysql: () => sql``, orElse: () => sql`on conflict do nothing` })}`,
193
+ );
194
+
195
+ changed = inserted === 1;
196
+ }
197
+ if (changed) {
198
+ const factor = {
199
+ ...a.encodeInsert({
200
+ subjectId: current.nativeId,
201
+ credentialId,
202
+ revision: credentialRevision,
203
+ }),
204
+ [a.subjectId]: current.nativeId,
205
+ [a.credentialId]: credentialId,
206
+ [a.revision]: credentialRevision,
207
+ };
208
+
209
+ invariant(a.isActiveStatus(factor[a.status]));
210
+ invariant(
211
+ (yield* mutation.change(
212
+ conditionalInsert(
213
+ authority,
214
+ factor,
215
+ sql.and([
216
+ before,
217
+ sql`exists(select 1 from ${credential.name} where ${credentialCondition(current.nativeId, identityKey, credentialId, credentialRevision)})`,
218
+ ]),
219
+ ),
220
+ )) === 1,
221
+ );
222
+ revision = {
223
+ ...current.revision,
224
+ credentials: [
225
+ ...current.revision.credentials,
226
+ { credentialId, revision: credentialRevision },
227
+ ].sort((left, right) => left.credentialId.localeCompare(right.credentialId)),
228
+ };
229
+ } else {
230
+ const rows =
231
+ existing.length === 1
232
+ ? existing
233
+ : yield* sql`select ${credential.fields("login_")} from ${credential.name} where ${identityCondition} and ${before}`;
234
+
235
+ invariant(rows.length === 1);
236
+ const row = credential.decode(rows[0]!, "login_");
237
+
238
+ invariant(
239
+ mapping.subjectId.equals(row[c.subjectId], current.nativeId) &&
240
+ row[c.moduleId] === context.moduleId &&
241
+ c.isActiveStatus(row[c.status]),
242
+ );
243
+ credentialId = yield* Schema.decodeUnknownEffect(
244
+ M.OAuthCredentialSnapshot.fields.credentialId,
245
+ )(row[c.credentialId]);
246
+ credentialRevision = yield* Schema.decodeUnknownEffect(
247
+ M.OAuthCredentialSnapshot.fields.credentialRevision,
248
+ )(row[c.credentialRevision]);
249
+ invariant(
250
+ revision.credentials.some(
251
+ (entry) =>
252
+ entry.credentialId === credentialId && entry.revision === credentialRevision,
253
+ ),
254
+ );
255
+ }
256
+ if (Option.isSome(external))
257
+ yield* mutation.finish(
258
+ "oauth-link-authority",
259
+ sql.and([
260
+ mutation.authorityCondition(current.nativeId, revision),
261
+ sql`${now} < ${deadline}`,
262
+ sql`exists(select 1 from ${credential.name} where ${credentialCondition(current.nativeId, identityKey, credentialId, credentialRevision)})`,
263
+ sql`exists(select 1 from ${mutation.ownership.name} where ${mutation.ownerCondition(identityKey, current.nativeId)})`,
264
+ ]),
265
+ );
266
+
267
+ const result = yield* Schema.decodeEffect(Schema.toType(M.OAuthCredentialSnapshot))({
268
+ moduleId: context.moduleId,
269
+ identity: verified.identity,
270
+ credentialId,
271
+ credentialRevision,
272
+ revision,
273
+ requirement: mapping.subject.decodeAuthenticationRequirement(current.subject),
274
+ });
275
+
276
+ return yield* prepareOAuthNative(
277
+ { _tag: "Linked", credential: result, changed },
278
+ prepare,
279
+ );
280
+ }),
281
+ ),
282
+ readCredential: (original) =>
283
+ read(
284
+ Effect.suspend(() =>
285
+ state.readCredential(M.snapshotOAuthSync(M.OAuthCredentialKey, original)),
286
+ ),
287
+ ),
288
+ unlink: (original, prepare) =>
289
+ run(
290
+ Effect.gen(function* () {
291
+ const captured = M.snapshotOAuthSync(M.OAuthCredentialSnapshot, original.credential);
292
+
293
+ const authorization = M.snapshotOAuthSync(
294
+ M.OAuthActionAuthorization,
295
+ original.authorization,
296
+ );
297
+
298
+ const invalidation = M.snapshotOAuthSync(
299
+ SessionInvalidationWindow,
300
+ original.invalidation,
301
+ );
302
+
303
+ invariant(
304
+ invalidation.trigger === "credential-change" &&
305
+ invalidation.oldAuthenticationEvidence === "rejected" &&
306
+ (mapping.sessionInvalidation === "same-authority-immediate"
307
+ ? (invalidation.existingSessions === "immediate" &&
308
+ invalidation.maximumExposureMillis === 0) ||
309
+ (invalidation.existingSessions === "cache-expiry" &&
310
+ invalidation.maximumExposureMillis > 0)
311
+ : invalidation.existingSessions === "original-absolute-expiry"),
312
+ );
313
+
314
+ const current = yield* mutation.capture(captured.revision.subjectId, batch === undefined);
315
+ const rejected = () => prepareOAuthNative({ _tag: "Rejected" } as const, prepare);
316
+
317
+ if (current === undefined || !sameRevision(current.revision, captured.revision))
318
+ return yield* rejected();
319
+
320
+ const key = yield* oauthIdentityKey(captured.identity);
321
+
322
+ const target = credentialCondition(
323
+ current.nativeId,
324
+ key,
325
+ captured.credentialId,
326
+ captured.credentialRevision,
327
+ );
328
+
329
+ const eligible = new Map<
330
+ string,
331
+ { readonly fact: OAuthEligibilityFact; readonly conditions: Array<Fragment> }
332
+ >();
333
+
334
+ invariant(mapping.eligibility.length <= 32);
335
+
336
+ const sources = mapping.eligibility.map((source) => ({
337
+ source,
338
+ table: tables(source.table),
339
+ }));
340
+
341
+ // Each branch owns distinct projection columns. Empty scalar subqueries
342
+ // retain each physical SQL type across derived SELECT and UNION coercion.
343
+ invariant(sources.reduce((count, entry) => count + entry.table.keys.length, 0) <= 1024);
344
+
345
+ const branches = sources.map(({ source, table }, branch) => {
346
+ const fields = sources.flatMap((entry, index) =>
347
+ entry.table.keys.map(
348
+ (key, ordinal) =>
349
+ sql`${index === branch ? table.selectedColumn(key) : sql`(select ${entry.table.selectedColumn(key)} from ${entry.table.name} where 1 = 0)`} as ${sql(`method_${index}_${ordinal}`)}`,
350
+ ),
351
+ );
352
+
353
+ // The bounded array ordinal is an integer SQL literal, matching the sentinel.
354
+ return sql`select * from (select ${sql.literal(String(branch))} as method_branch, 1 as target_present, ${sql.join(", ", false)(fields)} from ${table.name}
355
+ where ${table.column(source.subjectId)} = ${table.value(source.subjectId, current.nativeId)} and ${tables.expression(source.condition(current.nativeId))}
356
+ order by ${table.column(source.credentialId)} limit 65) as ${sql(`eligible_${branch}`)}`;
357
+ });
358
+
359
+ const allowed = sql.and([
360
+ mutation.authorityCondition(current.nativeId, current.revision),
361
+ sql`exists(select 1 from ${credential.name} where ${target} and ${exactSqlText(sql, credential.column(c.moduleId), credential.value(c.moduleId, captured.moduleId))})`,
362
+ ]);
363
+
364
+ const empty = sources.flatMap((entry, index) =>
365
+ entry.table.keys.map(
366
+ (key, ordinal) =>
367
+ sql`(select ${entry.table.selectedColumn(key)} from ${entry.table.name} where 1 = 0) as ${sql(`method_${index}_${ordinal}`)}`,
368
+ ),
369
+ );
370
+
371
+ const sentinel = sql`select -1 as method_branch, case when ${allowed} then 1 else 0 end as target_present ${empty.length === 0 ? sql`` : sql`, ${sql.join(", ", false)(empty)}`}`;
372
+ const candidates = yield* sql`${sql.join(" union all ", false)([sentinel, ...branches])}`;
373
+ const presence = candidates.find((row) => Number(row.method_branch) === -1);
374
+
375
+ invariant(presence !== undefined);
376
+ if (Number(presence.target_present) !== 1) return yield* rejected();
377
+
378
+ const counts = new Map<number, number>();
379
+
380
+ for (const row of candidates) {
381
+ const branch = yield* Schema.decodeEffect(Schema.Int)(Number(row.method_branch));
382
+
383
+ if (branch === -1) continue;
384
+ const entry = sources[branch];
385
+
386
+ invariant(entry !== undefined);
387
+ const count = (counts.get(branch) ?? 0) + 1;
388
+
389
+ invariant(count <= 64);
390
+ counts.set(branch, count);
391
+ const { source, table } = entry;
392
+ const decoded = source.decode(table.decode(row, `method_${branch}_`));
393
+
394
+ if (decoded === undefined) continue;
395
+ const parsed = yield* Schema.decodeEffect(OAuthEligibilityFact)(decoded);
396
+ const fact = { ...parsed, factors: [...new Set(parsed.factors)].sort() };
397
+
398
+ if (
399
+ fact.credentialId !== captured.credentialId &&
400
+ current.revision.credentials.some(
401
+ (item) =>
402
+ item.credentialId === fact.credentialId && item.revision === fact.revision,
403
+ )
404
+ ) {
405
+ const condition = sql`exists(select 1 from ${table.name} where
406
+ ${table.column(source.subjectId)} = ${table.value(source.subjectId, current.nativeId)}
407
+ and ${exactSqlText(sql, table.column(source.credentialId), table.value(source.credentialId, fact.credentialId))}
408
+ and ${exactSqlText(sql, table.column(source.revision), table.value(source.revision, fact.revision))}
409
+ and ${tables.expression(source.condition(current.nativeId))})`;
410
+
411
+ const previous = eligible.get(fact.credentialId);
412
+
413
+ if (previous === undefined)
414
+ eligible.set(fact.credentialId, { fact, conditions: [condition] });
415
+ else {
416
+ const encode = Schema.encodeEffect(
417
+ Schema.fromJsonString(Schema.toCodecJson(OAuthEligibilityFact)),
418
+ );
419
+
420
+ invariant((yield* encode(previous.fact)) === (yield* encode(fact)));
421
+ previous.conditions.push(condition);
422
+ }
423
+ }
424
+ }
425
+ const methods = [...eligible.values()];
426
+ const facts = methods.map((entry) => entry.fact);
427
+ const requirement = mapping.subject.decodeAuthenticationRequirement(current.subject);
428
+
429
+ if (!facts.some((fact) => fact.usablePrimary) || !satisfies(facts, requirement))
430
+ return yield* prepareOAuthNative({ _tag: "LastSignInMethod" }, prepare);
431
+
432
+ const present = (entry: (typeof methods)[number]) => anySqlCondition(entry.conditions);
433
+
434
+ // Preserve a usable remaining method through application work. These
435
+ // predicates describe eligible credential identities, not observed rows.
436
+ const remaining = sql.and([
437
+ anySqlCondition(methods.filter((entry) => entry.fact.usablePrimary).map(present)),
438
+ anySqlCondition(
439
+ requirement.alternatives.map((alternative) =>
440
+ sql.and([
441
+ ...alternative.factors.map((factor) =>
442
+ anySqlCondition(
443
+ methods.filter((entry) => entry.fact.factors.includes(factor)).map(present),
444
+ ),
445
+ ),
446
+ sql`(${sql.join(" + ", false)(methods.map((entry) => sql`case when ${present(entry)} then 1 else 0 end`))}) >= ${alternative.minimumCredentials}`,
447
+ anySqlCondition(
448
+ methods
449
+ .filter(
450
+ (entry) =>
451
+ (!alternative.userVerified || entry.fact.userVerified) &&
452
+ (!alternative.phishingResistant || entry.fact.phishingResistant),
453
+ )
454
+ .map(present),
455
+ ),
456
+ ]),
457
+ ),
458
+ ),
459
+ ]);
460
+
461
+ const required = sql.and([
462
+ mutation.subjectCondition(current.nativeId, current.revision),
463
+ mutation.factorCondition(current.nativeId, current.revision),
464
+ sql`${now} < ${authorization.validUntilMillis}`,
465
+ remaining,
466
+ ]);
467
+
468
+ if (batch !== undefined)
469
+ yield* mutation.assert(
470
+ sql.and([
471
+ mutation.authorityCondition(current.nativeId, current.revision),
472
+ sql`${now} < ${authorization.validUntilMillis}`,
473
+ remaining,
474
+ ]),
475
+ );
476
+
477
+ const nextSecurityRevision = mapping.subject.nextSecurityRevision(
478
+ current.revision.securityRevision,
479
+ );
480
+
481
+ invariant(nextSecurityRevision !== current.revision.securityRevision);
482
+ const subject = mutation.subject;
483
+
484
+ invariant(
485
+ (yield* mutation.change(sql`${subject.update({ [mapping.subject.securityRevision]: nextSecurityRevision })}
486
+ where ${subject.column(mapping.subject.id)} = ${subject.value(mapping.subject.id, current.nativeId)} and ${required}`)) ===
487
+ 1,
488
+ );
489
+ invariant(
490
+ (yield* mutation.change(sql`delete from ${credential.name} where ${target}`)) === 1,
491
+ );
492
+ invariant(
493
+ (yield* mutation.change(sql`delete from ${authority.name}
494
+ where ${authority.column(a.subjectId)} = ${authority.value(a.subjectId, current.nativeId)}
495
+ and ${exactSqlText(sql, authority.column(a.credentialId), authority.value(a.credentialId, captured.credentialId))}
496
+ and ${exactSqlText(sql, authority.column(a.revision), authority.value(a.revision, captured.credentialRevision))}`)) ===
497
+ 1,
498
+ );
499
+
500
+ const cleanup = {
501
+ subjectId: current.nativeId,
502
+ revision: current.revision,
503
+ removedCredentialId: captured.credentialId,
504
+ };
505
+
506
+ invariant(mapping.cleanup.length <= 32);
507
+ const absent = [];
508
+
509
+ for (const source of mapping.cleanup) {
510
+ const table = tables(source.table);
511
+
512
+ const condition = sql.and([
513
+ sql`${table.column(source.subjectId)} = ${table.value(source.subjectId, current.nativeId)}`,
514
+ tables.expression(source.condition(cleanup)),
515
+ ]);
516
+
517
+ const deletion = sql`delete from ${table.name} where ${condition}`;
518
+
519
+ // Deleting zero or many subject-scoped session/pending rows is valid.
520
+ absent.push(sql`not exists(select 1 from ${table.name} where ${condition})`);
521
+ yield* mutation.execute(deletion);
522
+ }
523
+
524
+ const referenced = sql.or([
525
+ sql`exists(select 1 from ${credential.name} where ${exactSqlText(sql, credential.column(c.identityKey), credential.value(c.identityKey, key))})`,
526
+ tables.expression(
527
+ mapping.otherReferences({ identityKey: key, subjectId: current.nativeId }),
528
+ ),
529
+ ]);
530
+
531
+ yield* mutation.releaseOwnership(key, current.nativeId, referenced);
532
+
533
+ const revision = {
534
+ ...current.revision,
535
+ securityRevision: nextSecurityRevision,
536
+ credentials: current.revision.credentials.filter(
537
+ (item) => item.credentialId !== captured.credentialId,
538
+ ),
539
+ };
540
+
541
+ yield* mutation.finish(
542
+ "oauth-unlink-authority",
543
+ sql.and([
544
+ mutation.authorityCondition(current.nativeId, revision),
545
+ sql`${now} < ${authorization.validUntilMillis}`,
546
+ remaining,
547
+ sql`not exists(select 1 from ${credential.name} where ${target})`,
548
+ ...absent,
549
+ ]),
550
+ );
551
+
552
+ return yield* prepareOAuthNative(
553
+ {
554
+ _tag: "Unlinked",
555
+ result: { _tag: "Unlinked", credentialId: captured.credentialId, invalidation },
556
+ },
557
+ prepare,
558
+ );
559
+ }),
560
+ ),
561
+ cleanup: (original, prepare) =>
562
+ run(
563
+ Effect.gen(function* () {
564
+ const input = M.snapshotOAuthSync(M.OAuthCleanupInput, original);
565
+
566
+ return yield* prepareOAuthNative(
567
+ yield* flow.cleanup(input.moduleId, input.limit),
568
+ prepare,
569
+ );
570
+ }),
571
+ "statement",
572
+ ),
573
+ };
574
+
575
+ return { oauthAccountsPersistence: service };
576
+ });