@yielded/auth-persistence-drizzle 0.1.0-beta.14 → 0.1.0-beta.16

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 (190) hide show
  1. package/dist/D1.d.mts +123 -9
  2. package/dist/Libsql.d.mts +141 -27
  3. package/dist/Mysql2.d.mts +124 -10
  4. package/dist/Pglite.d.mts +124 -10
  5. package/dist/Postgres.d.mts +129 -15
  6. package/dist/SqliteBun.d.mts +135 -21
  7. package/dist/SqliteDo.d.mts +135 -21
  8. package/dist/SqliteNode.d.mts +135 -21
  9. package/dist/SqliteWasm.d.mts +135 -21
  10. package/dist/drizzle/d1-emails.d.mts +3 -3
  11. package/dist/drizzle/d1-emails.mjs +56 -11
  12. package/dist/drizzle/d1-emails.mjs.map +1 -1
  13. package/dist/drizzle/d1-identity.mjs +9 -2
  14. package/dist/drizzle/d1-identity.mjs.map +1 -1
  15. package/dist/drizzle/d1-oauth.d.mts +6 -6
  16. package/dist/drizzle/d1-oauth.mjs +8 -8
  17. package/dist/drizzle/d1-oauth.mjs.map +1 -1
  18. package/dist/drizzle/d1-passkey.d.mts +2 -1
  19. package/dist/drizzle/d1-passkey.mjs +10 -8
  20. package/dist/drizzle/d1-passkey.mjs.map +1 -1
  21. package/dist/drizzle/d1-password-prepared.d.mts +1 -1
  22. package/dist/drizzle/d1-password-prepared.mjs +16 -5
  23. package/dist/drizzle/d1-password-prepared.mjs.map +1 -1
  24. package/dist/drizzle/d1-passwords.d.mts +2 -2
  25. package/dist/drizzle/d1-passwords.mjs +19 -6
  26. package/dist/drizzle/d1-passwords.mjs.map +1 -1
  27. package/dist/drizzle/d1-phone.d.mts +2 -1
  28. package/dist/drizzle/d1-phone.mjs +4 -2
  29. package/dist/drizzle/d1-phone.mjs.map +1 -1
  30. package/dist/drizzle/d1-proofs.d.mts +1 -1
  31. package/dist/drizzle/d1-proofs.mjs +18 -7
  32. package/dist/drizzle/d1-proofs.mjs.map +1 -1
  33. package/dist/drizzle/d1-sessions.d.mts +6 -5
  34. package/dist/drizzle/d1-sessions.mjs +29 -19
  35. package/dist/drizzle/d1-sessions.mjs.map +1 -1
  36. package/dist/drizzle/d1-totp.d.mts +2 -1
  37. package/dist/drizzle/d1-totp.mjs +4 -2
  38. package/dist/drizzle/d1-totp.mjs.map +1 -1
  39. package/dist/drizzle/email-registration.d.mts +5 -1
  40. package/dist/drizzle/email-registration.mjs +48 -4
  41. package/dist/drizzle/email-registration.mjs.map +1 -1
  42. package/dist/drizzle/email-target.d.mts +1 -0
  43. package/dist/drizzle/email-target.mjs +9 -0
  44. package/dist/drizzle/email-target.mjs.map +1 -1
  45. package/dist/drizzle/mysql-emails.d.mts +4 -4
  46. package/dist/drizzle/mysql-emails.mjs +8 -7
  47. package/dist/drizzle/mysql-emails.mjs.map +1 -1
  48. package/dist/drizzle/mysql-identity.mjs +11 -4
  49. package/dist/drizzle/mysql-identity.mjs.map +1 -1
  50. package/dist/drizzle/mysql-password-prepared.d.mts +2 -2
  51. package/dist/drizzle/mysql-password-prepared.mjs +4 -3
  52. package/dist/drizzle/mysql-password-prepared.mjs.map +1 -1
  53. package/dist/drizzle/mysql-passwords.d.mts +3 -3
  54. package/dist/drizzle/mysql-passwords.mjs +7 -6
  55. package/dist/drizzle/mysql-passwords.mjs.map +1 -1
  56. package/dist/drizzle/mysql-proofs.d.mts +2 -2
  57. package/dist/drizzle/mysql-proofs.mjs +4 -3
  58. package/dist/drizzle/mysql-proofs.mjs.map +1 -1
  59. package/dist/drizzle/mysql-sessions.d.mts +6 -6
  60. package/dist/drizzle/mysql-sessions.mjs +16 -15
  61. package/dist/drizzle/mysql-sessions.mjs.map +1 -1
  62. package/dist/drizzle/native-database.mjs +1 -1
  63. package/dist/drizzle/native-database.mjs.map +1 -1
  64. package/dist/drizzle/oauth-connected-drivers.mjs +10 -8
  65. package/dist/drizzle/oauth-connected-drivers.mjs.map +1 -1
  66. package/dist/drizzle/oauth-connected-flow.mjs +62 -13
  67. package/dist/drizzle/oauth-connected-flow.mjs.map +1 -1
  68. package/dist/drizzle/oauth-connected-input.mjs +6 -0
  69. package/dist/drizzle/oauth-connected-input.mjs.map +1 -1
  70. package/dist/drizzle/oauth-connected-maintenance.mjs +3 -3
  71. package/dist/drizzle/oauth-connected-maintenance.mjs.map +1 -1
  72. package/dist/drizzle/oauth-connected-target.d.mts +116 -2
  73. package/dist/drizzle/oauth-connected-target.mjs +5 -3
  74. package/dist/drizzle/oauth-connected-target.mjs.map +1 -1
  75. package/dist/drizzle/oauth-drivers.mjs +8 -8
  76. package/dist/drizzle/oauth-drivers.mjs.map +1 -1
  77. package/dist/drizzle/oauth-target.d.mts +3 -3
  78. package/dist/drizzle/oauth-target.mjs +8 -6
  79. package/dist/drizzle/oauth-target.mjs.map +1 -1
  80. package/dist/drizzle/passkey-drivers.mjs +4 -4
  81. package/dist/drizzle/passkey-drivers.mjs.map +1 -1
  82. package/dist/drizzle/passkey-write-drivers.mjs +4 -4
  83. package/dist/drizzle/passkey-write-drivers.mjs.map +1 -1
  84. package/dist/drizzle/passkey-write-target.d.mts +1 -1
  85. package/dist/drizzle/password-prepared-sql.mjs +5 -0
  86. package/dist/drizzle/password-prepared-sql.mjs.map +1 -1
  87. package/dist/drizzle/password-prepared-target.d.mts +2 -1
  88. package/dist/drizzle/password-prepared-target.mjs +5 -0
  89. package/dist/drizzle/password-prepared-target.mjs.map +1 -1
  90. package/dist/drizzle/password-registration.d.mts +1 -1
  91. package/dist/drizzle/password-registration.mjs +2 -0
  92. package/dist/drizzle/password-registration.mjs.map +1 -1
  93. package/dist/drizzle/password-target.d.mts +1 -0
  94. package/dist/drizzle/password-target.mjs +6 -0
  95. package/dist/drizzle/password-target.mjs.map +1 -1
  96. package/dist/drizzle/pg-emails.d.mts +4 -4
  97. package/dist/drizzle/pg-emails.mjs +8 -7
  98. package/dist/drizzle/pg-emails.mjs.map +1 -1
  99. package/dist/drizzle/pg-identity.mjs +12 -5
  100. package/dist/drizzle/pg-identity.mjs.map +1 -1
  101. package/dist/drizzle/pg-password-prepared.d.mts +2 -2
  102. package/dist/drizzle/pg-password-prepared.mjs +4 -3
  103. package/dist/drizzle/pg-password-prepared.mjs.map +1 -1
  104. package/dist/drizzle/pg-passwords.d.mts +3 -3
  105. package/dist/drizzle/pg-passwords.mjs +7 -6
  106. package/dist/drizzle/pg-passwords.mjs.map +1 -1
  107. package/dist/drizzle/pg-proofs.d.mts +2 -2
  108. package/dist/drizzle/pg-proofs.mjs +4 -3
  109. package/dist/drizzle/pg-proofs.mjs.map +1 -1
  110. package/dist/drizzle/pg-sessions.d.mts +6 -6
  111. package/dist/drizzle/pg-sessions.mjs +16 -15
  112. package/dist/drizzle/pg-sessions.mjs.map +1 -1
  113. package/dist/drizzle/phone-target.mjs +11 -2
  114. package/dist/drizzle/phone-target.mjs.map +1 -1
  115. package/dist/drizzle/proof-target.d.mts +1 -0
  116. package/dist/drizzle/proof-target.mjs +2 -0
  117. package/dist/drizzle/proof-target.mjs.map +1 -1
  118. package/dist/drizzle/query-operations.mjs +2 -0
  119. package/dist/drizzle/query-operations.mjs.map +1 -1
  120. package/dist/drizzle/session-target.d.mts +1 -0
  121. package/dist/drizzle/session-target.mjs +9 -6
  122. package/dist/drizzle/session-target.mjs.map +1 -1
  123. package/dist/drizzle/sqlite-emails.mjs +8 -7
  124. package/dist/drizzle/sqlite-emails.mjs.map +1 -1
  125. package/dist/drizzle/sqlite-identity.mjs +7 -2
  126. package/dist/drizzle/sqlite-identity.mjs.map +1 -1
  127. package/dist/drizzle/sqlite-password-prepared.mjs +4 -3
  128. package/dist/drizzle/sqlite-password-prepared.mjs.map +1 -1
  129. package/dist/drizzle/sqlite-passwords.mjs +7 -6
  130. package/dist/drizzle/sqlite-passwords.mjs.map +1 -1
  131. package/dist/drizzle/sqlite-proofs.mjs +4 -3
  132. package/dist/drizzle/sqlite-proofs.mjs.map +1 -1
  133. package/dist/drizzle/sqlite-sessions.mjs +16 -15
  134. package/dist/drizzle/sqlite-sessions.mjs.map +1 -1
  135. package/dist/drizzle/storage-validation.mjs +234 -0
  136. package/dist/drizzle/storage-validation.mjs.map +1 -0
  137. package/dist/drizzle/totp-target.mjs +5 -2
  138. package/dist/drizzle/totp-target.mjs.map +1 -1
  139. package/dist/drizzle/transaction-mysql.mjs +1 -1
  140. package/package.json +1 -1
  141. package/src/drizzle/d1-emails.ts +158 -40
  142. package/src/drizzle/d1-identity.ts +13 -0
  143. package/src/drizzle/d1-oauth.ts +8 -8
  144. package/src/drizzle/d1-passkey.ts +10 -8
  145. package/src/drizzle/d1-password-prepared.ts +14 -2
  146. package/src/drizzle/d1-passwords.ts +22 -4
  147. package/src/drizzle/d1-phone.ts +4 -2
  148. package/src/drizzle/d1-proofs.ts +55 -3
  149. package/src/drizzle/d1-sessions.ts +67 -18
  150. package/src/drizzle/d1-totp.ts +4 -2
  151. package/src/drizzle/email-registration.ts +160 -12
  152. package/src/drizzle/email-target.ts +22 -4
  153. package/src/drizzle/mysql-emails.ts +17 -7
  154. package/src/drizzle/mysql-identity.ts +13 -0
  155. package/src/drizzle/mysql-password-prepared.ts +7 -3
  156. package/src/drizzle/mysql-passwords.ts +13 -6
  157. package/src/drizzle/mysql-proofs.ts +7 -3
  158. package/src/drizzle/mysql-sessions.ts +31 -15
  159. package/src/drizzle/native-database.ts +5 -4
  160. package/src/drizzle/oauth-connected-drivers.ts +10 -8
  161. package/src/drizzle/oauth-connected-flow.ts +98 -10
  162. package/src/drizzle/oauth-connected-input.ts +7 -0
  163. package/src/drizzle/oauth-connected-maintenance.ts +3 -2
  164. package/src/drizzle/oauth-connected-target.ts +4 -2
  165. package/src/drizzle/oauth-drivers.ts +8 -8
  166. package/src/drizzle/oauth-target.ts +13 -7
  167. package/src/drizzle/passkey-drivers.ts +4 -4
  168. package/src/drizzle/passkey-write-drivers.ts +8 -5
  169. package/src/drizzle/password-prepared-sql.ts +13 -1
  170. package/src/drizzle/password-prepared-target.ts +7 -1
  171. package/src/drizzle/password-registration.ts +10 -3
  172. package/src/drizzle/password-target.ts +22 -4
  173. package/src/drizzle/pg-emails.ts +17 -7
  174. package/src/drizzle/pg-identity.ts +13 -0
  175. package/src/drizzle/pg-password-prepared.ts +7 -3
  176. package/src/drizzle/pg-passwords.ts +13 -6
  177. package/src/drizzle/pg-proofs.ts +7 -3
  178. package/src/drizzle/pg-sessions.ts +31 -15
  179. package/src/drizzle/phone-target.ts +30 -5
  180. package/src/drizzle/proof-target.ts +10 -2
  181. package/src/drizzle/query-operations.ts +2 -0
  182. package/src/drizzle/session-target.ts +20 -3
  183. package/src/drizzle/sqlite-emails.ts +17 -7
  184. package/src/drizzle/sqlite-identity.ts +5 -0
  185. package/src/drizzle/sqlite-password-prepared.ts +7 -3
  186. package/src/drizzle/sqlite-passwords.ts +13 -6
  187. package/src/drizzle/sqlite-proofs.ts +7 -3
  188. package/src/drizzle/sqlite-sessions.ts +31 -15
  189. package/src/drizzle/storage-validation.ts +285 -0
  190. package/src/drizzle/totp-target.ts +16 -4
@@ -0,0 +1,285 @@
1
+ import {
2
+ NativeDatabase,
3
+ PersistenceMappingError,
4
+ validateStorage,
5
+ } from "@yielded/auth-persistence/Adapter";
6
+ import { getTableColumns, getTableName, is, Table } from "drizzle-orm";
7
+ import { getTableConfig as getMysqlTableConfig, MySqlTable } from "drizzle-orm/mysql-core";
8
+ import { getTableConfig as getPgTableConfig, PgTable } from "drizzle-orm/pg-core";
9
+ import { Effect, Predicate, Schema } from "effect";
10
+ import { SqlClient } from "effect/sql/SqlClient";
11
+
12
+ // These are mapping contracts, not a parser for arbitrary constraint declarations.
13
+ // Role aliases are explicit because their documentation names differ from mapping fields.
14
+ const uniquePlans: Readonly<Record<string, readonly [string, ReadonlyArray<string>]>> = {
15
+ "unique(abuseEvent.moduleId,abuseEvent.action,abuseEvent.scopeKind,abuseEvent.scopeKey,abuseEvent.commandId)":
16
+ ["abuseEvent", ["moduleId", "action", "scopeKind", "scopeKey", "commandId"]],
17
+ "unique(admission.moduleId)": ["admission", ["moduleId"]],
18
+ "unique(attempt.moduleId,attempt.attemptId)": ["attempt", ["moduleId", "attemptId"]],
19
+ "unique(authority.subjectId,authority.credentialId)": [
20
+ "authority",
21
+ ["subjectId", "credentialId"],
22
+ ],
23
+ "unique(authorityCredential.subjectId,authorityCredential.credentialId)": [
24
+ "authorityCredential",
25
+ ["subjectId", "credentialId"],
26
+ ],
27
+ "unique(charge.moduleId,charge.action,charge.scopeKind,charge.scopeKey,charge.attemptId)": [
28
+ "charge",
29
+ ["moduleId", "action", "scopeKind", "scopeKey", "attemptId"],
30
+ ],
31
+ "unique(command.moduleId,command.commandId)": ["command", ["moduleId", "commandId"]],
32
+ "unique(connectedAdmission.admissionId)": ["admission", ["admissionId"]],
33
+ "unique(connectedClient.clientKey)": ["client", ["clientKey"]],
34
+ "unique(connectedCohort.cohortKey)": ["cohort", ["cohortKey"]],
35
+ "unique(connectedCommand.moduleId,connectedCommand.commandId)": [
36
+ "command",
37
+ ["moduleId", "commandId"],
38
+ ],
39
+ "unique(connectedFlow.moduleId,connectedFlow.commandId)": ["flow", ["moduleId", "commandId"]],
40
+ "unique(connectedFlow.moduleId,connectedFlow.flowId)": ["flow", ["moduleId", "flowId"]],
41
+ "unique(connectedFlow.stateDigest)": ["flow", ["stateDigest"]],
42
+ "unique(connectedGrant.moduleId,connectedGrant.grantId)": ["grant", ["moduleId", "grantId"]],
43
+ "unique(connectedGrant.moduleId,connectedGrant.subjectId,connectedGrant.profileKey,connectedGrant.activeIdentityKey)":
44
+ ["grant", ["moduleId", "subjectId", "profileKey", "activeIdentityKey"]],
45
+ "unique(connectedRevocation.jobId)": ["job", ["jobId"]],
46
+ "unique(continuation.moduleId,continuation.continuationId)": [
47
+ "continuation",
48
+ ["moduleId", "continuationId"],
49
+ ],
50
+ "unique(continuation.moduleId,continuation.digest)": ["continuation", ["moduleId", "digest"]],
51
+ "unique(credential.credentialId)": ["credential", ["credentialId"]],
52
+ "unique(credential.identityKey)": ["credential", ["identityKey"]],
53
+ "unique(credential.moduleId,credential.credentialId)": [
54
+ "credential",
55
+ ["moduleId", "credentialId"],
56
+ ],
57
+ "unique(credential.moduleId,credential.subjectId)": ["credential", ["moduleId", "subjectId"]],
58
+ "unique(emailCommand.moduleId,emailCommand.commandId)": ["command", ["moduleId", "commandId"]],
59
+ "unique(emailCredential.moduleId,emailCredential.credentialId)": [
60
+ "credential",
61
+ ["moduleId", "credentialId"],
62
+ ],
63
+ "unique(emailCredential.moduleId,emailCredential.identifierNamespace,emailCredential.identifierValue)":
64
+ ["credential", ["moduleId", "identifierNamespace", "identifierValue"]],
65
+ "unique(emailRegistration.moduleId,emailRegistration.commandId)": [
66
+ "registration",
67
+ ["moduleId", "commandId"],
68
+ ],
69
+ "unique(emailRegistration.pendingReference)": ["registration", ["pendingReference"]],
70
+ "unique(failureEvent.moduleId,failureEvent.seriesKey,failureEvent.commandId)": [
71
+ "failureEvent",
72
+ ["moduleId", "seriesKey", "commandId"],
73
+ ],
74
+ "unique(flow.flowId)": ["flow", ["flowId"]],
75
+ "unique(flow.moduleId,flow.commandId)": ["flow", ["moduleId", "commandId"]],
76
+ "unique(flow.moduleId,flow.flowId)": ["flow", ["moduleId", "flowId"]],
77
+ "unique(flow.stateDigest)": ["flow", ["stateDigest"]],
78
+ "unique(generation.moduleId,generation.deliveryId)": ["generation", ["moduleId", "deliveryId"]],
79
+ "unique(generation.moduleId,generation.proofId)": ["generation", ["moduleId", "proofId"]],
80
+ "unique(identifier.namespace,identifier.value)": ["identifier", ["namespace", "value"]],
81
+ "unique(intent.digest)": ["intent", ["digest"]],
82
+ "unique(intent.flowId)": ["intent", ["flowId"]],
83
+ "unique(intent.moduleId,intent.commandId)": ["intent", ["moduleId", "commandId"]],
84
+ "unique(intent.moduleId,intent.flowId)": ["intent", ["moduleId", "flowId"]],
85
+ "unique(intent.moduleId,intent.intentId)": ["intent", ["moduleId", "intentId"]],
86
+ "unique(intent.moduleId,intent.reference)": ["intent", ["moduleId", "reference"]],
87
+ "unique(ownership.identityKey)": ["ownership", ["identityKey"]],
88
+ "unique(pending.digest)": ["pending", ["digest"]],
89
+ "unique(pending.flowId)": ["pending", ["flowId"]],
90
+ "unique(rateScope.moduleId,rateScope.action,rateScope.scopeKind,rateScope.scopeKey)": [
91
+ "rateScope",
92
+ ["moduleId", "action", "scopeKind", "scopeKey"],
93
+ ],
94
+ "unique(rateScope.moduleId,rateScope.purpose,rateScope.action,rateScope.scopeKind,rateScope.scopeKey)":
95
+ ["rateScope", ["moduleId", "purpose", "action", "scopeKind", "scopeKey"]],
96
+ "unique(registration.moduleId,registration.requestId)": [
97
+ "registration",
98
+ ["moduleId", "requestId"],
99
+ ],
100
+ "unique(registration.recoveryReference)": ["registration", ["recoveryReference"]],
101
+ "unique(registrationCommand.moduleId,registrationCommand.commandId)": [
102
+ "command",
103
+ ["moduleId", "commandId"],
104
+ ],
105
+ "unique(request.moduleId,request.requestId)": ["request", ["moduleId", "requestId"]],
106
+ "unique(series.moduleId,series.purpose,series.scopeKey)": [
107
+ "series",
108
+ ["moduleId", "purpose", "scopeKey"],
109
+ ],
110
+ "unique(session.digest)": ["session", ["digest"]],
111
+ "unique(tombstone.subjectId,tombstone.sessionId)": ["tombstone", ["subjectId", "sessionId"]],
112
+ "unique(tuple.identityKey)": ["ownership.tuple", ["identityKey"]],
113
+ "unique(unlinkCommand.moduleId,unlinkCommand.commandId)": ["command", ["moduleId", "commandId"]],
114
+ };
115
+
116
+ const arrayRoles: Readonly<Record<string, string>> = {
117
+ credentialId: "credential",
118
+ factor: "authority",
119
+ credentialOwnership: "credentialOwnership",
120
+ handleOwnership: "handleOwnership",
121
+ boundSubjectHandle: "handleOwnership",
122
+ flow: "flow",
123
+ command: "flow",
124
+ admission: "admission",
125
+ charge: "charge",
126
+ intentFlow: "intent",
127
+ intentCommand: "intent",
128
+ handle: "handle",
129
+ };
130
+
131
+ const nestedMappings = [
132
+ "read",
133
+ "signIn",
134
+ "password",
135
+ "proof",
136
+ "proofs",
137
+ "revocation",
138
+ "pending",
139
+ "session",
140
+ "source",
141
+ ];
142
+
143
+ const constraintGroups = [
144
+ "constraints",
145
+ "tupleConstraints",
146
+ "registrationConstraints",
147
+ "managementConstraints",
148
+ ];
149
+
150
+ const MappingRecord = Schema.Record(Schema.String, Schema.Unknown);
151
+ const record = Schema.decodeUnknownSync(MappingRecord);
152
+
153
+ const plans = (mapping: Readonly<Record<string, unknown>>) => {
154
+ const result: Array<{ readonly table: Table; readonly keys: ReadonlyArray<string> }> = [];
155
+
156
+ for (const group of constraintGroups) {
157
+ if (mapping[group] === undefined) continue;
158
+ for (const [name, declaration] of Object.entries(record(mapping[group]))) {
159
+ if (declaration === "notNull(provisioningRequest.subjectId)") continue;
160
+ let role: string;
161
+ let keys: ReadonlyArray<string>;
162
+
163
+ if (typeof declaration === "string") {
164
+ const plan = uniquePlans[declaration];
165
+
166
+ if (plan !== undefined) [role, keys] = plan;
167
+ else if (declaration === "unique(id)") {
168
+ role = "subject";
169
+ keys = ["id"];
170
+ } else if (declaration === "unique(scope)") {
171
+ role = name;
172
+ keys = ["scope"];
173
+ } else if (declaration === "unique(credentialId)") {
174
+ role = "credential";
175
+ keys = ["id"];
176
+ } else if (declaration === "unique(namespace,value)") {
177
+ role = "identifier";
178
+ keys = ["namespace", "value"];
179
+ } else if (declaration === "unique(provider,issuer,subject)") {
180
+ role = "externalIdentity";
181
+ keys = ["provider", "issuer", "subject"];
182
+ } else if (declaration === "unique(requestId)") {
183
+ role = "provisioningRequest";
184
+ keys = ["requestId"];
185
+ } else throw new Error("Unknown storage constraint");
186
+ } else if (Array.isArray(declaration) && declaration.every(Predicate.isString)) {
187
+ const mappedRole =
188
+ group === "managementConstraints" && name === "command" ? "command" : arrayRoles[name];
189
+
190
+ if (mappedRole === undefined) throw new Error("Unknown storage constraint");
191
+ role = mappedRole;
192
+ keys = declaration;
193
+ } else throw new Error("Invalid storage constraint");
194
+
195
+ let target: unknown = mapping;
196
+
197
+ for (const segment of role.split(".")) target = record(target)[segment];
198
+ // Session authority places its pending tables in one nested mapping.
199
+ if (target === undefined && Schema.is(MappingRecord)(mapping.pending))
200
+ target = mapping.pending[role];
201
+ let mapped = record(target);
202
+
203
+ if (role === "pending" && mapped.table === undefined) mapped = record(mapped.pending);
204
+ // OAuth ownership can be integrated into its tuple authority or separate.
205
+ if (role === "ownership" && mapped.table === undefined)
206
+ mapped = record(mapped.external ?? mapped.tuple);
207
+ if (!is(mapped.table, Table)) throw new Error("Missing storage table");
208
+ const columns = getTableColumns(mapped.table);
209
+
210
+ result.push({
211
+ table: mapped.table,
212
+ keys: keys.map((key) => {
213
+ const value = mapped[key];
214
+
215
+ if (typeof value !== "string" || columns[value] === undefined)
216
+ throw new Error("Missing mapped key");
217
+
218
+ return value;
219
+ }),
220
+ });
221
+ }
222
+ }
223
+ for (const nested of nestedMappings) {
224
+ const value = mapping[nested];
225
+
226
+ if (Schema.is(MappingRecord)(value) && value.table === undefined) result.push(...plans(value));
227
+ }
228
+
229
+ return result;
230
+ };
231
+
232
+ /** Validate at acquisition using the root database supplied by the adapter. */
233
+ export const validateDrizzleStorage = Effect.fnUntraced(
234
+ function* (mapping: unknown) {
235
+ const physical = yield* Effect.try({
236
+ try: () => plans(record(mapping)),
237
+ catch: () =>
238
+ PersistenceMappingError.make({
239
+ operation: "mapping",
240
+ cause: "Invalid storage key mapping",
241
+ }),
242
+ });
243
+
244
+ const { $client: client } = yield* NativeDatabase;
245
+
246
+ for (const plan of physical) {
247
+ const pg = is(plan.table, PgTable);
248
+ const mysql = is(plan.table, MySqlTable);
249
+
250
+ const schema = pg
251
+ ? getPgTableConfig(plan.table).schema
252
+ : mysql
253
+ ? getMysqlTableConfig(plan.table).schema
254
+ : undefined;
255
+
256
+ const columns = getTableColumns(plan.table);
257
+
258
+ yield* validateStorage(
259
+ pg ? "pg" : mysql ? "mysql" : "sqlite",
260
+ {
261
+ name: getTableName(plan.table),
262
+ ...(schema === undefined ? {} : { schema }),
263
+ columns: Object.fromEntries(plan.keys.map((key) => [key, { name: columns[key]!.name }])),
264
+ },
265
+ [plan.keys],
266
+ ).pipe(
267
+ Effect.provideService(SqlClient, client),
268
+ Effect.mapError(() =>
269
+ PersistenceMappingError.make({
270
+ operation: "mapping",
271
+ cause: "Required SQL storage key is unavailable",
272
+ }),
273
+ ),
274
+ );
275
+ }
276
+ },
277
+ Effect.catchDefect(() =>
278
+ Effect.fail(
279
+ PersistenceMappingError.make({
280
+ operation: "mapping",
281
+ cause: "Cannot inspect SQL storage metadata",
282
+ }),
283
+ ),
284
+ ),
285
+ );
@@ -15,6 +15,7 @@ import type { Statement } from "effect/sql/Statement";
15
15
 
16
16
  import type { PersistenceMappingError } from "./model";
17
17
  import { nativeDatabase } from "./native-database";
18
+ import { validateDrizzleStorage } from "./storage-validation";
18
19
  import type { SuppliedService } from "./SuppliedService";
19
20
  import {
20
21
  type TotpMapping,
@@ -140,6 +141,10 @@ export const makeTargetTotpPersistence = <
140
141
  catch: () => TotpConfigurationError.make({}),
141
142
  });
142
143
 
144
+ yield* validateDrizzleStorage(mapping).pipe(
145
+ Effect.mapError(() => TotpConfigurationError.make({})),
146
+ );
147
+
143
148
  const execution = yield* makeTransactionExecution(
144
149
  CurrentTotpTransaction,
145
150
  configuration,
@@ -159,7 +164,7 @@ export const coordinateTargetTotp = <M, A, E, R, RSetup = never>(
159
164
  services: TotpPersistenceServices,
160
165
  append: (statement: Statement<any>) => void,
161
166
  ) => Effect.Effect<A, E, R>,
162
- ): Effect.Effect<A, TotpCoordinatorError<E>, R | RSetup | LifecycleHooks> =>
167
+ ): Effect.Effect<A, TotpCoordinatorError<E>, R | RSetup | LifecycleHooks | NativeDatabase> =>
163
168
  Effect.gen(function* () {
164
169
  const original = yield* Effect.isEffect(source) ? source : Effect.succeed(source);
165
170
 
@@ -168,6 +173,10 @@ export const coordinateTargetTotp = <M, A, E, R, RSetup = never>(
168
173
  catch: () => TotpConfigurationError.make({}),
169
174
  });
170
175
 
176
+ yield* validateDrizzleStorage(mapping).pipe(
177
+ Effect.mapError(() => TotpConfigurationError.make({})),
178
+ );
179
+
171
180
  return yield* coordinateTransactionOwner(
172
181
  database,
173
182
  CurrentTotpTransaction,
@@ -280,12 +289,15 @@ export const makeTotpTarget = <
280
289
  TotpCoordinatorError<E> | DatabaseError,
281
290
  Exclude<R, TotpPersistence> | LifecycleHooks | RSetup | DatabaseRequirements
282
291
  > {
283
- return Effect.flatMap(acquire, (database) =>
292
+ return Effect.flatMap(nativeDatabase(acquire), (database) =>
284
293
  coordinateTargetTotp(
285
294
  database as any,
286
295
  options.mapping,
287
296
  configuration,
288
- (transaction: TransactionOf<Database>, services) => {
297
+ (
298
+ transaction: TransactionOf<Database>,
299
+ services,
300
+ ): Effect.Effect<A, E, Exclude<R, TotpPersistence>> => {
289
301
  const provided = Context.make(TotpPersistence, services.totpPersistence);
290
302
  const work = Effect.provideContext(body, provided);
291
303
 
@@ -293,7 +305,7 @@ export const makeTotpTarget = <
293
305
  ? work
294
306
  : Effect.provideService(work, options.transaction, options.transaction.of(transaction));
295
307
  },
296
- ),
308
+ ).pipe(Effect.provideService(NativeDatabase, database)),
297
309
  );
298
310
  }
299
311