@yielded/auth-persistence-drizzle 0.1.0-beta.22 → 0.1.0-beta.24

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 (200) hide show
  1. package/README.md +3 -0
  2. package/dist/D1.d.mts +11 -11
  3. package/dist/D1.mjs +2 -1
  4. package/dist/D1.mjs.map +1 -1
  5. package/dist/Libsql.d.mts +40 -56
  6. package/dist/Libsql.mjs +2 -1
  7. package/dist/Libsql.mjs.map +1 -1
  8. package/dist/Mysql2.d.mts +16 -17
  9. package/dist/Pglite.d.mts +18 -18
  10. package/dist/Pglite.mjs +2 -1
  11. package/dist/Pglite.mjs.map +1 -1
  12. package/dist/Postgres.d.mts +24 -24
  13. package/dist/Postgres.mjs +2 -1
  14. package/dist/Postgres.mjs.map +1 -1
  15. package/dist/SqliteBun.d.mts +39 -55
  16. package/dist/SqliteBun.mjs +2 -1
  17. package/dist/SqliteBun.mjs.map +1 -1
  18. package/dist/SqliteDo.d.mts +48 -64
  19. package/dist/SqliteDo.mjs +2 -1
  20. package/dist/SqliteDo.mjs.map +1 -1
  21. package/dist/SqliteNode.d.mts +45 -61
  22. package/dist/SqliteNode.mjs +2 -1
  23. package/dist/SqliteNode.mjs.map +1 -1
  24. package/dist/SqliteWasm.d.mts +48 -64
  25. package/dist/SqliteWasm.mjs +2 -1
  26. package/dist/SqliteWasm.mjs.map +1 -1
  27. package/dist/drizzle/crypto.mjs +1 -1
  28. package/dist/drizzle/d1-emails.d.mts +1 -1
  29. package/dist/drizzle/d1-oauth.d.mts +14 -14
  30. package/dist/drizzle/d1-passkey.d.mts +1 -1
  31. package/dist/drizzle/d1-password-prepared.d.mts +4 -4
  32. package/dist/drizzle/d1-password-prepared.mjs.map +1 -1
  33. package/dist/drizzle/d1-passwords.d.mts +18 -35
  34. package/dist/drizzle/d1-passwords.mjs +78 -290
  35. package/dist/drizzle/d1-passwords.mjs.map +1 -1
  36. package/dist/drizzle/d1-phone.d.mts +1 -1
  37. package/dist/drizzle/d1-proofs.d.mts +2 -2
  38. package/dist/drizzle/d1-sessions.d.mts +3 -3
  39. package/dist/drizzle/d1-sessions.mjs +11 -6
  40. package/dist/drizzle/d1-sessions.mjs.map +1 -1
  41. package/dist/drizzle/d1-totp.d.mts +1 -1
  42. package/dist/drizzle/mysql-password-prepared.d.mts +5 -5
  43. package/dist/drizzle/mysql-password-prepared.mjs.map +1 -1
  44. package/dist/drizzle/mysql-passwords.d.mts +16 -32
  45. package/dist/drizzle/mysql-passwords.mjs.map +1 -1
  46. package/dist/drizzle/mysql-sessions.d.mts +1 -1
  47. package/dist/drizzle/native-sql-table.mjs +135 -0
  48. package/dist/drizzle/native-sql-table.mjs.map +1 -0
  49. package/dist/drizzle/oauth-connected-model.d.mts +20 -281
  50. package/dist/drizzle/oauth-connected-model.mjs +3 -17
  51. package/dist/drizzle/oauth-connected-model.mjs.map +1 -1
  52. package/dist/drizzle/oauth-connected-reference.d.mts +1 -3
  53. package/dist/drizzle/oauth-connected-reference.mjs +4 -53
  54. package/dist/drizzle/oauth-connected-reference.mjs.map +1 -1
  55. package/dist/drizzle/oauth-connected-target.d.mts +3 -990
  56. package/dist/drizzle/oauth-connected-target.mjs +8 -92
  57. package/dist/drizzle/oauth-connected-target.mjs.map +1 -1
  58. package/dist/drizzle/oauth-kernel.mjs +11 -0
  59. package/dist/drizzle/oauth-kernel.mjs.map +1 -0
  60. package/dist/drizzle/oauth-model.d.mts +35 -438
  61. package/dist/drizzle/oauth-model.mjs +6 -44
  62. package/dist/drizzle/oauth-model.mjs.map +1 -1
  63. package/dist/drizzle/oauth-mysql.mjs +2 -2
  64. package/dist/drizzle/oauth-mysql.mjs.map +1 -1
  65. package/dist/drizzle/oauth-proxy.d.mts +10 -0
  66. package/dist/drizzle/oauth-proxy.mjs +54 -0
  67. package/dist/drizzle/oauth-proxy.mjs.map +1 -0
  68. package/dist/drizzle/oauth-target.d.mts +6 -107
  69. package/dist/drizzle/oauth-target.mjs +15 -245
  70. package/dist/drizzle/oauth-target.mjs.map +1 -1
  71. package/dist/drizzle/password-model.d.mts +5 -8
  72. package/dist/drizzle/password-prepared-model.d.mts +5 -5
  73. package/dist/drizzle/password-prepared-model.mjs.map +1 -1
  74. package/dist/drizzle/password-prepared-state.mjs +1 -1
  75. package/dist/drizzle/password-sql.mjs +2 -1
  76. package/dist/drizzle/password-sql.mjs.map +1 -1
  77. package/dist/drizzle/password-target.mjs +13 -2
  78. package/dist/drizzle/password-target.mjs.map +1 -1
  79. package/dist/drizzle/pg-password-prepared.d.mts +5 -5
  80. package/dist/drizzle/pg-password-prepared.mjs.map +1 -1
  81. package/dist/drizzle/pg-passwords.d.mts +16 -32
  82. package/dist/drizzle/pg-passwords.mjs.map +1 -1
  83. package/dist/drizzle/pg-sessions.d.mts +1 -1
  84. package/dist/drizzle/phone-target.d.mts +2 -2
  85. package/dist/drizzle/phone-target.mjs +1 -1
  86. package/dist/drizzle/query-operations.mjs +3 -1
  87. package/dist/drizzle/query-operations.mjs.map +1 -1
  88. package/dist/drizzle/session-sql.mjs +2 -1
  89. package/dist/drizzle/session-sql.mjs.map +1 -1
  90. package/dist/drizzle/sqlite-password-prepared.mjs.map +1 -1
  91. package/dist/drizzle/sqlite-passwords.mjs.map +1 -1
  92. package/dist/drizzle/storage-validation.mjs +10 -198
  93. package/dist/drizzle/storage-validation.mjs.map +1 -1
  94. package/dist/drizzle/totp-target.d.mts +3 -3
  95. package/dist/drizzle/totp-target.mjs +2 -2
  96. package/dist/drizzle/transaction-execution.d.mts +2 -2
  97. package/dist/drizzle/transaction-owner.d.mts +1 -2
  98. package/dist/index.d.mts +3 -2
  99. package/dist/internal/drizzle-postgres-oauth-proxy.d.mts +138 -0
  100. package/dist/internal/drizzle-postgres-oauth-proxy.mjs +29 -0
  101. package/dist/internal/drizzle-postgres-oauth-proxy.mjs.map +1 -0
  102. package/dist/internal/drizzle-postgres.mjs +2 -0
  103. package/dist/internal/drizzle-postgres.mjs.map +1 -1
  104. package/dist/internal/drizzle-sqlite-oauth-proxy.d.mts +146 -0
  105. package/dist/internal/drizzle-sqlite-oauth-proxy.mjs +29 -0
  106. package/dist/internal/drizzle-sqlite-oauth-proxy.mjs.map +1 -0
  107. package/dist/internal/drizzle-sqlite.mjs +2 -0
  108. package/dist/internal/drizzle-sqlite.mjs.map +1 -1
  109. package/package.json +1 -1
  110. package/src/D1.ts +2 -0
  111. package/src/Libsql.ts +2 -0
  112. package/src/Pglite.ts +2 -0
  113. package/src/Postgres.ts +2 -0
  114. package/src/SqliteBun.ts +2 -0
  115. package/src/SqliteDo.ts +2 -0
  116. package/src/SqliteNode.ts +2 -0
  117. package/src/SqliteWasm.ts +2 -0
  118. package/src/drizzle/d1-password-prepared.ts +2 -23
  119. package/src/drizzle/d1-passwords.ts +158 -780
  120. package/src/drizzle/d1-sessions.ts +21 -6
  121. package/src/drizzle/mysql-password-prepared.ts +4 -16
  122. package/src/drizzle/mysql-passwords.ts +5 -20
  123. package/src/drizzle/native-sql-table.ts +259 -0
  124. package/src/drizzle/oauth-connected-model.ts +97 -315
  125. package/src/drizzle/oauth-connected-reference.ts +13 -93
  126. package/src/drizzle/oauth-connected-target.ts +14 -271
  127. package/src/drizzle/oauth-kernel.ts +10 -0
  128. package/src/drizzle/oauth-model.ts +188 -541
  129. package/src/drizzle/oauth-mysql.ts +2 -2
  130. package/src/drizzle/oauth-proxy.ts +101 -0
  131. package/src/drizzle/oauth-target.ts +33 -737
  132. package/src/drizzle/password-model.ts +0 -33
  133. package/src/drizzle/password-prepared-model.ts +0 -18
  134. package/src/drizzle/password-sql.ts +3 -0
  135. package/src/drizzle/password-target.ts +41 -4
  136. package/src/drizzle/pg-password-prepared.ts +4 -16
  137. package/src/drizzle/pg-passwords.ts +5 -20
  138. package/src/drizzle/query-operations.ts +4 -0
  139. package/src/drizzle/session-sql.ts +2 -1
  140. package/src/drizzle/sqlite-password-prepared.ts +4 -52
  141. package/src/drizzle/sqlite-passwords.ts +7 -28
  142. package/src/drizzle/storage-validation.ts +13 -222
  143. package/src/index.ts +2 -8
  144. package/src/internal/drizzle-postgres-oauth-proxy.ts +37 -0
  145. package/src/internal/drizzle-postgres.ts +2 -0
  146. package/src/internal/drizzle-sqlite-oauth-proxy.ts +44 -0
  147. package/src/internal/drizzle-sqlite.ts +2 -0
  148. package/dist/_virtual/_rolldown/runtime.mjs +0 -13
  149. package/dist/drizzle/oauth-accounts.mjs +0 -359
  150. package/dist/drizzle/oauth-accounts.mjs.map +0 -1
  151. package/dist/drizzle/oauth-connected-access.mjs +0 -212
  152. package/dist/drizzle/oauth-connected-access.mjs.map +0 -1
  153. package/dist/drizzle/oauth-connected-collection.mjs +0 -202
  154. package/dist/drizzle/oauth-connected-collection.mjs.map +0 -1
  155. package/dist/drizzle/oauth-connected-custody.mjs +0 -147
  156. package/dist/drizzle/oauth-connected-custody.mjs.map +0 -1
  157. package/dist/drizzle/oauth-connected-flow.mjs +0 -297
  158. package/dist/drizzle/oauth-connected-flow.mjs.map +0 -1
  159. package/dist/drizzle/oauth-connected-input.mjs +0 -122
  160. package/dist/drizzle/oauth-connected-input.mjs.map +0 -1
  161. package/dist/drizzle/oauth-connected-maintenance.mjs +0 -214
  162. package/dist/drizzle/oauth-connected-maintenance.mjs.map +0 -1
  163. package/dist/drizzle/oauth-connected-management.mjs +0 -229
  164. package/dist/drizzle/oauth-connected-management.mjs.map +0 -1
  165. package/dist/drizzle/oauth-connected-settlement.mjs +0 -128
  166. package/dist/drizzle/oauth-connected-settlement.mjs.map +0 -1
  167. package/dist/drizzle/oauth-connected-sign-in.mjs +0 -265
  168. package/dist/drizzle/oauth-connected-sign-in.mjs.map +0 -1
  169. package/dist/drizzle/oauth-connected-state.d.mts +0 -2062
  170. package/dist/drizzle/oauth-connected-state.mjs +0 -430
  171. package/dist/drizzle/oauth-connected-state.mjs.map +0 -1
  172. package/dist/drizzle/oauth-flow.d.mts +0 -22
  173. package/dist/drizzle/oauth-flow.mjs +0 -253
  174. package/dist/drizzle/oauth-flow.mjs.map +0 -1
  175. package/dist/drizzle/oauth-input.mjs +0 -127
  176. package/dist/drizzle/oauth-input.mjs.map +0 -1
  177. package/dist/drizzle/oauth-owner.d.mts +0 -10
  178. package/dist/drizzle/oauth-owner.mjs +0 -10
  179. package/dist/drizzle/oauth-owner.mjs.map +0 -1
  180. package/dist/drizzle/oauth-registration.d.mts +0 -5
  181. package/dist/drizzle/oauth-registration.mjs +0 -374
  182. package/dist/drizzle/oauth-registration.mjs.map +0 -1
  183. package/dist/drizzle/oauth-state.mjs +0 -107
  184. package/dist/drizzle/oauth-state.mjs.map +0 -1
  185. package/src/drizzle/oauth-accounts.ts +0 -805
  186. package/src/drizzle/oauth-connected-access.ts +0 -361
  187. package/src/drizzle/oauth-connected-collection.ts +0 -324
  188. package/src/drizzle/oauth-connected-custody.ts +0 -279
  189. package/src/drizzle/oauth-connected-flow.ts +0 -529
  190. package/src/drizzle/oauth-connected-input.ts +0 -147
  191. package/src/drizzle/oauth-connected-maintenance.ts +0 -346
  192. package/src/drizzle/oauth-connected-management.ts +0 -365
  193. package/src/drizzle/oauth-connected-settlement.ts +0 -251
  194. package/src/drizzle/oauth-connected-sign-in.ts +0 -443
  195. package/src/drizzle/oauth-connected-state.ts +0 -872
  196. package/src/drizzle/oauth-flow.ts +0 -462
  197. package/src/drizzle/oauth-input.ts +0 -201
  198. package/src/drizzle/oauth-owner.ts +0 -29
  199. package/src/drizzle/oauth-registration.ts +0 -640
  200. package/src/drizzle/oauth-state.ts +0 -218
@@ -7,16 +7,15 @@ import { D1BatchStatements } from "./D1BatchStatements.mjs";
7
7
  import { CurrentD1PlanningDatabase, makeD1Owner } from "./d1-planning.mjs";
8
8
  import { nativeDatabase } from "./native-database.mjs";
9
9
  import { compileD1ProofCompletionPlan } from "./d1-proofs.mjs";
10
- import { NativeDatabase } from "@yielded/auth-persistence/Adapter";
11
- import { and, count, eq, gte, inArray, lte, notExists, sql } from "drizzle-orm";
10
+ import { NativeDatabase, requireStandalone, samePasswordCredentialSnapshot } from "@yielded/auth-persistence/Adapter";
11
+ import { and, eq, getTableColumns, inArray, sql } from "drizzle-orm";
12
12
  import { Cause, Context, DateTime, Effect, Option, Predicate, Redacted, Schema } from "effect";
13
13
  import { CurrentCommitJournal, HookConfigurationError, LifecycleHooks, coordinateCommit, hasCommitScope } from "@yielded/auth/Hooks";
14
14
  import { AuthenticationEvidence, AuthenticationRequirement, snapshotAuthenticationEvidence } from "@yielded/auth/Sessions";
15
15
  import { reportPersistenceFailure } from "@yielded/auth/Persistence";
16
16
  import { ProofUnavailable } from "@yielded/auth/Proofs";
17
- import { PasswordPersistence, PasswordUnavailable, snapshotPasswordCredential, snapshotPasswordRequirement, snapshotPasswordRevision } from "@yielded/auth/Password";
17
+ import { EncodedPasswordHash, PasswordPersistence, PasswordUnavailable, snapshotPasswordCredential, snapshotPasswordRequirement, snapshotPasswordRevision } from "@yielded/auth/Password";
18
18
  //#region src/drizzle/d1-passwords.ts
19
- const mapAdapter = (effect) => effect;
20
19
  const unavailable = () => PasswordUnavailable.make({});
21
20
  const everyFailureMatches = (cause, classify) => classify !== void 0 && cause.reasons.length > 0 && cause.reasons.every((reason) => Cause.isFailReason(reason) && classify(Cause.fail(reason.error)));
22
21
  const validConstraints = (mapping) => Object.entries(requiredPasswordConstraints).every(([key, value]) => mapping.constraints[key] === value);
@@ -61,10 +60,6 @@ const readEngineNowMillis = Effect.fn("Drizzle.readEngineNowMillis")(function* (
61
60
  return Number.isFinite(value) ? Effect.succeed(value) : Effect.fail(unavailable());
62
61
  }));
63
62
  });
64
- const driverValues = (values, entries) => ({
65
- ...values,
66
- ...Object.fromEntries(entries)
67
- });
68
63
  const allocate = (asyncValue, syncValue) => asyncValue !== void 0 ? asyncValue : syncValue === void 0 ? Effect.fail(unavailable()) : Effect.try({
69
64
  try: syncValue,
70
65
  catch: (cause) => new PersistenceMappingError$1({
@@ -100,38 +95,6 @@ const authorityColumns = (mapping) => ({
100
95
  revision: column(mapping.authorityCredential.table, mapping.authorityCredential.revision),
101
96
  status: mapping.authorityCredential.status === void 0 ? void 0 : column(mapping.authorityCredential.table, mapping.authorityCredential.status)
102
97
  });
103
- const attemptColumns = (mapping) => ({
104
- moduleId: column(mapping.attempt.table, mapping.attempt.moduleId),
105
- action: column(mapping.attempt.table, mapping.attempt.action),
106
- attemptId: column(mapping.attempt.table, mapping.attempt.attemptId),
107
- identifierNamespace: column(mapping.attempt.table, mapping.attempt.identifierNamespace),
108
- identifierValue: column(mapping.attempt.table, mapping.attempt.identifierValue),
109
- subjectId: column(mapping.attempt.table, mapping.attempt.subjectId),
110
- credentialId: column(mapping.attempt.table, mapping.attempt.credentialId),
111
- securityRevision: column(mapping.attempt.table, mapping.attempt.securityRevision),
112
- credentialRevision: column(mapping.attempt.table, mapping.attempt.credentialRevision),
113
- verifierVersion: column(mapping.attempt.table, mapping.attempt.verifierVersion),
114
- identifierBindingRevision: column(mapping.attempt.table, mapping.attempt.identifierBindingRevision),
115
- admittedAt: column(mapping.attempt.table, mapping.attempt.admittedAt),
116
- deadline: column(mapping.attempt.table, mapping.attempt.deadline),
117
- retentionUntil: column(mapping.attempt.table, mapping.attempt.retentionUntil),
118
- state: column(mapping.attempt.table, mapping.attempt.state)
119
- });
120
- const scopeColumns = (mapping) => ({
121
- moduleId: column(mapping.rateScope.table, mapping.rateScope.moduleId),
122
- action: column(mapping.rateScope.table, mapping.rateScope.action),
123
- scopeKind: column(mapping.rateScope.table, mapping.rateScope.scopeKind),
124
- scopeKey: column(mapping.rateScope.table, mapping.rateScope.scopeKey)
125
- });
126
- const chargeColumns = (mapping) => ({
127
- moduleId: column(mapping.charge.table, mapping.charge.moduleId),
128
- action: column(mapping.charge.table, mapping.charge.action),
129
- scopeKind: column(mapping.charge.table, mapping.charge.scopeKind),
130
- scopeKey: column(mapping.charge.table, mapping.charge.scopeKey),
131
- attemptId: column(mapping.charge.table, mapping.charge.attemptId),
132
- occurredAt: column(mapping.charge.table, mapping.charge.occurredAt),
133
- retentionUntil: column(mapping.charge.table, mapping.charge.retentionUntil)
134
- });
135
98
  const commandColumns = (mapping) => ({
136
99
  moduleId: column(mapping.command.table, mapping.command.moduleId),
137
100
  commandId: column(mapping.command.table, mapping.command.commandId),
@@ -180,45 +143,6 @@ const resolveCredential = Effect.fn("DrizzleD1Password.resolveCredential")(funct
180
143
  }).pipe(Effect.flatMap(snapshotPasswordCredential))
181
144
  };
182
145
  });
183
- const scopeEntries = (keys, policy) => {
184
- const entries = [{
185
- kind: "action",
186
- key: keys.action,
187
- ...policy.action
188
- }, {
189
- kind: "identifier",
190
- key: keys.identifier,
191
- ...policy.identifier
192
- }];
193
- if (keys.subject !== void 0) entries.push({
194
- kind: "subject",
195
- key: keys.subject,
196
- ...policy.subject
197
- });
198
- return entries;
199
- };
200
- const livePendingAttempts = (mapping, moduleId, action, entry, identifier, nativeSubjectId, activeAt) => {
201
- const a = attemptColumns(mapping);
202
- return and(entry.kind === "action" ? and(eq(a.moduleId, moduleId), eq(a.action, action)) : entry.kind === "identifier" ? and(eq(a.moduleId, moduleId), eq(a.action, action), eq(a.identifierNamespace, identifier.namespace), eq(a.identifierValue, identifier.value)) : and(eq(a.moduleId, moduleId), eq(a.action, action), eq(a.subjectId, nativeSubjectId)), eq(a.state, "pending"), sql`${a.retentionUntil} > ${activeAt}`);
203
- };
204
- const scopeAdmissionCondition = (mapping, moduleId, action, entry, policy, identifier, nativeSubjectId) => {
205
- const c = chargeColumns(mapping);
206
- const pending = livePendingAttempts(mapping, moduleId, action, entry, identifier, nativeSubjectId, mapping.d1.engineNow);
207
- return sql`(select count(*) from ${mapping.charge.table} where ${c.moduleId} = ${sql.param(moduleId, c.moduleId)} and ${c.action} = ${sql.param(action, c.action)} and ${c.scopeKind} = ${sql.param(entry.kind, c.scopeKind)} and ${c.scopeKey} = ${sql.param(entry.key, c.scopeKey)} and ${c.occurredAt} >= ${mapping.d1.engineInstantMinus(entry.windowMillis)}) < ${entry.limit} and (select count(*) from ${mapping.attempt.table} where ${pending}) < ${policy.maximumPending}`;
208
- };
209
- const readScopeAdmission = Effect.fn("DrizzleD1Password.readScopeAdmission")(function* (mapping, moduleId, action, entries, policy, identifier, nativeSubjectId, now) {
210
- const database = yield* CurrentD1PlanningDatabase;
211
- const c = chargeColumns(mapping);
212
- const admitted = [];
213
- for (let index = 0; index < entries.length; index++) {
214
- const entry = entries[index];
215
- const chargeRows = yield* mapAdapter(database.select({ total: count() }).from(mapping.charge.table).where(and(eq(c.moduleId, moduleId), eq(c.action, action), eq(c.scopeKind, entry.kind), eq(c.scopeKey, entry.key), gte(c.occurredAt, mapping.encodeInstant(now - entry.windowMillis)))));
216
- const pendingRows = yield* mapAdapter(database.select({ total: count() }).from(mapping.attempt.table).where(livePendingAttempts(mapping, moduleId, action, entry, identifier, nativeSubjectId, mapping.encodeInstant(now))));
217
- if (Number(chargeRows[0]?.total ?? 0) < entry.limit && Number(pendingRows[0]?.total ?? 0) < policy.maximumPending) admitted.push(entry);
218
- else if (index === 0) break;
219
- }
220
- return admitted;
221
- });
222
146
  const evidenceProofsJson = Schema.encodeSync(Schema.fromJsonString(AuthenticationEvidence.fields.proofs));
223
147
  const alternativesJson = Schema.encodeSync(Schema.fromJsonString(AuthenticationRequirement.fields.alternatives));
224
148
  const assuranceCondition = (mapping, evidence, requirement) => {
@@ -233,7 +157,6 @@ const assuranceCondition = (mapping, evidence, requirement) => {
233
157
  )`;
234
158
  };
235
159
  const sameIdentifier = (left, right) => left.namespace === right.namespace && left.value === right.value;
236
- const sameSnapshot = (left, right) => left.moduleId === right.moduleId && left.revision.subjectId === right.revision.subjectId && left.revision.securityRevision === right.revision.securityRevision && left.credentialId === right.credentialId && left.credentialRevision === right.credentialRevision && left.verifierVersion === right.verifierVersion && left.normalization === right.normalization && left.identifierVerifiedAtMillis === right.identifierVerifiedAtMillis && left.revision.credentials.length === right.revision.credentials.length && left.revision.credentials.every((entry) => right.revision.credentials.some((other) => other.credentialId === entry.credentialId && other.revision === entry.revision)) && left.identifierBindingRevision === right.identifierBindingRevision && sameIdentifier(left.identifier, right.identifier) && Redacted.value(left.verifier) === Redacted.value(right.verifier);
237
160
  const evidenceSatisfiedAt = (evidence, requirement, now) => {
238
161
  if (evidence.proofs.length > 64 || evidence.revision.credentials.length > 64) return false;
239
162
  const revisions = /* @__PURE__ */ new Map();
@@ -325,7 +248,7 @@ const readAuthority = Effect.fn("DrizzleD1Password.readAuthority")(function* (ma
325
248
  identifier,
326
249
  credential
327
250
  });
328
- if (!sameSnapshot(snapshot, input.credential)) return void 0;
251
+ if (!samePasswordCredentialSnapshot(snapshot, input.credential)) return void 0;
329
252
  identifierCondition = identifierCurrentCondition(mapping, input, nativeSubjectId, identifier[mapping.identifier.verifiedAt]);
330
253
  const ic = identifierColumns(mapping);
331
254
  identifierCondition = and(identifierCondition, sql`exists(select 1 from ${mapping.identifier.table} where ${ic.namespace} = ${sql.param(input.credential.identifier.namespace, ic.namespace)} and ${ic.value} = ${sql.param(input.credential.identifier.value, ic.value)} and ${ic.subjectId} = ${sql.param(nativeSubjectId, ic.subjectId)} and ${ic.bindingRevision} = ${sql.param(input.credential.identifierBindingRevision, ic.bindingRevision)})`);
@@ -356,18 +279,6 @@ const credentialCurrentCondition = (mapping, input, nativeSubjectId) => {
356
279
  const c = credentialColumns(mapping);
357
280
  return input.credential === void 0 ? sql`false` : sql`exists(select 1 from ${mapping.credential.table} where ${c.moduleId} = ${sql.param(input.moduleId, c.moduleId)} and ${c.subjectId} = ${sql.param(nativeSubjectId, c.subjectId)} and ${c.credentialId} = ${sql.param(input.credential.credentialId, c.credentialId)} and ${c.credentialRevision} = ${sql.param(input.credential.credentialRevision, c.credentialRevision)} and ${c.verifierVersion} = ${sql.param(input.credential.verifierVersion, c.verifierVersion)} and ${c.verifier} = ${sql.param(Redacted.value(input.credential.verifier), c.verifier)} and ${c.normalization} = ${sql.param(input.credential.normalization, c.normalization)})`;
358
281
  };
359
- const admittedSnapshotCondition = (mapping, resolved, moduleId) => {
360
- const snapshot = resolved?.snapshot;
361
- if (snapshot === void 0 || resolved === void 0) return sql`true`;
362
- const s = subjectColumns(mapping);
363
- const i = identifierColumns(mapping);
364
- const c = credentialColumns(mapping);
365
- const ac = authorityColumns(mapping);
366
- return and(sql`exists(select 1 from ${mapping.subject.table} where ${s.id} = ${sql.param(resolved.nativeSubjectId, s.id)} and ${s.status} = ${sql.param(mapping.subject.d1ActiveStatusValue, s.status)} and ${s.securityRevision} = ${sql.param(snapshot.revision.securityRevision, s.securityRevision)})`, mapping.identifier.d1CurrentCondition({
367
- identifier: snapshot.identifier,
368
- nativeSubjectId: resolved.nativeSubjectId
369
- }), sql`exists(select 1 from ${mapping.identifier.table} where ${i.namespace} = ${sql.param(snapshot.identifier.namespace, i.namespace)} and ${i.value} = ${sql.param(snapshot.identifier.value, i.value)} and ${i.subjectId} = ${sql.param(resolved.nativeSubjectId, i.subjectId)} and ${i.bindingRevision} = ${sql.param(snapshot.identifierBindingRevision, i.bindingRevision)})`, sql`exists(select 1 from ${mapping.credential.table} where ${c.moduleId} = ${sql.param(moduleId, c.moduleId)} and ${c.subjectId} = ${sql.param(resolved.nativeSubjectId, c.subjectId)} and ${c.credentialId} = ${sql.param(snapshot.credentialId, c.credentialId)} and ${c.credentialRevision} = ${sql.param(snapshot.credentialRevision, c.credentialRevision)} and ${c.verifierVersion} = ${sql.param(snapshot.verifierVersion, c.verifierVersion)} and ${c.verifier} = ${sql.param(Redacted.value(snapshot.verifier), c.verifier)} and ${c.normalization} = ${sql.param(snapshot.normalization, c.normalization)})`, ...snapshot.revision.credentials.map((item) => sql`exists(select 1 from ${mapping.authorityCredential.table} where ${ac.subjectId} = ${sql.param(resolved.nativeSubjectId, ac.subjectId)} and ${ac.credentialId} = ${sql.param(item.credentialId, ac.credentialId)} and ${ac.revision} = ${sql.param(item.revision, ac.revision)}${ac.status === void 0 ? sql`` : sql` and ${ac.status} = ${sql.param(mapping.authorityCredential.d1ActiveStatusValue, ac.status)}`})`));
370
- };
371
282
  const mutationStatements = Effect.fn(function* (mapping, input, nativeSubjectId, revisions, now) {
372
283
  const database = yield* CurrentD1PlanningDatabase;
373
284
  const c = credentialColumns(mapping);
@@ -440,6 +351,7 @@ const compileAddMutation = Effect.fn("DrizzleD1Password.compileAddMutation")(fun
440
351
  yield* assertion(appliedCondition, marker)
441
352
  ],
442
353
  appliedCondition,
354
+ marker,
443
355
  retryable: (cause) => isGuardFailure(cause, marker)
444
356
  };
445
357
  });
@@ -449,136 +361,58 @@ const makePasswordPlans = (mapping, proofMapping) => {
449
361
  return yield* plan;
450
362
  });
451
363
  return {
452
- admitAttempt: (input, prepare) => Effect.gen(function* () {
453
- const database = yield* CurrentD1PlanningDatabase;
454
- const attemptId = yield* allocate(mapping.allocateAttemptId, mapping.allocateAttemptIdSync);
455
- return yield* run(Effect.gen(function* () {
456
- const journal = yield* CurrentCommitJournal;
457
- const now = yield* readEngineNowMillis(mapping);
458
- const resolved = yield* resolveCredential(mapping, input.moduleId, input.identifier, input.subjectId);
459
- const keys = mapping.scopeKeys({
460
- moduleId: input.moduleId,
461
- action: input.action,
462
- identifier: input.identifier,
463
- ...resolved?.snapshot === void 0 ? {} : { subjectId: resolved.snapshot.revision.subjectId }
464
- });
465
- const scopes = scopeEntries(keys, input.policy);
466
- const admitted = yield* readScopeAdmission(mapping, input.moduleId, input.action, scopes, input.policy, input.identifier, resolved?.nativeSubjectId, now);
467
- const allAdmitted = admitted.length === scopes.length;
468
- const considered = admitted.length === 0 ? scopes.slice(0, 1) : scopes;
469
- const receipt = prepare(allAdmitted ? {
470
- _tag: "Admitted",
471
- attemptId,
472
- ...resolved?.snapshot === void 0 ? {} : { credential: resolved.snapshot }
473
- } : { _tag: "Denied" }, journal);
474
- const marker = `effect-auth-password-guard:admit:${attemptId}`;
475
- const statements = [];
476
- for (const entry of considered) statements.push(yield* statement(database.insert(mapping.rateScope.table).values(mapping.rateScope.encodeInsert({
477
- moduleId: input.moduleId,
478
- action: input.action,
479
- scopeKind: entry.kind,
480
- scopeKey: entry.key
481
- })).onConflictDoNothing()));
482
- const conditions = considered.map((entry) => {
483
- const condition = scopeAdmissionCondition(mapping, input.moduleId, input.action, entry, input.policy, input.identifier, resolved?.nativeSubjectId);
484
- return admitted.includes(entry) ? condition : sql`not (${condition})`;
485
- });
486
- statements.push(yield* assertion(and(...conditions, ...allAdmitted ? [admittedSnapshotCondition(mapping, resolved, input.moduleId)] : []), marker));
487
- for (const entry of admitted) statements.push(yield* statement(database.insert(mapping.charge.table).values(driverValues(mapping.charge.encodeInsert({
488
- moduleId: input.moduleId,
489
- action: input.action,
490
- scopeKind: entry.kind,
491
- scopeKey: entry.key,
492
- attemptId,
493
- occurredAtMillis: now,
494
- retentionUntilMillis: now + entry.windowMillis
495
- }), [[mapping.charge.occurredAt, mapping.d1.engineNow], [mapping.charge.retentionUntil, mapping.d1.engineInstantPlus(entry.windowMillis)]]))));
496
- if (allAdmitted) {
497
- const snapshot = resolved?.snapshot;
498
- const record = {
499
- moduleId: input.moduleId,
500
- action: input.action,
501
- attemptId,
502
- identifier: input.identifier,
503
- ...snapshot === void 0 ? {} : {
504
- subjectId: snapshot.revision.subjectId,
505
- credentialId: snapshot.credentialId,
506
- securityRevision: snapshot.revision.securityRevision,
507
- credentialRevision: snapshot.credentialRevision,
508
- verifierVersion: snapshot.verifierVersion,
509
- identifierBindingRevision: snapshot.identifierBindingRevision
510
- },
511
- admittedAtMillis: now,
512
- deadlineMillis: now + input.policy.attemptLifetimeMillis,
513
- retentionUntilMillis: Math.max(now + input.policy.attemptLifetimeMillis, ...scopes.map((entry) => now + entry.windowMillis))
514
- };
515
- statements.push(yield* statement(database.insert(mapping.attempt.table).values(driverValues(mapping.attempt.encodeInsert(record, {
516
- ...resolved?.nativeSubjectId === void 0 ? {} : { nativeSubjectId: resolved.nativeSubjectId },
517
- state: "pending"
518
- }), [
519
- [mapping.attempt.admittedAt, mapping.d1.engineNow],
520
- [mapping.attempt.deadline, mapping.d1.engineInstantPlus(input.policy.attemptLifetimeMillis)],
521
- [mapping.attempt.retentionUntil, mapping.d1.engineInstantPlus(Math.max(input.policy.attemptLifetimeMillis, ...scopes.map((entry) => entry.windowMillis)))]
522
- ]))));
523
- }
524
- return {
525
- receipt,
526
- statements,
527
- retryable: (cause) => isGuardFailure(cause, marker)
528
- };
529
- }));
530
- }),
531
- settleAttempt: (uncaptured, prepare) => Effect.gen(function* () {
532
- const database = yield* CurrentD1PlanningDatabase;
533
- const captured = uncaptured.captured === void 0 ? void 0 : yield* snapshotPasswordCredential(uncaptured.captured);
364
+ findCredential: (uncaptured) => Effect.gen(function* () {
365
+ if (!validConstraints(mapping)) return yield* unavailable();
534
366
  const input = {
535
367
  ...uncaptured,
536
- ...captured === void 0 ? {} : { captured }
368
+ identifier: { ...uncaptured.identifier }
537
369
  };
538
- return yield* run(Effect.gen(function* () {
539
- const journal = yield* CurrentCommitJournal;
540
- const a = attemptColumns(mapping);
541
- const row = (yield* database.select().from(mapping.attempt.table).where(and(eq(a.moduleId, input.moduleId), eq(a.attemptId, input.attemptId))).limit(1))[0];
542
- const engineNow = yield* readEngineNowMillis(mapping);
543
- const deadline = row === void 0 ? void 0 : yield* mapping.decodeInstant(row[mapping.attempt.deadline]);
544
- let resolved;
545
- let verified = false;
546
- if (row !== void 0 && row[mapping.attempt.state] === "pending" && input.outcome === "verified" && input.captured !== void 0 && deadline !== void 0 && deadline > engineNow) {
547
- resolved = yield* resolveCredential(mapping, input.moduleId, input.captured.identifier, input.captured.revision.subjectId);
548
- verified = input.captured.moduleId === input.moduleId && resolved?.snapshot !== void 0 && sameSnapshot(resolved.snapshot, input.captured) && mapping.subjectId.equals(row[mapping.attempt.subjectId], resolved.nativeSubjectId) && row[mapping.attempt.identifierNamespace] === input.captured.identifier.namespace && row[mapping.attempt.identifierValue] === input.captured.identifier.value && row[mapping.attempt.credentialId] === input.captured.credentialId && row[mapping.attempt.securityRevision] === input.captured.revision.securityRevision && row[mapping.attempt.credentialRevision] === input.captured.credentialRevision && row[mapping.attempt.verifierVersion] === input.captured.verifierVersion && row[mapping.attempt.identifierBindingRevision] === input.captured.identifierBindingRevision;
549
- }
550
- const decision = verified ? "verified" : "rejected";
551
- const receipt = prepare(decision, journal);
552
- if (row === void 0 || row[mapping.attempt.state] !== "pending") return {
553
- receipt,
554
- statements: []
555
- };
556
- const marker = `effect-auth-password-guard:settle:${input.attemptId}`;
557
- const base = and(eq(a.moduleId, input.moduleId), eq(a.attemptId, input.attemptId), eq(a.state, "pending"));
558
- const condition = verified ? and(base, sql`${a.deadline} > ${mapping.d1.engineNow}`) : base;
559
- const statements = [yield* assertion(sql`exists(select 1 from ${mapping.attempt.table} where ${condition})`, marker), yield* statement(database.update(mapping.attempt.table).set(updateValues([[mapping.attempt.state, decision]])).where(condition))];
560
- if (verified && resolved?.snapshot !== void 0) {
561
- const captured = input.captured;
562
- const s = subjectColumns(mapping);
563
- const i = identifierColumns(mapping);
564
- const c = credentialColumns(mapping);
565
- const authority = and(sql`exists(select 1 from ${mapping.subject.table} where ${s.id} = ${sql.param(resolved.nativeSubjectId, s.id)} and ${s.status} = ${sql.param(mapping.subject.d1ActiveStatusValue, s.status)} and ${s.securityRevision} = ${sql.param(input.captured.revision.securityRevision, s.securityRevision)})`, mapping.identifier.d1CurrentCondition({
566
- identifier: input.captured.identifier,
567
- nativeSubjectId: resolved.nativeSubjectId
568
- }), sql`exists(select 1 from ${mapping.credential.table} where ${c.moduleId} = ${sql.param(input.moduleId, c.moduleId)} and ${c.subjectId} = ${sql.param(resolved.nativeSubjectId, c.subjectId)} and ${c.credentialId} = ${sql.param(input.captured.credentialId, c.credentialId)} and ${c.credentialRevision} = ${sql.param(input.captured.credentialRevision, c.credentialRevision)} and ${c.verifierVersion} = ${sql.param(input.captured.verifierVersion, c.verifierVersion)} and ${c.verifier} = ${sql.param(Redacted.value(input.captured.verifier), c.verifier)})`, sql`exists(select 1 from ${mapping.identifier.table} where ${i.namespace} = ${sql.param(input.captured.identifier.namespace, i.namespace)} and ${i.value} = ${sql.param(input.captured.identifier.value, i.value)} and ${i.subjectId} = ${sql.param(resolved.nativeSubjectId, i.subjectId)} and ${i.bindingRevision} = ${sql.param(input.captured.identifierBindingRevision, i.bindingRevision)})`);
569
- statements.splice(1, 0, yield* assertion(authority, marker));
570
- if (input.rehash !== void 0 && input.rehash.expectedVersion === captured.verifierVersion && Redacted.value(input.rehash.expectedVerifier) === Redacted.value(captured.verifier)) {
571
- const nextVersion = yield* allocate(mapping.allocateRevision, mapping.allocateRevisionSync);
572
- statements.push(yield* statement(database.update(mapping.credential.table).set(mapping.credential.encodeVerifier(input.rehash.nextVerifier, nextVersion)).where(and(eq(c.moduleId, input.moduleId), eq(c.subjectId, resolved.nativeSubjectId), eq(c.credentialId, captured.credentialId), eq(c.credentialRevision, captured.credentialRevision), eq(c.verifierVersion, input.rehash.expectedVersion), eq(c.verifier, Redacted.value(input.rehash.expectedVerifier))))));
573
- }
574
- }
575
- return {
576
- receipt,
577
- statements,
578
- retryable: (cause) => isGuardFailure(cause, marker)
579
- };
580
- }));
370
+ const database = yield* CurrentD1PlanningDatabase;
371
+ const identifierRow = (yield* readIdentifier(mapping, input.identifier))[0];
372
+ if (identifierRow === void 0 || !mapping.identifier.isCurrent(identifierRow)) return Option.none();
373
+ const nativeId = identifierRow[mapping.identifier.subjectId];
374
+ const sc = subjectColumns(mapping), ic = identifierColumns(mapping), cc = credentialColumns(mapping);
375
+ const rows = yield* database.select({
376
+ subject: getTableColumns(mapping.subject.table),
377
+ identifier: getTableColumns(mapping.identifier.table),
378
+ credential: getTableColumns(mapping.credential.table)
379
+ }).from(mapping.subject.table).innerJoin(mapping.identifier.table, and(eq(ic.namespace, input.identifier.namespace), eq(ic.value, input.identifier.value), eq(ic.subjectId, nativeId))).innerJoin(mapping.credential.table, and(eq(cc.moduleId, input.moduleId), eq(cc.subjectId, nativeId))).where(eq(sc.id, nativeId));
380
+ if (rows.length !== 1) return Option.none();
381
+ const row = rows[0];
382
+ if (!mapping.subject.isActiveStatus(row.subject[mapping.subject.status]) || !mapping.identifier.isCurrent(row.identifier) || !mapping.subjectId.equals(nativeId, row.subject[mapping.subject.id]) || !mapping.subjectId.equals(nativeId, row.identifier[mapping.identifier.subjectId]) || !mapping.subjectId.equals(nativeId, row.credential[mapping.credential.subjectId])) return Option.none();
383
+ const credential = yield* mapping.credential.decode({
384
+ moduleId: input.moduleId,
385
+ ...row
386
+ }).pipe(Effect.flatMap(snapshotPasswordCredential));
387
+ return input.subjectId !== void 0 && credential.revision.subjectId !== input.subjectId ? Option.none() : Option.some(credential);
581
388
  }),
389
+ rehashIfCurrent: (input) => run(Effect.gen(function* () {
390
+ const nextVerifier = Redacted.make(yield* Schema.decodeEffect(EncodedPasswordHash)(Redacted.value(input.nextVerifier)));
391
+ const credential = yield* snapshotPasswordCredential(input.credential);
392
+ const database = yield* CurrentD1PlanningDatabase;
393
+ const nativeId = yield* mapping.subjectId.toNative(credential.revision.subjectId);
394
+ const version = yield* allocate(mapping.allocateRevision, mapping.allocateRevisionSync);
395
+ if (version === credential.verifierVersion) return yield* unavailable();
396
+ const encoded = mapping.credential.encodeVerifier(nextVerifier, version);
397
+ const values = {
398
+ [mapping.credential.verifier]: encoded[mapping.credential.verifier],
399
+ [mapping.credential.verifierVersion]: encoded[mapping.credential.verifierVersion]
400
+ };
401
+ if (Object.values(values).some((value) => value === void 0)) return yield* unavailable();
402
+ const c = credentialColumns(mapping);
403
+ return {
404
+ receipt: void 0,
405
+ statements: [yield* statement(database.update(mapping.credential.table).set(values).where(and(eq(c.moduleId, credential.moduleId), eq(c.subjectId, nativeId), eq(c.credentialId, credential.credentialId), ...[
406
+ [c.moduleId, credential.moduleId],
407
+ [c.subjectId, nativeId],
408
+ [c.credentialId, credential.credentialId],
409
+ [c.credentialRevision, credential.credentialRevision],
410
+ [c.verifierVersion, credential.verifierVersion],
411
+ [c.verifier, Redacted.value(credential.verifier)],
412
+ [c.normalization, credential.normalization]
413
+ ].map(([column, value]) => typeof value === "string" ? sql`cast(${column} as blob) = cast(${sql.param(value, column)} as blob)` : eq(column, value)))))]
414
+ };
415
+ })),
582
416
  readForSubject: (input) => Effect.gen(function* () {
583
417
  const database = yield* CurrentD1PlanningDatabase;
584
418
  if (yield* hasCommitScope) return yield* unavailable();
@@ -617,10 +451,15 @@ const makePasswordPlans = (mapping, proofMapping) => {
617
451
  verifierVersion,
618
452
  nextSecurityRevision
619
453
  });
454
+ if (compiled === void 0) return {
455
+ receipt: prepare("rejected", journal),
456
+ statements: []
457
+ };
620
458
  return {
621
- receipt: prepare(compiled === void 0 ? "rejected" : "changed", journal),
622
- statements: compiled?.statements ?? [],
623
- ...compiled?.retryable === void 0 ? {} : { retryable: compiled.retryable }
459
+ receipt: prepare("changed", journal),
460
+ statements: compiled.statements,
461
+ postconditions: [yield* assertion(compiled.appliedCondition, compiled.marker)],
462
+ retryable: compiled.retryable
624
463
  };
625
464
  }));
626
465
  }),
@@ -635,10 +474,7 @@ const makePasswordPlans = (mapping, proofMapping) => {
635
474
  resetWithProof: (uncaptured, prepare) => proofMapping === void 0 ? Effect.fail(unavailable()) : Effect.flatMap(snapshotMutationInput(uncaptured), (captured) => replacementPlan(mapping, proofMapping, Object.freeze({
636
475
  ...captured,
637
476
  completion: uncaptured.completion
638
- }), prepare, "reset-password")),
639
- cleanupAttempts: (input, prepare) => run(Effect.gen(function* () {
640
- return yield* cleanupPlan(mapping, input, prepare);
641
- }))
477
+ }), prepare, "reset-password"))
642
478
  };
643
479
  };
644
480
  const compileReplacementMutation = Effect.fn("DrizzleD1Password.compileReplacementMutation")(function* (mapping, input, action, revisions) {
@@ -695,65 +531,15 @@ const replacementPlan = Effect.fn("Drizzle.replacementPlan")(function* (mapping,
695
531
  return {
696
532
  receipt: passwordReceipt ?? prepare("rejected", journal),
697
533
  statements: compiled.statements,
534
+ ...compiled.postconditions === void 0 ? {} : { postconditions: compiled.postconditions },
698
535
  retryable: (cause) => isGuardFailure(cause, marker) || isGuardFailure(cause, `effect-auth-proof-guard:complete:${input.completion.input.continuationId}`) || isGuardFailure(cause, `effect-auth-proof-guard:protected:${input.completion.input.continuationId}`)
699
536
  };
700
537
  }
538
+ const applied = yield* assertion(protectedMutation.appliedCondition, marker);
701
539
  return {
702
540
  receipt: prepare("changed", journal),
703
- statements: [...protectedMutation.statements, yield* assertion(protectedMutation.appliedCondition, marker)],
704
- retryable: (cause) => isGuardFailure(cause, marker)
705
- };
706
- });
707
- const cleanupPlan = Effect.fn("Drizzle.cleanupPlan")(function* (mapping, input, prepare) {
708
- const database = yield* CurrentD1PlanningDatabase;
709
- const journal = yield* CurrentCommitJournal;
710
- const now = yield* readEngineNowMillis(mapping);
711
- const nativeNow = mapping.encodeInstant(now);
712
- const a = attemptColumns(mapping);
713
- const c = chargeColumns(mapping);
714
- const sc = scopeColumns(mapping);
715
- let remaining = input.limit;
716
- let hasMore = false;
717
- const attempts = yield* database.select({ attemptId: a.attemptId }).from(mapping.attempt.table).where(and(eq(a.moduleId, input.moduleId), lte(a.retentionUntil, nativeNow))).limit(remaining + 1);
718
- if (attempts.length > remaining) hasMore = true;
719
- const selectedAttempts = attempts.slice(0, remaining);
720
- remaining -= selectedAttempts.length;
721
- const charges = yield* database.select({
722
- action: c.action,
723
- scopeKind: c.scopeKind,
724
- scopeKey: c.scopeKey,
725
- attemptId: c.attemptId
726
- }).from(mapping.charge.table).where(and(eq(c.moduleId, input.moduleId), lte(c.retentionUntil, nativeNow))).limit(remaining + 1);
727
- if (charges.length > remaining) hasMore = true;
728
- const selectedCharges = charges.slice(0, remaining);
729
- remaining -= selectedCharges.length;
730
- const scopes = yield* database.select({
731
- action: sc.action,
732
- scopeKind: sc.scopeKind,
733
- scopeKey: sc.scopeKey
734
- }).from(mapping.rateScope.table).where(and(eq(sc.moduleId, input.moduleId), notExists(database.select({ one: sql`1` }).from(mapping.charge.table).where(and(eq(c.moduleId, sc.moduleId), eq(c.action, sc.action), eq(c.scopeKind, sc.scopeKind), eq(c.scopeKey, sc.scopeKey)))))).limit(remaining + 1);
735
- if (scopes.length > remaining) hasMore = true;
736
- const selectedScopes = scopes.slice(0, remaining);
737
- const marker = `effect-auth-password-guard:cleanup:${input.moduleId}:${now}`;
738
- const statements = [];
739
- for (const row of selectedAttempts) {
740
- const condition = and(eq(a.moduleId, input.moduleId), eq(a.attemptId, row.attemptId), lte(a.retentionUntil, mapping.d1.engineNow));
741
- statements.push(yield* assertion(sql`exists(select 1 from ${mapping.attempt.table} where ${condition})`, marker), yield* statement(database.delete(mapping.attempt.table).where(condition)));
742
- }
743
- for (const row of selectedCharges) {
744
- const condition = and(eq(c.moduleId, input.moduleId), eq(c.action, row.action), eq(c.scopeKind, row.scopeKind), eq(c.scopeKey, row.scopeKey), eq(c.attemptId, row.attemptId), lte(c.retentionUntil, mapping.d1.engineNow));
745
- statements.push(yield* assertion(sql`exists(select 1 from ${mapping.charge.table} where ${condition})`, marker), yield* statement(database.delete(mapping.charge.table).where(condition)));
746
- }
747
- for (const row of selectedScopes) {
748
- const condition = and(eq(sc.moduleId, input.moduleId), eq(sc.action, row.action), eq(sc.scopeKind, row.scopeKind), eq(sc.scopeKey, row.scopeKey), notExists(database.select({ one: sql`1` }).from(mapping.charge.table).where(and(eq(c.moduleId, sc.moduleId), eq(c.action, sc.action), eq(c.scopeKind, sc.scopeKind), eq(c.scopeKey, sc.scopeKey)))));
749
- statements.push(yield* assertion(sql`exists(select 1 from ${mapping.rateScope.table} where ${condition})`, marker), yield* statement(database.delete(mapping.rateScope.table).where(condition)));
750
- }
751
- return {
752
- receipt: prepare({
753
- removed: selectedAttempts.length + selectedCharges.length + selectedScopes.length,
754
- hasMore
755
- }, journal),
756
- statements,
541
+ statements: [...protectedMutation.statements, applied],
542
+ postconditions: [applied],
757
543
  retryable: (cause) => isGuardFailure(cause, marker)
758
544
  };
759
545
  });
@@ -765,20 +551,23 @@ const makeD1PasswordPersistenceServices = Effect.fnUntraced(function* (mapping,
765
551
  }).pipe(Effect.provideServiceEffect(NativeDatabase, nativeDatabase(Database)), Effect.mapError(unavailable));
766
552
  const hooks = yield* LifecycleHooks;
767
553
  const plans = makePasswordPlans(mapping, proofMapping);
768
- const run = (plan) => Effect.gen(function* () {
554
+ const standalone = Effect.gen(function* () {
769
555
  if (yield* hasCommitScope) return yield* unavailable();
556
+ yield* requireStandalone(unavailable, database.$client.transactionService);
557
+ });
558
+ const run = (plan) => Effect.gen(function* () {
559
+ yield* standalone;
770
560
  return yield* executeStandalone(plan, 2);
771
561
  }).pipe(Effect.provideService(CurrentD1PlanningDatabase, database), Effect.provideService(LifecycleHooks, hooks));
772
562
  return { passwordPersistence: {
773
- admitAttempt: (input, prepare) => run(plans.admitAttempt(input, prepare)).pipe(translateFailure),
774
- settleAttempt: (input, prepare) => run(plans.settleAttempt(input, prepare)).pipe(translateFailure),
563
+ findCredential: (input) => standalone.pipe(Effect.andThen(plans.findCredential(input)), Effect.provideService(CurrentD1PlanningDatabase, database), translateFailure),
564
+ rehashIfCurrent: (input) => run(plans.rehashIfCurrent(input)).pipe(translateFailure),
775
565
  readForSubject: (input) => plans.readForSubject(input).pipe(Effect.provideService(CurrentD1PlanningDatabase, database), Effect.provideService(LifecycleHooks, hooks), translateFailure),
776
566
  recoveryTarget: (input) => plans.recoveryTarget(input).pipe(Effect.provideService(CurrentD1PlanningDatabase, database), Effect.provideService(LifecycleHooks, hooks), translateFailure),
777
567
  addIfAbsent: (input, prepare) => run(plans.addIfAbsent(input, prepare)).pipe(translateFailure),
778
568
  replaceIfCurrent: (input, prepare) => run(plans.replaceIfCurrent(input, prepare)).pipe(translateFailure),
779
569
  checkReset: (input) => plans.checkReset(input).pipe(Effect.provideService(CurrentD1PlanningDatabase, database), Effect.provideService(LifecycleHooks, hooks), translateFailure),
780
- resetWithProof: (input, prepare) => run(plans.resetWithProof(input, prepare)).pipe(translateFailure),
781
- cleanupAttempts: (input, prepare) => run(plans.cleanupAttempts(input, prepare)).pipe(translateFailure)
570
+ resetWithProof: (input, prepare) => run(plans.resetWithProof(input, prepare)).pipe(translateFailure)
782
571
  } };
783
572
  });
784
573
  /**
@@ -814,22 +603,21 @@ function coordinateD1PasswordPersistence(acquire, options, body) {
814
603
  return guarded.receipt;
815
604
  }), { mode: "batch" }).pipe(Effect.map((result) => result.value), Effect.provideService(CurrentD1PlanningDatabase, database), Effect.provideService(LifecycleHooks, hooks));
816
605
  const provided = Context.make(PasswordPersistence, {
817
- admitAttempt: (input, prepare) => owner.run(run(plans.admitAttempt(input, prepare)).pipe(translateFailure)),
818
- settleAttempt: (input, prepare) => owner.run(run(plans.settleAttempt(input, prepare)).pipe(translateFailure)),
606
+ findCredential: (input) => owner.run(plans.findCredential(input).pipe(Effect.provideService(CurrentD1PlanningDatabase, database), translateFailure)),
607
+ rehashIfCurrent: (input) => owner.run(run(plans.rehashIfCurrent(input)).pipe(translateFailure)),
819
608
  readForSubject: (input) => owner.run(plans.readForSubject(input).pipe(Effect.provideService(CurrentD1PlanningDatabase, database), Effect.provideService(LifecycleHooks, hooks), translateFailure)),
820
609
  recoveryTarget: (input) => owner.run(plans.recoveryTarget(input).pipe(Effect.provideService(CurrentD1PlanningDatabase, database), Effect.provideService(LifecycleHooks, hooks), translateFailure)),
821
610
  addIfAbsent: (input, prepare) => owner.run(run(plans.addIfAbsent(input, prepare)).pipe(translateFailure)),
822
611
  replaceIfCurrent: (input, prepare) => owner.run(run(plans.replaceIfCurrent(input, prepare)).pipe(translateFailure)),
823
612
  checkReset: (input) => owner.run(plans.checkReset(input).pipe(Effect.provideService(CurrentD1PlanningDatabase, database), Effect.provideService(LifecycleHooks, hooks), translateFailure)),
824
- resetWithProof: (input, prepare) => owner.run(run(plans.resetWithProof(input, prepare)).pipe(translateFailure)),
825
- cleanupAttempts: (input, prepare) => owner.run(run(plans.cleanupAttempts(input, prepare)).pipe(translateFailure))
613
+ resetWithProof: (input, prepare) => owner.run(run(plans.resetWithProof(input, prepare)).pipe(translateFailure))
826
614
  }).pipe(Context.add(D1BatchStatements, owner.collector));
827
615
  const value = yield* owner.close(Effect.provideContext(body, provided));
828
616
  if (mutation?.journalGuard !== void 0) {
829
617
  const status = yield* Effect.result(mutation.journalGuard.read);
830
618
  if (status._tag === "Success" || status.failure._tag !== "CommitPending") return yield* unavailable();
831
619
  }
832
- yield* database.$client.batch(statements).pipe(Effect.catchCause((cause) => Effect.failCause(Cause.map(cause, (error) => everyFailureMatches(cause, mutation?.retryable) ? unavailable() : error))), translateFailure);
620
+ yield* database.$client.batch([...statements, ...mutation?.postconditions ?? []]).pipe(Effect.catchCause((cause) => Effect.failCause(Cause.map(cause, (error) => everyFailureMatches(cause, mutation?.retryable) ? unavailable() : error))), translateFailure);
833
621
  return value;
834
622
  }), { mode: "batch" }).pipe(Effect.provideService(LifecycleHooks, hooks))).value;
835
623
  }).pipe(Effect.provideService(NativeDatabase, database)));