@yielded/auth-persistence-drizzle 0.1.0-beta.23 → 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 (185) hide show
  1. package/README.md +3 -0
  2. package/dist/D1.d.mts +9 -9
  3. package/dist/D1.mjs +2 -1
  4. package/dist/D1.mjs.map +1 -1
  5. package/dist/Libsql.d.mts +46 -62
  6. package/dist/Libsql.mjs +2 -1
  7. package/dist/Libsql.mjs.map +1 -1
  8. package/dist/Mysql2.d.mts +24 -25
  9. package/dist/Pglite.d.mts +26 -26
  10. package/dist/Pglite.mjs +2 -1
  11. package/dist/Pglite.mjs.map +1 -1
  12. package/dist/Postgres.d.mts +27 -27
  13. package/dist/Postgres.mjs +2 -1
  14. package/dist/Postgres.mjs.map +1 -1
  15. package/dist/SqliteBun.d.mts +47 -63
  16. package/dist/SqliteBun.mjs +2 -1
  17. package/dist/SqliteBun.mjs.map +1 -1
  18. package/dist/SqliteDo.d.mts +45 -61
  19. package/dist/SqliteDo.mjs +2 -1
  20. package/dist/SqliteDo.mjs.map +1 -1
  21. package/dist/SqliteNode.d.mts +49 -65
  22. package/dist/SqliteNode.mjs +2 -1
  23. package/dist/SqliteNode.mjs.map +1 -1
  24. package/dist/SqliteWasm.d.mts +45 -61
  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 +13 -13
  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 +17 -33
  34. package/dist/drizzle/d1-passwords.mjs +56 -198
  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 +1 -1
  38. package/dist/drizzle/d1-sessions.d.mts +2 -2
  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 +26 -1
  48. package/dist/drizzle/native-sql-table.mjs.map +1 -1
  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 -6
  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/pg-password-prepared.d.mts +5 -5
  76. package/dist/drizzle/pg-password-prepared.mjs.map +1 -1
  77. package/dist/drizzle/pg-passwords.d.mts +16 -32
  78. package/dist/drizzle/pg-passwords.mjs.map +1 -1
  79. package/dist/drizzle/pg-sessions.d.mts +1 -1
  80. package/dist/drizzle/phone-target.d.mts +1 -1
  81. package/dist/drizzle/phone-target.mjs +1 -1
  82. package/dist/drizzle/query-operations.mjs +3 -1
  83. package/dist/drizzle/query-operations.mjs.map +1 -1
  84. package/dist/drizzle/sqlite-password-prepared.mjs.map +1 -1
  85. package/dist/drizzle/sqlite-passwords.mjs.map +1 -1
  86. package/dist/drizzle/storage-validation.mjs +10 -185
  87. package/dist/drizzle/storage-validation.mjs.map +1 -1
  88. package/dist/drizzle/totp-target.d.mts +2 -2
  89. package/dist/drizzle/totp-target.mjs +2 -2
  90. package/dist/drizzle/transaction-execution.d.mts +2 -2
  91. package/dist/drizzle/transaction-owner.d.mts +1 -2
  92. package/dist/index.d.mts +3 -2
  93. package/dist/internal/drizzle-postgres-oauth-proxy.d.mts +138 -0
  94. package/dist/internal/drizzle-postgres-oauth-proxy.mjs +29 -0
  95. package/dist/internal/drizzle-postgres-oauth-proxy.mjs.map +1 -0
  96. package/dist/internal/drizzle-sqlite-oauth-proxy.d.mts +146 -0
  97. package/dist/internal/drizzle-sqlite-oauth-proxy.mjs +29 -0
  98. package/dist/internal/drizzle-sqlite-oauth-proxy.mjs.map +1 -0
  99. package/package.json +1 -1
  100. package/src/D1.ts +2 -0
  101. package/src/Libsql.ts +2 -0
  102. package/src/Pglite.ts +2 -0
  103. package/src/Postgres.ts +2 -0
  104. package/src/SqliteBun.ts +2 -0
  105. package/src/SqliteDo.ts +2 -0
  106. package/src/SqliteNode.ts +2 -0
  107. package/src/SqliteWasm.ts +2 -0
  108. package/src/drizzle/d1-password-prepared.ts +2 -5
  109. package/src/drizzle/d1-passwords.ts +123 -458
  110. package/src/drizzle/d1-sessions.ts +21 -6
  111. package/src/drizzle/mysql-password-prepared.ts +4 -8
  112. package/src/drizzle/mysql-passwords.ts +5 -10
  113. package/src/drizzle/native-sql-table.ts +30 -0
  114. package/src/drizzle/oauth-connected-model.ts +97 -315
  115. package/src/drizzle/oauth-connected-reference.ts +13 -93
  116. package/src/drizzle/oauth-connected-target.ts +14 -271
  117. package/src/drizzle/oauth-kernel.ts +10 -0
  118. package/src/drizzle/oauth-model.ts +188 -541
  119. package/src/drizzle/oauth-mysql.ts +2 -2
  120. package/src/drizzle/oauth-proxy.ts +101 -0
  121. package/src/drizzle/oauth-target.ts +33 -737
  122. package/src/drizzle/password-model.ts +0 -13
  123. package/src/drizzle/password-prepared-model.ts +0 -6
  124. package/src/drizzle/pg-password-prepared.ts +4 -8
  125. package/src/drizzle/pg-passwords.ts +5 -10
  126. package/src/drizzle/query-operations.ts +4 -0
  127. package/src/drizzle/sqlite-password-prepared.ts +4 -8
  128. package/src/drizzle/sqlite-passwords.ts +7 -14
  129. package/src/drizzle/storage-validation.ts +13 -214
  130. package/src/index.ts +2 -4
  131. package/src/internal/drizzle-postgres-oauth-proxy.ts +37 -0
  132. package/src/internal/drizzle-sqlite-oauth-proxy.ts +44 -0
  133. package/dist/_virtual/_rolldown/runtime.mjs +0 -13
  134. package/dist/drizzle/oauth-accounts.mjs +0 -359
  135. package/dist/drizzle/oauth-accounts.mjs.map +0 -1
  136. package/dist/drizzle/oauth-connected-access.mjs +0 -212
  137. package/dist/drizzle/oauth-connected-access.mjs.map +0 -1
  138. package/dist/drizzle/oauth-connected-collection.mjs +0 -202
  139. package/dist/drizzle/oauth-connected-collection.mjs.map +0 -1
  140. package/dist/drizzle/oauth-connected-custody.mjs +0 -147
  141. package/dist/drizzle/oauth-connected-custody.mjs.map +0 -1
  142. package/dist/drizzle/oauth-connected-flow.mjs +0 -297
  143. package/dist/drizzle/oauth-connected-flow.mjs.map +0 -1
  144. package/dist/drizzle/oauth-connected-input.mjs +0 -122
  145. package/dist/drizzle/oauth-connected-input.mjs.map +0 -1
  146. package/dist/drizzle/oauth-connected-maintenance.mjs +0 -214
  147. package/dist/drizzle/oauth-connected-maintenance.mjs.map +0 -1
  148. package/dist/drizzle/oauth-connected-management.mjs +0 -229
  149. package/dist/drizzle/oauth-connected-management.mjs.map +0 -1
  150. package/dist/drizzle/oauth-connected-settlement.mjs +0 -128
  151. package/dist/drizzle/oauth-connected-settlement.mjs.map +0 -1
  152. package/dist/drizzle/oauth-connected-sign-in.mjs +0 -265
  153. package/dist/drizzle/oauth-connected-sign-in.mjs.map +0 -1
  154. package/dist/drizzle/oauth-connected-state.d.mts +0 -2062
  155. package/dist/drizzle/oauth-connected-state.mjs +0 -430
  156. package/dist/drizzle/oauth-connected-state.mjs.map +0 -1
  157. package/dist/drizzle/oauth-flow.d.mts +0 -22
  158. package/dist/drizzle/oauth-flow.mjs +0 -253
  159. package/dist/drizzle/oauth-flow.mjs.map +0 -1
  160. package/dist/drizzle/oauth-input.mjs +0 -127
  161. package/dist/drizzle/oauth-input.mjs.map +0 -1
  162. package/dist/drizzle/oauth-owner.d.mts +0 -10
  163. package/dist/drizzle/oauth-owner.mjs +0 -10
  164. package/dist/drizzle/oauth-owner.mjs.map +0 -1
  165. package/dist/drizzle/oauth-registration.d.mts +0 -5
  166. package/dist/drizzle/oauth-registration.mjs +0 -374
  167. package/dist/drizzle/oauth-registration.mjs.map +0 -1
  168. package/dist/drizzle/oauth-state.mjs +0 -107
  169. package/dist/drizzle/oauth-state.mjs.map +0 -1
  170. package/src/drizzle/oauth-accounts.ts +0 -805
  171. package/src/drizzle/oauth-connected-access.ts +0 -361
  172. package/src/drizzle/oauth-connected-collection.ts +0 -324
  173. package/src/drizzle/oauth-connected-custody.ts +0 -279
  174. package/src/drizzle/oauth-connected-flow.ts +0 -529
  175. package/src/drizzle/oauth-connected-input.ts +0 -147
  176. package/src/drizzle/oauth-connected-maintenance.ts +0 -346
  177. package/src/drizzle/oauth-connected-management.ts +0 -365
  178. package/src/drizzle/oauth-connected-settlement.ts +0 -251
  179. package/src/drizzle/oauth-connected-sign-in.ts +0 -443
  180. package/src/drizzle/oauth-connected-state.ts +0 -872
  181. package/src/drizzle/oauth-flow.ts +0 -462
  182. package/src/drizzle/oauth-input.ts +0 -201
  183. package/src/drizzle/oauth-owner.ts +0 -29
  184. package/src/drizzle/oauth-registration.ts +0 -640
  185. package/src/drizzle/oauth-state.ts +0 -218
@@ -16,6 +16,7 @@ import {
16
16
  } from "@yielded/auth/Hooks";
17
17
  import type { LoginIdentifier } from "@yielded/auth/Identity";
18
18
  import {
19
+ EncodedPasswordHash,
19
20
  PasswordUnavailable,
20
21
  PasswordPersistence,
21
22
  type PasswordMutationInput,
@@ -24,11 +25,7 @@ import {
24
25
  snapshotPasswordRequirement,
25
26
  snapshotPasswordRevision,
26
27
  type PasswordAction,
27
- type PasswordAttemptDecision,
28
- type PasswordCredentialSnapshot,
29
28
  type PasswordMutationDecision,
30
- type PasswordAttemptPreparation,
31
- type PasswordAttemptAdmission,
32
29
  } from "@yielded/auth/Password";
33
30
  import { reportPersistenceFailure } from "@yielded/auth/Persistence";
34
31
  import {
@@ -43,7 +40,16 @@ import {
43
40
  AuthenticationRequirement,
44
41
  type SecurityRevision,
45
42
  } from "@yielded/auth/Sessions";
46
- import { and, eq, inArray, lte, sql, type AnyRelations, type SQL, type Table } from "drizzle-orm";
43
+ import {
44
+ and,
45
+ eq,
46
+ getTableColumns,
47
+ inArray,
48
+ sql,
49
+ type AnyRelations,
50
+ type SQL,
51
+ type Table,
52
+ } from "drizzle-orm";
47
53
  import type { EffectSQLiteD1Database } from "drizzle-orm/effect-d1";
48
54
  import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
49
55
  import { Cause, DateTime, Effect, Option, Predicate, Redacted, Schema, Context } from "effect";
@@ -59,7 +65,6 @@ import { nativeDatabase } from "./native-database";
59
65
  import {
60
66
  type AnyPasswordRegistrationMapping,
61
67
  type D1PasswordPersistenceMapping,
62
- type PasswordAttemptRecord,
63
68
  type PasswordRegistrationMapping,
64
69
  requiredPasswordConstraints,
65
70
  requiredPasswordRegistrationConstraints,
@@ -75,7 +80,7 @@ type PlanPrepare<Method extends (...args: any[]) => any, A> = (
75
80
  ) => PreparedCommit<A>;
76
81
 
77
82
  type Database = EffectSQLiteD1Database<AnyRelations> & { readonly $client: D1Client };
78
- type Mapping = D1PasswordPersistenceMapping<Table, Table, Table, Table, Table, Table, unknown>;
83
+ type Mapping = D1PasswordPersistenceMapping<Table, Table, Table, Table, Table, unknown>;
79
84
  type RegistrationMapping<Registration> = AnyPasswordRegistrationMapping<Registration>;
80
85
 
81
86
  interface Planned<A> {
@@ -189,11 +194,6 @@ const readEngineNowMillis = Effect.fn("Drizzle.readEngineNowMillis")(function* (
189
194
  );
190
195
  });
191
196
 
192
- const driverValues = (values: object, entries: ReadonlyArray<readonly [string, unknown]>) => ({
193
- ...values,
194
- ...Object.fromEntries(entries),
195
- });
196
-
197
197
  const allocate = <A>(
198
198
  asyncValue: Effect.Effect<A, PersistenceMappingError> | undefined,
199
199
  syncValue: (() => A) | undefined,
@@ -249,27 +249,6 @@ const authorityColumns = (mapping: Mapping) => ({
249
249
  : column(mapping.authorityCredential.table, mapping.authorityCredential.status),
250
250
  });
251
251
 
252
- const attemptColumns = (mapping: Mapping) => ({
253
- moduleId: column(mapping.attempt.table, mapping.attempt.moduleId),
254
- action: column(mapping.attempt.table, mapping.attempt.action),
255
- attemptId: column(mapping.attempt.table, mapping.attempt.attemptId),
256
- identifierNamespace: column(mapping.attempt.table, mapping.attempt.identifierNamespace),
257
- identifierValue: column(mapping.attempt.table, mapping.attempt.identifierValue),
258
- subjectId: column(mapping.attempt.table, mapping.attempt.subjectId),
259
- credentialId: column(mapping.attempt.table, mapping.attempt.credentialId),
260
- securityRevision: column(mapping.attempt.table, mapping.attempt.securityRevision),
261
- credentialRevision: column(mapping.attempt.table, mapping.attempt.credentialRevision),
262
- verifierVersion: column(mapping.attempt.table, mapping.attempt.verifierVersion),
263
- identifierBindingRevision: column(
264
- mapping.attempt.table,
265
- mapping.attempt.identifierBindingRevision,
266
- ),
267
- admittedAt: column(mapping.attempt.table, mapping.attempt.admittedAt),
268
- deadline: column(mapping.attempt.table, mapping.attempt.deadline),
269
- retentionUntil: column(mapping.attempt.table, mapping.attempt.retentionUntil),
270
- state: column(mapping.attempt.table, mapping.attempt.state),
271
- });
272
-
273
252
  const commandColumns = (mapping: Mapping) => ({
274
253
  moduleId: column(mapping.command.table, mapping.command.moduleId),
275
254
  commandId: column(mapping.command.table, mapping.command.commandId),
@@ -323,14 +302,6 @@ const readCredentialNative = Effect.fn("Drizzle.readCredentialNative")(function*
323
302
  .limit(1);
324
303
  });
325
304
 
326
- interface Resolved {
327
- readonly nativeSubjectId: unknown;
328
- readonly subject: any;
329
- readonly identifier: any;
330
- readonly credential?: any;
331
- readonly snapshot?: PasswordCredentialSnapshot;
332
- }
333
-
334
305
  const resolveCredential = Effect.fn("DrizzleD1Password.resolveCredential")(function* (
335
306
  mapping: Mapping,
336
307
  moduleId: string,
@@ -741,34 +712,6 @@ const credentialCurrentCondition = (
741
712
  : 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)})`;
742
713
  };
743
714
 
744
- const admittedSnapshotCondition = (
745
- mapping: Mapping,
746
- resolved: Resolved | undefined,
747
- moduleId: string,
748
- ) => {
749
- const snapshot = resolved?.snapshot;
750
-
751
- if (snapshot === undefined || resolved === undefined) return sql`true`;
752
- const s = subjectColumns(mapping);
753
- const i = identifierColumns(mapping);
754
- const c = credentialColumns(mapping);
755
- const ac = authorityColumns(mapping);
756
-
757
- return and(
758
- 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)})`,
759
- mapping.identifier.d1CurrentCondition({
760
- identifier: snapshot.identifier,
761
- nativeSubjectId: resolved.nativeSubjectId,
762
- }),
763
- 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)} and ${Predicate.isNullish(resolved.identifier[mapping.identifier.verifiedAt]) ? sql`${i.verifiedAt} is null` : sql`${i.verifiedAt} = ${sql.param(resolved.identifier[mapping.identifier.verifiedAt], i.verifiedAt)}`})`,
764
- 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)})`,
765
- ...snapshot.revision.credentials.map(
766
- (item) =>
767
- 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 === undefined ? sql`` : sql` and ${ac.status} = ${sql.param(mapping.authorityCredential.d1ActiveStatusValue, ac.status)}`})`,
768
- ),
769
- )!;
770
- };
771
-
772
715
  const mutationStatements = Effect.fn(function* (
773
716
  mapping: Mapping,
774
717
  input: PasswordMutationInput,
@@ -993,312 +936,121 @@ const makePasswordPlans = (
993
936
  });
994
937
 
995
938
  return {
996
- prepareAttempt: (uncaptured: Parameters<PasswordPersistence["Service"]["prepareAttempt"]>[0]) =>
939
+ findCredential: (uncaptured: Parameters<PasswordPersistence["Service"]["findCredential"]>[0]) =>
997
940
  Effect.gen(function* () {
998
941
  if (!validConstraints(mapping)) return yield* unavailable();
942
+ const input = { ...uncaptured, identifier: { ...uncaptured.identifier } };
943
+ const database = yield* CurrentD1PlanningDatabase;
944
+ const identifierRow = (yield* readIdentifier(mapping, input.identifier))[0];
999
945
 
1000
- const input = Object.freeze({
1001
- ...uncaptured,
1002
- identifier: Object.freeze({ ...uncaptured.identifier }),
1003
- });
1004
-
1005
- const resolved = yield* resolveCredential(
1006
- mapping,
1007
- input.moduleId,
1008
- input.identifier,
1009
- input.subjectId,
1010
- );
1011
-
1012
- const snapshot = resolved?.snapshot;
1013
- const attemptId = yield* allocate(mapping.allocateAttemptId, mapping.allocateAttemptIdSync);
1014
- let used = false;
1015
-
1016
- return Object.freeze({
1017
- ...(snapshot === undefined ? {} : { credential: snapshot }),
1018
- // Claim outside execution: a definite batch rollback may replan other
1019
- // operations, but a prepared admission never consumes its limits twice.
1020
- claim: Effect.suspend(() => {
1021
- if (used) return Effect.fail(unavailable());
1022
- used = true;
1023
-
1024
- return Effect.void;
1025
- }),
1026
- admit: <A>(prepare: PreparePasswordCommit<PasswordAttemptAdmission, A>) =>
1027
- run(
1028
- Effect.gen(function* () {
1029
- const database = yield* CurrentD1PlanningDatabase;
1030
- const journal = yield* CurrentCommitJournal;
1031
- const now = yield* readEngineNowMillis(mapping);
1032
- const marker = `effect-auth-password-guard:admit:${attemptId}`;
1033
- const a = attemptColumns(mapping);
1034
- const key = and(eq(a.moduleId, input.moduleId), eq(a.attemptId, attemptId))!;
1035
- const current = admittedSnapshotCondition(mapping, resolved, input.moduleId);
1036
-
1037
- const record: PasswordAttemptRecord = {
1038
- moduleId: input.moduleId,
1039
- action: input.action,
1040
- attemptId,
1041
- identifier: input.identifier,
1042
- ...(snapshot === undefined
1043
- ? {}
1044
- : {
1045
- subjectId: snapshot.revision.subjectId,
1046
- credentialId: snapshot.credentialId,
1047
- securityRevision: snapshot.revision.securityRevision,
1048
- credentialRevision: snapshot.credentialRevision,
1049
- verifierVersion: snapshot.verifierVersion,
1050
- identifierBindingRevision: snapshot.identifierBindingRevision,
1051
- }),
1052
- admittedAtMillis: now,
1053
- deadlineMillis: now + input.attemptLifetimeMillis,
1054
- retentionUntilMillis: now + input.attemptLifetimeMillis,
1055
- };
1056
-
1057
- const receipt = prepare(
1058
- {
1059
- _tag: "Admitted",
1060
- attemptId,
1061
- ...(snapshot === undefined ? {} : { credential: snapshot }),
1062
- },
1063
- journal,
1064
- );
1065
-
1066
- const admitted = and(
1067
- current,
1068
- sql`exists(select 1 from ${mapping.attempt.table} where ${key} and ${a.state} = ${"pending"} and ${a.deadline} > ${mapping.d1.engineNow})`,
1069
- )!;
1070
-
1071
- const statements: Statement<any>[] = [
1072
- yield* assertion(
1073
- and(
1074
- current,
1075
- sql`not exists(select 1 from ${mapping.attempt.table} where ${key})`,
1076
- )!,
1077
- marker,
1078
- ),
1079
- yield* statement(
1080
- database.insert(mapping.attempt.table).values(
1081
- driverValues(
1082
- mapping.attempt.encodeInsert(record, {
1083
- ...(resolved?.nativeSubjectId === undefined
1084
- ? {}
1085
- : { nativeSubjectId: resolved.nativeSubjectId }),
1086
- state: "pending",
1087
- }),
1088
- [
1089
- [mapping.attempt.admittedAt, mapping.d1.engineNow],
1090
- [
1091
- mapping.attempt.deadline,
1092
- mapping.d1.engineInstantPlus(input.attemptLifetimeMillis),
1093
- ],
1094
- [
1095
- mapping.attempt.retentionUntil,
1096
- mapping.d1.engineInstantPlus(input.attemptLifetimeMillis),
1097
- ],
1098
- ],
1099
- ),
1100
- ),
1101
- ),
1102
- yield* assertion(and(sql`changes() = 1`, admitted)!, marker),
1103
- ];
1104
-
1105
- // A failed captured-snapshot guard cannot authorize refreshing the
1106
- // candidate after the strategy has consumed that subject's limit.
1107
- return {
1108
- receipt,
1109
- statements,
1110
- postconditions: [yield* assertion(admitted, marker)],
1111
- };
1112
- }),
946
+ if (identifierRow === undefined || !mapping.identifier.isCurrent(identifierRow))
947
+ return Option.none();
948
+ const nativeId = identifierRow[mapping.identifier.subjectId];
949
+
950
+ const sc = subjectColumns(mapping),
951
+ ic = identifierColumns(mapping),
952
+ cc = credentialColumns(mapping);
953
+
954
+ // Bind the same native ID through each column's codec, then inspect one coherent snapshot.
955
+ const rows = yield* database
956
+ .select({
957
+ subject: getTableColumns(mapping.subject.table),
958
+ identifier: getTableColumns(mapping.identifier.table),
959
+ credential: getTableColumns(mapping.credential.table),
960
+ })
961
+ .from(mapping.subject.table)
962
+ .innerJoin(
963
+ mapping.identifier.table,
964
+ and(
965
+ eq(ic.namespace, input.identifier.namespace),
966
+ eq(ic.value, input.identifier.value),
967
+ eq(ic.subjectId, nativeId),
1113
968
  ),
1114
- });
1115
- }),
1116
- settleAttempt: <A>(
1117
- uncaptured: Parameters<PasswordPersistence["Service"]["settleAttempt"]>[0],
1118
- prepare: PlanPrepare<PasswordPersistence["Service"]["settleAttempt"], A>,
1119
- ) =>
1120
- Effect.gen(function* () {
1121
- const database = yield* CurrentD1PlanningDatabase;
969
+ )
970
+ .innerJoin(
971
+ mapping.credential.table,
972
+ and(eq(cc.moduleId, input.moduleId), eq(cc.subjectId, nativeId)),
973
+ )
974
+ .where(eq(sc.id, nativeId));
1122
975
 
1123
- const captured =
1124
- uncaptured.captured === undefined
1125
- ? undefined
1126
- : yield* snapshotPasswordCredential(uncaptured.captured);
976
+ if (rows.length !== 1) return Option.none();
977
+ const row = rows[0]!;
1127
978
 
1128
- const input = { ...uncaptured, ...(captured === undefined ? {} : { captured }) };
979
+ if (
980
+ !mapping.subject.isActiveStatus(row.subject[mapping.subject.status]) ||
981
+ !mapping.identifier.isCurrent(row.identifier) ||
982
+ !mapping.subjectId.equals(nativeId, row.subject[mapping.subject.id]) ||
983
+ !mapping.subjectId.equals(nativeId, row.identifier[mapping.identifier.subjectId]) ||
984
+ !mapping.subjectId.equals(nativeId, row.credential[mapping.credential.subjectId])
985
+ )
986
+ return Option.none();
1129
987
 
1130
- return yield* run(
1131
- Effect.gen(function* () {
1132
- const journal = yield* CurrentCommitJournal;
988
+ const credential = yield* mapping.credential
989
+ .decode({ moduleId: input.moduleId, ...row })
990
+ .pipe(Effect.flatMap(snapshotPasswordCredential));
1133
991
 
1134
- const a = attemptColumns(mapping);
992
+ return input.subjectId !== undefined && credential.revision.subjectId !== input.subjectId
993
+ ? Option.none()
994
+ : Option.some(credential);
995
+ }),
996
+ rehashIfCurrent: (input: Parameters<PasswordPersistence["Service"]["rehashIfCurrent"]>[0]) =>
997
+ run(
998
+ Effect.gen(function* () {
999
+ const nextVerifier = Redacted.make(
1000
+ yield* Schema.decodeEffect(EncodedPasswordHash)(Redacted.value(input.nextVerifier)),
1001
+ );
1135
1002
 
1136
- const rows = yield* database
1137
- .select()
1138
- .from(mapping.attempt.table)
1139
- .where(and(eq(a.moduleId, input.moduleId), eq(a.attemptId, input.attemptId)))
1140
- .limit(1);
1003
+ const credential = yield* snapshotPasswordCredential(input.credential);
1004
+ const database = yield* CurrentD1PlanningDatabase;
1005
+ const nativeId = yield* mapping.subjectId.toNative(credential.revision.subjectId);
1006
+ const version = yield* allocate(mapping.allocateRevision, mapping.allocateRevisionSync);
1141
1007
 
1142
- const row = rows[0];
1143
- const engineNow = yield* readEngineNowMillis(mapping);
1008
+ if (version === credential.verifierVersion) return yield* unavailable();
1009
+ const encoded = mapping.credential.encodeVerifier(nextVerifier, version);
1144
1010
 
1145
- const deadline =
1146
- row === undefined
1147
- ? undefined
1148
- : yield* mapping.decodeInstant(row[mapping.attempt.deadline]);
1011
+ const values = {
1012
+ [mapping.credential.verifier]: encoded[mapping.credential.verifier],
1013
+ [mapping.credential.verifierVersion]: encoded[mapping.credential.verifierVersion],
1014
+ };
1149
1015
 
1150
- let resolved: Resolved | undefined;
1151
- let verified = false;
1016
+ if (Object.values(values).some((value) => value === undefined))
1017
+ return yield* unavailable();
1018
+ const c = credentialColumns(mapping);
1152
1019
 
1153
- if (
1154
- row !== undefined &&
1155
- row[mapping.attempt.state] === "pending" &&
1156
- input.outcome === "verified" &&
1157
- input.captured !== undefined &&
1158
- deadline !== undefined &&
1159
- deadline > engineNow
1160
- ) {
1161
- resolved = yield* resolveCredential(
1162
- mapping,
1163
- input.moduleId,
1164
- input.captured.identifier,
1165
- input.captured.revision.subjectId,
1166
- );
1167
- verified =
1168
- input.captured.moduleId === input.moduleId &&
1169
- resolved?.snapshot !== undefined &&
1170
- sameSnapshot(resolved.snapshot, input.captured) &&
1171
- mapping.subjectId.equals(
1172
- row[mapping.attempt.subjectId],
1173
- resolved.nativeSubjectId,
1174
- ) &&
1175
- row[mapping.attempt.identifierNamespace] === input.captured.identifier.namespace &&
1176
- row[mapping.attempt.identifierValue] === input.captured.identifier.value &&
1177
- row[mapping.attempt.credentialId] === input.captured.credentialId &&
1178
- row[mapping.attempt.securityRevision] ===
1179
- input.captured.revision.securityRevision &&
1180
- row[mapping.attempt.credentialRevision] === input.captured.credentialRevision &&
1181
- row[mapping.attempt.verifierVersion] === input.captured.verifierVersion &&
1182
- row[mapping.attempt.identifierBindingRevision] ===
1183
- input.captured.identifierBindingRevision;
1184
- }
1185
- const decision: PasswordAttemptDecision = verified ? "verified" : "rejected";
1186
- const receipt = prepare(decision, journal);
1187
-
1188
- if (row === undefined || row[mapping.attempt.state] !== "pending")
1189
- return { receipt, statements: [] };
1190
- const marker = `effect-auth-password-guard:settle:${input.attemptId}`;
1191
-
1192
- const base = and(
1193
- eq(a.moduleId, input.moduleId),
1194
- eq(a.attemptId, input.attemptId),
1195
- eq(a.state, "pending"),
1196
- )!;
1197
-
1198
- const attemptSnapshot =
1199
- verified && resolved?.snapshot !== undefined
1200
- ? and(
1201
- eq(a.action, row[mapping.attempt.action]),
1202
- eq(a.subjectId, resolved.nativeSubjectId),
1203
- eq(a.identifierNamespace, input.captured!.identifier.namespace),
1204
- eq(a.identifierValue, input.captured!.identifier.value),
1205
- eq(a.credentialId, input.captured!.credentialId),
1206
- eq(a.securityRevision, input.captured!.revision.securityRevision),
1207
- eq(a.credentialRevision, input.captured!.credentialRevision),
1208
- eq(a.verifierVersion, input.captured!.verifierVersion),
1209
- eq(a.identifierBindingRevision, input.captured!.identifierBindingRevision),
1210
- eq(a.deadline, row[mapping.attempt.deadline]),
1211
- )!
1212
- : sql`true`;
1213
-
1214
- const condition = verified
1215
- ? and(base, attemptSnapshot, sql`${a.deadline} > ${mapping.d1.engineNow}`)!
1216
- : base;
1217
-
1218
- const statements: Statement<any>[] = [
1219
- yield* assertion(
1220
- sql`exists(select 1 from ${mapping.attempt.table} where ${condition})`,
1221
- marker,
1222
- ),
1020
+ return {
1021
+ receipt: undefined,
1022
+ statements: [
1223
1023
  yield* statement(
1224
1024
  database
1225
- .update(mapping.attempt.table)
1226
- .set(updateValues([[mapping.attempt.state, decision]]))
1227
- .where(condition),
1228
- ),
1229
- ];
1230
-
1231
- let finalAuthority = sql`true`;
1232
-
1233
- if (verified && resolved?.snapshot !== undefined) {
1234
- const captured = input.captured!;
1235
- const c = credentialColumns(mapping);
1236
-
1237
- finalAuthority = admittedSnapshotCondition(mapping, resolved, input.moduleId);
1238
-
1239
- statements.splice(1, 0, yield* assertion(finalAuthority, marker));
1240
- if (
1241
- input.rehash !== undefined &&
1242
- input.rehash.expectedVersion === captured.verifierVersion &&
1243
- Redacted.value(input.rehash.expectedVerifier) === Redacted.value(captured.verifier)
1244
- ) {
1245
- const nextVersion = yield* allocate(
1246
- mapping.allocateRevision,
1247
- mapping.allocateRevisionSync,
1248
- );
1249
-
1250
- statements.push(
1251
- yield* assertion(sql`changes() = 1`, marker),
1252
- yield* statement(
1253
- database
1254
- .update(mapping.credential.table)
1255
- .set(
1256
- mapping.credential.encodeVerifier(input.rehash.nextVerifier, nextVersion),
1257
- )
1258
- .where(
1259
- and(
1260
- eq(c.moduleId, input.moduleId),
1261
- eq(c.subjectId, resolved.nativeSubjectId),
1262
- eq(c.credentialId, captured.credentialId),
1263
- eq(c.credentialRevision, captured.credentialRevision),
1264
- eq(c.verifierVersion, input.rehash.expectedVersion),
1265
- eq(c.verifier, Redacted.value(input.rehash.expectedVerifier)),
1266
- ),
1025
+ .update(mapping.credential.table)
1026
+ .set(values)
1027
+ .where(
1028
+ and(
1029
+ eq(c.moduleId, credential.moduleId),
1030
+ eq(c.subjectId, nativeId),
1031
+ eq(c.credentialId, credential.credentialId),
1032
+ ...(
1033
+ [
1034
+ [c.moduleId, credential.moduleId],
1035
+ [c.subjectId, nativeId],
1036
+ [c.credentialId, credential.credentialId],
1037
+ [c.credentialRevision, credential.credentialRevision],
1038
+ [c.verifierVersion, credential.verifierVersion],
1039
+ [c.verifier, Redacted.value(credential.verifier)],
1040
+ [c.normalization, credential.normalization],
1041
+ ] as const
1042
+ ).map(([column, value]) =>
1043
+ typeof value === "string"
1044
+ ? sql`cast(${column} as blob) = cast(${sql.param(value, column)} as blob)`
1045
+ : eq(column, value),
1267
1046
  ),
1047
+ ),
1268
1048
  ),
1269
- );
1270
- finalAuthority = admittedSnapshotCondition(
1271
- mapping,
1272
- {
1273
- ...resolved,
1274
- snapshot: {
1275
- ...resolved.snapshot,
1276
- verifierVersion: nextVersion,
1277
- verifier: input.rehash.nextVerifier,
1278
- },
1279
- },
1280
- input.moduleId,
1281
- );
1282
- }
1283
- }
1284
-
1285
- const settled = and(
1286
- finalAuthority,
1287
- sql`exists(select 1 from ${mapping.attempt.table} where ${a.moduleId} = ${sql.param(input.moduleId, a.moduleId)} and ${a.attemptId} = ${sql.param(input.attemptId, a.attemptId)} and ${a.state} = ${sql.param(decision, a.state)} and ${attemptSnapshot}${verified ? sql` and ${a.deadline} > ${mapping.d1.engineNow}` : sql``})`,
1288
- )!;
1289
-
1290
- // Run after attempt/rehash triggers, before the batch can publish its journal.
1291
- statements.push(yield* assertion(and(sql`changes() = 1`, settled)!, marker));
1292
-
1293
- return {
1294
- receipt,
1295
- statements,
1296
- postconditions: [yield* assertion(settled, marker)],
1297
- retryable: (cause: unknown) => isGuardFailure(cause, marker),
1298
- };
1299
- }),
1300
- );
1301
- }),
1049
+ ),
1050
+ ],
1051
+ };
1052
+ }),
1053
+ ),
1302
1054
  readForSubject: (input: Parameters<PasswordPersistence["Service"]["readForSubject"]>[0]) =>
1303
1055
  Effect.gen(function* () {
1304
1056
  const database = yield* CurrentD1PlanningDatabase;
@@ -1456,15 +1208,6 @@ const makePasswordPlans = (
1456
1208
  "reset-password",
1457
1209
  ),
1458
1210
  ),
1459
- cleanupAttempts: <A>(
1460
- input: Parameters<PasswordPersistence["Service"]["cleanupAttempts"]>[0],
1461
- prepare: PlanPrepare<PasswordPersistence["Service"]["cleanupAttempts"], A>,
1462
- ) =>
1463
- run(
1464
- Effect.gen(function* () {
1465
- return yield* cleanupPlan(mapping, input, prepare);
1466
- }),
1467
- ),
1468
1211
  };
1469
1212
  };
1470
1213
 
@@ -1623,68 +1366,15 @@ const replacementPlan = Effect.fn("Drizzle.replacementPlan")(function* <A>(
1623
1366
  };
1624
1367
  });
1625
1368
 
1626
- const cleanupPlan = Effect.fn("Drizzle.cleanupPlan")(function* <A>(
1627
- mapping: Mapping,
1628
- input: { readonly moduleId: string; readonly limit: number },
1629
- prepare: PreparePasswordCommit<{ readonly removed: number; readonly hasMore: boolean }, A>,
1630
- ) {
1631
- const database = yield* CurrentD1PlanningDatabase;
1632
- const journal = yield* CurrentCommitJournal;
1633
-
1634
- const now = yield* readEngineNowMillis(mapping);
1635
- const nativeNow = mapping.encodeInstant(now);
1636
- const a = attemptColumns(mapping);
1637
-
1638
- const attempts = yield* database
1639
- .select({ attemptId: a.attemptId })
1640
- .from(mapping.attempt.table)
1641
- .where(and(eq(a.moduleId, input.moduleId), lte(a.retentionUntil, nativeNow)))
1642
- .limit(input.limit + 1);
1643
-
1644
- const selectedAttempts = attempts.slice(0, input.limit);
1645
- const hasMore = attempts.length > selectedAttempts.length;
1646
- const marker = `effect-auth-password-guard:cleanup:${input.moduleId}:${now}`;
1647
- const statements: Statement<any>[] = [];
1648
-
1649
- for (const row of selectedAttempts) {
1650
- const condition = and(
1651
- eq(a.moduleId, input.moduleId),
1652
- eq(a.attemptId, row.attemptId),
1653
- lte(a.retentionUntil, mapping.d1.engineNow),
1654
- )!;
1655
-
1656
- statements.push(
1657
- yield* assertion(
1658
- sql`exists(select 1 from ${mapping.attempt.table} where ${condition})`,
1659
- marker,
1660
- ),
1661
- yield* statement(database.delete(mapping.attempt.table).where(condition)),
1662
- );
1663
- }
1664
-
1665
- return {
1666
- receipt: prepare(
1667
- {
1668
- removed: selectedAttempts.length,
1669
- hasMore,
1670
- },
1671
- journal,
1672
- ),
1673
- statements,
1674
- retryable: (cause: unknown) => isGuardFailure(cause, marker),
1675
- };
1676
- });
1677
-
1678
1369
  export const makeD1PasswordPersistenceServices = Effect.fnUntraced(function* <
1679
1370
  S extends AnySQLiteTable,
1680
1371
  I extends AnySQLiteTable,
1681
1372
  C extends AnySQLiteTable,
1682
1373
  AC extends AnySQLiteTable,
1683
- A extends AnySQLiteTable,
1684
1374
  M extends AnySQLiteTable,
1685
1375
  NativeId,
1686
1376
  >(
1687
- mapping: D1PasswordPersistenceMapping<S, I, C, AC, A, M, NativeId>,
1377
+ mapping: D1PasswordPersistenceMapping<S, I, C, AC, M, NativeId>,
1688
1378
  proofMapping?: D1ProofPersistenceMapping<
1689
1379
  any,
1690
1380
  any,
@@ -1726,21 +1416,13 @@ export const makeD1PasswordPersistenceServices = Effect.fnUntraced(function* <
1726
1416
  );
1727
1417
 
1728
1418
  const service: PasswordPersistence["Service"] = {
1729
- prepareAttempt: (input) =>
1419
+ findCredential: (input) =>
1730
1420
  standalone.pipe(
1731
- Effect.andThen(plans.prepareAttempt(input)),
1732
- Effect.map((planned): PasswordAttemptPreparation =>
1733
- Object.freeze({
1734
- ...(planned.credential === undefined ? {} : { credential: planned.credential }),
1735
- admit: <A>(prepare: PreparePasswordCommit<PasswordAttemptAdmission, A>) =>
1736
- planned.claim.pipe(Effect.andThen(run(planned.admit(prepare))), translateFailure),
1737
- }),
1738
- ),
1421
+ Effect.andThen(plans.findCredential(input)),
1739
1422
  Effect.provideService(CurrentD1PlanningDatabase, database),
1740
1423
  translateFailure,
1741
1424
  ),
1742
- settleAttempt: (input, prepare) =>
1743
- run(plans.settleAttempt(input, prepare)).pipe(translateFailure),
1425
+ rehashIfCurrent: (input) => run(plans.rehashIfCurrent(input)).pipe(translateFailure),
1744
1426
  readForSubject: (input) =>
1745
1427
  plans
1746
1428
  .readForSubject(input)
@@ -1770,8 +1452,6 @@ export const makeD1PasswordPersistenceServices = Effect.fnUntraced(function* <
1770
1452
  ),
1771
1453
  resetWithProof: (input, prepare) =>
1772
1454
  run(plans.resetWithProof(input, prepare)).pipe(translateFailure),
1773
- cleanupAttempts: (input, prepare) =>
1774
- run(plans.cleanupAttempts(input, prepare)).pipe(translateFailure),
1775
1455
  };
1776
1456
 
1777
1457
  return { passwordPersistence: service };
@@ -1789,7 +1469,6 @@ export function coordinateD1PasswordPersistence<
1789
1469
  I extends AnySQLiteTable,
1790
1470
  C extends AnySQLiteTable,
1791
1471
  AC extends AnySQLiteTable,
1792
- At extends AnySQLiteTable,
1793
1472
  M extends AnySQLiteTable,
1794
1473
  NativeId,
1795
1474
  A,
@@ -1800,7 +1479,7 @@ export function coordinateD1PasswordPersistence<
1800
1479
  >(
1801
1480
  acquire: Effect.Effect<Database, DatabaseError, DatabaseRequirements>,
1802
1481
  options: {
1803
- readonly mapping: D1PasswordPersistenceMapping<S, I, C, AC, At, M, NativeId>;
1482
+ readonly mapping: D1PasswordPersistenceMapping<S, I, C, AC, M, NativeId>;
1804
1483
  readonly proofMapping?:
1805
1484
  | D1ProofPersistenceMapping<any, any, any, any, any, any, any, any, any, any, any, any>
1806
1485
  | undefined;
@@ -1864,29 +1543,17 @@ export function coordinateD1PasswordPersistence<
1864
1543
  );
1865
1544
 
1866
1545
  const service: PasswordPersistence["Service"] = {
1867
- prepareAttempt: (input) =>
1546
+ findCredential: (input) =>
1868
1547
  owner.run(
1869
- plans.prepareAttempt(input).pipe(
1870
- Effect.map((planned): PasswordAttemptPreparation =>
1871
- Object.freeze({
1872
- ...(planned.credential === undefined
1873
- ? {}
1874
- : { credential: planned.credential }),
1875
- admit: <A>(prepare: PreparePasswordCommit<PasswordAttemptAdmission, A>) =>
1876
- owner.run(
1877
- planned.claim.pipe(
1878
- Effect.andThen(run(planned.admit(prepare))),
1879
- translateFailure,
1880
- ),
1881
- ),
1882
- }),
1548
+ plans
1549
+ .findCredential(input)
1550
+ .pipe(
1551
+ Effect.provideService(CurrentD1PlanningDatabase, database),
1552
+ translateFailure,
1883
1553
  ),
1884
- Effect.provideService(CurrentD1PlanningDatabase, database),
1885
- translateFailure,
1886
- ),
1887
1554
  ),
1888
- settleAttempt: (input, prepare) =>
1889
- owner.run(run(plans.settleAttempt(input, prepare)).pipe(translateFailure)),
1555
+ rehashIfCurrent: (input) =>
1556
+ owner.run(run(plans.rehashIfCurrent(input)).pipe(translateFailure)),
1890
1557
  readForSubject: (input) =>
1891
1558
  owner.run(
1892
1559
  plans
@@ -1923,8 +1590,6 @@ export function coordinateD1PasswordPersistence<
1923
1590
  ),
1924
1591
  resetWithProof: (input, prepare) =>
1925
1592
  owner.run(run(plans.resetWithProof(input, prepare)).pipe(translateFailure)),
1926
- cleanupAttempts: (input, prepare) =>
1927
- owner.run(run(plans.cleanupAttempts(input, prepare)).pipe(translateFailure)),
1928
1593
  };
1929
1594
 
1930
1595
  const provided = Context.make(PasswordPersistence, service).pipe(