@yielded/auth-persistence-drizzle 0.1.0-beta.0 → 0.1.0-beta.1

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 (204) hide show
  1. package/README.md +6 -1
  2. package/dist/D1.d.mts +135 -25
  3. package/dist/D1.mjs +1 -3
  4. package/dist/D1.mjs.map +1 -1
  5. package/dist/Libsql.d.mts +192 -75
  6. package/dist/Libsql.mjs +1 -7
  7. package/dist/Libsql.mjs.map +1 -1
  8. package/dist/Mysql2.d.mts +177 -37
  9. package/dist/Mysql2.mjs +1 -3
  10. package/dist/Mysql2.mjs.map +1 -1
  11. package/dist/Pglite.d.mts +171 -30
  12. package/dist/Pglite.mjs +2 -3
  13. package/dist/Pglite.mjs.map +1 -1
  14. package/dist/Postgres.d.mts +177 -36
  15. package/dist/Postgres.mjs +2 -3
  16. package/dist/Postgres.mjs.map +1 -1
  17. package/dist/SqliteBun.d.mts +192 -74
  18. package/dist/SqliteBun.mjs +1 -5
  19. package/dist/SqliteBun.mjs.map +1 -1
  20. package/dist/SqliteDo.d.mts +194 -110
  21. package/dist/SqliteDo.mjs +2 -32
  22. package/dist/SqliteDo.mjs.map +1 -1
  23. package/dist/SqliteNode.d.mts +190 -72
  24. package/dist/SqliteNode.mjs +1 -5
  25. package/dist/SqliteNode.mjs.map +1 -1
  26. package/dist/SqliteWasm.d.mts +192 -74
  27. package/dist/SqliteWasm.mjs +1 -5
  28. package/dist/SqliteWasm.mjs.map +1 -1
  29. package/dist/drizzle/D1BatchStatements.d.mts +1 -1
  30. package/dist/drizzle/D1BatchStatements.mjs.map +1 -1
  31. package/dist/drizzle/d1-emails.mjs.map +1 -1
  32. package/dist/drizzle/d1-generated-statement.mjs.map +1 -1
  33. package/dist/drizzle/d1-identity.d.mts +1 -1
  34. package/dist/drizzle/d1-oauth.d.mts +35 -5
  35. package/dist/drizzle/d1-oauth.mjs +1 -1
  36. package/dist/drizzle/d1-password-prepared.mjs.map +1 -1
  37. package/dist/drizzle/d1-passwords.d.mts +1 -1
  38. package/dist/drizzle/d1-passwords.mjs.map +1 -1
  39. package/dist/drizzle/d1-proofs.d.mts +1 -1
  40. package/dist/drizzle/d1-proofs.mjs.map +1 -1
  41. package/dist/drizzle/d1-sessions.d.mts +3 -3
  42. package/dist/drizzle/d1-sessions.mjs.map +1 -1
  43. package/dist/drizzle/email-registration.d.mts +1 -1
  44. package/dist/drizzle/email-target.d.mts +1 -1
  45. package/dist/drizzle/email-target.mjs.map +1 -1
  46. package/dist/drizzle/model.d.mts +2 -126
  47. package/dist/drizzle/model.mjs +2 -21
  48. package/dist/drizzle/model.mjs.map +1 -1
  49. package/dist/drizzle/mysql-emails.d.mts +1 -1
  50. package/dist/drizzle/mysql-emails.mjs.map +1 -1
  51. package/dist/drizzle/mysql-identity.d.mts +1 -1
  52. package/dist/drizzle/mysql-password-prepared.d.mts +1 -1
  53. package/dist/drizzle/mysql-password-prepared.mjs.map +1 -1
  54. package/dist/drizzle/mysql-passwords.d.mts +1 -1
  55. package/dist/drizzle/mysql-passwords.mjs.map +1 -1
  56. package/dist/drizzle/mysql-proofs.d.mts +1 -1
  57. package/dist/drizzle/mysql-proofs.mjs.map +1 -1
  58. package/dist/drizzle/mysql-sessions.d.mts +1 -1
  59. package/dist/drizzle/mysql-sessions.mjs.map +1 -1
  60. package/dist/drizzle/oauth-accounts.mjs +1 -1
  61. package/dist/drizzle/oauth-connected-access.mjs +14 -12
  62. package/dist/drizzle/oauth-connected-access.mjs.map +1 -1
  63. package/dist/drizzle/oauth-connected-custody.mjs +2 -2
  64. package/dist/drizzle/oauth-connected-drivers.mjs +4 -1
  65. package/dist/drizzle/oauth-connected-drivers.mjs.map +1 -1
  66. package/dist/drizzle/oauth-connected-flow.mjs +2 -2
  67. package/dist/drizzle/oauth-connected-input.mjs +14 -1
  68. package/dist/drizzle/oauth-connected-input.mjs.map +1 -1
  69. package/dist/drizzle/oauth-connected-maintenance.mjs +1 -1
  70. package/dist/drizzle/oauth-connected-management.mjs +2 -2
  71. package/dist/drizzle/oauth-connected-model.d.mts +17 -3
  72. package/dist/drizzle/oauth-connected-model.mjs.map +1 -1
  73. package/dist/drizzle/oauth-connected-settlement.mjs +51 -41
  74. package/dist/drizzle/oauth-connected-settlement.mjs.map +1 -1
  75. package/dist/drizzle/oauth-connected-sign-in.mjs +263 -0
  76. package/dist/drizzle/oauth-connected-sign-in.mjs.map +1 -0
  77. package/dist/drizzle/oauth-connected-state.d.mts +110 -2
  78. package/dist/drizzle/oauth-connected-state.mjs +9 -4
  79. package/dist/drizzle/oauth-connected-state.mjs.map +1 -1
  80. package/dist/drizzle/oauth-connected-target.d.mts +137 -9
  81. package/dist/drizzle/oauth-connected-target.mjs +6 -2
  82. package/dist/drizzle/oauth-connected-target.mjs.map +1 -1
  83. package/dist/drizzle/oauth-drivers.mjs +1 -1
  84. package/dist/drizzle/oauth-flow.d.mts +1 -1
  85. package/dist/drizzle/oauth-flow.mjs +1 -1
  86. package/dist/drizzle/oauth-input.mjs +1 -1
  87. package/dist/drizzle/oauth-model.d.mts +1 -1
  88. package/dist/drizzle/oauth-owner.d.mts +1 -1
  89. package/dist/drizzle/oauth-registration.d.mts +2 -2
  90. package/dist/drizzle/oauth-registration.mjs +1 -1
  91. package/dist/drizzle/oauth-state.mjs +5 -4
  92. package/dist/drizzle/oauth-state.mjs.map +1 -1
  93. package/dist/drizzle/oauth-target.d.mts +2 -2
  94. package/dist/drizzle/oauth-target.mjs +1 -1
  95. package/dist/drizzle/oauth-target.mjs.map +1 -1
  96. package/dist/drizzle/passkey-write-target.d.mts +1 -1
  97. package/dist/drizzle/password-prepared-state.mjs +1 -1
  98. package/dist/drizzle/password-prepared-target.d.mts +1 -1
  99. package/dist/drizzle/password-prepared-target.mjs.map +1 -1
  100. package/dist/drizzle/password-target.d.mts +1 -1
  101. package/dist/drizzle/password-target.mjs.map +1 -1
  102. package/dist/drizzle/pg-emails.d.mts +1 -1
  103. package/dist/drizzle/pg-emails.mjs.map +1 -1
  104. package/dist/drizzle/pg-identity.d.mts +22 -0
  105. package/dist/drizzle/pg-identity.mjs +89 -0
  106. package/dist/drizzle/pg-identity.mjs.map +1 -0
  107. package/dist/drizzle/pg-password-prepared.d.mts +1 -1
  108. package/dist/drizzle/pg-password-prepared.mjs.map +1 -1
  109. package/dist/drizzle/pg-passwords.d.mts +1 -1
  110. package/dist/drizzle/pg-passwords.mjs.map +1 -1
  111. package/dist/drizzle/pg-proofs.d.mts +1 -1
  112. package/dist/drizzle/pg-proofs.mjs.map +1 -1
  113. package/dist/drizzle/pg-sessions.d.mts +1 -1
  114. package/dist/drizzle/pg-sessions.mjs.map +1 -1
  115. package/dist/drizzle/phone-model.d.mts +1 -1
  116. package/dist/drizzle/phone-target.d.mts +1 -1
  117. package/dist/drizzle/phone-target.mjs.map +1 -1
  118. package/dist/drizzle/proof-target.d.mts +1 -1
  119. package/dist/drizzle/proof-target.mjs.map +1 -1
  120. package/dist/drizzle/session-target.d.mts +1 -1
  121. package/dist/drizzle/session-target.mjs.map +1 -1
  122. package/dist/drizzle/sqlite-emails.mjs.map +1 -1
  123. package/dist/drizzle/sqlite-password-prepared.mjs.map +1 -1
  124. package/dist/drizzle/sqlite-passwords.mjs.map +1 -1
  125. package/dist/drizzle/sqlite-proofs.mjs.map +1 -1
  126. package/dist/drizzle/sqlite-sessions.mjs.map +1 -1
  127. package/dist/drizzle/standalone-guard.mjs.map +1 -1
  128. package/dist/drizzle/totp-model.d.mts +1 -1
  129. package/dist/drizzle/totp-target.d.mts +1 -1
  130. package/dist/drizzle/totp-target.mjs.map +1 -1
  131. package/dist/drizzle/transaction-mysql.mjs +1 -1
  132. package/dist/drizzle/transaction-mysql.mjs.map +1 -1
  133. package/dist/index.d.mts +2 -4
  134. package/dist/index.mjs +2 -4
  135. package/dist/internal/drizzle-postgres.mjs.map +1 -1
  136. package/dist/internal/drizzle-sqlite.mjs.map +1 -1
  137. package/package.json +1 -1
  138. package/src/D1.ts +1 -17
  139. package/src/Libsql.ts +0 -44
  140. package/src/Mysql2.ts +1 -23
  141. package/src/Pglite.ts +2 -18
  142. package/src/Postgres.ts +2 -18
  143. package/src/SqliteBun.ts +1 -38
  144. package/src/SqliteDo.ts +1 -256
  145. package/src/SqliteNode.ts +1 -38
  146. package/src/SqliteWasm.ts +1 -38
  147. package/src/drizzle/D1BatchStatements.ts +1 -1
  148. package/src/drizzle/d1-emails.ts +2 -4
  149. package/src/drizzle/d1-generated-statement.ts +1 -1
  150. package/src/drizzle/d1-password-prepared.ts +2 -5
  151. package/src/drizzle/d1-passwords.ts +2 -4
  152. package/src/drizzle/d1-proofs.ts +2 -4
  153. package/src/drizzle/d1-sessions.ts +2 -2
  154. package/src/drizzle/email-target.ts +1 -1
  155. package/src/drizzle/model.ts +1 -208
  156. package/src/drizzle/mysql-emails.ts +1 -1
  157. package/src/drizzle/mysql-password-prepared.ts +1 -1
  158. package/src/drizzle/mysql-passwords.ts +1 -1
  159. package/src/drizzle/mysql-proofs.ts +1 -1
  160. package/src/drizzle/mysql-sessions.ts +1 -1
  161. package/src/drizzle/oauth-connected-access.ts +8 -1
  162. package/src/drizzle/oauth-connected-drivers.ts +3 -0
  163. package/src/drizzle/oauth-connected-input.ts +13 -0
  164. package/src/drizzle/oauth-connected-model.ts +17 -0
  165. package/src/drizzle/oauth-connected-settlement.ts +65 -42
  166. package/src/drizzle/oauth-connected-sign-in.ts +438 -0
  167. package/src/drizzle/oauth-connected-state.ts +19 -7
  168. package/src/drizzle/oauth-connected-target.ts +10 -1
  169. package/src/drizzle/oauth-state.ts +5 -4
  170. package/src/drizzle/oauth-target.ts +1 -1
  171. package/src/drizzle/password-prepared-target.ts +1 -1
  172. package/src/drizzle/password-target.ts +1 -1
  173. package/src/drizzle/pg-emails.ts +1 -1
  174. package/src/drizzle/pg-identity.ts +301 -0
  175. package/src/drizzle/pg-password-prepared.ts +1 -1
  176. package/src/drizzle/pg-passwords.ts +1 -1
  177. package/src/drizzle/pg-proofs.ts +1 -1
  178. package/src/drizzle/pg-sessions.ts +1 -1
  179. package/src/drizzle/phone-target.ts +1 -1
  180. package/src/drizzle/proof-target.ts +1 -1
  181. package/src/drizzle/session-target.ts +1 -1
  182. package/src/drizzle/sqlite-emails.ts +1 -1
  183. package/src/drizzle/sqlite-password-prepared.ts +1 -1
  184. package/src/drizzle/sqlite-passwords.ts +1 -1
  185. package/src/drizzle/sqlite-proofs.ts +1 -1
  186. package/src/drizzle/sqlite-sessions.ts +1 -1
  187. package/src/drizzle/standalone-guard.ts +1 -1
  188. package/src/drizzle/totp-target.ts +1 -1
  189. package/src/drizzle/transaction-mysql.ts +1 -1
  190. package/src/index.ts +0 -24
  191. package/src/internal/drizzle-postgres.ts +1 -1
  192. package/src/internal/drizzle-sqlite.ts +1 -1
  193. package/dist/drizzle/mysql.d.mts +0 -69
  194. package/dist/drizzle/mysql.mjs +0 -218
  195. package/dist/drizzle/mysql.mjs.map +0 -1
  196. package/dist/drizzle/pg.d.mts +0 -80
  197. package/dist/drizzle/pg.mjs +0 -289
  198. package/dist/drizzle/pg.mjs.map +0 -1
  199. package/dist/drizzle/sqlite.d.mts +0 -77
  200. package/dist/drizzle/sqlite.mjs +0 -217
  201. package/dist/drizzle/sqlite.mjs.map +0 -1
  202. package/src/drizzle/mysql.ts +0 -687
  203. package/src/drizzle/pg.ts +0 -984
  204. package/src/drizzle/sqlite.ts +0 -705
@@ -7,7 +7,7 @@ import {
7
7
  import { PasswordUnavailable, PasswordPersistence } from "@yielded/auth/Password";
8
8
  /* oxlint-disable no-explicit-any -- public driver wrappers restore concrete Drizzle generics. */
9
9
  import { type Context, Effect, Layer } from "effect";
10
- import type * as SqlError from "effect/unstable/sql/SqlError";
10
+ import type * as SqlError from "effect/sql/SqlError";
11
11
 
12
12
  import {
13
13
  makeSqlPasswordRegistrationAuthority,
@@ -6,7 +6,7 @@ import type { EffectPgDatabase as PgliteDatabase } from "drizzle-orm/effect-pgli
6
6
  import type { EffectPgDatabase as PostgresDatabase } from "drizzle-orm/effect-postgres";
7
7
  import type { AnyPgTable } from "drizzle-orm/pg-core";
8
8
  import { Effect, Context } from "effect";
9
- import type { SqlError } from "effect/unstable/sql/SqlError";
9
+ import type { SqlError } from "effect/sql/SqlError";
10
10
 
11
11
  import type {
12
12
  EmailAddressMapping,
@@ -0,0 +1,301 @@
1
+ import {
2
+ ExternalIdentityMutation,
3
+ IdentityConflict,
4
+ IdentityUnavailable,
5
+ SubjectProvisioned,
6
+ type ExternalIdentity,
7
+ SubjectProvisioner,
8
+ type SubjectProvisioningInput,
9
+ } from "@yielded/auth/Identity";
10
+ import { reportPersistenceFailure } from "@yielded/auth/Persistence";
11
+ import type { SubjectId } from "@yielded/auth/Schema";
12
+ /* oxlint-disable no-explicit-any -- Drizzle's generic query builders lose the concrete consumer table through a runtime column map. Assertions stay in this adapter. */
13
+ import { and, eq, type AnyRelations } from "drizzle-orm";
14
+ import type { EffectPgDatabase as PgliteDatabase } from "drizzle-orm/effect-pglite";
15
+ import type { EffectPgDatabase as PgDatabase } from "drizzle-orm/effect-postgres";
16
+ import { type AnyPgTable, type PgColumn } from "drizzle-orm/pg-core";
17
+ import { Cause, Effect, Schema } from "effect";
18
+
19
+ import {
20
+ column,
21
+ PersistenceMappingError,
22
+ isMappedConstraintConflict,
23
+ provisioningFingerprint,
24
+ type ExternalIdentityTables,
25
+ type IdentityTables,
26
+ type SubjectProvisioningTables,
27
+ } from "./model";
28
+
29
+ type Database = PgDatabase<AnyRelations> | PgliteDatabase<AnyRelations>;
30
+ type RuntimeDatabase = PgDatabase<any>;
31
+
32
+ const identityUnavailable = () => IdentityUnavailable.make();
33
+ const isIdentityFailure = Schema.is(Schema.Union([IdentityConflict, IdentityUnavailable]));
34
+
35
+ const mapIdentityFailure = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
36
+ reportPersistenceFailure(effect, isIdentityFailure).pipe(
37
+ Effect.catchCause((cause) =>
38
+ Effect.failCause(
39
+ Cause.map(cause, (error) => (isIdentityFailure(error) ? error : identityUnavailable())),
40
+ ),
41
+ ),
42
+ );
43
+
44
+ export const makePgSubjectProvisioningServices = <
45
+ Subject extends AnyPgTable,
46
+ Identifier extends AnyPgTable,
47
+ Request extends AnyPgTable,
48
+ NativeId,
49
+ >(
50
+ database: Database,
51
+ mapping: SubjectProvisioningTables<Subject, Identifier, Request, NativeId>,
52
+ ) => {
53
+ const db = database as RuntimeDatabase;
54
+ const requestTable = mapping.provisioningRequest.table;
55
+ const requestIdColumn = column(requestTable, mapping.provisioningRequest.requestId);
56
+ const requestFingerprintColumn = column(requestTable, mapping.provisioningRequest.fingerprint);
57
+ const requestSubjectColumn = column(requestTable, mapping.provisioningRequest.subjectId);
58
+ const identifierTable = mapping.identifier.table;
59
+ const identifierNamespaceColumn = column(identifierTable, mapping.identifier.namespace);
60
+ const identifierValueColumn = column(identifierTable, mapping.identifier.value);
61
+
62
+ const findReceipt = Effect.fn("DrizzlePgIdentity.findReceipt")(function* (requestId: string) {
63
+ const rows = yield* db
64
+ .select({
65
+ fingerprint: requestFingerprintColumn as PgColumn,
66
+ subjectId: requestSubjectColumn as PgColumn,
67
+ })
68
+ .from(requestTable as any)
69
+ .where(eq(requestIdColumn, requestId))
70
+ .limit(1);
71
+
72
+ return rows[0];
73
+ });
74
+
75
+ const provision = Effect.fn("DrizzlePgIdentity.provision")(function* (
76
+ input: SubjectProvisioningInput,
77
+ ) {
78
+ const fingerprint = provisioningFingerprint(input);
79
+ const existing = yield* findReceipt(input.requestId);
80
+
81
+ if (existing !== undefined) {
82
+ if (existing.fingerprint !== fingerprint) return yield* IdentityConflict.make();
83
+ const id = yield* mapping.subjectId.toSubject(existing.subjectId as NativeId);
84
+
85
+ return SubjectProvisioned.make({ subjectId: id });
86
+ }
87
+
88
+ const attempt = db.transaction((tx) =>
89
+ Effect.gen(function* () {
90
+ const allocated =
91
+ mapping.subject.allocateId === undefined ? undefined : yield* mapping.subject.allocateId;
92
+
93
+ const inserted = yield* tx
94
+ .insert(mapping.subject.table as any)
95
+ .values(mapping.subject.encodeInsert(input, allocated) as any)
96
+ .returning({ id: column(mapping.subject.table, mapping.subject.id) as PgColumn });
97
+
98
+ const nativeId = (inserted[0]?.id ?? allocated) as NativeId | undefined;
99
+
100
+ if (nativeId === undefined) {
101
+ return yield* PersistenceMappingError.make({
102
+ operation: "provisionSubject.generatedId",
103
+ cause: new Error("subject insert did not return its native id"),
104
+ });
105
+ }
106
+ if (input.identifier !== undefined) {
107
+ yield* tx
108
+ .insert(identifierTable as any)
109
+ .values(
110
+ mapping.identifier.encodeInsert(input.identifier, nativeId, input.verifiedAt) as any,
111
+ );
112
+ }
113
+ yield* tx
114
+ .insert(requestTable as any)
115
+ .values(
116
+ mapping.provisioningRequest.encodeInsert(input.requestId, fingerprint, nativeId) as any,
117
+ );
118
+
119
+ return nativeId;
120
+ }),
121
+ );
122
+
123
+ const nativeId = yield* attempt.pipe(
124
+ Effect.catchCause(
125
+ (
126
+ cause,
127
+ ): Effect.Effect<
128
+ NativeId,
129
+ | Effect.Error<typeof attempt>
130
+ | Effect.Error<ReturnType<typeof findReceipt>>
131
+ | IdentityConflict
132
+ > => {
133
+ if (
134
+ cause.reasons.length === 0 ||
135
+ !cause.reasons.every(
136
+ (reason) =>
137
+ Cause.isFailReason(reason) &&
138
+ isMappedConstraintConflict(mapping.isConstraintConflict, reason.error),
139
+ )
140
+ )
141
+ return Effect.failCause(cause);
142
+
143
+ return findReceipt(input.requestId).pipe(
144
+ Effect.flatMap((receipt) => {
145
+ if (receipt !== undefined && receipt.fingerprint === fingerprint) {
146
+ return Effect.succeed(receipt.subjectId as NativeId);
147
+ }
148
+ if (input.identifier === undefined) return IdentityConflict.make();
149
+
150
+ return db
151
+ .select({
152
+ subjectId: column(identifierTable, mapping.identifier.subjectId) as PgColumn,
153
+ })
154
+ .from(identifierTable as any)
155
+ .where(
156
+ and(
157
+ eq(identifierNamespaceColumn, input.identifier.namespace),
158
+ eq(identifierValueColumn, input.identifier.value),
159
+ ),
160
+ )
161
+ .limit(1)
162
+ .pipe(Effect.flatMap(() => IdentityConflict.make()));
163
+ }),
164
+ );
165
+ },
166
+ ),
167
+ );
168
+
169
+ const subjectId = yield* mapping.subjectId.toSubject(nativeId);
170
+
171
+ return SubjectProvisioned.make({ subjectId });
172
+ }, mapIdentityFailure);
173
+
174
+ return {
175
+ subjectProvisioner: SubjectProvisioner.of({ provision }),
176
+ } as const;
177
+ };
178
+
179
+ export const makePgExternalIdentityServices = <
180
+ Subject extends AnyPgTable,
181
+ External extends AnyPgTable,
182
+ NativeId,
183
+ >(
184
+ database: Database,
185
+ mapping: ExternalIdentityTables<Subject, External, NativeId>,
186
+ ) => {
187
+ const db = database as RuntimeDatabase;
188
+ const externalTable = mapping.externalIdentity.table;
189
+
190
+ const externalProviderColumn = column(
191
+ externalTable,
192
+ mapping.externalIdentity.provider,
193
+ ) as PgColumn;
194
+
195
+ const externalIssuerColumn = column(externalTable, mapping.externalIdentity.issuer) as PgColumn;
196
+ const externalSubjectColumn = column(externalTable, mapping.externalIdentity.subject) as PgColumn;
197
+
198
+ const externalSubjectIdColumn = column(
199
+ externalTable,
200
+ mapping.externalIdentity.subjectId,
201
+ ) as PgColumn;
202
+
203
+ const subjectIdColumn = column(mapping.subject.table, mapping.subject.id) as PgColumn;
204
+ const subjectStatusColumn = column(mapping.subject.table, mapping.subject.status) as PgColumn;
205
+
206
+ const bind = Effect.fn("DrizzlePgIdentity.bindExternalIdentity")(function* (
207
+ subjectId: SubjectId,
208
+ identity: ExternalIdentity,
209
+ ) {
210
+ const nativeId = yield* mapping.subjectId.toNative(subjectId);
211
+
212
+ const decision = yield* db
213
+ .transaction((tx) =>
214
+ Effect.gen(function* () {
215
+ const subjects = yield* tx
216
+ .select({ status: subjectStatusColumn })
217
+ .from(mapping.subject.table as any)
218
+ .where(eq(subjectIdColumn, nativeId))
219
+ .for("update")
220
+ .limit(1);
221
+
222
+ if (subjects[0] === undefined || !mapping.subject.isActiveStatus(subjects[0].status))
223
+ return { _tag: "conflict" } as const;
224
+
225
+ const links = yield* tx
226
+ .select({ subjectId: externalSubjectIdColumn })
227
+ .from(externalTable as any)
228
+ .where(
229
+ and(
230
+ eq(externalProviderColumn, identity.provider),
231
+ eq(externalIssuerColumn, identity.issuer),
232
+ eq(externalSubjectColumn, identity.subject),
233
+ ),
234
+ )
235
+ .for("update")
236
+ .limit(1);
237
+
238
+ if (links[0] !== undefined)
239
+ return mapping.subjectId.equals(links[0].subjectId as NativeId, nativeId)
240
+ ? ({ _tag: "bound" } as const)
241
+ : ({ _tag: "conflict" } as const);
242
+ yield* tx
243
+ .insert(externalTable as any)
244
+ .values(mapping.externalIdentity.encodeInsert(identity, nativeId) as any);
245
+
246
+ return { _tag: "bound" } as const;
247
+ }),
248
+ )
249
+ .pipe(
250
+ Effect.catchCause((cause) => {
251
+ if (
252
+ cause.reasons.length === 0 ||
253
+ !cause.reasons.every(
254
+ (reason) =>
255
+ Cause.isFailReason(reason) &&
256
+ isMappedConstraintConflict(mapping.isConstraintConflict, reason.error),
257
+ )
258
+ )
259
+ return Effect.failCause(cause);
260
+
261
+ return db
262
+ .select({ subjectId: externalSubjectIdColumn })
263
+ .from(externalTable as any)
264
+ .where(
265
+ and(
266
+ eq(externalProviderColumn, identity.provider),
267
+ eq(externalIssuerColumn, identity.issuer),
268
+ eq(externalSubjectColumn, identity.subject),
269
+ ),
270
+ )
271
+ .limit(1)
272
+ .pipe(
273
+ Effect.map((rows) =>
274
+ rows[0] !== undefined &&
275
+ mapping.subjectId.equals(rows[0].subjectId as NativeId, nativeId)
276
+ ? ({ _tag: "bound" } as const)
277
+ : ({ _tag: "conflict" } as const),
278
+ ),
279
+ );
280
+ }),
281
+ );
282
+
283
+ if (decision._tag === "conflict") return yield* IdentityConflict.make();
284
+ }, mapIdentityFailure);
285
+
286
+ return { externalIdentityMutation: ExternalIdentityMutation.of({ bind }) } as const;
287
+ };
288
+
289
+ export const makePgIdentityServices = <
290
+ Subject extends AnyPgTable,
291
+ Identifier extends AnyPgTable,
292
+ External extends AnyPgTable,
293
+ Request extends AnyPgTable,
294
+ NativeId,
295
+ >(
296
+ database: Database,
297
+ mapping: IdentityTables<Subject, Identifier, External, Request, NativeId>,
298
+ ) => ({
299
+ ...makePgSubjectProvisioningServices(database, mapping),
300
+ ...makePgExternalIdentityServices(database, mapping),
301
+ });
@@ -5,7 +5,7 @@ import type { EffectPgDatabase as PgliteDatabase } from "drizzle-orm/effect-pgli
5
5
  import type { EffectPgDatabase as PostgresDatabase } from "drizzle-orm/effect-postgres";
6
6
  import type { AnyPgTable } from "drizzle-orm/pg-core";
7
7
  import { Effect, Context } from "effect";
8
- import type { SqlError } from "effect/unstable/sql/SqlError";
8
+ import type { SqlError } from "effect/sql/SqlError";
9
9
 
10
10
  import type {
11
11
  PasswordPreparedPersistenceMapping,
@@ -6,7 +6,7 @@ import type { EffectPgDatabase as PgliteDatabase } from "drizzle-orm/effect-pgli
6
6
  import type { EffectPgDatabase as PostgresDatabase } from "drizzle-orm/effect-postgres";
7
7
  import type { AnyPgTable } from "drizzle-orm/pg-core";
8
8
  import { Effect, Context } from "effect";
9
- import type { SqlError } from "effect/unstable/sql/SqlError";
9
+ import type { SqlError } from "effect/sql/SqlError";
10
10
 
11
11
  import type { PasswordPersistenceMapping, PasswordRegistrationMapping } from "./password-model";
12
12
  import type { PasswordRegistrationAuthority } from "./password-registration";
@@ -5,7 +5,7 @@ import type { EffectPgDatabase as PgliteDatabase } from "drizzle-orm/effect-pgli
5
5
  import type { EffectPgDatabase as PostgresDatabase } from "drizzle-orm/effect-postgres";
6
6
  import type { AnyPgTable } from "drizzle-orm/pg-core";
7
7
  import { Effect, Context } from "effect";
8
- import type { SqlError } from "effect/unstable/sql/SqlError";
8
+ import type { SqlError } from "effect/sql/SqlError";
9
9
 
10
10
  import type { ProofPersistenceMapping } from "./proof-model";
11
11
  import type { ProofSqlQuery } from "./proof-sql";
@@ -13,7 +13,7 @@ import type { EffectPgDatabase as PgliteDatabase } from "drizzle-orm/effect-pgli
13
13
  import type { EffectPgDatabase as PostgresDatabase } from "drizzle-orm/effect-postgres";
14
14
  import type { AnyPgTable } from "drizzle-orm/pg-core";
15
15
  import { Effect, Context } from "effect";
16
- import type { SqlError } from "effect/unstable/sql/SqlError";
16
+ import type { SqlError } from "effect/sql/SqlError";
17
17
 
18
18
  import type {
19
19
  AuthenticationAuthorityMapping,
@@ -16,7 +16,7 @@ import { ProofBinding } from "@yielded/auth/Proofs";
16
16
  import { sql, type Table } from "drizzle-orm";
17
17
  /* oxlint-disable no-explicit-any -- shared native implementation; driver entrypoints retain exact database and table types. */
18
18
  import { Context, Effect, Schema } from "effect";
19
- import type { Statement } from "effect/unstable/sql/Statement";
19
+ import type { Statement } from "effect/sql/Statement";
20
20
 
21
21
  import { CurrentD1PlanningDatabase } from "./d1-planning";
22
22
  import { compileD1ProofCompletionPlan } from "./d1-proofs";
@@ -7,7 +7,7 @@ import {
7
7
  import { ProofUnavailable, ProofPersistence } from "@yielded/auth/Proofs";
8
8
  /* oxlint-disable no-explicit-any -- public driver entrypoints restore concrete Drizzle types. */
9
9
  import { Effect, Layer } from "effect";
10
- import type * as SqlError from "effect/unstable/sql/SqlError";
10
+ import type * as SqlError from "effect/sql/SqlError";
11
11
 
12
12
  import {
13
13
  makeSqlProofPersistence,
@@ -15,7 +15,7 @@ import {
15
15
  } from "@yielded/auth/Sessions";
16
16
  /* oxlint-disable no-explicit-any -- target entrypoints restore each concrete Drizzle database/table type. */
17
17
  import { type Context, Effect, Layer } from "effect";
18
- import type * as SqlError from "effect/unstable/sql/SqlError";
18
+ import type * as SqlError from "effect/sql/SqlError";
19
19
 
20
20
  import {
21
21
  makeSqlAuthenticationAuthority,
@@ -9,7 +9,7 @@ import type { EffectSQLiteNodeDatabase as NodeDatabase } from "drizzle-orm/effec
9
9
  import type { EffectSQLiteWasmDatabase as WasmDatabase } from "drizzle-orm/effect-sqlite-wasm";
10
10
  import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
11
11
  import { Effect, Context } from "effect";
12
- import type { SqlError } from "effect/unstable/sql/SqlError";
12
+ import type { SqlError } from "effect/sql/SqlError";
13
13
 
14
14
  import type {
15
15
  EmailAddressMapping,
@@ -2,7 +2,7 @@ import type { LifecycleHooks, HookConfigurationError } from "@yielded/auth/Hooks
2
2
  import type { PasswordUnavailable, PasswordPreparedPersistence } from "@yielded/auth/Password";
3
3
  import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
4
4
  import { Effect, Context } from "effect";
5
- import type { SqlError } from "effect/unstable/sql/SqlError";
5
+ import type { SqlError } from "effect/sql/SqlError";
6
6
 
7
7
  import type {
8
8
  PasswordPreparedPersistenceMapping,
@@ -9,7 +9,7 @@ import type { EffectSQLiteNodeDatabase as NodeDatabase } from "drizzle-orm/effec
9
9
  import type { EffectSQLiteWasmDatabase as WasmDatabase } from "drizzle-orm/effect-sqlite-wasm";
10
10
  import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
11
11
  import { Effect, Context } from "effect";
12
- import type { SqlError } from "effect/unstable/sql/SqlError";
12
+ import type { SqlError } from "effect/sql/SqlError";
13
13
 
14
14
  import type { PasswordPersistenceMapping, PasswordRegistrationMapping } from "./password-model";
15
15
  import type { PasswordRegistrationAuthority } from "./password-registration";
@@ -8,7 +8,7 @@ import type { EffectSQLiteNodeDatabase } from "drizzle-orm/effect-sqlite-node";
8
8
  import type { EffectSQLiteWasmDatabase } from "drizzle-orm/effect-sqlite-wasm";
9
9
  import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
10
10
  import { Effect, Context } from "effect";
11
- import type { SqlError } from "effect/unstable/sql/SqlError";
11
+ import type { SqlError } from "effect/sql/SqlError";
12
12
 
13
13
  import type { ProofPersistenceMapping } from "./proof-model";
14
14
  import {
@@ -16,7 +16,7 @@ import type { EffectSQLiteNodeDatabase } from "drizzle-orm/effect-sqlite-node";
16
16
  import type { EffectSQLiteWasmDatabase } from "drizzle-orm/effect-sqlite-wasm";
17
17
  import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
18
18
  import { Context, Effect } from "effect";
19
- import type { SqlError } from "effect/unstable/sql/SqlError";
19
+ import type { SqlError } from "effect/sql/SqlError";
20
20
 
21
21
  import type {
22
22
  AuthenticationAuthorityMapping,
@@ -1,6 +1,6 @@
1
1
  import { requireStandalone } from "@yielded/auth-persistence/Adapter";
2
2
  import { Context, Effect, Predicate } from "effect";
3
- import type * as SqlClient from "effect/unstable/sql/SqlClient";
3
+ import type * as SqlClient from "effect/sql/SqlClient";
4
4
 
5
5
  type TransactionService = Context.Key<
6
6
  SqlClient.TransactionConnection,
@@ -10,7 +10,7 @@ import {
10
10
  import type { Table } from "drizzle-orm";
11
11
  /* oxlint-disable no-explicit-any -- shared native implementation; driver entrypoints retain exact database and table types. */
12
12
  import { Context, Effect, Schema } from "effect";
13
- import type { Statement } from "effect/unstable/sql/Statement";
13
+ import type { Statement } from "effect/sql/Statement";
14
14
 
15
15
  import type { PersistenceMappingError } from "./model";
16
16
  import type { SuppliedService } from "./SuppliedService";
@@ -1,7 +1,7 @@
1
1
  /* oxlint-disable no-explicit-any -- this private acquisition preserves the installed native transaction; public wrappers retain its exact type. */
2
2
  import { EffectMysql2Transaction } from "drizzle-orm/effect-mysql2";
3
3
  import { Effect } from "effect";
4
- import { makeWithTransaction, type SqlClient } from "effect/unstable/sql/SqlClient";
4
+ import { makeWithTransaction, type SqlClient } from "effect/sql/SqlClient";
5
5
 
6
6
  /** Locking discovery and later SQL policy predicates must see the same current
7
7
  * authority. MySQL's default repeatable-read snapshot is unsuitable after a
package/src/index.ts CHANGED
@@ -105,15 +105,7 @@ export {
105
105
  } from "./drizzle/proof-model";
106
106
 
107
107
  export {
108
- type AuthStoreTables,
109
- type AuthTables,
110
- type ChallengeColumns,
111
- type ChallengeConsumeDecision,
112
- type ChallengeInput,
113
- type ChallengeRecord,
114
- type ChallengeTableMapping,
115
108
  type CommitMode,
116
- type ConsumeDecision,
117
109
  type D1ExternalIdentityMapping,
118
110
  type D1GeneratedIdentityMapping,
119
111
  type D1SubjectProvisioningMapping,
@@ -121,31 +113,17 @@ export {
121
113
  type ExternalIdentityTables,
122
114
  type IdentityTables,
123
115
  type InstantCodec,
124
- type OAuthStateColumns,
125
- type OAuthStateTableMapping,
126
- type OAuthStateTables,
127
- type RegistrationColumns,
128
- type RegistrationRecord,
129
- type RegistrationTableMapping,
130
- type RequiredAuthConstraints,
131
- type RequiredAuthStoreConstraints,
132
116
  type RequiredExternalIdentityConstraints,
133
117
  type RequiredIdentityConstraints,
134
- type RequiredOAuthStateConstraints,
135
118
  type RequiredSubjectProvisioningConstraints,
136
119
  type SubjectIdCodec,
137
120
  type SubjectProvisioningTables,
138
- authServicesLayer,
139
121
  column,
140
122
  identityServicesLayer,
141
123
  isMappedConstraintConflict,
142
124
  provisioningFingerprint,
143
- requireStandaloneConsume,
144
- requiredAuthConstraints,
145
- requiredAuthStoreConstraints,
146
125
  requiredExternalIdentityConstraints,
147
126
  requiredIdentityConstraints,
148
- requiredOAuthStateConstraints,
149
127
  requiredSubjectProvisioningConstraints,
150
128
  updateValues,
151
129
  } from "./drizzle/model";
@@ -334,7 +312,6 @@ export {
334
312
  export { passwordPersistenceLayer, passwordRegistrationLayer } from "./drizzle/password-target";
335
313
  export { passwordPreparedPersistenceLayer } from "./drizzle/password-prepared-target";
336
314
  export { proofPersistenceLayer } from "./drizzle/proof-target";
337
- export { AuthStoreDecisions } from "@yielded/auth/Persistence";
338
315
 
339
316
  export {
340
317
  type TotpMapping,
@@ -346,7 +323,6 @@ export {
346
323
  } from "./drizzle/totp-model";
347
324
 
348
325
  export { encodeTotpRecord, decodeTotpRecord } from "./drizzle/totp-state";
349
- export { OAuthStateDecisions } from "@yielded/auth/OAuth";
350
326
 
351
327
  export {
352
328
  type PasskeyCredentialInsert,
@@ -15,7 +15,7 @@ import {
15
15
  type PgTable,
16
16
  } from "drizzle-orm/pg-core";
17
17
  import type { Effect } from "effect";
18
- import type { SqlClient } from "effect/unstable/sql/SqlClient";
18
+ import type { SqlClient } from "effect/sql/SqlClient";
19
19
 
20
20
  import { drizzleQueryOperations } from "../drizzle/query-operations";
21
21
 
@@ -14,7 +14,7 @@ import {
14
14
  type SQLiteTable,
15
15
  } from "drizzle-orm/sqlite-core";
16
16
  import type { Effect } from "effect";
17
- import type { SqlClient } from "effect/unstable/sql/SqlClient";
17
+ import type { SqlClient } from "effect/sql/SqlClient";
18
18
 
19
19
  import { drizzleQueryOperations } from "../drizzle/query-operations";
20
20
 
@@ -1,69 +0,0 @@
1
- import { AuthStoreTables, AuthTables, ChallengeConsumeDecision, ConsumeDecision, OAuthStateTables } from "./model.mjs";
2
- import { SuppliedService } from "./SuppliedService.mjs";
3
- import { EmailOtpRejected } from "@yielded/auth/AuthStore";
4
- import { AuthStoreError, InvalidRegistration } from "@yielded/auth/Errors";
5
- import { InvalidOAuthState, OAuthState, OAuthStateDecisions } from "@yielded/auth/OAuth";
6
- import { ConsumeChallenge, ConsumeRegistration, NewChallenge, NewRegistration, TokenDigest, VerifiedEmail } from "@yielded/auth/Schema";
7
- import { AnyRelations } from "drizzle-orm";
8
- import { Effect } from "effect";
9
- import { AuthStoreDecisions } from "@yielded/auth/Persistence";
10
- import { EffectMysql2Database } from "drizzle-orm/effect-mysql2";
11
- import { SqlError } from "effect/unstable/sql/SqlError";
12
- import { AnyMySqlTable } from "drizzle-orm/mysql-core";
13
- //#region src/drizzle/mysql.d.ts
14
- type Database = EffectMysql2Database<AnyRelations>;
15
- type TransactionOf<D extends Database> = Parameters<Parameters<D["transaction"]>[0]>[0];
16
- /** MySQL/InnoDB implementation. Multi-step decisions stay values until commit. */
17
- export declare const makeMysqlAuthStoreServices: <C extends AnyMySqlTable, R extends AnyMySqlTable>(database: Database, mapping: AuthStoreTables<C, R>) => {
18
- readonly authStore: {
19
- readonly issueChallenge: (input: NewChallenge) => Effect.Effect<void, import("@yielded/auth/AuthStore").IssueChallengeError>;
20
- readonly consumeChallenge: (input: ConsumeChallenge) => Effect.Effect<VerifiedEmail, EmailOtpRejected | AuthStoreError>;
21
- readonly issueRegistration: (input: NewRegistration) => Effect.Effect<void, AuthStoreError>;
22
- readonly inspectRegistration: (tokenDigest: TokenDigest) => Effect.Effect<import("@yielded/auth/Schema").PendingRegistration, InvalidRegistration | AuthStoreError>;
23
- readonly consumeRegistration: (input: ConsumeRegistration) => Effect.Effect<void, InvalidRegistration | AuthStoreError>;
24
- };
25
- readonly decisions: {
26
- readonly consumeChallenge: (input: ConsumeChallenge) => Effect.Effect<ChallengeConsumeDecision, AuthStoreError, never>;
27
- readonly consumeRegistration: (input: ConsumeRegistration) => Effect.Effect<ConsumeDecision<void>, AuthStoreError, never>;
28
- };
29
- };
30
- export declare const makeMysqlOAuthStateServices: <S extends AnyMySqlTable>(database: Database, mapping: OAuthStateTables<S>) => {
31
- readonly oauthStateStore: {
32
- readonly issue: (state: OAuthState) => Effect.Effect<void, AuthStoreError>;
33
- readonly consume: (stateDigest: TokenDigest) => Effect.Effect<OAuthState, InvalidOAuthState | AuthStoreError>;
34
- };
35
- readonly decisions: {
36
- readonly consumeOAuthState: (digest: string & import("effect/Brand").Brand<"effect-auth/TokenDigest">) => Effect.Effect<ConsumeDecision<OAuthState>, AuthStoreError, never>;
37
- };
38
- };
39
- /**
40
- * Owns the native outer commit. Keep rejection as a decision value inside the
41
- * owner; interruption before commit rolls back. This adapter never retries a
42
- * deadlock inside the caller's transaction.
43
- */
44
- export declare function coordinateMysqlAuthStoreTransaction<D extends Database, C extends AnyMySqlTable, R extends AnyMySqlTable, A, E, Requirements, DatabaseError, DatabaseRequirements>(acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>, options: {
45
- readonly mapping: AuthStoreTables<C, R>;
46
- readonly transaction?: never;
47
- }, body: Effect.Effect<A, E, Requirements>): Effect.Effect<A, E | DatabaseError | SqlError, Exclude<Requirements, AuthStoreDecisions> | DatabaseRequirements>;
48
- export declare function coordinateMysqlAuthStoreTransaction<D extends Database, C extends AnyMySqlTable, R extends AnyMySqlTable, A, E, Requirements, DatabaseError, DatabaseRequirements, TxId, TxShape>(acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>, options: {
49
- readonly mapping: AuthStoreTables<C, R>;
50
- readonly transaction: SuppliedService<TxId, NoInfer<TransactionOf<D>>, TxShape>;
51
- }, body: Effect.Effect<A, E, Requirements>): Effect.Effect<A, E | DatabaseError | SqlError, Exclude<Requirements, AuthStoreDecisions | TxId> | DatabaseRequirements>;
52
- export declare function coordinateMysqlOAuthStateTransaction<D extends Database, S extends AnyMySqlTable, A, E, Requirements, DatabaseError, DatabaseRequirements>(acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>, options: {
53
- readonly mapping: OAuthStateTables<S>;
54
- readonly transaction?: never;
55
- }, body: Effect.Effect<A, E, Requirements>): Effect.Effect<A, E | DatabaseError | SqlError, Exclude<Requirements, OAuthStateDecisions> | DatabaseRequirements>;
56
- export declare function coordinateMysqlOAuthStateTransaction<D extends Database, S extends AnyMySqlTable, A, E, Requirements, DatabaseError, DatabaseRequirements, TxId, TxShape>(acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>, options: {
57
- readonly mapping: OAuthStateTables<S>;
58
- readonly transaction: SuppliedService<TxId, NoInfer<TransactionOf<D>>, TxShape>;
59
- }, body: Effect.Effect<A, E, Requirements>): Effect.Effect<A, E | DatabaseError | SqlError, Exclude<Requirements, OAuthStateDecisions | TxId> | DatabaseRequirements>;
60
- export declare function coordinateMysqlAuthTransaction<D extends Database, C extends AnyMySqlTable, R extends AnyMySqlTable, S extends AnyMySqlTable, A, E, Requirements, DatabaseError, DatabaseRequirements>(acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>, options: {
61
- readonly mapping: AuthTables<C, R, S>;
62
- readonly transaction?: never;
63
- }, body: Effect.Effect<A, E, Requirements>): Effect.Effect<A, E | DatabaseError | SqlError, Exclude<Requirements, AuthStoreDecisions | OAuthStateDecisions> | DatabaseRequirements>;
64
- export declare function coordinateMysqlAuthTransaction<D extends Database, C extends AnyMySqlTable, R extends AnyMySqlTable, S extends AnyMySqlTable, A, E, Requirements, DatabaseError, DatabaseRequirements, TxId, TxShape>(acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>, options: {
65
- readonly mapping: AuthTables<C, R, S>;
66
- readonly transaction: SuppliedService<TxId, NoInfer<TransactionOf<D>>, TxShape>;
67
- }, body: Effect.Effect<A, E, Requirements>): Effect.Effect<A, E | DatabaseError | SqlError, Exclude<Requirements, AuthStoreDecisions | OAuthStateDecisions | TxId> | DatabaseRequirements>;
68
- //#endregion
69
- //# sourceMappingURL=mysql.d.mts.map