@yielded/auth-persistence-drizzle 0.1.0-beta.17 → 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 (142) hide show
  1. package/README.md +8 -1
  2. package/dist/D1.d.mts +120 -120
  3. package/dist/Libsql.d.mts +154 -154
  4. package/dist/Mysql2.d.mts +166 -166
  5. package/dist/Pglite.d.mts +166 -166
  6. package/dist/Postgres.d.mts +152 -152
  7. package/dist/SqliteBun.d.mts +168 -168
  8. package/dist/SqliteDo.d.mts +237 -259
  9. package/dist/SqliteDo.mjs +30 -45
  10. package/dist/SqliteDo.mjs.map +1 -1
  11. package/dist/SqliteNode.d.mts +154 -154
  12. package/dist/SqliteWasm.d.mts +163 -163
  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 +16 -16
  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-password-prepared.mjs +3 -3
  22. package/dist/drizzle/d1-password-prepared.mjs.map +1 -1
  23. package/dist/drizzle/d1-passwords.d.mts +1 -1
  24. package/dist/drizzle/d1-passwords.mjs +1 -1
  25. package/dist/drizzle/d1-passwords.mjs.map +1 -1
  26. package/dist/drizzle/d1-phone.d.mts +3 -3
  27. package/dist/drizzle/d1-phone.mjs.map +1 -1
  28. package/dist/drizzle/d1-proofs.d.mts +1 -1
  29. package/dist/drizzle/d1-sessions.d.mts +1 -1
  30. package/dist/drizzle/d1-sessions.mjs +4 -3
  31. package/dist/drizzle/d1-sessions.mjs.map +1 -1
  32. package/dist/drizzle/d1-totp.d.mts +3 -3
  33. package/dist/drizzle/d1-totp.mjs.map +1 -1
  34. package/dist/drizzle/mysql-identity.mjs +2 -2
  35. package/dist/drizzle/mysql-identity.mjs.map +1 -1
  36. package/dist/drizzle/oauth-accounts.mjs +9 -8
  37. package/dist/drizzle/oauth-accounts.mjs.map +1 -1
  38. package/dist/drizzle/oauth-connected-collection.mjs +1 -1
  39. package/dist/drizzle/oauth-connected-collection.mjs.map +1 -1
  40. package/dist/drizzle/oauth-connected-custody.mjs +5 -4
  41. package/dist/drizzle/oauth-connected-custody.mjs.map +1 -1
  42. package/dist/drizzle/oauth-connected-drivers.mjs.map +1 -1
  43. package/dist/drizzle/oauth-connected-flow.mjs +8 -7
  44. package/dist/drizzle/oauth-connected-flow.mjs.map +1 -1
  45. package/dist/drizzle/oauth-connected-management.mjs +3 -2
  46. package/dist/drizzle/oauth-connected-management.mjs.map +1 -1
  47. package/dist/drizzle/oauth-connected-reference.mjs +8 -4
  48. package/dist/drizzle/oauth-connected-reference.mjs.map +1 -1
  49. package/dist/drizzle/oauth-connected-settlement.mjs +4 -4
  50. package/dist/drizzle/oauth-connected-settlement.mjs.map +1 -1
  51. package/dist/drizzle/oauth-connected-sign-in.mjs +11 -10
  52. package/dist/drizzle/oauth-connected-sign-in.mjs.map +1 -1
  53. package/dist/drizzle/oauth-connected-state.d.mts +265 -265
  54. package/dist/drizzle/oauth-connected-state.mjs +23 -20
  55. package/dist/drizzle/oauth-connected-state.mjs.map +1 -1
  56. package/dist/drizzle/oauth-connected-target.d.mts +105 -105
  57. package/dist/drizzle/oauth-connected-target.mjs.map +1 -1
  58. package/dist/drizzle/oauth-drivers.mjs.map +1 -1
  59. package/dist/drizzle/oauth-flow.mjs +1 -1
  60. package/dist/drizzle/oauth-flow.mjs.map +1 -1
  61. package/dist/drizzle/oauth-model.d.mts +4 -4
  62. package/dist/drizzle/oauth-model.mjs.map +1 -1
  63. package/dist/drizzle/oauth-registration.mjs +2 -2
  64. package/dist/drizzle/oauth-registration.mjs.map +1 -1
  65. package/dist/drizzle/oauth-state.mjs +11 -12
  66. package/dist/drizzle/oauth-state.mjs.map +1 -1
  67. package/dist/drizzle/oauth-target.d.mts +4 -4
  68. package/dist/drizzle/oauth-target.mjs.map +1 -1
  69. package/dist/drizzle/passkey-drivers.mjs.map +1 -1
  70. package/dist/drizzle/passkey-write-drivers.mjs.map +1 -1
  71. package/dist/drizzle/passkey-write-target.d.mts +1 -1
  72. package/dist/drizzle/password-prepared-sql.mjs +1 -1
  73. package/dist/drizzle/password-prepared-sql.mjs.map +1 -1
  74. package/dist/drizzle/password-prepared-state.mjs +1 -1
  75. package/dist/drizzle/pg-identity.mjs +2 -2
  76. package/dist/drizzle/pg-identity.mjs.map +1 -1
  77. package/dist/drizzle/phone-model.d.mts +2 -2
  78. package/dist/drizzle/phone-model.mjs +3 -3
  79. package/dist/drizzle/phone-model.mjs.map +1 -1
  80. package/dist/drizzle/phone-target.d.mts +1 -1
  81. package/dist/drizzle/phone-target.mjs +2 -2
  82. package/dist/drizzle/phone-target.mjs.map +1 -1
  83. package/dist/drizzle/sqlite-do-database.d.mts +29 -0
  84. package/dist/drizzle/sqlite-do-database.mjs +45 -0
  85. package/dist/drizzle/sqlite-do-database.mjs.map +1 -0
  86. package/dist/drizzle/sqlite-emails.mjs.map +1 -1
  87. package/dist/drizzle/sqlite-identity.mjs +2 -2
  88. package/dist/drizzle/sqlite-identity.mjs.map +1 -1
  89. package/dist/drizzle/sqlite-passwords.mjs.map +1 -1
  90. package/dist/drizzle/sqlite-proofs.mjs.map +1 -1
  91. package/dist/drizzle/sqlite-sessions.mjs.map +1 -1
  92. package/dist/drizzle/storage-validation.mjs +17 -9
  93. package/dist/drizzle/storage-validation.mjs.map +1 -1
  94. package/dist/drizzle/totp-state.mjs +8 -8
  95. package/dist/drizzle/totp-state.mjs.map +1 -1
  96. package/dist/drizzle/totp-target.d.mts +1 -1
  97. package/dist/drizzle/totp-target.mjs +2 -2
  98. package/dist/drizzle/totp-target.mjs.map +1 -1
  99. package/package.json +1 -1
  100. package/src/SqliteDo.ts +63 -70
  101. package/src/drizzle/crypto.ts +14 -0
  102. package/src/drizzle/d1-identity.ts +2 -2
  103. package/src/drizzle/d1-oauth.ts +14 -5
  104. package/src/drizzle/d1-passkey.ts +5 -1
  105. package/src/drizzle/d1-password-prepared.ts +11 -5
  106. package/src/drizzle/d1-passwords.ts +1 -1
  107. package/src/drizzle/d1-phone.ts +2 -1
  108. package/src/drizzle/d1-sessions.ts +8 -2
  109. package/src/drizzle/d1-totp.ts +6 -2
  110. package/src/drizzle/mysql-identity.ts +2 -2
  111. package/src/drizzle/oauth-accounts.ts +14 -13
  112. package/src/drizzle/oauth-connected-collection.ts +1 -1
  113. package/src/drizzle/oauth-connected-custody.ts +8 -6
  114. package/src/drizzle/oauth-connected-drivers.ts +9 -3
  115. package/src/drizzle/oauth-connected-flow.ts +6 -6
  116. package/src/drizzle/oauth-connected-management.ts +1 -1
  117. package/src/drizzle/oauth-connected-reference.ts +14 -9
  118. package/src/drizzle/oauth-connected-settlement.ts +4 -4
  119. package/src/drizzle/oauth-connected-sign-in.ts +11 -10
  120. package/src/drizzle/oauth-connected-state.ts +40 -25
  121. package/src/drizzle/oauth-connected-target.ts +9 -5
  122. package/src/drizzle/oauth-drivers.ts +13 -5
  123. package/src/drizzle/oauth-flow.ts +1 -1
  124. package/src/drizzle/oauth-model.ts +4 -4
  125. package/src/drizzle/oauth-registration.ts +2 -2
  126. package/src/drizzle/oauth-state.ts +17 -15
  127. package/src/drizzle/oauth-target.ts +11 -3
  128. package/src/drizzle/passkey-drivers.ts +7 -3
  129. package/src/drizzle/passkey-write-drivers.ts +11 -2
  130. package/src/drizzle/password-prepared-sql.ts +1 -1
  131. package/src/drizzle/pg-identity.ts +2 -2
  132. package/src/drizzle/phone-model.ts +3 -3
  133. package/src/drizzle/phone-target.ts +11 -4
  134. package/src/drizzle/sqlite-do-database.ts +87 -0
  135. package/src/drizzle/sqlite-emails.ts +1 -1
  136. package/src/drizzle/sqlite-identity.ts +2 -2
  137. package/src/drizzle/sqlite-passwords.ts +1 -1
  138. package/src/drizzle/sqlite-proofs.ts +2 -2
  139. package/src/drizzle/sqlite-sessions.ts +2 -2
  140. package/src/drizzle/storage-validation.ts +15 -6
  141. package/src/drizzle/totp-state.ts +8 -8
  142. package/src/drizzle/totp-target.ts +11 -5
@@ -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
  > {
@@ -45,7 +45,7 @@ export const pending = Effect.fn("oauthConnected.pending")(function* (
45
45
  row[f.flowId] === c.flowId &&
46
46
  row[f.commandId] === c.commandId &&
47
47
  mapping.subjectId.equals(native, row[f.subjectId]) &&
48
- row[f.clientKey] === S.clientKey(S.configuration(flow)) &&
48
+ row[f.clientKey] === (yield* S.clientKey(S.configuration(flow))) &&
49
49
  row[f.stateDigest] === c.stateDigest &&
50
50
  mapping.clock.decodeInstant(row[f.expiresAt]) === c.expiresAtMillis &&
51
51
  mapping.clock.decodeInstant(row[f.retentionUntil]) === flow.retentionUntilMillis,
@@ -238,7 +238,7 @@ export const inspectPrepared = Effect.fn("oauthConnected.inspectPrepared")(funct
238
238
  row[f.flowId] === c.flowId &&
239
239
  row[f.commandId] === c.commandId &&
240
240
  mapping.subjectId.equals(found.nativeId, row[f.subjectId]) &&
241
- row[f.clientKey] === S.clientKey(S.configuration(flow)) &&
241
+ row[f.clientKey] === (yield* S.clientKey(S.configuration(flow))) &&
242
242
  row[f.stateDigest] === c.stateDigest &&
243
243
  mapping.clock.decodeInstant(row[f.expiresAt]) === c.expiresAtMillis &&
244
244
  mapping.clock.decodeInstant(row[f.retentionUntil]) === flow.retentionUntilMillis,
@@ -356,7 +356,7 @@ export const claim = Effect.fn("oauthConnected.claim")(function* (
356
356
  {
357
357
  [f.state]: "Claimed",
358
358
  [f.claimId]: claimed.claimId,
359
- [f.claimDigest]: digest(S.claimStorage.encode(claimed)),
359
+ [f.claimDigest]: yield* digest(S.claimStorage.encode(claimed)),
360
360
  [f.claimOrder]: mapping.order.encode(order),
361
361
  [f.claimedAt]: mapping.clock.encodeInstant(now),
362
362
  [f.claimExpiresAt]: mapping.clock.encodeInstant(expires),
@@ -392,7 +392,7 @@ export const exact = Effect.fn("oauthConnected.exactClaim")(function* (
392
392
  row === undefined ||
393
393
  row[f.work] !== "Unresolved" ||
394
394
  row[f.claimId] !== claim.claimId ||
395
- row[f.claimDigest] !== digest(S.claimStorage.encode(claim))
395
+ row[f.claimDigest] !== (yield* digest(S.claimStorage.encode(claim)))
396
396
  )
397
397
  return undefined;
398
398
  const native = yield* mapping.subjectId.toNative(c.revision.subjectId);
@@ -403,7 +403,7 @@ export const exact = Effect.fn("oauthConnected.exactClaim")(function* (
403
403
  row[f.stateDigest] === c.stateDigest &&
404
404
  mapping.clock.decodeInstant(row[f.expiresAt]) === c.expiresAtMillis &&
405
405
  mapping.clock.decodeInstant(row[f.retentionUntil]) === claim.flow.retentionUntilMillis &&
406
- row[f.clientKey] === S.clientKey(S.configuration(claim.flow)) &&
406
+ row[f.clientKey] === (yield* S.clientKey(S.configuration(claim.flow))) &&
407
407
  S.nativeOrder(mapping, row[f.claimOrder]) === S.orderNumber(claim.order) &&
408
408
  mapping.clock.decodeInstant(row[f.claimedAt]) === claim.claimedAtMillis &&
409
409
  mapping.clock.decodeInstant(row[f.claimExpiresAt]) === claim.claimExpiresAtMillis,
@@ -448,7 +448,7 @@ export const inspectTuple = Effect.fn("oauthConnected.inspectTuple")(function* (
448
448
  const owner = yield* CurrentOAuthTransaction;
449
449
 
450
450
  const t = mapping.ownership.tuple,
451
- key = oauthIdentityKey(identity);
451
+ key = yield* oauthIdentityKey(identity);
452
452
 
453
453
  const read = yield* owner.read(t.table, equal(t.table, { [t.identityKey]: key }), { limit: 1 });
454
454
  const row = read.rows[0];
@@ -341,7 +341,7 @@ export const disconnect = Effect.fn("oauthConnected.disconnect")(function* (
341
341
  [d.grantId]: token.grantId,
342
342
  [d.intent]: S.commandStorage.encode({
343
343
  context: token,
344
- originalDigest: digest(S.disconnectStorage.encode(input.grant)),
344
+ originalDigest: yield* digest(S.disconnectStorage.encode(input.grant)),
345
345
  ...(input.revocation === undefined
346
346
  ? {}
347
347
  : { revocationJobId: input.revocation.context.jobId }),
@@ -1,6 +1,7 @@
1
1
  import { eq, is, sql, type SQL, type Table } from "drizzle-orm";
2
2
  import { MySqlTable } from "drizzle-orm/mysql-core/table";
3
3
  import { PgTable } from "drizzle-orm/pg-core/table";
4
+ import { Effect } from "effect";
4
5
 
5
6
  import type { OAuthConnectedMapping } from "./oauth-connected-model";
6
7
  import { scopeKey, type Mapping } from "./oauth-connected-state";
@@ -52,15 +53,19 @@ export const oauthConnectedOwnershipReferences = <
52
53
  table: c.table,
53
54
  orderBy: c.clientKey,
54
55
  condition: (input) =>
55
- both(
56
- equal(c.table, {
57
- [c.clientKey]: scopeKey(input.identity.provider, input.identity.issuer),
58
- [c.counter]: mapping.order.encode(0),
59
- }),
60
- exactText(c.table, c.provider, input.identity.provider),
61
- exactText(c.table, c.issuer, input.identity.issuer),
62
- exactText(c.table, c.clientRegistrationId, ""),
63
- ),
56
+ Effect.gen(function* () {
57
+ const key = yield* scopeKey(input.identity.provider, input.identity.issuer);
58
+
59
+ return both(
60
+ equal(c.table, {
61
+ [c.clientKey]: key,
62
+ [c.counter]: mapping.order.encode(0),
63
+ }),
64
+ exactText(c.table, c.provider, input.identity.provider),
65
+ exactText(c.table, c.issuer, input.identity.issuer),
66
+ exactText(c.table, c.clientRegistrationId, ""),
67
+ );
68
+ }),
64
69
  }),
65
70
  ],
66
71
  connectedReference: (input) => connectedReferenceCondition(mapping, input.identityKey),
@@ -46,9 +46,9 @@ export const activateGrant = Effect.fn("oauthConnected.activateGrant")(function*
46
46
  const owner = yield* CurrentOAuthTransaction;
47
47
  const token = grant.context;
48
48
  const g = mapping.grant;
49
- const identityKey = oauthIdentityKey(token.identity);
50
- const clientKey = S.clientKey(token.configuration);
51
- const cohortKey = S.cohortKey(clientKey, identityKey);
49
+ const identityKey = yield* oauthIdentityKey(token.identity);
50
+ const clientKey = yield* S.clientKey(token.configuration);
51
+ const cohortKey = yield* S.cohortKey(clientKey, identityKey);
52
52
 
53
53
  const values = {
54
54
  ...g.encodeInsert({
@@ -143,7 +143,7 @@ export const settle = Effect.fn("oauthConnected.settle")(function* (
143
143
  token.grantId === c.grantId &&
144
144
  token.identity.provider === c.provider &&
145
145
  token.identity.issuer === c.issuer &&
146
- S.clientKey(token.configuration) === cl.id &&
146
+ (yield* S.clientKey(token.configuration)) === cl.id &&
147
147
  S.tokenContextStorage.encode({
148
148
  ...token,
149
149
  configuration: S.configuration(input.claim.flow),
@@ -83,7 +83,7 @@ export const claimSignIn = Effect.fn("oauthConnected.claimSignIn")(function* (
83
83
  [f.stateDigest]: c.stateDigest,
84
84
  [f.snapshot]: null,
85
85
  [f.claimId]: claim.claimId,
86
- [f.claimDigest]: digest(reservationStorage.encode(reservation)),
86
+ [f.claimDigest]: yield* digest(reservationStorage.encode(reservation)),
87
87
  [f.claimOrder]: mapping.order.encode(order),
88
88
  [f.claimedAt]: mapping.clock.encodeInstant(claim.claimedAtMillis),
89
89
  [f.claimExpiresAt]: mapping.clock.encodeInstant(claim.claimExpiresAtMillis),
@@ -116,13 +116,13 @@ const exact = Effect.fn("oauthConnected.exactSignIn")(function* (
116
116
  row === undefined ||
117
117
  row[f.work] !== "Unresolved" ||
118
118
  row[f.claimId] !== reservation.claim.claimId ||
119
- row[f.claimDigest] !== digest(reservationStorage.encode(reservation))
119
+ row[f.claimDigest] !== (yield* digest(reservationStorage.encode(reservation)))
120
120
  )
121
121
  return undefined;
122
122
  invariant(
123
123
  row[f.subjectId] === null &&
124
124
  row[f.commandId] === c.commandId &&
125
- row[f.clientKey] === S.clientKey(reservation.configuration) &&
125
+ row[f.clientKey] === (yield* S.clientKey(reservation.configuration)) &&
126
126
  S.nativeOrder(mapping, row[f.claimOrder]) === S.orderNumber(reservation.order),
127
127
  );
128
128
  const now = yield* owner.now(mapping.clock);
@@ -164,7 +164,7 @@ const credential = Effect.fn("oauthConnected.signInCredential")(function* (
164
164
  [c.moduleId]: context.moduleId,
165
165
  [c.credentialId]: input.credentialId,
166
166
  [c.subjectId]: found.nativeId,
167
- [c.identityKey]: oauthIdentityKey(input.identity),
167
+ [c.identityKey]: yield* oauthIdentityKey(input.identity),
168
168
  [c.credentialRevision]: input.credentialRevision,
169
169
  }),
170
170
  c.activeCondition,
@@ -242,18 +242,19 @@ const slot = Effect.fn("oauthConnected.signInSlot")(function* (
242
242
 
243
243
  /** Orders belong to one client registration. Moving a profile to another client
244
244
  * requires a newer profile generation; retired clients cannot replace it later. */
245
- const canReplace = (
245
+ const canReplace = Effect.fnUntraced(function* (
246
246
  reservation: M.OAuthSignInAccessClaim,
247
247
  previous: M.OAuthConnectedTokenContext,
248
- ) => {
248
+ ) {
249
249
  const generation = reservation.configuration.profile.generation;
250
250
  const oldGeneration = previous.configuration.profile.generation;
251
251
 
252
- return S.clientKey(reservation.configuration) === S.clientKey(previous.configuration)
252
+ return (yield* S.clientKey(reservation.configuration)) ===
253
+ (yield* S.clientKey(previous.configuration))
253
254
  ? generation >= oldGeneration &&
254
255
  S.orderNumber(reservation.order) > S.orderNumber(previous.exchangeOrder)
255
256
  : generation > oldGeneration;
256
- };
257
+ });
257
258
 
258
259
  export const inspectSignIn = Effect.fn("oauthConnected.inspectSignIn")(function* (
259
260
  mapping: S.Mapping,
@@ -293,7 +294,7 @@ export const inspectSignIn = Effect.fn("oauthConnected.inspectSignIn")(function*
293
294
  co.blocked ||
294
295
  S.orderNumber(r.order) <= co.cutoff ||
295
296
  previous?.row[mapping.grant.refreshWork] === "Unresolved" ||
296
- (previous !== undefined && !canReplace(r, previous.context));
297
+ (previous !== undefined && !(yield* canReplace(r, previous.context)));
297
298
 
298
299
  return snapshotOAuthSync(M.OAuthSignInAccessInspection, {
299
300
  _tag: quarantine ? "Quarantine" : "Target",
@@ -377,7 +378,7 @@ export const settleSignIn = Effect.fn("oauthConnected.settleSignIn")(function* (
377
378
  F.sameTarget(out.previous, previous.context) &&
378
379
  sameIdentity(previous.context.identity, token.identity) &&
379
380
  previous.row[mapping.grant.refreshWork] !== "Unresolved" &&
380
- canReplace(r, previous.context);
381
+ (yield* canReplace(r, previous.context));
381
382
 
382
383
  const previousSafe = yield* C.noFormerOwner(mapping, tuple.key, cl.id, native);
383
384
 
@@ -107,11 +107,11 @@ export const nativeOrder = (mapping: Authority, value: unknown) =>
107
107
  const encoder = new TextEncoder();
108
108
  const decoder = new TextDecoder("utf-8", { fatal: true });
109
109
 
110
- const key = (domain: string, fields: ReadonlyArray<string>) => {
110
+ const key = Effect.fnUntraced(function* (domain: string, fields: ReadonlyArray<string>) {
111
111
  for (const value of fields) invariant(decoder.decode(encoder.encode(value)) === value);
112
112
 
113
- return "v1:" + digest(strings.encode([domain, ...fields]));
114
- };
113
+ return "v1:" + (yield* digest(strings.encode([domain, ...fields])));
114
+ });
115
115
 
116
116
  export const clientKey = (configuration: M.OAuthConnectedConfiguration) =>
117
117
  key("effect-auth/oauth-connected-client/v1", [
@@ -124,7 +124,9 @@ export const cohortKey = (client: string, identity: string) =>
124
124
  key("effect-auth/oauth-connected-cohort/v1", [client, identity]);
125
125
 
126
126
  export const initialGeneration = (cohort: string) =>
127
- M.OAuthConnectedTarget.fields.cohortGeneration.make("initial:" + digest(cohort));
127
+ digest(cohort).pipe(
128
+ Effect.map((value) => M.OAuthConnectedTarget.fields.cohortGeneration.make("initial:" + value)),
129
+ );
128
130
 
129
131
  export const configuration = (flow: M.OAuthConnectedPendingFlow): M.OAuthConnectedConfiguration =>
130
132
  snapshotOAuthSync(M.OAuthConnectedConfiguration, flow.context);
@@ -254,9 +256,9 @@ export const action = Effect.fn("oauthConnected.action")(function* (
254
256
  ? "connected-disconnect"
255
257
  : "connected-complete";
256
258
 
257
- const intentDigest = digest(expected.intent);
259
+ const intentDigest = yield* digest(expected.intent);
258
260
 
259
- const binding = digest(
261
+ const binding = yield* digest(
260
262
  strings.encode([
261
263
  "effect-auth/oauth-connected-action/v1",
262
264
  expected.moduleId,
@@ -364,7 +366,9 @@ export const useAuthority = Effect.fn("oauthConnected.useAuthority")(function* (
364
366
  });
365
367
 
366
368
  export const scopeKey = (provider: string, issuer: string) =>
367
- "scope:" + key("effect-auth/oauth-connected-provider-issuer/v1", [provider, issuer]);
369
+ key("effect-auth/oauth-connected-provider-issuer/v1", [provider, issuer]).pipe(
370
+ Effect.map((value) => "scope:" + value),
371
+ );
368
372
 
369
373
  export interface ScopeAnchor {
370
374
  readonly id: string;
@@ -412,7 +416,7 @@ export const scope = Effect.fn("oauthConnected.scope")(function* (
412
416
  const owner = yield* CurrentOAuthTransaction;
413
417
 
414
418
  const c = mapping.client,
415
- id = scopeKey(config.provider, config.issuer),
419
+ id = yield* scopeKey(config.provider, config.issuer),
416
420
  cache = yield* anchors(scopeCache, c.table);
417
421
 
418
422
  const existing = cache.get(id);
@@ -497,7 +501,7 @@ export const readScope = Effect.fn("oauthConnected.readScope")(function* (
497
501
  const owner = yield* CurrentOAuthTransaction;
498
502
 
499
503
  const c = mapping.client,
500
- id = scopeKey(provider, issuer),
504
+ id = yield* scopeKey(provider, issuer),
501
505
  cache = yield* anchors(scopeCache, c.table),
502
506
  cached = cache.get(id);
503
507
 
@@ -549,12 +553,12 @@ export const lockClientById = Effect.fn("oauthConnected.lockClientById")(functio
549
553
  row !== undefined &&
550
554
  row[c.clientRegistrationId] !== "" &&
551
555
  id ===
552
- key("effect-auth/oauth-connected-client/v1", [
556
+ (yield* key("effect-auth/oauth-connected-client/v1", [
553
557
  row[c.provider],
554
558
  row[c.issuer],
555
559
  row[c.clientRegistrationId],
556
- ]) &&
557
- scope.id === scopeKey(row[c.provider], row[c.issuer]),
560
+ ])) &&
561
+ scope.id === (yield* scopeKey(row[c.provider], row[c.issuer])),
558
562
  );
559
563
  const found = { id, row, counter: nativeOrder(mapping, row[c.counter]), scope };
560
564
 
@@ -563,10 +567,15 @@ export const lockClientById = Effect.fn("oauthConnected.lockClientById")(functio
563
567
  return found;
564
568
  });
565
569
 
566
- export const heldScope = (mapping: Authority, provider: string, issuer: string) =>
567
- Effect.map(anchors(scopeCache, mapping.client.table), (cache) =>
568
- cache.get(scopeKey(provider, issuer)),
569
- );
570
+ export const heldScope = Effect.fnUntraced(function* (
571
+ mapping: Authority,
572
+ provider: string,
573
+ issuer: string,
574
+ ) {
575
+ const cache = yield* anchors(scopeCache, mapping.client.table);
576
+
577
+ return cache.get(yield* scopeKey(provider, issuer));
578
+ });
570
579
 
571
580
  /** Cleanup obtains every scope then every client before entering any tuple. */
572
581
  export const prelockClients = Effect.fn("oauthConnected.prelockClients")(function* (
@@ -583,7 +592,7 @@ export const prelockClients = Effect.fn("oauthConnected.prelockClients")(functio
583
592
  const scopes = new Map<string, { provider: string; issuer: string; required: boolean }>();
584
593
 
585
594
  for (const value of releaseScopes)
586
- scopes.set(scopeKey(value.provider, value.issuer), { ...value, required: false });
595
+ scopes.set(yield* scopeKey(value.provider, value.issuer), { ...value, required: false });
587
596
  for (const id of unique) {
588
597
  const read = yield* owner.read(c.table, equal(c.table, { [c.clientKey]: id }), {
589
598
  limit: 1,
@@ -593,7 +602,7 @@ export const prelockClients = Effect.fn("oauthConnected.prelockClients")(functio
593
602
  row = read.rows[0];
594
603
 
595
604
  invariant(row !== undefined);
596
- scopes.set(scopeKey(row[c.provider], row[c.issuer]), {
605
+ scopes.set(yield* scopeKey(row[c.provider], row[c.issuer]), {
597
606
  provider: row[c.provider],
598
607
  issuer: row[c.issuer],
599
608
  required: true,
@@ -629,7 +638,7 @@ export const client = Effect.fn("oauthConnected.client")(function* (
629
638
  const owner = yield* CurrentOAuthTransaction;
630
639
 
631
640
  const c = mapping.client,
632
- id = clientKey(config),
641
+ id = yield* clientKey(config),
633
642
  where = { [c.clientKey]: id };
634
643
 
635
644
  const heldScope = yield* scope(mapping, config, create);
@@ -723,7 +732,7 @@ export const cohort = Effect.fn("oauthConnected.cohort")(function* (
723
732
  const owner = yield* CurrentOAuthTransaction;
724
733
 
725
734
  const c = mapping.cohort,
726
- id = cohortKey(clientId, identityId),
735
+ id = yield* cohortKey(clientId, identityId),
727
736
  where = { [c.cohortKey]: id };
728
737
 
729
738
  let read = yield* owner.read(c.table, equal(c.table, where), { limit: 1 });
@@ -755,7 +764,7 @@ export const cohort = Effect.fn("oauthConnected.cohort")(function* (
755
764
  ...where,
756
765
  [c.clientKey]: clientId,
757
766
  [c.identityKey]: identityId,
758
- [c.generation]: initialGeneration(id),
767
+ [c.generation]: yield* initialGeneration(id),
759
768
  [c.cutoff]: mapping.order.encode(0),
760
769
  [c.state]: "Open",
761
770
  [c.version]: owner.marker,
@@ -769,7 +778,13 @@ export const cohort = Effect.fn("oauthConnected.cohort")(function* (
769
778
  const row = read.rows[0];
770
779
 
771
780
  if (row === undefined)
772
- return { id, row: undefined, generation: initialGeneration(id), cutoff: 0, blocked: false };
781
+ return {
782
+ id,
783
+ row: undefined,
784
+ generation: yield* initialGeneration(id),
785
+ cutoff: 0,
786
+ blocked: false,
787
+ };
773
788
  invariant(
774
789
  row[c.clientKey] === clientId &&
775
790
  row[c.identityKey] === identityId &&
@@ -816,9 +831,9 @@ export const readGrant = Effect.fn("oauthConnected.readGrant")(function* (
816
831
  context.moduleId === moduleId &&
817
832
  context.grantId === grantId &&
818
833
  mapping.subjectId.equals(native, row[g.subjectId]) &&
819
- row[g.identityKey] === oauthIdentityKey(context.identity) &&
820
- row[g.clientKey] === clientKey(context.configuration) &&
821
- row[g.cohortKey] === cohortKey(row[g.clientKey], row[g.identityKey]) &&
834
+ row[g.identityKey] === (yield* oauthIdentityKey(context.identity)) &&
835
+ row[g.clientKey] === (yield* clientKey(context.configuration)) &&
836
+ row[g.cohortKey] === (yield* cohortKey(row[g.clientKey], row[g.identityKey])) &&
822
837
  row[g.profileKey] === context.configuration.profile.key &&
823
838
  row[g.grantVersion] === context.grantVersion &&
824
839
  row[g.tokenVersion] === context.tokenVersion &&
@@ -8,7 +8,7 @@ import {
8
8
  type PrepareOAuthCommit,
9
9
  } from "@yielded/auth/OAuth";
10
10
  /* oxlint-disable no-explicit-any -- concrete adapters retain the native database, table and ID types. */
11
- import { Effect, Layer, Schema } from "effect";
11
+ import { type Crypto, Effect, Layer, type PlatformError, Schema } from "effect";
12
12
  import type { Statement } from "effect/sql/Statement";
13
13
 
14
14
  import type { PersistenceMappingError } from "./model";
@@ -94,8 +94,10 @@ const mutation =
94
94
  input: Input,
95
95
  ) => Effect.Effect<
96
96
  Value,
97
- import("@yielded/auth/OAuth").OAuthUnavailable | PersistenceMappingError,
98
- CurrentOAuthTransaction
97
+ | import("@yielded/auth/OAuth").OAuthUnavailable
98
+ | PersistenceMappingError
99
+ | PlatformError.PlatformError,
100
+ CurrentOAuthTransaction | Crypto.Crypto
99
101
  >,
100
102
  schema: Schema.Codec<Value, Encoded, never, never>,
101
103
  ) =>
@@ -111,8 +113,10 @@ const read =
111
113
  input: Input,
112
114
  ) => Effect.Effect<
113
115
  Value,
114
- import("@yielded/auth/OAuth").OAuthUnavailable | PersistenceMappingError,
115
- CurrentOAuthTransaction
116
+ | import("@yielded/auth/OAuth").OAuthUnavailable
117
+ | PersistenceMappingError
118
+ | PlatformError.PlatformError,
119
+ CurrentOAuthTransaction | Crypto.Crypto
116
120
  >,
117
121
  ) =>
118
122
  (input: Input) =>