@yielded/auth-persistence-drizzle 0.1.0-beta.18 → 0.1.0-beta.19

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 (130) hide show
  1. package/README.md +8 -1
  2. package/dist/D1.d.mts +8 -8
  3. package/dist/Libsql.d.mts +44 -44
  4. package/dist/Mysql2.d.mts +44 -44
  5. package/dist/Pglite.d.mts +44 -44
  6. package/dist/Postgres.d.mts +44 -44
  7. package/dist/SqliteBun.d.mts +44 -44
  8. package/dist/SqliteDo.d.mts +112 -134
  9. package/dist/SqliteDo.mjs +30 -45
  10. package/dist/SqliteDo.mjs.map +1 -1
  11. package/dist/SqliteNode.d.mts +38 -38
  12. package/dist/SqliteWasm.d.mts +38 -38
  13. package/dist/drizzle/crypto.mjs +11 -0
  14. package/dist/drizzle/crypto.mjs.map +1 -0
  15. package/dist/drizzle/d1-identity.mjs +2 -2
  16. package/dist/drizzle/d1-identity.mjs.map +1 -1
  17. package/dist/drizzle/d1-oauth.d.mts +11 -11
  18. package/dist/drizzle/d1-oauth.mjs.map +1 -1
  19. package/dist/drizzle/d1-passkey.d.mts +6 -6
  20. package/dist/drizzle/d1-passkey.mjs.map +1 -1
  21. package/dist/drizzle/d1-passwords.d.mts +1 -1
  22. package/dist/drizzle/d1-phone.d.mts +3 -3
  23. package/dist/drizzle/d1-phone.mjs.map +1 -1
  24. package/dist/drizzle/d1-proofs.d.mts +1 -1
  25. package/dist/drizzle/d1-sessions.d.mts +1 -1
  26. package/dist/drizzle/d1-totp.d.mts +3 -3
  27. package/dist/drizzle/d1-totp.mjs.map +1 -1
  28. package/dist/drizzle/mysql-identity.mjs +2 -2
  29. package/dist/drizzle/mysql-identity.mjs.map +1 -1
  30. package/dist/drizzle/oauth-accounts.mjs +9 -8
  31. package/dist/drizzle/oauth-accounts.mjs.map +1 -1
  32. package/dist/drizzle/oauth-connected-collection.mjs +1 -1
  33. package/dist/drizzle/oauth-connected-collection.mjs.map +1 -1
  34. package/dist/drizzle/oauth-connected-custody.mjs +5 -4
  35. package/dist/drizzle/oauth-connected-custody.mjs.map +1 -1
  36. package/dist/drizzle/oauth-connected-drivers.mjs.map +1 -1
  37. package/dist/drizzle/oauth-connected-flow.mjs +8 -7
  38. package/dist/drizzle/oauth-connected-flow.mjs.map +1 -1
  39. package/dist/drizzle/oauth-connected-management.mjs +3 -2
  40. package/dist/drizzle/oauth-connected-management.mjs.map +1 -1
  41. package/dist/drizzle/oauth-connected-reference.mjs +8 -4
  42. package/dist/drizzle/oauth-connected-reference.mjs.map +1 -1
  43. package/dist/drizzle/oauth-connected-settlement.mjs +4 -4
  44. package/dist/drizzle/oauth-connected-settlement.mjs.map +1 -1
  45. package/dist/drizzle/oauth-connected-sign-in.mjs +11 -10
  46. package/dist/drizzle/oauth-connected-sign-in.mjs.map +1 -1
  47. package/dist/drizzle/oauth-connected-state.d.mts +13 -13
  48. package/dist/drizzle/oauth-connected-state.mjs +23 -20
  49. package/dist/drizzle/oauth-connected-state.mjs.map +1 -1
  50. package/dist/drizzle/oauth-connected-target.d.mts +7 -7
  51. package/dist/drizzle/oauth-connected-target.mjs.map +1 -1
  52. package/dist/drizzle/oauth-drivers.mjs.map +1 -1
  53. package/dist/drizzle/oauth-flow.mjs +1 -1
  54. package/dist/drizzle/oauth-flow.mjs.map +1 -1
  55. package/dist/drizzle/oauth-model.d.mts +4 -4
  56. package/dist/drizzle/oauth-model.mjs.map +1 -1
  57. package/dist/drizzle/oauth-registration.mjs +2 -2
  58. package/dist/drizzle/oauth-registration.mjs.map +1 -1
  59. package/dist/drizzle/oauth-state.mjs +11 -12
  60. package/dist/drizzle/oauth-state.mjs.map +1 -1
  61. package/dist/drizzle/oauth-target.d.mts +4 -4
  62. package/dist/drizzle/oauth-target.mjs.map +1 -1
  63. package/dist/drizzle/passkey-drivers.mjs.map +1 -1
  64. package/dist/drizzle/passkey-write-drivers.mjs.map +1 -1
  65. package/dist/drizzle/passkey-write-target.d.mts +1 -1
  66. package/dist/drizzle/password-prepared-state.mjs +1 -1
  67. package/dist/drizzle/pg-identity.mjs +2 -2
  68. package/dist/drizzle/pg-identity.mjs.map +1 -1
  69. package/dist/drizzle/phone-model.d.mts +2 -2
  70. package/dist/drizzle/phone-model.mjs +3 -3
  71. package/dist/drizzle/phone-model.mjs.map +1 -1
  72. package/dist/drizzle/phone-target.d.mts +1 -1
  73. package/dist/drizzle/phone-target.mjs +2 -2
  74. package/dist/drizzle/phone-target.mjs.map +1 -1
  75. package/dist/drizzle/sqlite-do-database.d.mts +29 -0
  76. package/dist/drizzle/sqlite-do-database.mjs +45 -0
  77. package/dist/drizzle/sqlite-do-database.mjs.map +1 -0
  78. package/dist/drizzle/sqlite-emails.mjs.map +1 -1
  79. package/dist/drizzle/sqlite-identity.mjs +2 -2
  80. package/dist/drizzle/sqlite-identity.mjs.map +1 -1
  81. package/dist/drizzle/sqlite-passwords.mjs.map +1 -1
  82. package/dist/drizzle/sqlite-proofs.mjs.map +1 -1
  83. package/dist/drizzle/sqlite-sessions.mjs.map +1 -1
  84. package/dist/drizzle/storage-validation.mjs +17 -9
  85. package/dist/drizzle/storage-validation.mjs.map +1 -1
  86. package/dist/drizzle/totp-state.mjs +8 -8
  87. package/dist/drizzle/totp-state.mjs.map +1 -1
  88. package/dist/drizzle/totp-target.d.mts +1 -1
  89. package/dist/drizzle/totp-target.mjs +2 -2
  90. package/dist/drizzle/totp-target.mjs.map +1 -1
  91. package/package.json +1 -1
  92. package/src/SqliteDo.ts +63 -70
  93. package/src/drizzle/crypto.ts +14 -0
  94. package/src/drizzle/d1-identity.ts +2 -2
  95. package/src/drizzle/d1-oauth.ts +14 -5
  96. package/src/drizzle/d1-passkey.ts +5 -1
  97. package/src/drizzle/d1-phone.ts +2 -1
  98. package/src/drizzle/d1-totp.ts +6 -2
  99. package/src/drizzle/mysql-identity.ts +2 -2
  100. package/src/drizzle/oauth-accounts.ts +14 -13
  101. package/src/drizzle/oauth-connected-collection.ts +1 -1
  102. package/src/drizzle/oauth-connected-custody.ts +8 -6
  103. package/src/drizzle/oauth-connected-drivers.ts +9 -3
  104. package/src/drizzle/oauth-connected-flow.ts +6 -6
  105. package/src/drizzle/oauth-connected-management.ts +1 -1
  106. package/src/drizzle/oauth-connected-reference.ts +14 -9
  107. package/src/drizzle/oauth-connected-settlement.ts +4 -4
  108. package/src/drizzle/oauth-connected-sign-in.ts +11 -10
  109. package/src/drizzle/oauth-connected-state.ts +40 -25
  110. package/src/drizzle/oauth-connected-target.ts +9 -5
  111. package/src/drizzle/oauth-drivers.ts +13 -5
  112. package/src/drizzle/oauth-flow.ts +1 -1
  113. package/src/drizzle/oauth-model.ts +4 -4
  114. package/src/drizzle/oauth-registration.ts +2 -2
  115. package/src/drizzle/oauth-state.ts +17 -15
  116. package/src/drizzle/oauth-target.ts +11 -3
  117. package/src/drizzle/passkey-drivers.ts +7 -3
  118. package/src/drizzle/passkey-write-drivers.ts +11 -2
  119. package/src/drizzle/pg-identity.ts +2 -2
  120. package/src/drizzle/phone-model.ts +3 -3
  121. package/src/drizzle/phone-target.ts +11 -4
  122. package/src/drizzle/sqlite-do-database.ts +87 -0
  123. package/src/drizzle/sqlite-emails.ts +1 -1
  124. package/src/drizzle/sqlite-identity.ts +2 -2
  125. package/src/drizzle/sqlite-passwords.ts +1 -1
  126. package/src/drizzle/sqlite-proofs.ts +2 -2
  127. package/src/drizzle/sqlite-sessions.ts +2 -2
  128. package/src/drizzle/storage-validation.ts +15 -6
  129. package/src/drizzle/totp-state.ts +8 -8
  130. package/src/drizzle/totp-target.ts +11 -5
package/src/SqliteDo.ts CHANGED
@@ -1,18 +1,21 @@
1
1
  import { SqliteClient } from "@effect/sql-sqlite-do/SqliteClient";
2
2
  import { NativeDatabase, PersistenceConfigurationError } from "@yielded/auth-persistence/Adapter";
3
3
  import type { AnyRelations } from "drizzle-orm";
4
- import { type EffectSQLiteDoDatabase, makeWithDefaults } from "drizzle-orm/effect-sqlite-do";
4
+ import { makeWithDefaults } from "drizzle-orm/effect-sqlite-do";
5
+
6
+ import { type DatabaseValue, makeDatabase } from "./drizzle/sqlite-do-database";
7
+ export { type DatabaseValue, type Transaction, makeDatabase } from "./drizzle/sqlite-do-database";
5
8
  import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
6
9
  import { Context, Effect, Layer } from "effect";
7
10
 
8
11
  import { nativeDatabase } from "./drizzle/native-database";
9
12
 
10
- /** The application-owned Drizzle database used to construct persistence services. */
11
- export class Database extends Context.Service<Database, EffectSQLiteDoDatabase<AnyRelations>>()(
13
+ /** The application-owned Drizzle queries with Effect SQL transaction ownership. */
14
+ export class Database extends Context.Service<Database, DatabaseValue<AnyRelations>>()(
12
15
  "effect-auth/persistence-drizzle/SqliteDo/Database",
13
16
  ) {}
14
17
 
15
- /** The SQL-client Layer must configure storage so Drizzle owns transactionSync. */
18
+ /** Construct Drizzle with asynchronous transactions owned by Effect SQL. */
16
19
  export const databaseLayer = Layer.effect(
17
20
  Database,
18
21
  Effect.gen(function* () {
@@ -21,19 +24,22 @@ export const databaseLayer = Layer.effect(
21
24
 
22
25
  if (storage === undefined)
23
26
  return yield* PersistenceConfigurationError.make({
24
- reason:
25
- "SqliteDo requires a SQL client configured with storage for synchronous transactions",
27
+ reason: "SqliteDo requires a SQL client configured with Durable Object storage",
26
28
  });
27
29
 
28
- return yield* makeWithDefaults({ storage });
30
+ return yield* makeDatabase(yield* makeWithDefaults({ storage }));
29
31
  }),
30
32
  );
31
33
 
34
+ import { sqlClientEmailStandaloneGuard } from "./drizzle/email-target";
32
35
  import type {
33
36
  ExternalIdentityTables,
34
37
  IdentityTables,
35
38
  SubjectProvisioningTables,
36
39
  } from "./drizzle/model";
40
+ import { sqlClientPasswordStandaloneGuard } from "./drizzle/password-target";
41
+ import { sqlClientProofStandaloneGuard } from "./drizzle/proof-target";
42
+ import { sqlClientSessionStandaloneGuard } from "./drizzle/session-target";
37
43
  import { makeSqliteEmailTarget, sqliteEmailConfiguration } from "./drizzle/sqlite-emails";
38
44
  import {
39
45
  makeSqliteExternalIdentityServices,
@@ -44,31 +50,36 @@ import { makeSqlitePasswordTarget, sqlitePasswordConfiguration } from "./drizzle
44
50
  import { makeSqliteProofTarget, sqliteProofConfiguration } from "./drizzle/sqlite-proofs";
45
51
  import { makeSqliteSessionTarget, sqliteSessionConfiguration } from "./drizzle/sqlite-sessions";
46
52
 
47
- const sessionTarget = makeSqliteSessionTarget<Database, EffectSQLiteDoDatabase<AnyRelations>, true>(
53
+ const sessionTarget = makeSqliteSessionTarget<Database, DatabaseValue<AnyRelations>>(
48
54
  Database,
49
- sqliteSessionConfiguration("synchronous", Effect.void),
55
+ (service) => sqliteSessionConfiguration("interactive", sqlClientSessionStandaloneGuard(service)),
50
56
  );
51
57
 
52
- const proofTarget = makeSqliteProofTarget<Database, EffectSQLiteDoDatabase<AnyRelations>, true>(
58
+ const proofTarget = makeSqliteProofTarget<Database, DatabaseValue<AnyRelations>>(
53
59
  Database,
54
- sqliteProofConfiguration("synchronous", Effect.void),
60
+ (service) => sqliteProofConfiguration("interactive", sqlClientProofStandaloneGuard(service)),
55
61
  );
56
62
 
57
- const passwordTarget = makeSqlitePasswordTarget<
63
+ const passwordTarget = makeSqlitePasswordTarget<Database, DatabaseValue<AnyRelations>>(
58
64
  Database,
59
- EffectSQLiteDoDatabase<AnyRelations>,
60
- true
61
- >(Database, sqlitePasswordConfiguration("synchronous", Effect.void, Effect.void));
65
+ (service) =>
66
+ sqlitePasswordConfiguration(
67
+ "interactive",
68
+ sqlClientPasswordStandaloneGuard(service),
69
+ sqlClientProofStandaloneGuard(service),
70
+ ),
71
+ );
62
72
 
63
- const emailTarget = makeSqliteEmailTarget<Database, EffectSQLiteDoDatabase<AnyRelations>, true>(
73
+ const emailTarget = makeSqliteEmailTarget<Database, DatabaseValue<AnyRelations>>(
64
74
  Database,
65
- sqliteEmailConfiguration("synchronous", Effect.void, Effect.void),
75
+ (service) =>
76
+ sqliteEmailConfiguration(
77
+ "interactive",
78
+ sqlClientEmailStandaloneGuard(service),
79
+ sqlClientProofStandaloneGuard(service),
80
+ ),
66
81
  );
67
82
 
68
- /**
69
- * Email mutations must own their outer transactionSync. Arbitrary raw Drizzle
70
- * nesting is not detectable; owner bodies and mapping allocators stay runSync-compatible.
71
- */
72
83
  export const {
73
84
  coordinateEmailAddress,
74
85
  coordinateEmailRegistration,
@@ -77,11 +88,6 @@ export const {
77
88
  makeEmailSignInServices,
78
89
  } = emailTarget;
79
90
 
80
- /**
81
- * Password mutations and registration must own their outer transactionSync.
82
- * Arbitrary raw Drizzle nesting is not detectable; owner bodies must remain
83
- * runSync-compatible. Detectable Effect commit scopes are rejected pre-write.
84
- */
85
91
  export const {
86
92
  coordinatePasswordPersistence,
87
93
  coordinatePasswordRegistration,
@@ -89,21 +95,8 @@ export const {
89
95
  makePasswordRegistrationServices,
90
96
  } = passwordTarget;
91
97
 
92
- /**
93
- * Proof mutations and coordinateProofPersistence must own their outermost
94
- * transactionSync call. The installed driver cannot detect an arbitrary raw
95
- * Drizzle outer transaction; calling either boundary from one is unsupported.
96
- * The owner body must remain runSync-compatible.
97
- */
98
98
  export const { coordinateProofPersistence, makeProofPersistenceServices } = proofTarget;
99
99
 
100
- /**
101
- * Session mutation methods and coordinate* functions must own their outermost
102
- * transactionSync call. The installed Drizzle driver exposes no context marker
103
- * for an arbitrary raw outer database.transaction call, so invoking either
104
- * boundary from one is unsupported and cannot be detected. Detectable Effect
105
- * commit scopes are rejected before writes.
106
- */
107
100
  export const {
108
101
  coordinateAuthenticationAuthority,
109
102
  coordinatePendingAuthentication,
@@ -117,7 +110,7 @@ export const {
117
110
  makeStatefulSessionServices,
118
111
  } = sessionTarget;
119
112
 
120
- export const commitMode = "synchronous" as const;
113
+ export const commitMode = "interactive" as const;
121
114
 
122
115
  export const makeIdentityServices = <
123
116
  Subject extends AnySQLiteTable,
@@ -128,7 +121,7 @@ export const makeIdentityServices = <
128
121
  >(
129
122
  mapping: IdentityTables<Subject, Identifier, External, Request, NativeId>,
130
123
  ) =>
131
- makeSqliteIdentityServices(mapping, "synchronous").pipe(
124
+ makeSqliteIdentityServices(mapping, "interactive").pipe(
132
125
  Effect.provideServiceEffect(NativeDatabase, nativeDatabase(Database)),
133
126
  );
134
127
 
@@ -140,7 +133,7 @@ export const makeSubjectProvisioningServices = <
140
133
  >(
141
134
  mapping: SubjectProvisioningTables<Subject, Identifier, Request, NativeId>,
142
135
  ) =>
143
- makeSqliteSubjectProvisioningServices(mapping, "synchronous").pipe(
136
+ makeSqliteSubjectProvisioningServices(mapping, "interactive").pipe(
144
137
  Effect.provideServiceEffect(NativeDatabase, nativeDatabase(Database)),
145
138
  );
146
139
 
@@ -159,9 +152,14 @@ import { makeSqlitePasswordPreparedTarget } from "./drizzle/sqlite-password-prep
159
152
 
160
153
  const passwordPreparedTarget = makeSqlitePasswordPreparedTarget<
161
154
  Database,
162
- EffectSQLiteDoDatabase<AnyRelations>,
163
- true
164
- >(Database, sqlitePasswordConfiguration("synchronous", Effect.void, Effect.void));
155
+ DatabaseValue<AnyRelations>
156
+ >(Database, (service) =>
157
+ sqlitePasswordConfiguration(
158
+ "interactive",
159
+ sqlClientPasswordStandaloneGuard(service),
160
+ sqlClientProofStandaloneGuard(service),
161
+ ),
162
+ );
165
163
 
166
164
  export const { makePasswordPreparedPersistenceServices, coordinatePasswordPreparedPersistence } =
167
165
  passwordPreparedTarget;
@@ -169,17 +167,17 @@ export const { makePasswordPreparedPersistenceServices, coordinatePasswordPrepar
169
167
  export { passwordPreparedPersistenceLayer } from "./drizzle/password-prepared-target";
170
168
 
171
169
  import { makeOAuthTarget } from "./drizzle/oauth-drivers";
170
+ import { sqlClientOAuthStandaloneGuard } from "./drizzle/oauth-target";
172
171
 
173
172
  const oauthTarget = makeOAuthTarget<
174
173
  Database,
175
- EffectSQLiteDoDatabase<AnyRelations>,
176
- AnySQLiteTable<{ dialect: "sqlite" }>,
177
- true
174
+ DatabaseValue<AnyRelations>,
175
+ AnySQLiteTable<{ dialect: "sqlite" }>
178
176
  >(Database, {
179
- mode: "synchronous",
177
+ mode: "interactive",
180
178
  dialect: "sqlite",
181
179
  locking: false,
182
- standaloneGuard: () => Effect.void,
180
+ standaloneGuard: sqlClientOAuthStandaloneGuard,
183
181
  });
184
182
 
185
183
  export const {
@@ -198,18 +196,17 @@ export const {
198
196
  } = oauthTarget;
199
197
 
200
198
  import { makePasskeyTarget } from "./drizzle/passkey-drivers";
199
+ import { sqlClientPasskeyStandaloneGuard } from "./drizzle/passkey-target";
201
200
 
202
201
  const passkeyTarget = makePasskeyTarget<
203
202
  Database,
204
- EffectSQLiteDoDatabase<AnyRelations>,
205
- AnySQLiteTable<{ dialect: "sqlite" }>,
206
- unknown,
207
- true
203
+ DatabaseValue<AnyRelations>,
204
+ AnySQLiteTable<{ dialect: "sqlite" }>
208
205
  >(Database, {
209
- mode: "synchronous",
206
+ mode: "interactive",
210
207
  dialect: "sqlite",
211
208
  locking: false,
212
- standaloneGuard: () => Effect.void,
209
+ standaloneGuard: sqlClientPasskeyStandaloneGuard,
213
210
  });
214
211
 
215
212
  export const {
@@ -225,36 +222,32 @@ export const {
225
222
  coordinatePasskeyRegistration,
226
223
  } = passkeyTarget;
227
224
 
228
- import { makeTotpTarget } from "./drizzle/totp-target";
225
+ import { makeTotpTarget, sqlClientTotpStandaloneGuard } from "./drizzle/totp-target";
229
226
 
230
227
  const totpTarget = makeTotpTarget<
231
228
  Database,
232
- EffectSQLiteDoDatabase<AnyRelations>,
233
- AnySQLiteTable<{ dialect: "sqlite" }>,
234
- unknown,
235
- true
229
+ DatabaseValue<AnyRelations>,
230
+ AnySQLiteTable<{ dialect: "sqlite" }>
236
231
  >(Database, {
237
- mode: "synchronous",
232
+ mode: "interactive",
238
233
  dialect: "sqlite",
239
234
  locking: false,
240
- standaloneGuard: () => Effect.void,
235
+ standaloneGuard: sqlClientTotpStandaloneGuard,
241
236
  });
242
237
 
243
238
  export const { makeTotpPersistenceServices, coordinateTotpPersistence } = totpTarget;
244
239
 
245
- import { makePhoneTarget } from "./drizzle/phone-target";
240
+ import { makePhoneTarget, sqlClientPhoneStandaloneGuard } from "./drizzle/phone-target";
246
241
 
247
242
  const phoneTarget = makePhoneTarget<
248
243
  Database,
249
- EffectSQLiteDoDatabase<AnyRelations>,
250
- AnySQLiteTable<{ dialect: "sqlite" }>,
251
- unknown,
252
- true
244
+ DatabaseValue<AnyRelations>,
245
+ AnySQLiteTable<{ dialect: "sqlite" }>
253
246
  >(Database, {
254
- mode: "synchronous",
247
+ mode: "interactive",
255
248
  dialect: "sqlite",
256
249
  locking: false,
257
- standaloneGuard: () => Effect.void,
250
+ standaloneGuard: sqlClientPhoneStandaloneGuard,
258
251
  });
259
252
 
260
253
  export const { makePhonePersistenceServices, coordinatePhonePersistence } = phoneTarget;
@@ -0,0 +1,14 @@
1
+ import { TokenDigest } from "@yielded/auth/Schema";
2
+ import { Crypto, Effect } from "effect";
3
+ import { Base64Url } from "effect/encoding";
4
+
5
+ const encoder = new TextEncoder();
6
+
7
+ export const randomId = Effect.flatMap(Crypto.Crypto, (crypto) =>
8
+ crypto.randomBytes(32).pipe(Effect.map(Base64Url.encode)),
9
+ );
10
+
11
+ export const digest = (value: string) =>
12
+ Effect.flatMap(Crypto.Crypto, (crypto) => crypto.digest("SHA-256", encoder.encode(value))).pipe(
13
+ Effect.map((bytes) => TokenDigest.make(Base64Url.encode(bytes))),
14
+ );
@@ -1,5 +1,5 @@
1
1
  import type { D1Client } from "@effect/sql-d1/D1Client";
2
- import { NativeDatabase } from "@yielded/auth-persistence/Adapter";
2
+ import { withStorageValidation, NativeDatabase } from "@yielded/auth-persistence/Adapter";
3
3
  import {
4
4
  ExternalIdentityMutation,
5
5
  type ExternalIdentity,
@@ -382,4 +382,4 @@ export const makeD1IdentityServices = Effect.fnUntraced(function* <
382
382
  ...(yield* makeD1SubjectProvisioningServices(mapping)),
383
383
  ...(yield* makeD1ExternalIdentityServices(mapping)),
384
384
  };
385
- });
385
+ }, withStorageValidation);
@@ -10,7 +10,7 @@ import {
10
10
  import type { AnyRelations } from "drizzle-orm";
11
11
  import type { EffectSQLiteD1Database } from "drizzle-orm/effect-d1";
12
12
  import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
13
- import { Effect, Context } from "effect";
13
+ import { type Crypto, Effect, Context } from "effect";
14
14
 
15
15
  import { Database as DatabaseService } from "./d1-database";
16
16
  import { makeD1Owner } from "./d1-planning";
@@ -129,7 +129,10 @@ export function coordinateD1OAuthSignIn<
129
129
  ): Effect.Effect<
130
130
  A,
131
131
  OAuthCoordinatorError<E> | DatabaseError,
132
- Exclude<R, OAuthSignInPersistence | D1BatchStatements> | LifecycleHooks | DatabaseRequirements
132
+ | Exclude<R, OAuthSignInPersistence | D1BatchStatements>
133
+ | Crypto.Crypto
134
+ | LifecycleHooks
135
+ | DatabaseRequirements
133
136
  > {
134
137
  return Effect.flatMap(nativeDatabase(acquire), (database) =>
135
138
  coordinateTargetOAuthSignIn<
@@ -189,7 +192,10 @@ export function coordinateD1OAuthRegistrationIntents<
189
192
  ): Effect.Effect<
190
193
  A,
191
194
  OAuthCoordinatorError<E> | DatabaseError,
192
- Exclude<R, OAuthRegistrationIntents | D1BatchStatements> | LifecycleHooks | DatabaseRequirements
195
+ | Exclude<R, OAuthRegistrationIntents | D1BatchStatements>
196
+ | Crypto.Crypto
197
+ | LifecycleHooks
198
+ | DatabaseRequirements
193
199
  > {
194
200
  return Effect.flatMap(nativeDatabase(acquire), (database) =>
195
201
  coordinateTargetOAuthRegistrationIntents<
@@ -244,7 +250,10 @@ export function coordinateD1OAuthAccounts<
244
250
  ): Effect.Effect<
245
251
  A,
246
252
  OAuthCoordinatorError<E> | DatabaseError,
247
- Exclude<R, OAuthAccountsPersistence | D1BatchStatements> | LifecycleHooks | DatabaseRequirements
253
+ | Exclude<R, OAuthAccountsPersistence | D1BatchStatements>
254
+ | Crypto.Crypto
255
+ | LifecycleHooks
256
+ | DatabaseRequirements
248
257
  > {
249
258
  return Effect.flatMap(nativeDatabase(acquire), (database) =>
250
259
  coordinateTargetOAuthAccounts<
@@ -312,7 +321,7 @@ export function coordinateD1OAuthRegistration<
312
321
  ): Effect.Effect<
313
322
  A,
314
323
  OAuthCoordinatorError<E> | DatabaseError,
315
- Exclude<R, TargetId | D1BatchStatements> | LifecycleHooks | DatabaseRequirements
324
+ Exclude<R, TargetId | D1BatchStatements> | Crypto.Crypto | LifecycleHooks | DatabaseRequirements
316
325
  > {
317
326
  return Effect.flatMap(nativeDatabase(acquire), (database) =>
318
327
  coordinateTargetOAuthRegistration<
@@ -9,7 +9,7 @@ import {
9
9
  import type { AnyRelations } from "drizzle-orm";
10
10
  import type { EffectSQLiteD1Database } from "drizzle-orm/effect-d1";
11
11
  import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
12
- import { Effect, Context } from "effect";
12
+ import { type Crypto, Effect, Context } from "effect";
13
13
 
14
14
  import { Database as DatabaseService } from "./d1-database";
15
15
  import { makeD1Owner } from "./d1-planning";
@@ -94,6 +94,7 @@ export function coordinatePasskeyPersistence<
94
94
  A,
95
95
  PasskeyCoordinatorError<E> | DatabaseError,
96
96
  | Exclude<R, PasskeyPersistence | D1BatchStatements>
97
+ | Crypto.Crypto
97
98
  | LifecycleHooks
98
99
  | DatabaseRequirements
99
100
  | RSetup
@@ -156,6 +157,7 @@ export function coordinatePasskeyRegistrationCeremony<
156
157
  A,
157
158
  PasskeyCoordinatorError<E> | DatabaseError,
158
159
  | Exclude<R, PasskeyPersistence | D1BatchStatements>
160
+ | Crypto.Crypto
159
161
  | LifecycleHooks
160
162
  | DatabaseRequirements
161
163
  | RSetup
@@ -239,6 +241,7 @@ export function coordinatePasskeyManagement<
239
241
  A,
240
242
  PasskeyCoordinatorError<E> | DatabaseError,
241
243
  | Exclude<R, PasskeyPersistence | PasskeyManagementPersistence | D1BatchStatements>
244
+ | Crypto.Crypto
242
245
  | LifecycleHooks
243
246
  | DatabaseRequirements
244
247
  | RSetup
@@ -328,6 +331,7 @@ export function coordinatePasskeyRegistration<
328
331
  A,
329
332
  PasskeyCoordinatorError<E> | DatabaseError,
330
333
  | Exclude<R, PasskeyPersistence | AuthorityId | D1BatchStatements>
334
+ | Crypto.Crypto
331
335
  | LifecycleHooks
332
336
  | DatabaseRequirements
333
337
  | RSetup
@@ -10,7 +10,7 @@ import {
10
10
  import type { AnyRelations } from "drizzle-orm";
11
11
  import type { EffectSQLiteD1Database } from "drizzle-orm/effect-d1";
12
12
  import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
13
- import { Context, Effect } from "effect";
13
+ import { type Crypto, Context, Effect } from "effect";
14
14
 
15
15
  import { Database as DatabaseService } from "./d1-database";
16
16
  import { makeD1Owner } from "./d1-planning";
@@ -59,6 +59,7 @@ export const coordinatePhonePersistence = <
59
59
  A,
60
60
  PhoneCoordinatorError<E> | DatabaseError,
61
61
  | Exclude<R, PhonePersistence | PhoneAdmission | PhoneSignInTargets | D1BatchStatements>
62
+ | Crypto.Crypto
62
63
  | LifecycleHooks
63
64
  | RSetup
64
65
  | DatabaseRequirements
@@ -5,7 +5,7 @@ import { TotpUnavailable, TotpPersistence } from "@yielded/auth/Totp";
5
5
  import type { AnyRelations } from "drizzle-orm";
6
6
  import type { EffectSQLiteD1Database } from "drizzle-orm/effect-d1";
7
7
  import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
8
- import { Context, Effect } from "effect";
8
+ import { type Crypto, Context, Effect } from "effect";
9
9
 
10
10
  import { Database as DatabaseService } from "./d1-database";
11
11
  import { makeD1Owner } from "./d1-planning";
@@ -50,7 +50,11 @@ export const coordinateTotpPersistence = <
50
50
  ): Effect.Effect<
51
51
  A,
52
52
  TotpCoordinatorError<E> | DatabaseError,
53
- Exclude<R, TotpPersistence | D1BatchStatements> | LifecycleHooks | RSetup | DatabaseRequirements
53
+ | Exclude<R, TotpPersistence | D1BatchStatements>
54
+ | Crypto.Crypto
55
+ | LifecycleHooks
56
+ | RSetup
57
+ | DatabaseRequirements
54
58
  > =>
55
59
  Effect.flatMap(nativeDatabase(acquire), (database) =>
56
60
  coordinateTargetTotp(
@@ -1,4 +1,4 @@
1
- import { NativeDatabase } from "@yielded/auth-persistence/Adapter";
1
+ import { withStorageValidation, NativeDatabase } from "@yielded/auth-persistence/Adapter";
2
2
  import {
3
3
  ExternalIdentityMutation,
4
4
  type ExternalIdentity,
@@ -331,4 +331,4 @@ export const makeMysqlIdentityServices = Effect.fnUntraced(function* <
331
331
  ...(yield* makeMysqlSubjectProvisioningServices(mapping)),
332
332
  ...(yield* makeMysqlExternalIdentityServices(mapping)),
333
333
  };
334
- });
334
+ }, withStorageValidation);
@@ -30,7 +30,7 @@ import {
30
30
  readFlow,
31
31
  terminalFlow,
32
32
  } from "./oauth-flow";
33
- import type { OAuthEligibilityFact } from "./oauth-model";
33
+ import type { OAuthReferenceGuardDescriptor, OAuthEligibilityFact } from "./oauth-model";
34
34
  import { both, col, copiedRow, equal, CurrentOAuthTransaction } from "./oauth-owner";
35
35
  import { acquireTuple, readTuple } from "./oauth-registration";
36
36
  import {
@@ -71,7 +71,7 @@ const authorization = (
71
71
  mapping.subject.decodeActionRequirement(copiedRow(current.row), action),
72
72
  );
73
73
 
74
- const accepted = validAction(
74
+ const accepted = yield* validAction(
75
75
  auth,
76
76
  {
77
77
  moduleId: auth.challenge.moduleId,
@@ -493,7 +493,7 @@ const targetCredential = (mapping: any, moduleId: string, credentialId: string,
493
493
  subject: ownership[t.externalSubject],
494
494
  };
495
495
 
496
- invariant(oauthIdentityKey(identity) === row[cr.identityKey]);
496
+ invariant((yield* oauthIdentityKey(identity)) === row[cr.identityKey]);
497
497
 
498
498
  const shared = current.revision.credentials.find(
499
499
  (item: any) => item.credentialId === credentialId,
@@ -648,23 +648,24 @@ export const unlink = (mapping: any, input: any) =>
648
648
  const current = yield* accountCurrent(mapping, expected.revision.subjectId);
649
649
 
650
650
  if (current === undefined) return { _tag: "Rejected" } as const;
651
- const guards = mapping.connectedReferenceGuards ?? [];
651
+
652
+ const guards: ReadonlyArray<OAuthReferenceGuardDescriptor<unknown>> =
653
+ mapping.connectedReferenceGuards ?? [];
652
654
 
653
655
  invariant(guards.length <= 32);
654
656
  let referencesGuarded = mapping.connectedReference !== undefined && guards.length > 0;
655
657
 
656
658
  if (mapping.connectedReference !== undefined)
657
659
  for (const guard of guards) {
660
+ const condition = guard.condition({
661
+ identity: snapshotOAuthSync(OAuthCredentialSnapshot.fields.identity, expected.identity),
662
+ identityKey: yield* oauthIdentityKey(expected.identity),
663
+ subjectId: copiedRow({ value: current.nativeId }).value,
664
+ });
665
+
658
666
  const read = yield* owner.read(
659
667
  guard.table,
660
- guard.condition({
661
- identity: snapshotOAuthSync(
662
- OAuthCredentialSnapshot.fields.identity,
663
- expected.identity,
664
- ),
665
- identityKey: oauthIdentityKey(expected.identity),
666
- subjectId: copiedRow({ value: current.nativeId }).value,
667
- }),
668
+ Effect.isEffect(condition) ? yield* condition : condition,
668
669
  { orderBy: col(guard.table, guard.orderBy) },
669
670
  );
670
671
 
@@ -730,7 +731,7 @@ export const unlink = (mapping: any, input: any) =>
730
731
  cr = mapping.credential,
731
732
  a = mapping.authority;
732
733
 
733
- const key = oauthIdentityKey(expected.identity),
734
+ const key = yield* oauthIdentityKey(expected.identity),
734
735
  t = mapping.ownership.tuple;
735
736
 
736
737
  const connected = mapping.connectedReference?.({
@@ -285,7 +285,7 @@ export const collect = Effect.fn("oauthConnected.collect")(function* (
285
285
  subject: candidate[t.externalSubject],
286
286
  };
287
287
 
288
- invariant(oauthIdentityKey(identity) === candidate[t.identityKey]);
288
+ invariant((yield* oauthIdentityKey(identity)) === candidate[t.identityKey]);
289
289
 
290
290
  const tuple = yield* F.inspectTuple(mapping, identity),
291
291
  row = tuple.row;
@@ -113,9 +113,9 @@ export const storeJob = Effect.fn("oauthConnected.storeJob")(function* (
113
113
  );
114
114
 
115
115
  const j = mapping.revocation.job,
116
- identity = oauthIdentityKey(token.identity),
117
- client = S.clientKey(token.configuration),
118
- cohort = S.cohortKey(client, identity);
116
+ identity = yield* oauthIdentityKey(token.identity),
117
+ client = yield* S.clientKey(token.configuration),
118
+ cohort = yield* S.cohortKey(client, identity);
119
119
 
120
120
  const h = mapping.cohort;
121
121
 
@@ -254,14 +254,16 @@ export const noFormerOwner = (
254
254
  client: string,
255
255
  native: unknown,
256
256
  ) =>
257
- Effect.flatMap(CurrentOAuthTransaction, (owner) => {
257
+ Effect.gen(function* () {
258
+ const owner = yield* CurrentOAuthTransaction;
259
+
258
260
  const g = mapping.grant,
259
261
  f = mapping.flow,
260
262
  j = S.jobTable(mapping);
261
263
 
262
264
  const condition = both(
263
265
  sql`not exists(select 1 from ${g.table} where ${both(eq(col(g.table, g.identityKey), identity), sql`not (${owner.exact(g.table, { [g.subjectId]: native })})`)})`,
264
- sql`not exists(select 1 from ${f.table} where ${both(eq(col(f.table, f.cohortKey), S.cohortKey(client, identity)), eq(col(f.table, f.work), "Unresolved"), sql`not (${owner.exact(f.table, { [f.subjectId]: native })})`)})`,
266
+ sql`not exists(select 1 from ${f.table} where ${both(eq(col(f.table, f.cohortKey), yield* S.cohortKey(client, identity)), eq(col(f.table, f.work), "Unresolved"), sql`not (${owner.exact(f.table, { [f.subjectId]: native })})`)})`,
265
267
  ...(j === undefined
266
268
  ? []
267
269
  : [
@@ -269,7 +271,7 @@ export const noFormerOwner = (
269
271
  ]),
270
272
  );
271
273
 
272
- return Effect.map(owner.check(condition), (accepted) => {
274
+ return yield* Effect.map(owner.check(condition), (accepted) => {
273
275
  if (accepted) owner.postconditions.push(condition);
274
276
 
275
277
  return accepted;
@@ -6,7 +6,7 @@ import {
6
6
  OAuthUnavailable,
7
7
  } from "@yielded/auth/OAuth";
8
8
  import type { Table } from "drizzle-orm";
9
- import { Effect, Context } from "effect";
9
+ import { type Crypto, Effect, Context } from "effect";
10
10
 
11
11
  import { makeD1Owner } from "./d1-planning";
12
12
  import { D1BatchStatements } from "./D1BatchStatements";
@@ -69,6 +69,7 @@ export const makeOAuthConnectedTarget = <
69
69
  A,
70
70
  OAuthCoordinatorError<E> | DatabaseError,
71
71
  | (Synchronous extends true ? never : Exclude<R, OAuthConnectedPersistence>)
72
+ | Crypto.Crypto
72
73
  | LifecycleHooks
73
74
  | DatabaseRequirements
74
75
  >;
@@ -108,6 +109,7 @@ export const makeOAuthConnectedTarget = <
108
109
  A,
109
110
  OAuthCoordinatorError<E> | DatabaseError,
110
111
  | (Synchronous extends true ? never : Exclude<R, OAuthConnectedPersistence | TxId>)
112
+ | Crypto.Crypto
111
113
  | LifecycleHooks
112
114
  | DatabaseRequirements
113
115
  >;
@@ -142,7 +144,7 @@ export const makeOAuthConnectedTarget = <
142
144
  ): Effect.Effect<
143
145
  A,
144
146
  OAuthCoordinatorError<E> | DatabaseError,
145
- Exclude<R, OAuthConnectedPersistence> | LifecycleHooks | DatabaseRequirements
147
+ Exclude<R, OAuthConnectedPersistence> | Crypto.Crypto | LifecycleHooks | DatabaseRequirements
146
148
  > {
147
149
  return Effect.flatMap(nativeDatabase(acquire), (database) =>
148
150
  coordinateTargetOAuthConnected<TransactionOf<D>, A, E, Exclude<R, OAuthConnectedPersistence>>(
@@ -193,6 +195,7 @@ export const makeOAuthConnectedTarget = <
193
195
  A,
194
196
  OAuthCoordinatorError<E> | DatabaseError,
195
197
  | (Synchronous extends true ? never : Exclude<R, OAuthConnectedRevocations>)
198
+ | Crypto.Crypto
196
199
  | LifecycleHooks
197
200
  | DatabaseRequirements
198
201
  >;
@@ -228,6 +231,7 @@ export const makeOAuthConnectedTarget = <
228
231
  A,
229
232
  OAuthCoordinatorError<E> | DatabaseError,
230
233
  | (Synchronous extends true ? never : Exclude<R, OAuthConnectedRevocations | TxId>)
234
+ | Crypto.Crypto
231
235
  | LifecycleHooks
232
236
  | DatabaseRequirements
233
237
  >;
@@ -258,7 +262,7 @@ export const makeOAuthConnectedTarget = <
258
262
  ): Effect.Effect<
259
263
  A,
260
264
  OAuthCoordinatorError<E> | DatabaseError,
261
- Exclude<R, OAuthConnectedRevocations> | LifecycleHooks | DatabaseRequirements
265
+ Exclude<R, OAuthConnectedRevocations> | Crypto.Crypto | LifecycleHooks | DatabaseRequirements
262
266
  > {
263
267
  return Effect.flatMap(nativeDatabase(acquire), (database) =>
264
268
  coordinateTargetOAuthConnectedRevocations<
@@ -361,6 +365,7 @@ export const makeD1OAuthConnectedTarget = <DatabaseId, Database, Family extends
361
365
  A,
362
366
  OAuthCoordinatorError<E> | DatabaseError,
363
367
  | Exclude<R, OAuthConnectedPersistence | D1BatchStatements>
368
+ | Crypto.Crypto
364
369
  | LifecycleHooks
365
370
  | DatabaseRequirements
366
371
  > {
@@ -435,6 +440,7 @@ export const makeD1OAuthConnectedTarget = <DatabaseId, Database, Family extends
435
440
  A,
436
441
  OAuthCoordinatorError<E> | DatabaseError,
437
442
  | Exclude<R, OAuthConnectedRevocations | D1BatchStatements>
443
+ | Crypto.Crypto
438
444
  | LifecycleHooks
439
445
  | DatabaseRequirements
440
446
  > {