@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
@@ -0,0 +1,212 @@
1
+ import { PersistenceMappingError, isMappedConstraintConflict } from "./mapping-error.mjs";
2
+ import { sqlBatchAssertion } from "./d1-planning.mjs";
3
+ import { CurrentSqlCommit, SqlBatchCommit, makeSqlCommitExecutor, registerSqlBatchPostcondition, registerSqlPostcondition } from "./sql-commit.mjs";
4
+ import { normalizeSessionOperation, sameSessionRevision, sessionInvariant, sessionUnavailable } from "./session-native-state.mjs";
5
+ import { makeConditionalSqlInsert, makeNativeSessionRecords, sameSessionRecord } from "./session-native-record.mjs";
6
+ import { prepareNativeSession } from "./session-native-pending.mjs";
7
+ import { allocateSessionValue } from "./session-policy.mjs";
8
+ import { sessionEvidenceDeadline } from "./session-native-authority.mjs";
9
+ import { makeNativeSessionPendingReader } from "./session-native-pending-read.mjs";
10
+ import { decodeStepUpIntent, encodeStepUpIntent, stepUpIntentLive, validateStepUpPlan } from "./step-up-state.mjs";
11
+ import { DateTime, Effect, Option, Schema } from "effect";
12
+ import { SessionConflict, SessionMetadata, SessionStepUpInvalid, StaleAuthentication } from "@yielded/auth/Sessions";
13
+ import { AuthenticationAssurance } from "@yielded/auth/Operations";
14
+ //#region src/internal/session-native-step-up.ts
15
+ const assuranceJson = Schema.encodeSync(Schema.fromJsonString(SessionMetadata.fields.assurance));
16
+ const makeNativeSessionStepUpServices = Effect.fnUntraced(function* (tables, mapping) {
17
+ const batch = yield* SqlBatchCommit;
18
+ const { state, pending, read: readPending } = yield* makeNativeSessionPendingReader(tables, mapping, batch !== void 0);
19
+ const conditionalInsert = yield* makeConditionalSqlInsert();
20
+ const records = mapping.source.kind === "Stateful" ? yield* makeNativeSessionRecords(tables, {
21
+ ...mapping,
22
+ ...mapping.source
23
+ }) : void 0;
24
+ const executor = yield* makeSqlCommitExecutor(sessionUnavailable), external = yield* Effect.serviceOption(CurrentSqlCommit);
25
+ const { sql, now } = state;
26
+ const native = (work, mode = "transaction") => batch === void 0 ? executor.operation(normalizeSessionOperation(work), mode) : executor.operationBatch(normalizeSessionOperation(work)).pipe(Effect.provideService(SqlBatchCommit, batch));
27
+ const decode = Effect.fnUntraced(function* (stored) {
28
+ const intent = yield* decodeStepUpIntent(stored.snapshot);
29
+ sessionInvariant(intent.digest === stored.digest && intent.version === stored.version && intent.flowId === stored.flowId && intent.bindingDigest === stored.bindingDigest && intent.revision.subjectId === stored.subjectId && DateTime.toEpochMillis(intent.expiresAt) === stored.expiresAtMillis && intent.attemptLimit === stored.attemptLimit);
30
+ return intent;
31
+ });
32
+ const source = Effect.fnUntraced(function* (intent, instant) {
33
+ if (!stepUpIntentLive(intent, mapping.source.kind, DateTime.makeUnsafe(instant))) return void 0;
34
+ const subject = yield* mapping.subjectId.toNative(intent.revision.subjectId);
35
+ if (mapping.source.kind === "Stateful") {
36
+ sessionInvariant(records !== void 0);
37
+ const nativeSession = yield* mapping.source.sessionId.toNative(intent.sourceSessionId), { s, table } = records;
38
+ const rows = yield* sql`select ${table.fields("source_")} from ${table.name} where ${state.id(table, s.subjectId, subject)} and ${state.id(table, s.sessionId, nativeSession)} and ${records.live(table)} limit 2`;
39
+ sessionInvariant(rows.length <= 1);
40
+ if (rows[0] === void 0) return void 0;
41
+ const record = yield* records.decode(table.decode(rows[0], "source_"));
42
+ if (record.credentialVersion !== intent.sourceCredentialVersion || record.subjectId !== intent.revision.subjectId || record.sessionId !== intent.sourceSessionId || record.securityRevision !== intent.revision.securityRevision || DateTime.toEpochMillis(record.assurance.authenticatedAt) !== DateTime.toEpochMillis(intent.sourceAuthenticatedAt) || DateTime.toEpochMillis(record.expiresAt) !== DateTime.toEpochMillis(intent.sourceExpiresAt) || DateTime.toEpochMillis(record.absoluteExpiresAt) !== DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt)) return void 0;
43
+ return {
44
+ record,
45
+ condition: sql`exists(select 1 from ${table.name} where ${yield* records.owner(record)} and ${records.live(table)})`
46
+ };
47
+ }
48
+ if (mapping.source.kind === "StateAssistedSigned") {
49
+ const t = mapping.source.tombstone, table = tables(t.table), session = yield* mapping.source.sessionId.toNative(intent.sourceSessionId);
50
+ const condition = sql`not exists(select 1 from ${table.name} where ${state.exact(table, t.moduleId, mapping.moduleId)} and ${state.id(table, t.subjectId, subject)} and ${state.id(table, t.sessionId, session)})`;
51
+ if ((yield* sql`select 1 where ${condition}`).length !== 1) return void 0;
52
+ return {
53
+ record: void 0,
54
+ condition
55
+ };
56
+ }
57
+ return {
58
+ record: void 0,
59
+ condition: sql`1 = 1`
60
+ };
61
+ });
62
+ const final = Effect.fnUntraced(function* (name, condition) {
63
+ if (batch !== void 0) yield* registerSqlBatchPostcondition({
64
+ name,
65
+ statement: sqlBatchAssertion(sql, condition)
66
+ });
67
+ else if (Option.isSome(external)) yield* registerSqlPostcondition({
68
+ name,
69
+ check: Effect.gen(function* () {
70
+ sessionInvariant((yield* sql`select 1 where ${condition}`).length === 1);
71
+ }).pipe(Effect.mapError((cause) => PersistenceMappingError.make({
72
+ operation: "decode",
73
+ cause
74
+ })))
75
+ });
76
+ });
77
+ const snapshotCondition = (intent, snapshot) => sql.and([
78
+ state.exact(pending.table, pending.p.snapshot, snapshot),
79
+ sql`${tables.expression(mapping.clock.toMillis(pending.table.column(pending.p.expiresAt)))} = ${DateTime.toEpochMillis(intent.expiresAt)}`,
80
+ sql`${pending.table.column(pending.p.attemptLimit)} = ${pending.table.value(pending.p.attemptLimit, intent.attemptLimit)}`
81
+ ]);
82
+ return { sessionStepUpPersistence: {
83
+ create: (input, _now, project) => native(Effect.gen(function* () {
84
+ const current = yield* state.read(input.revision.subjectId, true);
85
+ if (current === void 0 || !sameSessionRevision(current.revision, input.revision)) return yield* StaleAuthentication.make({});
86
+ const original = yield* source(input, current.now);
87
+ if (original === void 0) return yield* StaleAuthentication.make({});
88
+ const version = yield* allocateSessionValue("interactive", mapping.pending.allocateVersion, mapping.pending.allocateVersionSync);
89
+ const intent = {
90
+ ...input,
91
+ version
92
+ }, snapshot = yield* encodeStepUpIntent(intent);
93
+ const condition = sql`${state.authorityCondition(intent.revision, current.native)} and ${state.policyCondition(current.row, current.native)} and ${original.condition} and ${now} >= ${DateTime.toEpochMillis(intent.sourceAuthenticatedAt)} and ${now} < ${DateTime.toEpochMillis(intent.expiresAt)}`;
94
+ yield* pending.insert({
95
+ moduleId: mapping.moduleId,
96
+ kind: "StepUp",
97
+ digest: intent.digest,
98
+ version,
99
+ flowId: intent.flowId,
100
+ subjectId: current.native,
101
+ bindingDigest: intent.bindingDigest,
102
+ snapshot,
103
+ expiresAt: intent.expiresAt,
104
+ attemptLimit: intent.attemptLimit
105
+ }, condition).pipe(Effect.mapError((error) => isMappedConstraintConflict(mapping.isConstraintConflict, error) ? SessionConflict.make({}) : error));
106
+ yield* final("session-step-up-issuance", sql`${condition} and exists(select 1 from ${pending.table.name} where ${pending.predicate(pending.table, "StepUp", intent.digest)} and ${state.exact(pending.table, pending.p.version, version)} and ${snapshotCondition(intent, snapshot)})`);
107
+ return yield* prepareNativeSession(intent, project);
108
+ })),
109
+ read: ({ digest }) => executor.read(Effect.gen(function* () {
110
+ const selected = yield* readPending("StepUp", digest);
111
+ if (selected === void 0) return void 0;
112
+ const intent = yield* decode(selected.record), current = selected.authority;
113
+ if (!sameSessionRevision(current.revision, intent.revision) || !stepUpIntentLive(intent, mapping.source.kind, DateTime.makeUnsafe(current.now))) return void 0;
114
+ return {
115
+ intent,
116
+ requirement: current.requirement
117
+ };
118
+ })).pipe(Effect.flatMap((value) => value === void 0 ? Effect.fail(SessionStepUpInvalid.make({})) : Effect.succeed(value))),
119
+ reject: ({ digest }, project) => native(Effect.gen(function* () {
120
+ yield* pending.reject("StepUp", digest);
121
+ return yield* prepareNativeSession({ _tag: "Rejected" }, project);
122
+ }), "statement"),
123
+ complete: (plan, project) => native(Effect.gen(function* () {
124
+ yield* validateStepUpPlan(plan);
125
+ const { intent, evidence, replacement } = plan;
126
+ const current = yield* state.read(intent.revision.subjectId, true);
127
+ if (current === void 0 || !sameSessionRevision(current.revision, evidence.revision) || !sameSessionRevision(current.revision, intent.revision)) return yield* StaleAuthentication.make({});
128
+ const original = yield* source(intent, current.now);
129
+ if (original === void 0) return yield* SessionStepUpInvalid.make({});
130
+ if (plan.source.guard._tag === "Stateful") {
131
+ if (original.record === void 0 || original.record.digest !== plan.source.guard.digest || !sameSessionRecord(original.record, {
132
+ ...plan.source.inspection.session,
133
+ provenance: plan.source.inspection.provenance,
134
+ credentialVersion: plan.source.inspection.credentialVersion,
135
+ digest: plan.source.guard.digest
136
+ })) return yield* SessionStepUpInvalid.make({});
137
+ }
138
+ const base = yield* sessionEvidenceDeadline(evidence, current.requirement, current.now), profile = yield* sessionEvidenceDeadline(evidence, intent.requirement, current.now);
139
+ if (assuranceJson(AuthenticationAssurance.make({
140
+ ...base.assessed.assurance,
141
+ authenticatedAt: profile.assessed.assurance.authenticatedAt
142
+ })) !== assuranceJson(replacement.inspection.session.assurance)) return yield* SessionStepUpInvalid.make({});
143
+ const deadline = Math.min(base.validUntil, profile.validUntil, DateTime.toEpochMillis(intent.expiresAt), DateTime.toEpochMillis(replacement.inspection.session.expiresAt));
144
+ if (current.now >= deadline || DateTime.toEpochMillis(replacement.inspection.session.issuedAt) > current.now || DateTime.toEpochMillis(replacement.inspection.session.expiresAt) > DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt)) return yield* SessionStepUpInvalid.make({});
145
+ const snapshot = yield* encodeStepUpIntent(intent);
146
+ const authority = sql`${state.authorityCondition(evidence.revision, current.native)} and ${state.policyCondition(current.row, current.native)} and ${now} >= ${current.now} and ${now} < ${deadline}`;
147
+ yield* pending.consume("StepUp", intent, current.native, sql`${snapshotCondition(intent, snapshot)} and ${authority} and ${original.condition}`);
148
+ let replacementCondition = sql`1 = 1`;
149
+ let replacementRecord;
150
+ if (replacement._tag === "Stateful" && mapping.source.kind === "Stateful") {
151
+ sessionInvariant(records !== void 0 && original.record !== void 0);
152
+ const next = {
153
+ ...replacement.inspection.session,
154
+ digest: replacement.nextDigest,
155
+ credentialVersion: replacement.inspection.credentialVersion,
156
+ provenance: replacement.inspection.provenance
157
+ };
158
+ if (next.digest === original.record.digest || next.credentialVersion === original.record.credentialVersion) return yield* SessionStepUpInvalid.make({});
159
+ const { s, table } = records;
160
+ const values = {
161
+ ...s.encodeRotation(next),
162
+ [s.digest]: next.digest,
163
+ [s.issuedAt]: s.encodeInstant(next.issuedAt),
164
+ [s.expiresAt]: s.encodeInstant(next.expiresAt),
165
+ [s.absoluteExpiresAt]: s.encodeInstant(next.absoluteExpiresAt),
166
+ [s.securityRevision]: next.securityRevision
167
+ };
168
+ yield* pending.stage(sql`${table.update(values)} where ${yield* records.owner(original.record)} and ${records.live(table)} and ${authority}`, 1);
169
+ replacementCondition = sql`exists(select 1 from ${table.name} where ${yield* records.owner(next)} and ${records.encodedCondition(values)} and ${records.live(table)})`;
170
+ replacementRecord = next;
171
+ } else if (replacement._tag === "StateAssistedSigned" && mapping.source.kind === "StateAssistedSigned") {
172
+ const t = mapping.source.tombstone, table = tables(t.table), session = yield* mapping.source.sessionId.toNative(intent.sourceSessionId);
173
+ const values = {
174
+ ...t.encodeInsert({
175
+ moduleId: mapping.moduleId,
176
+ subjectId: current.native,
177
+ sessionId: session,
178
+ absoluteExpiresAt: intent.sourceAbsoluteExpiresAt
179
+ }),
180
+ [t.moduleId]: mapping.moduleId,
181
+ [t.subjectId]: current.native,
182
+ [t.sessionId]: session,
183
+ [t.absoluteExpiresAt]: t.encodeInstant(intent.sourceAbsoluteExpiresAt)
184
+ };
185
+ yield* pending.stage(conditionalInsert(table, values, sql`${authority} and ${original.condition}`), 1);
186
+ replacementCondition = sql`exists(select 1 from ${table.name} where ${state.exact(table, t.moduleId, mapping.moduleId)} and ${state.id(table, t.subjectId, current.native)} and ${state.id(table, t.sessionId, session)} and ${tables.expression(mapping.clock.toMillis(table.column(t.absoluteExpiresAt)))} >= ${DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt)})`;
187
+ } else if (replacement._tag !== "StatelessSigned" || mapping.source.kind !== "StatelessSigned") return yield* SessionStepUpInvalid.make({});
188
+ yield* final("session-step-up-completion", sql`${authority} and ${pending.consumedCondition("StepUp", intent, current.native)} and ${replacementCondition}`);
189
+ if (batch === void 0 && Option.isSome(external)) yield* registerSqlPostcondition({
190
+ name: "session-step-up-current-policy",
191
+ check: Effect.gen(function* () {
192
+ const actual = yield* state.read(intent.revision.subjectId);
193
+ sessionInvariant(actual !== void 0 && sameSessionRevision(actual.revision, evidence.revision));
194
+ yield* sessionEvidenceDeadline(evidence, actual.requirement, actual.now);
195
+ yield* sessionEvidenceDeadline(evidence, intent.requirement, actual.now);
196
+ if (replacementRecord !== void 0 && records !== void 0) {
197
+ const row = yield* records.read(replacementRecord.digest);
198
+ sessionInvariant(row !== void 0 && sameSessionRecord(row, replacementRecord));
199
+ }
200
+ }).pipe(Effect.mapError((cause) => PersistenceMappingError.make({
201
+ operation: "decode",
202
+ cause
203
+ })))
204
+ });
205
+ return yield* prepareNativeSession(replacement.inspection, project);
206
+ }))
207
+ } };
208
+ });
209
+ //#endregion
210
+ export { makeNativeSessionStepUpServices };
211
+
212
+ //# sourceMappingURL=session-native-step-up.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-native-step-up.mjs","names":[],"sources":["../../src/internal/session-native-step-up.ts"],"sourcesContent":["import type { LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport { AuthenticationAssurance } from \"@yielded/auth/Operations\";\nimport {\n SessionConflict,\n SessionStepUpInvalid,\n StaleAuthentication,\n SessionMetadata,\n type SessionStepUpPersistence,\n type SessionStepUpIntent,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Option, Schema } from \"effect\";\nimport type { SqlClient } from \"effect/sql/SqlClient\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport { sqlBatchAssertion } from \"./d1-planning\";\nimport { PersistenceMappingError, isMappedConstraintConflict } from \"./mapping-error\";\nimport type { SessionStepUpMapping } from \"./models/step-up-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { sessionEvidenceDeadline } from \"./session-native-authority\";\nimport { prepareNativeSession, type StoredSessionPending } from \"./session-native-pending\";\nimport { makeNativeSessionPendingReader } from \"./session-native-pending-read\";\nimport {\n makeNativeSessionRecords,\n sameSessionRecord,\n makeConditionalSqlInsert,\n} from \"./session-native-record\";\nimport {\n sameSessionRevision,\n normalizeSessionOperation,\n sessionInvariant,\n sessionUnavailable,\n} from \"./session-native-state\";\nimport { allocateSessionValue } from \"./session-policy\";\nimport {\n CurrentSqlCommit,\n makeSqlCommitExecutor,\n SqlBatchCommit,\n registerSqlPostcondition,\n registerSqlBatchPostcondition,\n} from \"./sql-commit\";\nimport {\n encodeStepUpIntent,\n decodeStepUpIntent,\n stepUpIntentLive,\n validateStepUpPlan,\n} from \"./step-up-state\";\nimport type { AnyTableModel } from \"./table-model\";\n\nexport type NativeSessionStepUpMapping<Claims> = SessionStepUpMapping<\n Claims,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n unknown,\n unknown\n>;\n\nconst assuranceJson = Schema.encodeSync(Schema.fromJsonString(SessionMetadata.fields.assurance));\n\nexport const makeNativeSessionStepUpServices = Effect.fnUntraced(function* <Claims>(\n tables: NativeSqlTables,\n mapping: NativeSessionStepUpMapping<Claims>,\n): Effect.fn.Return<\n { readonly sessionStepUpPersistence: SessionStepUpPersistence<Claims> },\n never,\n SqlClient | LifecycleHooks | SqlBatchCommit\n> {\n const batch = yield* SqlBatchCommit;\n\n const {\n state,\n pending,\n read: readPending,\n } = yield* makeNativeSessionPendingReader(tables, mapping, batch !== undefined);\n\n const conditionalInsert = yield* makeConditionalSqlInsert();\n\n const records =\n mapping.source.kind === \"Stateful\"\n ? yield* makeNativeSessionRecords(tables, { ...mapping, ...mapping.source })\n : undefined;\n\n const executor = yield* makeSqlCommitExecutor(sessionUnavailable),\n external = yield* Effect.serviceOption(CurrentSqlCommit);\n\n const { sql, now } = state;\n\n const native = <A, E, R>(\n work: Effect.Effect<A, E, R>,\n mode: \"transaction\" | \"statement\" = \"transaction\",\n ) =>\n batch === undefined\n ? executor.operation(normalizeSessionOperation(work), mode)\n : executor\n .operationBatch(normalizeSessionOperation(work))\n .pipe(Effect.provideService(SqlBatchCommit, batch));\n\n const decode = Effect.fnUntraced(function* (stored: StoredSessionPending) {\n const intent = yield* decodeStepUpIntent(stored.snapshot);\n\n sessionInvariant(\n intent.digest === stored.digest &&\n intent.version === stored.version &&\n intent.flowId === stored.flowId &&\n intent.bindingDigest === stored.bindingDigest &&\n intent.revision.subjectId === stored.subjectId &&\n DateTime.toEpochMillis(intent.expiresAt) === stored.expiresAtMillis &&\n intent.attemptLimit === stored.attemptLimit,\n );\n\n return intent;\n });\n\n const source = Effect.fnUntraced(function* (\n intent: Omit<SessionStepUpIntent, \"version\">,\n instant: number,\n ) {\n if (!stepUpIntentLive(intent, mapping.source.kind, DateTime.makeUnsafe(instant)))\n return undefined;\n const subject = yield* mapping.subjectId.toNative(intent.revision.subjectId);\n\n if (mapping.source.kind === \"Stateful\") {\n sessionInvariant(records !== undefined);\n\n const nativeSession = yield* mapping.source.sessionId.toNative(intent.sourceSessionId),\n { s, table } = records;\n\n const rows =\n yield* sql`select ${table.fields(\"source_\")} from ${table.name} where ${state.id(table, s.subjectId, subject)} and ${state.id(table, s.sessionId, nativeSession)} and ${records.live(table)} limit 2`;\n\n sessionInvariant(rows.length <= 1);\n if (rows[0] === undefined) return undefined;\n const record = yield* records.decode(table.decode(rows[0], \"source_\"));\n\n if (\n record.credentialVersion !== intent.sourceCredentialVersion ||\n record.subjectId !== intent.revision.subjectId ||\n record.sessionId !== intent.sourceSessionId ||\n record.securityRevision !== intent.revision.securityRevision ||\n DateTime.toEpochMillis(record.assurance.authenticatedAt) !==\n DateTime.toEpochMillis(intent.sourceAuthenticatedAt) ||\n DateTime.toEpochMillis(record.expiresAt) !==\n DateTime.toEpochMillis(intent.sourceExpiresAt) ||\n DateTime.toEpochMillis(record.absoluteExpiresAt) !==\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt)\n )\n return undefined;\n\n return {\n record,\n condition: sql`exists(select 1 from ${table.name} where ${yield* records.owner(record)} and ${records.live(table)})`,\n };\n }\n if (mapping.source.kind === \"StateAssistedSigned\") {\n const t = mapping.source.tombstone,\n table = tables(t.table),\n session = yield* mapping.source.sessionId.toNative(intent.sourceSessionId);\n\n const condition = sql`not exists(select 1 from ${table.name} where ${state.exact(table, t.moduleId, mapping.moduleId)} and ${state.id(table, t.subjectId, subject)} and ${state.id(table, t.sessionId, session)})`;\n\n if ((yield* sql`select 1 where ${condition}`).length !== 1) return undefined;\n\n return { record: undefined, condition };\n }\n\n return { record: undefined, condition: sql`1 = 1` };\n });\n\n const final = Effect.fnUntraced(function* (name: string, condition: Fragment) {\n if (batch !== undefined)\n yield* registerSqlBatchPostcondition({ name, statement: sqlBatchAssertion(sql, condition) });\n else if (Option.isSome(external))\n yield* registerSqlPostcondition({\n name,\n check: Effect.gen(function* () {\n sessionInvariant((yield* sql`select 1 where ${condition}`).length === 1);\n }).pipe(\n Effect.mapError((cause) => PersistenceMappingError.make({ operation: \"decode\", cause })),\n ),\n });\n });\n\n const snapshotCondition = (intent: SessionStepUpIntent, snapshot: string) =>\n sql.and([\n state.exact(pending.table, pending.p.snapshot, snapshot),\n sql`${tables.expression(mapping.clock.toMillis(pending.table.column(pending.p.expiresAt)))} = ${DateTime.toEpochMillis(intent.expiresAt)}`,\n sql`${pending.table.column(pending.p.attemptLimit)} = ${pending.table.value(pending.p.attemptLimit, intent.attemptLimit)}`,\n ]);\n\n const sessionStepUpPersistence: SessionStepUpPersistence<Claims> = {\n create: (input, _now, project) =>\n native(\n Effect.gen(function* () {\n const current = yield* state.read(input.revision.subjectId, true);\n\n if (current === undefined || !sameSessionRevision(current.revision, input.revision))\n return yield* StaleAuthentication.make({});\n const original = yield* source(input, current.now);\n\n if (original === undefined) return yield* StaleAuthentication.make({});\n\n const version = yield* allocateSessionValue(\n \"interactive\",\n mapping.pending.allocateVersion,\n mapping.pending.allocateVersionSync,\n );\n\n const intent = { ...input, version },\n snapshot = yield* encodeStepUpIntent(intent);\n\n const condition = sql`${state.authorityCondition(intent.revision, current.native)} and ${state.policyCondition(current.row, current.native)} and ${original.condition} and ${now} >= ${DateTime.toEpochMillis(intent.sourceAuthenticatedAt)} and ${now} < ${DateTime.toEpochMillis(intent.expiresAt)}`;\n\n yield* pending\n .insert(\n {\n moduleId: mapping.moduleId,\n kind: \"StepUp\",\n digest: intent.digest,\n version,\n flowId: intent.flowId,\n subjectId: current.native,\n bindingDigest: intent.bindingDigest,\n snapshot,\n expiresAt: intent.expiresAt,\n attemptLimit: intent.attemptLimit,\n },\n condition,\n )\n .pipe(\n Effect.mapError((error) =>\n isMappedConstraintConflict(mapping.isConstraintConflict, error)\n ? SessionConflict.make({})\n : error,\n ),\n );\n yield* final(\n \"session-step-up-issuance\",\n sql`${condition} and exists(select 1 from ${pending.table.name} where ${pending.predicate(pending.table, \"StepUp\", intent.digest)} and ${state.exact(pending.table, pending.p.version, version)} and ${snapshotCondition(intent, snapshot)})`,\n );\n\n return yield* prepareNativeSession(intent, project);\n }),\n ),\n read: ({ digest }) =>\n executor\n .read(\n Effect.gen(function* () {\n const selected = yield* readPending(\"StepUp\", digest);\n\n if (selected === undefined) return undefined;\n\n const intent = yield* decode(selected.record),\n current = selected.authority;\n\n if (\n !sameSessionRevision(current.revision, intent.revision) ||\n !stepUpIntentLive(intent, mapping.source.kind, DateTime.makeUnsafe(current.now))\n )\n return undefined;\n\n // The detached source stays original. Its live digest and metadata\n // are rechecked by complete before any replacement is committed.\n return { intent, requirement: current.requirement };\n }),\n )\n .pipe(\n Effect.flatMap((value) =>\n value === undefined\n ? Effect.fail(SessionStepUpInvalid.make({}))\n : Effect.succeed(value),\n ),\n ),\n reject: ({ digest }, project) =>\n native(\n Effect.gen(function* () {\n yield* pending.reject(\"StepUp\", digest);\n\n return yield* prepareNativeSession({ _tag: \"Rejected\" } as const, project);\n }),\n \"statement\",\n ),\n complete: (plan, project) =>\n native(\n Effect.gen(function* () {\n yield* validateStepUpPlan(plan);\n const { intent, evidence, replacement } = plan;\n const current = yield* state.read(intent.revision.subjectId, true);\n\n if (\n current === undefined ||\n !sameSessionRevision(current.revision, evidence.revision) ||\n !sameSessionRevision(current.revision, intent.revision)\n )\n return yield* StaleAuthentication.make({});\n const original = yield* source(intent, current.now);\n\n if (original === undefined) return yield* SessionStepUpInvalid.make({});\n if (plan.source.guard._tag === \"Stateful\") {\n if (\n original.record === undefined ||\n original.record.digest !== plan.source.guard.digest ||\n !sameSessionRecord(original.record, {\n ...plan.source.inspection.session,\n provenance: plan.source.inspection.provenance,\n credentialVersion: plan.source.inspection.credentialVersion,\n digest: plan.source.guard.digest,\n })\n )\n return yield* SessionStepUpInvalid.make({});\n }\n\n const base = yield* sessionEvidenceDeadline(evidence, current.requirement, current.now),\n profile = yield* sessionEvidenceDeadline(evidence, intent.requirement, current.now);\n\n if (\n assuranceJson(\n AuthenticationAssurance.make({\n ...base.assessed.assurance,\n authenticatedAt: profile.assessed.assurance.authenticatedAt,\n }),\n ) !== assuranceJson(replacement.inspection.session.assurance)\n )\n return yield* SessionStepUpInvalid.make({});\n\n const deadline = Math.min(\n base.validUntil,\n profile.validUntil,\n DateTime.toEpochMillis(intent.expiresAt),\n DateTime.toEpochMillis(replacement.inspection.session.expiresAt),\n );\n\n if (\n current.now >= deadline ||\n DateTime.toEpochMillis(replacement.inspection.session.issuedAt) > current.now ||\n DateTime.toEpochMillis(replacement.inspection.session.expiresAt) >\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt)\n )\n return yield* SessionStepUpInvalid.make({});\n const snapshot = yield* encodeStepUpIntent(intent);\n const authority = sql`${state.authorityCondition(evidence.revision, current.native)} and ${state.policyCondition(current.row, current.native)} and ${now} >= ${current.now} and ${now} < ${deadline}`;\n\n yield* pending.consume(\n \"StepUp\",\n intent,\n current.native,\n sql`${snapshotCondition(intent, snapshot)} and ${authority} and ${original.condition}`,\n );\n let replacementCondition: Fragment = sql`1 = 1`;\n let replacementRecord: StatefulSessionRecord<Claims> | undefined;\n\n if (replacement._tag === \"Stateful\" && mapping.source.kind === \"Stateful\") {\n sessionInvariant(records !== undefined && original.record !== undefined);\n\n const next: StatefulSessionRecord<Claims> = {\n ...replacement.inspection.session,\n digest: replacement.nextDigest,\n credentialVersion: replacement.inspection.credentialVersion,\n provenance: replacement.inspection.provenance,\n };\n\n if (\n next.digest === original.record.digest ||\n next.credentialVersion === original.record.credentialVersion\n )\n return yield* SessionStepUpInvalid.make({});\n const { s, table } = records;\n\n const values = {\n ...s.encodeRotation(next),\n [s.digest]: next.digest,\n [s.issuedAt]: s.encodeInstant(next.issuedAt),\n [s.expiresAt]: s.encodeInstant(next.expiresAt),\n [s.absoluteExpiresAt]: s.encodeInstant(next.absoluteExpiresAt),\n [s.securityRevision]: next.securityRevision,\n };\n\n yield* pending.stage(\n sql`${table.update(values)} where ${yield* records.owner(original.record)} and ${records.live(table)} and ${authority}`,\n 1,\n );\n replacementCondition = sql`exists(select 1 from ${table.name} where ${yield* records.owner(next)} and ${records.encodedCondition(values)} and ${records.live(table)})`;\n replacementRecord = next;\n } else if (\n replacement._tag === \"StateAssistedSigned\" &&\n mapping.source.kind === \"StateAssistedSigned\"\n ) {\n const t = mapping.source.tombstone,\n table = tables(t.table),\n session = yield* mapping.source.sessionId.toNative(intent.sourceSessionId);\n\n const values = {\n ...t.encodeInsert({\n moduleId: mapping.moduleId,\n subjectId: current.native,\n sessionId: session,\n absoluteExpiresAt: intent.sourceAbsoluteExpiresAt,\n }),\n [t.moduleId]: mapping.moduleId,\n [t.subjectId]: current.native,\n [t.sessionId]: session,\n [t.absoluteExpiresAt]: t.encodeInstant(intent.sourceAbsoluteExpiresAt),\n };\n\n yield* pending.stage(\n conditionalInsert(table, values, sql`${authority} and ${original.condition}`),\n 1,\n );\n replacementCondition = sql`exists(select 1 from ${table.name} where ${state.exact(table, t.moduleId, mapping.moduleId)} and ${state.id(table, t.subjectId, current.native)} and ${state.id(table, t.sessionId, session)} and ${tables.expression(mapping.clock.toMillis(table.column(t.absoluteExpiresAt)))} >= ${DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt)})`;\n } else if (\n replacement._tag !== \"StatelessSigned\" ||\n mapping.source.kind !== \"StatelessSigned\"\n )\n return yield* SessionStepUpInvalid.make({});\n yield* final(\n \"session-step-up-completion\",\n sql`${authority} and ${pending.consumedCondition(\"StepUp\", intent, current.native)} and ${replacementCondition}`,\n );\n if (batch === undefined && Option.isSome(external))\n yield* registerSqlPostcondition({\n name: \"session-step-up-current-policy\",\n check: Effect.gen(function* () {\n const actual = yield* state.read(intent.revision.subjectId);\n\n sessionInvariant(\n actual !== undefined && sameSessionRevision(actual.revision, evidence.revision),\n );\n yield* sessionEvidenceDeadline(evidence, actual.requirement, actual.now);\n yield* sessionEvidenceDeadline(evidence, intent.requirement, actual.now);\n if (replacementRecord !== undefined && records !== undefined) {\n const row = yield* records.read(replacementRecord.digest);\n\n sessionInvariant(row !== undefined && sameSessionRecord(row, replacementRecord));\n }\n }).pipe(\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"decode\", cause }),\n ),\n ),\n });\n\n return yield* prepareNativeSession(replacement.inspection, project);\n }),\n ),\n };\n\n return { sessionStepUpPersistence };\n});\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { AnyTableModel } from "./table-model.mjs";
2
+ import { SignedSessionValidityMapping } from "./models/session-model.mjs";
3
+ import { NativeSqlTables } from "./native-sql-table.mjs";
4
+ import { SqlBatchCommit } from "./sql-commit.mjs";
5
+ import { Effect } from "effect";
6
+ import { LifecycleHooks } from "@yielded/auth/Hooks";
7
+ import { SignedSessionValidity } from "@yielded/auth/Sessions";
8
+ import { SqlClient } from "effect/sql/SqlClient";
9
+ //#region src/internal/session-native-validity.d.ts
10
+ export type NativeSignedSessionValidityMapping = SignedSessionValidityMapping<AnyTableModel, AnyTableModel, unknown, unknown>;
11
+ export declare const makeNativeSignedSessionValidityServices: (tables: NativeSqlTables, mapping: NativeSignedSessionValidityMapping) => Effect.Effect<{
12
+ readonly signedSessionValidity: SignedSessionValidity;
13
+ }, never, LifecycleHooks | SqlBatchCommit | SqlClient>;
14
+ //#endregion
15
+ //# sourceMappingURL=session-native-validity.d.mts.map
@@ -0,0 +1,97 @@
1
+ import { PersistenceMappingError } from "./mapping-error.mjs";
2
+ import { sqlBatchAssertion } from "./d1-planning.mjs";
3
+ import { exactSqlText, executeSqlChange } from "./sql-change.mjs";
4
+ import { CurrentSqlCommit, SqlBatchCommit, appendSqlBatchStatement, makeSqlCommitExecutor, registerSqlBatchPostcondition, registerSqlPostcondition } from "./sql-commit.mjs";
5
+ import { normalizeSessionOperation, sessionInvariant, sessionUnavailable } from "./session-native-state.mjs";
6
+ import { prepareNativeSession } from "./session-native-pending.mjs";
7
+ import { allocateSessionValue } from "./session-policy.mjs";
8
+ import { DateTime, Effect, Option } from "effect";
9
+ import { SessionInvalid, StaleAuthentication } from "@yielded/auth/Sessions";
10
+ import { SqlClient } from "effect/sql/SqlClient";
11
+ //#region src/internal/session-native-validity.ts
12
+ const makeNativeSignedSessionValidityServices = Effect.fnUntraced(function* (tables, mapping) {
13
+ const batch = yield* SqlBatchCommit;
14
+ const sql = (yield* SqlClient).withoutTransforms(), executor = yield* makeSqlCommitExecutor(sessionUnavailable);
15
+ const external = yield* Effect.serviceOption(CurrentSqlCommit);
16
+ const s = mapping.subject, t = mapping.tombstone, subject = tables(s.table), tombstone = tables(t.table), now = tables.expression(mapping.clock.engineNowMillis);
17
+ const id = (table, key, value) => sql`${table.column(key)} = ${table.value(key, value)}`;
18
+ const exact = (table, key, value) => exactSqlText(sql, table.column(key), table.value(key, value));
19
+ const key = (nativeSubject, nativeSession) => sql.and([
20
+ exact(tombstone, t.moduleId, mapping.moduleId),
21
+ id(tombstone, t.subjectId, nativeSubject),
22
+ id(tombstone, t.sessionId, nativeSession)
23
+ ]);
24
+ const native = (work) => batch === void 0 ? executor.operation(normalizeSessionOperation(work), "statement") : executor.operationBatch(normalizeSessionOperation(work)).pipe(Effect.provideService(SqlBatchCommit, batch));
25
+ const stage = Effect.fnUntraced(function* (statement, expected) {
26
+ if (batch !== void 0) {
27
+ yield* appendSqlBatchStatement(sql`${statement}`);
28
+ if (expected !== void 0) yield* appendSqlBatchStatement(sqlBatchAssertion(sql, sql`changes() = ${expected}`));
29
+ return expected;
30
+ }
31
+ return yield* executeSqlChange(sql, statement);
32
+ });
33
+ const final = Effect.fnUntraced(function* (name, condition) {
34
+ if (batch !== void 0) yield* registerSqlBatchPostcondition({
35
+ name,
36
+ statement: sqlBatchAssertion(sql, condition)
37
+ });
38
+ else if (Option.isSome(external)) yield* registerSqlPostcondition({
39
+ name,
40
+ check: Effect.gen(function* () {
41
+ sessionInvariant((yield* sql`select 1 where ${condition}`).length === 1);
42
+ }).pipe(Effect.mapError((cause) => PersistenceMappingError.make({
43
+ operation: "decode",
44
+ cause
45
+ })))
46
+ });
47
+ });
48
+ return { signedSessionValidity: {
49
+ verify: (record) => executor.verify(Effect.gen(function* () {
50
+ const nativeSubject = yield* mapping.subjectId.toNative(record.subjectId), nativeSession = yield* mapping.sessionId.toNative(record.sessionId);
51
+ const rows = yield* sql`select ${subject.fields("subject_")} from ${subject.name} where ${id(subject, s.id, nativeSubject)} and ${id(subject, s.status, s.activeStatusValue)} and ${exact(subject, s.securityRevision, record.securityRevision)} and ${now} >= ${DateTime.toEpochMillis(record.issuedAt)} and ${now} < ${DateTime.toEpochMillis(record.expiresAt)} and ${DateTime.toEpochMillis(record.expiresAt)} <= ${DateTime.toEpochMillis(record.absoluteExpiresAt)} and not exists(select 1 from ${tombstone.name} where ${key(nativeSubject, nativeSession)}) limit 2`;
52
+ sessionInvariant(rows.length <= 1);
53
+ if (rows[0] === void 0) return false;
54
+ const decoded = subject.decode(rows[0], "subject_");
55
+ sessionInvariant((yield* mapping.subjectId.toSubject(decoded[s.id])) === record.subjectId && s.isActiveStatus(decoded[s.status]));
56
+ return true;
57
+ })).pipe(Effect.flatMap((valid) => valid ? Effect.void : Effect.fail(SessionInvalid.make({})))),
58
+ revoke: (input, project) => native(Effect.gen(function* () {
59
+ const nativeSubject = yield* mapping.subjectId.toNative(input.subjectId), nativeSession = yield* mapping.sessionId.toNative(input.sessionId);
60
+ const row = {
61
+ ...t.encodeInsert({
62
+ moduleId: mapping.moduleId,
63
+ subjectId: nativeSubject,
64
+ sessionId: nativeSession,
65
+ absoluteExpiresAt: input.absoluteExpiresAt
66
+ }),
67
+ [t.moduleId]: mapping.moduleId,
68
+ [t.subjectId]: nativeSubject,
69
+ [t.sessionId]: nativeSession,
70
+ [t.absoluteExpiresAt]: t.encodeInstant(input.absoluteExpiresAt)
71
+ };
72
+ const expiry = tables.expression(mapping.clock.toMillis(tombstone.column(t.absoluteExpiresAt)));
73
+ const next = DateTime.toEpochMillis(input.absoluteExpiresAt);
74
+ const longest = sql`case when ${expiry} >= ${next} then ${tombstone.column(t.absoluteExpiresAt)} else ${tombstone.value(t.absoluteExpiresAt, row[t.absoluteExpiresAt])} end`;
75
+ const insert = tombstone.insert(row);
76
+ const statement = sql.onDialectOrElse({
77
+ mysql: () => sql`${insert} on duplicate key update ${tombstone.columnName(t.absoluteExpiresAt)} = ${longest}`,
78
+ orElse: () => sql`${insert} on conflict (${tombstone.columnName(t.moduleId)}, ${tombstone.columnName(t.subjectId)}, ${tombstone.columnName(t.sessionId)}) do update set ${tombstone.columnName(t.absoluteExpiresAt)} = ${longest}`
79
+ });
80
+ yield* stage(statement);
81
+ yield* final("assisted-session-revocation", sql`exists(select 1 from ${tombstone.name} where ${key(nativeSubject, nativeSession)} and ${expiry} >= ${next})`);
82
+ return yield* prepareNativeSession(void 0, project);
83
+ })),
84
+ revokeAll: (input, project) => native(Effect.gen(function* () {
85
+ const nativeSubject = yield* mapping.subjectId.toNative(input.subjectId);
86
+ const next = yield* allocateSessionValue("interactive", s.nextSecurityRevision?.(input.expectedSecurityRevision), s.nextSecurityRevisionSync === void 0 ? void 0 : () => s.nextSecurityRevisionSync(input.expectedSecurityRevision));
87
+ sessionInvariant(next !== input.expectedSecurityRevision);
88
+ if ((yield* stage(sql`${subject.update({ [s.securityRevision]: next })} where ${id(subject, s.id, nativeSubject)} and ${id(subject, s.status, s.activeStatusValue)} and ${exact(subject, s.securityRevision, input.expectedSecurityRevision)}`, 1)) !== 1) return yield* StaleAuthentication.make({});
89
+ yield* final("all-assisted-session-revocation", sql`exists(select 1 from ${subject.name} where ${id(subject, s.id, nativeSubject)} and ${exact(subject, s.securityRevision, next)})`);
90
+ return yield* prepareNativeSession(void 0, project);
91
+ }))
92
+ } };
93
+ });
94
+ //#endregion
95
+ export { makeNativeSignedSessionValidityServices };
96
+
97
+ //# sourceMappingURL=session-native-validity.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-native-validity.mjs","names":[],"sources":["../../src/internal/session-native-validity.ts"],"sourcesContent":["import type { LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport {\n SessionInvalid,\n StaleAuthentication,\n type SignedSessionValidity,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Option } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport { sqlBatchAssertion } from \"./d1-planning\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { SignedSessionValidityMapping } from \"./models/session-model\";\nimport type { NativeSqlTables, SqlTable } from \"./native-sql-table\";\nimport { prepareNativeSession } from \"./session-native-pending\";\nimport {\n normalizeSessionOperation,\n sessionInvariant,\n sessionUnavailable,\n} from \"./session-native-state\";\nimport { allocateSessionValue } from \"./session-policy\";\nimport { exactSqlText, executeSqlChange } from \"./sql-change\";\nimport {\n CurrentSqlCommit,\n makeSqlCommitExecutor,\n SqlBatchCommit,\n appendSqlBatchStatement,\n registerSqlBatchPostcondition,\n registerSqlPostcondition,\n} from \"./sql-commit\";\nimport type { AnyTableModel } from \"./table-model\";\n\nexport type NativeSignedSessionValidityMapping = SignedSessionValidityMapping<\n AnyTableModel,\n AnyTableModel,\n unknown,\n unknown\n>;\n\nexport const makeNativeSignedSessionValidityServices = Effect.fnUntraced(function* (\n tables: NativeSqlTables,\n mapping: NativeSignedSessionValidityMapping,\n): Effect.fn.Return<\n { readonly signedSessionValidity: SignedSessionValidity },\n never,\n SqlClient | LifecycleHooks | SqlBatchCommit\n> {\n const batch = yield* SqlBatchCommit;\n\n const sql = (yield* SqlClient).withoutTransforms(),\n executor = yield* makeSqlCommitExecutor(sessionUnavailable);\n\n const external = yield* Effect.serviceOption(CurrentSqlCommit);\n\n const s = mapping.subject,\n t = mapping.tombstone,\n subject = tables(s.table),\n tombstone = tables(t.table),\n now = tables.expression(mapping.clock.engineNowMillis);\n\n const id = (table: SqlTable, key: string, value: unknown) =>\n sql`${table.column(key)} = ${table.value(key, value)}`;\n\n const exact = (table: SqlTable, key: string, value: unknown) =>\n exactSqlText(sql, table.column(key), table.value(key, value));\n\n const key = (nativeSubject: unknown, nativeSession: unknown) =>\n sql.and([\n exact(tombstone, t.moduleId, mapping.moduleId),\n id(tombstone, t.subjectId, nativeSubject),\n id(tombstone, t.sessionId, nativeSession),\n ]);\n\n const native = <A, E, R>(work: Effect.Effect<A, E, R>) =>\n batch === undefined\n ? executor.operation(normalizeSessionOperation(work), \"statement\")\n : executor\n .operationBatch(normalizeSessionOperation(work))\n .pipe(Effect.provideService(SqlBatchCommit, batch));\n\n const stage = Effect.fnUntraced(function* (statement: Fragment, expected?: number) {\n if (batch !== undefined) {\n yield* appendSqlBatchStatement(sql`${statement}`);\n if (expected !== undefined)\n yield* appendSqlBatchStatement(sqlBatchAssertion(sql, sql`changes() = ${expected}`));\n\n return expected;\n }\n\n return yield* executeSqlChange(sql, statement);\n });\n\n const final = Effect.fnUntraced(function* (name: string, condition: Fragment) {\n if (batch !== undefined)\n yield* registerSqlBatchPostcondition({ name, statement: sqlBatchAssertion(sql, condition) });\n else if (Option.isSome(external))\n yield* registerSqlPostcondition({\n name,\n check: Effect.gen(function* () {\n sessionInvariant((yield* sql`select 1 where ${condition}`).length === 1);\n }).pipe(\n Effect.mapError((cause) => PersistenceMappingError.make({ operation: \"decode\", cause })),\n ),\n });\n });\n\n const signedSessionValidity: SignedSessionValidity = {\n verify: (record) =>\n executor\n .verify(\n Effect.gen(function* () {\n const nativeSubject = yield* mapping.subjectId.toNative(record.subjectId),\n nativeSession = yield* mapping.sessionId.toNative(record.sessionId);\n\n const rows =\n yield* sql`select ${subject.fields(\"subject_\")} from ${subject.name} where ${id(subject, s.id, nativeSubject)} and ${id(subject, s.status, s.activeStatusValue)} and ${exact(subject, s.securityRevision, record.securityRevision)} and ${now} >= ${DateTime.toEpochMillis(record.issuedAt)} and ${now} < ${DateTime.toEpochMillis(record.expiresAt)} and ${DateTime.toEpochMillis(record.expiresAt)} <= ${DateTime.toEpochMillis(record.absoluteExpiresAt)} and not exists(select 1 from ${tombstone.name} where ${key(nativeSubject, nativeSession)}) limit 2`;\n\n sessionInvariant(rows.length <= 1);\n if (rows[0] === undefined) return false;\n const decoded = subject.decode(rows[0], \"subject_\");\n\n sessionInvariant(\n (yield* mapping.subjectId.toSubject(decoded[s.id])) === record.subjectId &&\n s.isActiveStatus(decoded[s.status]),\n );\n\n return true;\n }),\n )\n .pipe(\n Effect.flatMap((valid) => (valid ? Effect.void : Effect.fail(SessionInvalid.make({})))),\n ),\n revoke: (input, project) =>\n native(\n Effect.gen(function* () {\n const nativeSubject = yield* mapping.subjectId.toNative(input.subjectId),\n nativeSession = yield* mapping.sessionId.toNative(input.sessionId);\n\n const row = {\n ...t.encodeInsert({\n moduleId: mapping.moduleId,\n subjectId: nativeSubject,\n sessionId: nativeSession,\n absoluteExpiresAt: input.absoluteExpiresAt,\n }),\n [t.moduleId]: mapping.moduleId,\n [t.subjectId]: nativeSubject,\n [t.sessionId]: nativeSession,\n [t.absoluteExpiresAt]: t.encodeInstant(input.absoluteExpiresAt),\n };\n\n const expiry = tables.expression(\n mapping.clock.toMillis(tombstone.column(t.absoluteExpiresAt)),\n );\n\n const next = DateTime.toEpochMillis(input.absoluteExpiresAt);\n const longest = sql`case when ${expiry} >= ${next} then ${tombstone.column(t.absoluteExpiresAt)} else ${tombstone.value(t.absoluteExpiresAt, row[t.absoluteExpiresAt])} end`;\n const insert = tombstone.insert(row);\n\n const statement = sql.onDialectOrElse({\n mysql: () =>\n sql`${insert} on duplicate key update ${tombstone.columnName(t.absoluteExpiresAt)} = ${longest}`,\n orElse: () =>\n sql`${insert} on conflict (${tombstone.columnName(t.moduleId)}, ${tombstone.columnName(t.subjectId)}, ${tombstone.columnName(t.sessionId)}) do update set ${tombstone.columnName(t.absoluteExpiresAt)} = ${longest}`,\n });\n\n yield* stage(statement);\n yield* final(\n \"assisted-session-revocation\",\n sql`exists(select 1 from ${tombstone.name} where ${key(nativeSubject, nativeSession)} and ${expiry} >= ${next})`,\n );\n\n return yield* prepareNativeSession(undefined, project);\n }),\n ),\n revokeAll: (input, project) =>\n native(\n Effect.gen(function* () {\n const nativeSubject = yield* mapping.subjectId.toNative(input.subjectId);\n\n const next = yield* allocateSessionValue(\n \"interactive\",\n s.nextSecurityRevision?.(input.expectedSecurityRevision),\n s.nextSecurityRevisionSync === undefined\n ? undefined\n : () => s.nextSecurityRevisionSync!(input.expectedSecurityRevision),\n );\n\n sessionInvariant(next !== input.expectedSecurityRevision);\n\n const changed = yield* stage(\n sql`${subject.update({ [s.securityRevision]: next })} where ${id(subject, s.id, nativeSubject)} and ${id(subject, s.status, s.activeStatusValue)} and ${exact(subject, s.securityRevision, input.expectedSecurityRevision)}`,\n 1,\n );\n\n if (changed !== 1) return yield* StaleAuthentication.make({});\n yield* final(\n \"all-assisted-session-revocation\",\n sql`exists(select 1 from ${subject.name} where ${id(subject, s.id, nativeSubject)} and ${exact(subject, s.securityRevision, next)})`,\n );\n\n return yield* prepareNativeSession(undefined, project);\n }),\n ),\n };\n\n return { signedSessionValidity };\n});\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { PersistenceMappingError } from "./mapping-error.mjs";
2
+ import { DateTime, Effect } from "effect";
3
+ import { SessionUnavailable } from "@yielded/auth/Sessions";
4
+ //#region src/internal/session-policy.ts
5
+ const preservesRevision = (completed, original) => {
6
+ const current = new Map(completed.revision.credentials.map((item) => [item.credentialId, item.revision]));
7
+ return completed.flowId === original.flowId && completed.bindingDigest === original.bindingDigest && completed.revision.subjectId === original.revision.subjectId && completed.revision.securityRevision === original.revision.securityRevision && original.revision.credentials.every((item) => current.get(item.credentialId) === item.revision) && original.proofs.every((expected) => completed.proofs.some((proof) => proof.method === expected.method && proof.credentialId === expected.credentialId && proof.userVerified === expected.userVerified && proof.phishingResistant === expected.phishingResistant && DateTime.toEpochMillis(proof.verifiedAt) === DateTime.toEpochMillis(expected.verifiedAt) && proof.factors.length === expected.factors.length && proof.factors.every((factor, index) => factor === expected.factors[index])));
8
+ };
9
+ const allocateSessionValue = (mode, asynchronous, synchronous) => {
10
+ if (mode === "synchronous") return synchronous === void 0 ? Effect.fail(SessionUnavailable.make({})) : Effect.try({
11
+ try: synchronous,
12
+ catch: (cause) => PersistenceMappingError.make({
13
+ operation: "allocate",
14
+ cause
15
+ })
16
+ });
17
+ if (asynchronous !== void 0) return asynchronous;
18
+ return synchronous === void 0 ? Effect.fail(SessionUnavailable.make({})) : Effect.try({
19
+ try: synchronous,
20
+ catch: (cause) => PersistenceMappingError.make({
21
+ operation: "allocate",
22
+ cause
23
+ })
24
+ });
25
+ };
26
+ //#endregion
27
+ export { allocateSessionValue, preservesRevision };
28
+
29
+ //# sourceMappingURL=session-policy.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-policy.mjs","names":[],"sources":["../../src/internal/session-policy.ts"],"sourcesContent":["import type { AuthenticationEvidence } from \"@yielded/auth/Sessions\";\nimport { SessionUnavailable } from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect } from \"effect\";\n\nimport { PersistenceMappingError } from \"./mapping-error\";\n\nexport const preservesRevision = (\n completed: AuthenticationEvidence,\n original: AuthenticationEvidence,\n): boolean => {\n const current = new Map(\n completed.revision.credentials.map((item) => [item.credentialId, item.revision]),\n );\n\n return (\n completed.flowId === original.flowId &&\n completed.bindingDigest === original.bindingDigest &&\n completed.revision.subjectId === original.revision.subjectId &&\n completed.revision.securityRevision === original.revision.securityRevision &&\n original.revision.credentials.every(\n (item) => current.get(item.credentialId) === item.revision,\n ) &&\n original.proofs.every((expected) =>\n completed.proofs.some(\n (proof) =>\n proof.method === expected.method &&\n proof.credentialId === expected.credentialId &&\n proof.userVerified === expected.userVerified &&\n proof.phishingResistant === expected.phishingResistant &&\n DateTime.toEpochMillis(proof.verifiedAt) ===\n DateTime.toEpochMillis(expected.verifiedAt) &&\n proof.factors.length === expected.factors.length &&\n proof.factors.every((factor, index) => factor === expected.factors[index]),\n ),\n )\n );\n};\n\nexport const allocateSessionValue = <A>(\n mode: \"interactive\" | \"synchronous\" | \"batch\",\n asynchronous: Effect.Effect<A, PersistenceMappingError> | undefined,\n synchronous: (() => A) | undefined,\n): Effect.Effect<A, PersistenceMappingError | SessionUnavailable> => {\n if (mode === \"synchronous\")\n return synchronous === undefined\n ? Effect.fail(SessionUnavailable.make({}))\n : Effect.try({\n try: synchronous,\n catch: (cause) => PersistenceMappingError.make({ operation: \"allocate\", cause }),\n });\n if (asynchronous !== undefined) return asynchronous;\n\n return synchronous === undefined\n ? Effect.fail(SessionUnavailable.make({}))\n : Effect.try({\n try: synchronous,\n catch: (cause) => PersistenceMappingError.make({ operation: \"allocate\", cause }),\n });\n};\n"],"mappings":";;;;AAMA,MAAa,qBACX,WACA,aACY;CACZ,MAAM,UAAU,IAAI,IAClB,UAAU,SAAS,YAAY,KAAK,SAAS,CAAC,KAAK,cAAc,KAAK,QAAQ,CAAC,CACjF;CAEA,OACE,UAAU,WAAW,SAAS,UAC9B,UAAU,kBAAkB,SAAS,iBACrC,UAAU,SAAS,cAAc,SAAS,SAAS,aACnD,UAAU,SAAS,qBAAqB,SAAS,SAAS,oBAC1D,SAAS,SAAS,YAAY,OAC3B,SAAS,QAAQ,IAAI,KAAK,YAAY,MAAM,KAAK,QACpD,KACA,SAAS,OAAO,OAAO,aACrB,UAAU,OAAO,MACd,UACC,MAAM,WAAW,SAAS,UAC1B,MAAM,iBAAiB,SAAS,gBAChC,MAAM,iBAAiB,SAAS,gBAChC,MAAM,sBAAsB,SAAS,qBACrC,SAAS,cAAc,MAAM,UAAU,MACrC,SAAS,cAAc,SAAS,UAAU,KAC5C,MAAM,QAAQ,WAAW,SAAS,QAAQ,UAC1C,MAAM,QAAQ,OAAO,QAAQ,UAAU,WAAW,SAAS,QAAQ,MAAM,CAC7E,CACF;AAEJ;AAEA,MAAa,wBACX,MACA,cACA,gBACmE;CACnE,IAAI,SAAS,eACX,OAAO,gBAAgB,KAAA,IACnB,OAAO,KAAK,mBAAmB,KAAK,CAAC,CAAC,CAAC,IACvC,OAAO,IAAI;EACT,KAAK;EACL,QAAQ,UAAU,wBAAwB,KAAK;GAAE,WAAW;GAAY;EAAM,CAAC;CACjF,CAAC;CACP,IAAI,iBAAiB,KAAA,GAAW,OAAO;CAEvC,OAAO,gBAAgB,KAAA,IACnB,OAAO,KAAK,mBAAmB,KAAK,CAAC,CAAC,CAAC,IACvC,OAAO,IAAI;EACT,KAAK;EACL,QAAQ,UAAU,wBAAwB,KAAK;GAAE,WAAW;GAAY;EAAM,CAAC;CACjF,CAAC;AACP"}
@@ -0,0 +1,16 @@
1
+ import { SqlExpression } from "./sql-expression.mjs";
2
+ import { AuthenticationAuthorityMapping } from "./models/session-model.mjs";
3
+ import { PersistenceConfigurationError } from "./configuration.mjs";
4
+ import { Table } from "./sql-table.mjs";
5
+ import { SqlTableModel } from "./sql-oauth-model.mjs";
6
+ import { Effect } from "effect";
7
+ import { SqlClient } from "effect/sql";
8
+ import { LifecycleHooks } from "@yielded/auth/Hooks";
9
+ import { AuthenticationAuthority, SessionUnavailable } from "@yielded/auth/Sessions";
10
+ //#region src/internal/sql-authority.d.ts
11
+ /** Direct SQL uses the same authority owner as the mapped Drizzle adapters. */
12
+ export declare const makeAuthenticationAuthorityServices: <Claims, N>(mapping: AuthenticationAuthorityMapping<Claims, SqlTableModel<Table>, SqlTableModel<Table>, SqlTableModel<Table>, N, SqlExpression>) => Effect.Effect<{
13
+ readonly authenticationAuthority: AuthenticationAuthority["Service"];
14
+ }, PersistenceConfigurationError | SessionUnavailable, LifecycleHooks | SqlClient.SqlClient>;
15
+ //#endregion
16
+ //# sourceMappingURL=sql-authority.d.mts.map
@@ -0,0 +1,32 @@
1
+ import { PersistenceConfigurationError } from "./configuration.mjs";
2
+ import { SqlExpression, compileSqlExpression } from "./sql-expression.mjs";
3
+ import { makeNativeSqlTables } from "./native-sql-table.mjs";
4
+ import { SqlBatchCommit } from "./sql-commit.mjs";
5
+ import { makeNativeAuthenticationAuthorityServices } from "./session-native-authority.mjs";
6
+ import { validateSqlStorage } from "./sql-storage-validation.mjs";
7
+ import { Effect } from "effect";
8
+ import { SqlClient } from "effect/sql";
9
+ import { SessionUnavailable } from "@yielded/auth/Sessions";
10
+ //#region src/internal/sql-authority.ts
11
+ /** Direct SQL uses the same authority owner as the mapped Drizzle adapters. */
12
+ const makeAuthenticationAuthorityServices = Effect.fnUntraced(function* (mapping) {
13
+ const sql = yield* SqlClient.SqlClient;
14
+ if (!sql.onDialectOrElse({
15
+ pg: () => true,
16
+ sqlite: () => true,
17
+ orElse: () => false
18
+ })) return yield* PersistenceConfigurationError.make({ reason: "Authentication authority requires PostgreSQL or SQLite" });
19
+ yield* validateSqlStorage(mapping).pipe(Effect.mapError(() => SessionUnavailable.make({})));
20
+ return yield* makeNativeAuthenticationAuthorityServices(makeNativeSqlTables(sql), {
21
+ ...mapping,
22
+ clock: {
23
+ ...mapping.clock,
24
+ toMillis: (value) => mapping.clock.toMillis(new SqlExpression((client) => compileSqlExpression(client, value))),
25
+ fromMillis: (value) => mapping.clock.fromMillis(new SqlExpression((client) => compileSqlExpression(client, value)))
26
+ }
27
+ }).pipe(Effect.provideService(SqlBatchCommit, void 0));
28
+ });
29
+ //#endregion
30
+ export { makeAuthenticationAuthorityServices };
31
+
32
+ //# sourceMappingURL=sql-authority.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-authority.mjs","names":[],"sources":["../../src/internal/sql-authority.ts"],"sourcesContent":["import type { LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport { SessionUnavailable, type AuthenticationAuthority } from \"@yielded/auth/Sessions\";\nimport { Effect } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\n\nimport { PersistenceConfigurationError } from \"./configuration\";\nimport type { AuthenticationAuthorityMapping } from \"./models/session-model\";\nimport { makeNativeSqlTables } from \"./native-sql-table\";\nimport {\n makeNativeAuthenticationAuthorityServices,\n type NativeAuthenticationAuthorityMapping,\n} from \"./session-native-authority\";\nimport { SqlBatchCommit } from \"./sql-commit\";\nimport { SqlExpression, compileSqlExpression } from \"./sql-expression\";\nimport type { SqlTableModel } from \"./sql-oauth-model\";\nimport { validateSqlStorage } from \"./sql-storage-validation\";\n\n/** Direct SQL uses the same authority owner as the mapped Drizzle adapters. */\nexport const makeAuthenticationAuthorityServices = Effect.fnUntraced(function* <Claims, N>(\n mapping: AuthenticationAuthorityMapping<\n Claims,\n SqlTableModel,\n SqlTableModel,\n SqlTableModel,\n N,\n SqlExpression\n >,\n): Effect.fn.Return<\n { readonly authenticationAuthority: AuthenticationAuthority[\"Service\"] },\n SessionUnavailable | PersistenceConfigurationError,\n LifecycleHooks | SqlClient.SqlClient\n> {\n const sql = yield* SqlClient.SqlClient;\n\n if (!sql.onDialectOrElse({ pg: () => true, sqlite: () => true, orElse: () => false }))\n return yield* PersistenceConfigurationError.make({\n reason: \"Authentication authority requires PostgreSQL or SQLite\",\n });\n yield* validateSqlStorage(mapping).pipe(Effect.mapError(() => SessionUnavailable.make({})));\n\n // Erase mapped native ID callback variance, never decoded authentication data.\n return yield* makeNativeAuthenticationAuthorityServices(makeNativeSqlTables(sql), {\n ...mapping,\n clock: {\n ...mapping.clock,\n toMillis: (value: unknown) =>\n mapping.clock.toMillis(new SqlExpression((client) => compileSqlExpression(client, value))),\n fromMillis: (value: unknown) =>\n mapping.clock.fromMillis(\n new SqlExpression((client) => compileSqlExpression(client, value)),\n ),\n },\n } as unknown as NativeAuthenticationAuthorityMapping<Claims>).pipe(\n Effect.provideService(SqlBatchCommit, undefined),\n );\n});\n"],"mappings":";;;;;;;;;;;AAkBA,MAAa,sCAAsC,OAAO,WAAW,WACnE,SAYA;CACA,MAAM,MAAM,OAAO,UAAU;CAE7B,IAAI,CAAC,IAAI,gBAAgB;EAAE,UAAU;EAAM,cAAc;EAAM,cAAc;CAAM,CAAC,GAClF,OAAO,OAAO,8BAA8B,KAAK,EAC/C,QAAQ,yDACV,CAAC;CACH,OAAO,mBAAmB,OAAO,CAAC,CAAC,KAAK,OAAO,eAAe,mBAAmB,KAAK,CAAC,CAAC,CAAC,CAAC;CAG1F,OAAO,OAAO,0CAA0C,oBAAoB,GAAG,GAAG;EAChF,GAAG;EACH,OAAO;GACL,GAAG,QAAQ;GACX,WAAW,UACT,QAAQ,MAAM,SAAS,IAAI,eAAe,WAAW,qBAAqB,QAAQ,KAAK,CAAC,CAAC;GAC3F,aAAa,UACX,QAAQ,MAAM,WACZ,IAAI,eAAe,WAAW,qBAAqB,QAAQ,KAAK,CAAC,CACnE;EACJ;CACF,CAA4D,CAAC,CAAC,KAC5D,OAAO,eAAe,gBAAgB,KAAA,CAAS,CACjD;AACF,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { join } from "effect/sql/Statement";
3
+ //#region src/internal/sql-change.ts
4
+ /** Native row counts are the authority for conditional writes. MySQL exposes
5
+ * its result header; PostgreSQL and SQLite return one row for every match. */
6
+ const executeSqlChange = (sql, query) => sql.onDialectOrElse({
7
+ mysql: () => sql`${query}`.raw.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.Struct({ affectedRows: Schema.Int }))), Effect.map((result) => result.affectedRows)),
8
+ orElse: () => sql`${query} returning 1 as changed`.pipe(Effect.map((rows) => rows.length))
9
+ });
10
+ /** Effect renders an empty OR as `1=1`; an empty set of alternatives matches nothing. */
11
+ const anySqlCondition = join(" OR ", true, "1 = 0");
12
+ /** Keep the indexed predicate and reject collation aliases of security tokens. */
13
+ const exactSqlText = (sql, left, right) => sql.and([sql`${left} = ${right}`, sql.onDialectOrElse({
14
+ mysql: () => sql`binary ${left} = binary ${right}`,
15
+ pg: () => sql`convert_to(cast(${left} as text), 'UTF8') = convert_to(cast(${right} as text), 'UTF8')`,
16
+ orElse: () => sql`cast(${left} as blob) = cast(${right} as blob)`
17
+ })]);
18
+ //#endregion
19
+ export { anySqlCondition, exactSqlText, executeSqlChange };
20
+
21
+ //# sourceMappingURL=sql-change.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-change.mjs","names":[],"sources":["../../src/internal/sql-change.ts"],"sourcesContent":["import { Effect, Schema } from \"effect\";\nimport type { SqlClient } from \"effect/sql/SqlClient\";\nimport type { SqlError } from \"effect/sql/SqlError\";\nimport { join, type Fragment } from \"effect/sql/Statement\";\n\n/** Native row counts are the authority for conditional writes. MySQL exposes\n * its result header; PostgreSQL and SQLite return one row for every match. */\nexport const executeSqlChange = (\n sql: SqlClient,\n query: Fragment,\n): Effect.Effect<number, SqlError | Schema.SchemaError> =>\n sql.onDialectOrElse({\n mysql: () =>\n sql`${query}`.raw.pipe(\n Effect.flatMap(Schema.decodeUnknownEffect(Schema.Struct({ affectedRows: Schema.Int }))),\n Effect.map((result) => result.affectedRows),\n ),\n orElse: () => sql`${query} returning 1 as changed`.pipe(Effect.map((rows) => rows.length)),\n });\n\n/** Effect renders an empty OR as `1=1`; an empty set of alternatives matches nothing. */\nexport const anySqlCondition: (conditions: ReadonlyArray<Fragment>) => Fragment = join(\n \" OR \",\n true,\n \"1 = 0\",\n);\n\n/** Keep the indexed predicate and reject collation aliases of security tokens. */\nexport const exactSqlText = (sql: SqlClient, left: Fragment, right: Fragment) =>\n sql.and([\n sql`${left} = ${right}`,\n sql.onDialectOrElse({\n mysql: () => sql`binary ${left} = binary ${right}`,\n pg: () =>\n sql`convert_to(cast(${left} as text), 'UTF8') = convert_to(cast(${right} as text), 'UTF8')`,\n orElse: () => sql`cast(${left} as blob) = cast(${right} as blob)`,\n }),\n ]);\n"],"mappings":";;;;;AAOA,MAAa,oBACX,KACA,UAEA,IAAI,gBAAgB;CAClB,aACE,GAAG,GAAG,QAAQ,IAAI,KAChB,OAAO,QAAQ,OAAO,oBAAoB,OAAO,OAAO,EAAE,cAAc,OAAO,IAAI,CAAC,CAAC,CAAC,GACtF,OAAO,KAAK,WAAW,OAAO,YAAY,CAC5C;CACF,cAAc,GAAG,GAAG,MAAM,yBAAyB,KAAK,OAAO,KAAK,SAAS,KAAK,MAAM,CAAC;AAC3F,CAAC;;AAGH,MAAa,kBAAqE,KAChF,QACA,MACA,OACF;;AAGA,MAAa,gBAAgB,KAAgB,MAAgB,UAC3D,IAAI,IAAI,CACN,GAAG,GAAG,KAAK,KAAK,SAChB,IAAI,gBAAgB;CAClB,aAAa,GAAG,UAAU,KAAK,YAAY;CAC3C,UACE,GAAG,mBAAmB,KAAK,uCAAuC,MAAM;CAC1E,cAAc,GAAG,QAAQ,KAAK,mBAAmB,MAAM;AACzD,CAAC,CACH,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { sqlBatchAssertion } from "./d1-planning.mjs";
2
+ import { executeSqlChange } from "./sql-change.mjs";
3
+ import { appendSqlBatchStatement } from "./sql-commit.mjs";
4
+ import { Effect, Schema } from "effect";
5
+ import { CleanupLimit, CleanupResult } from "@yielded/auth/Persistence";
6
+ import { SqlClient } from "effect/sql/SqlClient";
7
+ //#region src/internal/sql-cleanup.ts
8
+ const cleanupSqlRows = Effect.fnUntraced(function* (candidates, requested, batch = false) {
9
+ const limit = yield* Schema.decodeEffect(CleanupLimit)(requested);
10
+ const sql = (yield* SqlClient).withoutTransforms();
11
+ let removed = 0;
12
+ for (const candidate of candidates) {
13
+ const remaining = limit - removed;
14
+ if (remaining === 0) break;
15
+ const { table, keys, due } = candidate;
16
+ const aliases = keys.map((_, index) => sql.literal(`cleanup_key_${index}`));
17
+ const selection = sql`select ${sql.join(", ", false)(keys.map((key, index) => sql`${table.column(key)} as ${aliases[index]}`))} from ${table.name} where ${due} order by ${sql.join(", ", false)(candidate.order.length === 0 ? keys.map(table.column) : candidate.order)} limit ${remaining}`;
18
+ const rowKey = keys.length === 1 ? table.column(keys[0]) : sql`(${sql.join(", ", false)(keys.map(table.column))})`;
19
+ const deletion = sql`delete from ${table.name} where ${due} and ${rowKey} in (select ${sql.join(", ", false)(aliases)} from (${selection}) as cleanup_candidates)`;
20
+ if (batch) {
21
+ const planned = (yield* sql`${selection}`).length;
22
+ yield* appendSqlBatchStatement(deletion);
23
+ yield* appendSqlBatchStatement(sqlBatchAssertion(sql, sql`changes() = ${planned}`));
24
+ removed += planned;
25
+ } else removed += yield* executeSqlChange(sql, deletion);
26
+ }
27
+ return yield* Schema.decodeEffect(CleanupResult)({
28
+ removed,
29
+ hasMore: removed === limit
30
+ });
31
+ });
32
+ //#endregion
33
+ export { cleanupSqlRows };
34
+
35
+ //# sourceMappingURL=sql-cleanup.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-cleanup.mjs","names":[],"sources":["../../src/internal/sql-cleanup.ts"],"sourcesContent":["import { CleanupLimit, CleanupResult } from \"@yielded/auth/Persistence\";\nimport { Effect, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport { sqlBatchAssertion } from \"./d1-planning\";\nimport type { SqlTable } from \"./native-sql-table\";\nimport { executeSqlChange } from \"./sql-change\";\nimport { appendSqlBatchStatement } from \"./sql-commit\";\n\n/** A mapped row key and its strategy-owned retention predicate are the entire\n * cleanup authority. All tables share the same remaining row budget. */\nexport interface SqlCleanupTable {\n readonly table: SqlTable;\n readonly keys: readonly [string, ...string[]];\n readonly due: Fragment;\n readonly order: ReadonlyArray<Fragment>;\n}\n\nexport const cleanupSqlRows = Effect.fnUntraced(function* (\n candidates: ReadonlyArray<SqlCleanupTable>,\n requested: CleanupLimit,\n batch = false,\n) {\n const limit = yield* Schema.decodeEffect(CleanupLimit)(requested);\n const sql = (yield* SqlClient).withoutTransforms();\n let removed = 0;\n\n for (const candidate of candidates) {\n const remaining = limit - removed;\n\n if (remaining === 0) break;\n const { table, keys, due } = candidate;\n const aliases = keys.map((_, index) => sql.literal(`cleanup_key_${index}`));\n const selection = sql`select ${sql.join(\", \", false)(keys.map((key, index) => sql`${table.column(key)} as ${aliases[index]!}`))} from ${table.name} where ${due} order by ${sql.join(\", \", false)(candidate.order.length === 0 ? keys.map(table.column) : candidate.order)} limit ${remaining}`;\n\n // An uncorrelated IN selection materializes the bounded key set before\n // deletion, including SQLite's self-table case and composite tombstone keys.\n const rowKey =\n keys.length === 1\n ? table.column(keys[0])\n : sql`(${sql.join(\", \", false)(keys.map(table.column))})`;\n\n const deletion = sql`delete from ${table.name} where ${due} and ${rowKey} in (select ${sql.join(\", \", false)(aliases)} from (${selection}) as cleanup_candidates)`;\n\n if (batch) {\n const planned = (yield* sql`${selection}`).length;\n\n yield* appendSqlBatchStatement(deletion);\n yield* appendSqlBatchStatement(sqlBatchAssertion(sql, sql`changes() = ${planned}`));\n removed += planned;\n } else removed += yield* executeSqlChange(sql, deletion);\n }\n\n return yield* Schema.decodeEffect(CleanupResult)({ removed, hasMore: removed === limit });\n});\n"],"mappings":";;;;;;;AAmBA,MAAa,iBAAiB,OAAO,WAAW,WAC9C,YACA,WACA,QAAQ,OACR;CACA,MAAM,QAAQ,OAAO,OAAO,aAAa,YAAY,CAAC,CAAC,SAAS;CAChE,MAAM,OAAO,OAAO,UAAA,CAAW,kBAAkB;CACjD,IAAI,UAAU;CAEd,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,YAAY,QAAQ;EAE1B,IAAI,cAAc,GAAG;EACrB,MAAM,EAAE,OAAO,MAAM,QAAQ;EAC7B,MAAM,UAAU,KAAK,KAAK,GAAG,UAAU,IAAI,QAAQ,eAAe,OAAO,CAAC;EAC1E,MAAM,YAAY,GAAG,UAAU,IAAI,KAAK,MAAM,KAAK,CAAC,CAAC,KAAK,KAAK,KAAK,UAAU,GAAG,GAAG,MAAM,OAAO,GAAG,EAAE,MAAM,QAAQ,QAAS,CAAC,EAAE,QAAQ,MAAM,KAAK,SAAS,IAAI,YAAY,IAAI,KAAK,MAAM,KAAK,CAAC,CAAC,UAAU,MAAM,WAAW,IAAI,KAAK,IAAI,MAAM,MAAM,IAAI,UAAU,KAAK,EAAE,SAAS;EAIpR,MAAM,SACJ,KAAK,WAAW,IACZ,MAAM,OAAO,KAAK,EAAE,IACpB,GAAG,IAAI,IAAI,KAAK,MAAM,KAAK,CAAC,CAAC,KAAK,IAAI,MAAM,MAAM,CAAC,EAAE;EAE3D,MAAM,WAAW,GAAG,eAAe,MAAM,KAAK,SAAS,IAAI,OAAO,OAAO,cAAc,IAAI,KAAK,MAAM,KAAK,CAAC,CAAC,OAAO,EAAE,SAAS,UAAU;EAEzI,IAAI,OAAO;GACT,MAAM,WAAW,OAAO,GAAG,GAAG,YAAA,CAAa;GAE3C,OAAO,wBAAwB,QAAQ;GACvC,OAAO,wBAAwB,kBAAkB,KAAK,GAAG,eAAe,SAAS,CAAC;GAClF,WAAW;EACb,OAAO,WAAW,OAAO,iBAAiB,KAAK,QAAQ;CACzD;CAEA,OAAO,OAAO,OAAO,aAAa,aAAa,CAAC,CAAC;EAAE;EAAS,SAAS,YAAY;CAAM,CAAC;AAC1F,CAAC"}