@riffdb/better-auth 0.0.0-stage → 0.2.0

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 (91) hide show
  1. package/DESIGN.md +197 -0
  2. package/LICENSE-APACHE +201 -0
  3. package/LICENSE-MIT +27 -0
  4. package/README.md +191 -2
  5. package/dist/adapter.d.ts +28 -0
  6. package/dist/adapter.d.ts.map +1 -0
  7. package/dist/adapter.js +1262 -0
  8. package/dist/adapter.js.map +1 -0
  9. package/dist/generated/client.d.ts +1925 -0
  10. package/dist/generated/client.d.ts.map +1 -0
  11. package/dist/generated/client.js +2044 -0
  12. package/dist/generated/client.js.map +1 -0
  13. package/dist/index.d.ts +5 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +5 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/profile-contract.d.ts +60 -0
  18. package/dist/profile-contract.d.ts.map +1 -0
  19. package/dist/profile-contract.js +535 -0
  20. package/dist/profile-contract.js.map +1 -0
  21. package/dist/profile-plan.d.ts +69 -0
  22. package/dist/profile-plan.d.ts.map +1 -0
  23. package/dist/profile-plan.js +180 -0
  24. package/dist/profile-plan.js.map +1 -0
  25. package/dist/profile.d.ts +47 -0
  26. package/dist/profile.d.ts.map +1 -0
  27. package/dist/profile.js +131 -0
  28. package/dist/profile.js.map +1 -0
  29. package/dist/testing.d.ts +11 -0
  30. package/dist/testing.d.ts.map +1 -0
  31. package/dist/testing.js +12 -0
  32. package/dist/testing.js.map +1 -0
  33. package/generated/go/client.go +1996 -0
  34. package/generated/mcp/tools.json +12352 -0
  35. package/generated/python/client.py +3877 -0
  36. package/generated/riffdb.application.exact.json +1 -0
  37. package/generated/riffdb.contract.bundle +0 -0
  38. package/generated/rust/client.rs +6028 -0
  39. package/package.json +62 -4
  40. package/profiles/organization-security/materialization.json +48 -0
  41. package/profiles/organization-security/riffdb/contract.riff +766 -0
  42. package/profiles/organization-security/riffdb/queries/find_account_by_id.riffq +18 -0
  43. package/profiles/organization-security/riffdb/queries/find_invitation_by_email.riffq +17 -0
  44. package/profiles/organization-security/riffdb/queries/find_invitation_by_id.riffq +18 -0
  45. package/profiles/organization-security/riffdb/queries/find_invitation_by_organization_id.riffq +17 -0
  46. package/profiles/organization-security/riffdb/queries/find_member_by_id.riffq +18 -0
  47. package/profiles/organization-security/riffdb/queries/find_member_by_user_id.riffq +17 -0
  48. package/profiles/organization-security/riffdb/queries/find_organization_by_id.riffq +16 -0
  49. package/profiles/organization-security/riffdb/queries/find_organization_by_slug.riffq +16 -0
  50. package/profiles/organization-security/riffdb/queries/find_session_by_id.riffq +18 -0
  51. package/profiles/organization-security/riffdb/queries/find_session_by_token.riffq +16 -0
  52. package/profiles/organization-security/riffdb/queries/find_team_by_id.riffq +18 -0
  53. package/profiles/organization-security/riffdb/queries/find_team_member_by_id.riffq +18 -0
  54. package/profiles/organization-security/riffdb/queries/find_team_member_by_team_id.riffq +17 -0
  55. package/profiles/organization-security/riffdb/queries/find_two_factor_by_id.riffq +18 -0
  56. package/profiles/organization-security/riffdb/queries/find_two_factor_by_secret.riffq +17 -0
  57. package/profiles/organization-security/riffdb/queries/find_user_by_email.riffq +16 -0
  58. package/profiles/organization-security/riffdb/queries/find_user_by_id.riffq +16 -0
  59. package/profiles/organization-security/riffdb/queries/find_verification_by_id.riffq +16 -0
  60. package/profiles/organization-security/riffdb/queries/find_verification_by_identifier.riffq +17 -0
  61. package/profiles/organization-security/riffdb.application.json +167 -0
  62. package/profiles/organization-security/riffdb.toml +7 -0
  63. package/profiles/organization-security.profile.json +954 -0
  64. package/riffdb/contract.riff +489 -0
  65. package/riffdb/queries/admin_users_all_email_asc.riffq +24 -0
  66. package/riffdb/queries/admin_users_contains_asc.riffq +24 -0
  67. package/riffdb/queries/admin_users_contains_desc.riffq +24 -0
  68. package/riffdb/queries/admin_users_email_contains_created_desc.riffq +24 -0
  69. package/riffdb/queries/admin_users_email_contains_image_asc.riffq +24 -0
  70. package/riffdb/queries/admin_users_ends_with_asc.riffq +24 -0
  71. package/riffdb/queries/admin_users_ends_with_desc.riffq +24 -0
  72. package/riffdb/queries/admin_users_starts_with_asc.riffq +24 -0
  73. package/riffdb/queries/admin_users_starts_with_desc.riffq +24 -0
  74. package/riffdb/queries/find_account_by_id.riffq +24 -0
  75. package/riffdb/queries/find_account_by_provider.riffq +26 -0
  76. package/riffdb/queries/find_accounts_by_user_id.riffq +25 -0
  77. package/riffdb/queries/find_accounts_by_user_provider.riffq +27 -0
  78. package/riffdb/queries/find_active_sessions_by_user_id.riffq +23 -0
  79. package/riffdb/queries/find_expired_verification_ids.riffq +17 -0
  80. package/riffdb/queries/find_latest_verification.riffq +20 -0
  81. package/riffdb/queries/find_session_by_id.riffq +20 -0
  82. package/riffdb/queries/find_session_by_token.riffq +20 -0
  83. package/riffdb/queries/find_sessions_by_token_set.riffq +20 -0
  84. package/riffdb/queries/find_sessions_by_user_id.riffq +21 -0
  85. package/riffdb/queries/find_user_by_email.riffq +16 -0
  86. package/riffdb/queries/find_user_by_id.riffq +15 -0
  87. package/riffdb/queries/find_verification_by_id.riffq +19 -0
  88. package/riffdb/queries/find_verifications_by_identifier.riffq +21 -0
  89. package/riffdb.application.json +107 -0
  90. package/riffdb.application.lock.json +1 -0
  91. package/scripts/real-riffdb-runner.mjs +333 -0
@@ -0,0 +1,1262 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join, parse } from "node:path";
3
+ import { createRequire } from "node:module";
4
+ import { DriverGeneratedApplicationTransport, } from "@riffdb/client";
5
+ import { createAdapterFactory, } from "better-auth/adapters";
6
+ import { RiffDBBetterAuthCoreClient, } from "./generated/client.js";
7
+ import { BETTER_AUTH_PINNED_VERSION } from "./profile.js";
8
+ const CORE_SCHEMA_FIELDS = {
9
+ user: ["name", "email", "emailVerified", "image", "createdAt", "updatedAt"],
10
+ session: [
11
+ "expiresAt",
12
+ "token",
13
+ "createdAt",
14
+ "updatedAt",
15
+ "ipAddress",
16
+ "userAgent",
17
+ "userId",
18
+ ],
19
+ account: [
20
+ "accountId",
21
+ "providerId",
22
+ "userId",
23
+ "accessToken",
24
+ "refreshToken",
25
+ "idToken",
26
+ "accessTokenExpiresAt",
27
+ "refreshTokenExpiresAt",
28
+ "scope",
29
+ "password",
30
+ "createdAt",
31
+ "updatedAt",
32
+ ],
33
+ verification: ["identifier", "value", "expiresAt", "createdAt", "updatedAt"],
34
+ };
35
+ export class RiffDBBetterAuthCallError extends Error {
36
+ code;
37
+ operation;
38
+ outcome;
39
+ constructor(code, operation, outcome) {
40
+ super(outcome === undefined
41
+ ? `RiffDB Better Auth ${operation} failed: ${code}`
42
+ : `RiffDB Better Auth ${operation} failed: ${code} (${outcome})`);
43
+ this.code = code;
44
+ this.operation = operation;
45
+ this.outcome = outcome;
46
+ this.name = "RiffDBBetterAuthCallError";
47
+ }
48
+ }
49
+ function fail(code, operation, outcome) {
50
+ throw new RiffDBBetterAuthCallError(code, operation, outcome);
51
+ }
52
+ function isUuid(value) {
53
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(value);
54
+ }
55
+ function validateConfiguration(config) {
56
+ if (!isUuid(config.organizationId))
57
+ fail("invalid_configuration", "configuration");
58
+ if (config.debugLogs !== undefined && config.debugLogs !== false) {
59
+ fail("invalid_configuration", "debugLogs");
60
+ }
61
+ }
62
+ export function assertSupportedBetterAuthInstallation(installedVersion = resolveInstalledBetterAuthVersion()) {
63
+ if (installedVersion !== BETTER_AUTH_PINNED_VERSION) {
64
+ fail("invalid_configuration", "betterAuthVersion");
65
+ }
66
+ }
67
+ function resolveInstalledBetterAuthVersion() {
68
+ const require = createRequire(import.meta.url);
69
+ let directory = dirname(require.resolve("better-auth"));
70
+ const root = parse(directory).root;
71
+ for (let depth = 0; depth < 8 && directory !== root; depth += 1) {
72
+ try {
73
+ const manifest = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
74
+ if (manifest.name === "better-auth" && typeof manifest.version === "string") {
75
+ return manifest.version;
76
+ }
77
+ }
78
+ catch (error) {
79
+ const code = error.code;
80
+ if (code !== "ENOENT" && code !== "ENOTDIR")
81
+ throw error;
82
+ }
83
+ directory = dirname(directory);
84
+ }
85
+ fail("invalid_configuration", "betterAuthVersion");
86
+ }
87
+ function validateResolvedSchema(options, schema) {
88
+ if (options.advanced?.database?.generateId !== "uuid") {
89
+ fail("invalid_configuration", "generateId");
90
+ }
91
+ if ((options.plugins?.length ?? 0) !== 0)
92
+ fail("invalid_configuration", "plugins");
93
+ const modelNames = Object.keys(schema).sort();
94
+ const expectedModels = Object.keys(CORE_SCHEMA_FIELDS).sort();
95
+ if (JSON.stringify(modelNames) !== JSON.stringify(expectedModels)) {
96
+ fail("invalid_configuration", "models");
97
+ }
98
+ for (const model of expectedModels) {
99
+ const resolved = schema[model];
100
+ if (resolved === undefined || resolved.modelName !== model) {
101
+ fail("invalid_configuration", `${model}.modelName`);
102
+ }
103
+ const fields = Object.keys(resolved.fields).sort();
104
+ const expected = [...CORE_SCHEMA_FIELDS[model]].sort();
105
+ if (JSON.stringify(fields) !== JSON.stringify(expected)) {
106
+ fail("invalid_configuration", `${model}.fields`);
107
+ }
108
+ for (const field of expected) {
109
+ const attributes = resolved.fields[field];
110
+ if (attributes?.fieldName !== undefined && attributes.fieldName !== field) {
111
+ fail("invalid_configuration", `${model}.${field}.fieldName`);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ function asRecord(value, operation) {
117
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
118
+ fail("invalid_record_shape", operation);
119
+ }
120
+ return value;
121
+ }
122
+ function requiredString(record, field, operation) {
123
+ const value = record[field];
124
+ if (typeof value !== "string" || value.length === 0)
125
+ fail("invalid_record_shape", operation);
126
+ return value;
127
+ }
128
+ function nullableString(record, field, operation) {
129
+ const value = record[field];
130
+ if (value === undefined || value === null)
131
+ return null;
132
+ if (typeof value !== "string")
133
+ fail("invalid_record_shape", operation);
134
+ return value;
135
+ }
136
+ function requiredBoolean(record, field, operation) {
137
+ const value = record[field];
138
+ if (typeof value !== "boolean")
139
+ fail("invalid_record_shape", operation);
140
+ return value;
141
+ }
142
+ function requiredDate(record, field, operation) {
143
+ const value = record[field];
144
+ if (!(value instanceof Date) || !Number.isFinite(value.getTime())) {
145
+ fail("invalid_record_shape", operation);
146
+ }
147
+ return value;
148
+ }
149
+ function nullableDate(record, field, operation) {
150
+ const value = record[field];
151
+ if (value === undefined || value === null)
152
+ return null;
153
+ if (!(value instanceof Date) || !Number.isFinite(value.getTime())) {
154
+ fail("invalid_record_shape", operation);
155
+ }
156
+ return value;
157
+ }
158
+ function toTimestamp(value) {
159
+ const milliseconds = value.getTime();
160
+ const seconds = Math.floor(milliseconds / 1_000);
161
+ return {
162
+ seconds: BigInt(seconds),
163
+ nanos: (milliseconds - seconds * 1_000) * 1_000_000,
164
+ };
165
+ }
166
+ function fromTimestamp(value) {
167
+ const milliseconds = Number(value.seconds) * 1_000 + value.nanos / 1_000_000;
168
+ if (!Number.isSafeInteger(milliseconds))
169
+ fail("invalid_record_shape", "timestamp result");
170
+ return new Date(milliseconds);
171
+ }
172
+ function timestampOrNull(value) {
173
+ return value === null ? null : toTimestamp(value);
174
+ }
175
+ function exactWhere(where, fields) {
176
+ if (where === undefined || where.length !== fields.length)
177
+ return false;
178
+ const actualFields = new Set(where.map((clause) => clause.field));
179
+ return (actualFields.size === fields.length &&
180
+ fields.every((field) => actualFields.has(field)) &&
181
+ where.every((clause) => clause.operator === "eq" &&
182
+ clause.connector === "AND" &&
183
+ clause.mode === "sensitive"));
184
+ }
185
+ function exactWhereShape(where, shape) {
186
+ if (where === undefined || where.length !== shape.length)
187
+ return false;
188
+ return (new Set(where.map((clause) => clause.field)).size === shape.length &&
189
+ shape.every(({ field, operator }) => where.some((clause) => clause.field === field && clause.operator === operator)) &&
190
+ where.every((clause) => clause.connector === "AND" && clause.mode === "sensitive"));
191
+ }
192
+ function whereValue(where, field, operation) {
193
+ const value = where.find((clause) => clause.field === field)?.value;
194
+ if (typeof value !== "string")
195
+ fail("unsupported_call_shape", operation);
196
+ return value;
197
+ }
198
+ function whereDate(where, field, operation) {
199
+ const value = where.find((clause) => clause.field === field)?.value;
200
+ if (!(value instanceof Date) || !Number.isFinite(value.getTime())) {
201
+ fail("unsupported_call_shape", operation);
202
+ }
203
+ return value;
204
+ }
205
+ function whereStringSet(where, field, operation) {
206
+ const value = where.find((clause) => clause.field === field)?.value;
207
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.length === 0)) {
208
+ fail("unsupported_call_shape", operation);
209
+ }
210
+ const values = [...new Set(value)];
211
+ if (values.length === 0 || values.length > 32)
212
+ fail("result_bound_exceeded", operation);
213
+ return values;
214
+ }
215
+ function adminUserWhere(where, operation) {
216
+ const clauses = where ?? [];
217
+ if (clauses.length > 2)
218
+ fail("unsupported_call_shape", operation);
219
+ const id = clauses.find((clause) => clause.field === "id");
220
+ const search = clauses.find((clause) => clause.field !== "id");
221
+ if ((id !== undefined &&
222
+ (id.operator !== "eq" || id.connector !== "AND" || id.mode !== "sensitive")) ||
223
+ (search !== undefined &&
224
+ (search.field !== "email" ||
225
+ !new Set(["contains", "starts_with", "ends_with"]).has(search.operator ?? "") ||
226
+ search.connector !== "AND" ||
227
+ search.mode !== "sensitive")) ||
228
+ (id === undefined && search === undefined && clauses.length !== 0) ||
229
+ Number(id !== undefined) + Number(search !== undefined) !== clauses.length) {
230
+ fail("unsupported_call_shape", operation);
231
+ }
232
+ const userId = id === undefined ? null : id.value;
233
+ if (userId !== null && (typeof userId !== "string" || !isUuid(userId))) {
234
+ fail("unsupported_call_shape", operation);
235
+ }
236
+ if (search === undefined)
237
+ return Object.freeze({ userId });
238
+ if (typeof search.value !== "string" || search.value.length === 0) {
239
+ fail("unsupported_call_shape", operation);
240
+ }
241
+ return Object.freeze({
242
+ operator: search.operator,
243
+ needle: search.value,
244
+ userId,
245
+ });
246
+ }
247
+ function assertNoJoin(join, operation) {
248
+ if (join !== undefined && Object.keys(join).length !== 0) {
249
+ fail("unsupported_call_shape", operation);
250
+ }
251
+ }
252
+ function requireOutcome(result, operation, expected) {
253
+ const outcome = result.outcome.outcome;
254
+ if (outcome !== expected)
255
+ fail("declared_outcome", operation, outcome);
256
+ }
257
+ function mapUser(row) {
258
+ return {
259
+ id: row.user_id,
260
+ name: row.name,
261
+ email: row.email,
262
+ emailVerified: row.email_verified,
263
+ image: row.image,
264
+ createdAt: fromTimestamp(row.created_at),
265
+ updatedAt: fromTimestamp(row.updated_at),
266
+ };
267
+ }
268
+ function mapSession(row) {
269
+ return {
270
+ id: row.id,
271
+ userId: row.user_id,
272
+ token: row.token,
273
+ expiresAt: fromTimestamp(row.expires_at),
274
+ ipAddress: row.ip_address,
275
+ userAgent: row.user_agent,
276
+ createdAt: fromTimestamp(row.created_at),
277
+ updatedAt: fromTimestamp(row.updated_at),
278
+ };
279
+ }
280
+ function mapAccount(row) {
281
+ return {
282
+ id: row.id,
283
+ userId: row.user_id,
284
+ accountId: row.account_id,
285
+ providerId: row.provider_id,
286
+ accessToken: row.access_token,
287
+ refreshToken: row.refresh_token,
288
+ accessTokenExpiresAt: row.access_token_expires_at === null ? null : fromTimestamp(row.access_token_expires_at),
289
+ refreshTokenExpiresAt: row.refresh_token_expires_at === null ? null : fromTimestamp(row.refresh_token_expires_at),
290
+ scope: row.scope,
291
+ idToken: row.id_token,
292
+ password: row.password,
293
+ createdAt: fromTimestamp(row.created_at),
294
+ updatedAt: fromTimestamp(row.updated_at),
295
+ };
296
+ }
297
+ function mapVerification(row) {
298
+ return {
299
+ id: row.verification_id,
300
+ identifier: row.identifier,
301
+ value: row.value,
302
+ expiresAt: fromTimestamp(row.expires_at),
303
+ createdAt: fromTimestamp(row.created_at),
304
+ updatedAt: fromTimestamp(row.updated_at),
305
+ };
306
+ }
307
+ class CoreDispatcher {
308
+ client;
309
+ organizationId;
310
+ resetModelForTesting;
311
+ constructor(client, organizationId, resetModelForTesting) {
312
+ this.client = client;
313
+ this.organizationId = organizationId;
314
+ this.resetModelForTesting = resetModelForTesting;
315
+ }
316
+ async create(model, dataValue) {
317
+ const data = asRecord(dataValue, `create ${model}`);
318
+ if (model === "user") {
319
+ const result = await this.client.createUser({
320
+ request_id: "",
321
+ organization_id: this.organizationId,
322
+ id: requiredString(data, "id", "create user"),
323
+ name: requiredString(data, "name", "create user"),
324
+ email: requiredString(data, "email", "create user"),
325
+ email_verified: requiredBoolean(data, "emailVerified", "create user"),
326
+ image: nullableString(data, "image", "create user"),
327
+ created_at: toTimestamp(requiredDate(data, "createdAt", "create user")),
328
+ updated_at: toTimestamp(requiredDate(data, "updatedAt", "create user")),
329
+ });
330
+ requireOutcome(result, "create user", "UserCreated");
331
+ return { ...data, image: data.image ?? null };
332
+ }
333
+ if (model === "account") {
334
+ const result = await this.client.createAccount({
335
+ request_id: "",
336
+ organization_id: this.organizationId,
337
+ id: requiredString(data, "id", "create account"),
338
+ user_id: requiredString(data, "userId", "create account"),
339
+ account_id: requiredString(data, "accountId", "create account"),
340
+ provider_id: requiredString(data, "providerId", "create account"),
341
+ access_token: nullableString(data, "accessToken", "create account"),
342
+ refresh_token: nullableString(data, "refreshToken", "create account"),
343
+ access_token_expires_at: timestampOrNull(nullableDate(data, "accessTokenExpiresAt", "create account")),
344
+ refresh_token_expires_at: timestampOrNull(nullableDate(data, "refreshTokenExpiresAt", "create account")),
345
+ scope: nullableString(data, "scope", "create account"),
346
+ id_token: nullableString(data, "idToken", "create account"),
347
+ password: nullableString(data, "password", "create account"),
348
+ created_at: toTimestamp(requiredDate(data, "createdAt", "create account")),
349
+ updated_at: toTimestamp(requiredDate(data, "updatedAt", "create account")),
350
+ });
351
+ requireOutcome(result, "create account", "AccountCreated");
352
+ return { ...data };
353
+ }
354
+ if (model === "session") {
355
+ const token = requiredString(data, "token", "create session");
356
+ const result = await this.client.createSession({
357
+ request_id: "",
358
+ organization_id: this.organizationId,
359
+ id: requiredString(data, "id", "create session"),
360
+ user_id: requiredString(data, "userId", "create session"),
361
+ token,
362
+ expires_at: toTimestamp(requiredDate(data, "expiresAt", "create session")),
363
+ ip_address: nullableString(data, "ipAddress", "create session"),
364
+ user_agent: nullableString(data, "userAgent", "create session"),
365
+ created_at: toTimestamp(requiredDate(data, "createdAt", "create session")),
366
+ updated_at: toTimestamp(requiredDate(data, "updatedAt", "create session")),
367
+ });
368
+ requireOutcome(result, "create session", "SessionCreated");
369
+ return { ...data };
370
+ }
371
+ if (model === "verification") {
372
+ const result = await this.client.createVerification({
373
+ request_id: "",
374
+ organization_id: this.organizationId,
375
+ id: requiredString(data, "id", "create verification"),
376
+ identifier: requiredString(data, "identifier", "create verification"),
377
+ value: requiredString(data, "value", "create verification"),
378
+ expires_at: toTimestamp(requiredDate(data, "expiresAt", "create verification")),
379
+ created_at: toTimestamp(requiredDate(data, "createdAt", "create verification")),
380
+ updated_at: toTimestamp(requiredDate(data, "updatedAt", "create verification")),
381
+ });
382
+ requireOutcome(result, "create verification", "VerificationCreated");
383
+ return { ...data };
384
+ }
385
+ return fail("unsupported_model", `create ${model}`);
386
+ }
387
+ async findOne(model, where, join) {
388
+ assertNoJoin(join, `findOne ${model}`);
389
+ if (model === "user" && exactWhere(where, ["email"])) {
390
+ const result = await this.client.findUserByEmail({
391
+ organization_id: this.organizationId,
392
+ email: whereValue(where, "email", "findOne user"),
393
+ });
394
+ const row = result.value.users[0];
395
+ return row === undefined ? null : mapUser(row);
396
+ }
397
+ if (model === "user" && exactWhere(where, ["id"])) {
398
+ const result = await this.client.findUserById({
399
+ organization_id: this.organizationId,
400
+ id: whereValue(where, "id", "findOne user"),
401
+ });
402
+ return result.value.outcome === "Missing" ? null : mapUser(result.value.user);
403
+ }
404
+ if (model === "session" && exactWhere(where, ["token"])) {
405
+ const token = whereValue(where, "token", "findOne session");
406
+ const result = await this.client.findSessionByToken({
407
+ organization_id: this.organizationId,
408
+ token,
409
+ });
410
+ const row = result.value.sessions[0];
411
+ return row === undefined ? null : mapSession(row);
412
+ }
413
+ if (model === "session" && exactWhere(where, ["id"])) {
414
+ const result = await this.client.findSessionById({
415
+ organization_id: this.organizationId,
416
+ id: whereValue(where, "id", "findOne session"),
417
+ });
418
+ const row = result.value.sessions[0];
419
+ return row === undefined ? null : mapSession(row);
420
+ }
421
+ if (model === "account" && exactWhere(where, ["id"])) {
422
+ const result = await this.client.findAccountById({
423
+ organization_id: this.organizationId,
424
+ id: whereValue(where, "id", "findOne account"),
425
+ });
426
+ const row = result.value.accounts[0];
427
+ return row === undefined ? null : mapAccount(row);
428
+ }
429
+ if (model === "account" && exactWhere(where, ["accountId", "providerId"])) {
430
+ const result = await this.client.findAccountByProvider({
431
+ organization_id: this.organizationId,
432
+ account_id: whereValue(where, "accountId", "findOne account"),
433
+ provider_id: whereValue(where, "providerId", "findOne account"),
434
+ });
435
+ const row = result.value.accounts[0];
436
+ return row === undefined ? null : mapAccount(row);
437
+ }
438
+ if (model === "verification" && exactWhere(where, ["id"])) {
439
+ const result = await this.client.findVerificationById({
440
+ organization_id: this.organizationId,
441
+ id: whereValue(where, "id", "findOne verification"),
442
+ });
443
+ const value = result.value;
444
+ return value.outcome === "Missing"
445
+ ? null
446
+ : mapVerification(value.verification);
447
+ }
448
+ return fail("unsupported_call_shape", `findOne ${model}`);
449
+ }
450
+ async update(model, where, updateValue) {
451
+ if (model === "account" && exactWhere(where, ["id"])) {
452
+ const update = asRecord(updateValue, "update account");
453
+ const allowed = new Set([
454
+ "accountId",
455
+ "providerId",
456
+ "accessToken",
457
+ "refreshToken",
458
+ "accessTokenExpiresAt",
459
+ "refreshTokenExpiresAt",
460
+ "scope",
461
+ "idToken",
462
+ "password",
463
+ "updatedAt",
464
+ ]);
465
+ const fields = Object.keys(update);
466
+ if (fields.length === 0 || fields.some((field) => !allowed.has(field))) {
467
+ fail("unsupported_call_shape", "update account");
468
+ }
469
+ const id = whereValue(where, "id", "update account");
470
+ for (let attempt = 0; attempt < 3; attempt += 1) {
471
+ const found = await this.client.findAccountById({
472
+ organization_id: this.organizationId,
473
+ id,
474
+ });
475
+ const row = found.value.accounts[0];
476
+ if (row === undefined)
477
+ return null;
478
+ const successor = { ...mapAccount(row), ...update };
479
+ const updated = await this.client.updateAccount({
480
+ request_id: "",
481
+ organization_id: this.organizationId,
482
+ user_id: row.user_id,
483
+ account_id: row.id,
484
+ expected_revision: row.revision,
485
+ external_account_id: requiredString(successor, "accountId", "update account"),
486
+ provider_id: requiredString(successor, "providerId", "update account"),
487
+ access_token: nullableString(successor, "accessToken", "update account"),
488
+ refresh_token: nullableString(successor, "refreshToken", "update account"),
489
+ access_token_expires_at: timestampOrNull(nullableDate(successor, "accessTokenExpiresAt", "update account")),
490
+ refresh_token_expires_at: timestampOrNull(nullableDate(successor, "refreshTokenExpiresAt", "update account")),
491
+ scope: nullableString(successor, "scope", "update account"),
492
+ id_token: nullableString(successor, "idToken", "update account"),
493
+ password: nullableString(successor, "password", "update account"),
494
+ updated_at: toTimestamp(requiredDate(successor, "updatedAt", "update account")),
495
+ });
496
+ if (updated.outcome.outcome === "AccountUpdateStale")
497
+ continue;
498
+ if (updated.outcome.outcome === "AccountUpdateMissing")
499
+ return null;
500
+ requireOutcome(updated, "update account", "AccountUpdated");
501
+ return successor;
502
+ }
503
+ return fail("declared_outcome", "update account", "AccountUpdateStale");
504
+ }
505
+ if (model === "session" &&
506
+ (exactWhere(where, ["id"]) || exactWhere(where, ["token"]))) {
507
+ const update = asRecord(updateValue, "update session");
508
+ const allowed = new Set(["expiresAt", "ipAddress", "userAgent", "updatedAt"]);
509
+ const fields = Object.keys(update);
510
+ if (fields.length === 0 || fields.some((field) => !allowed.has(field))) {
511
+ fail("unsupported_call_shape", "update session");
512
+ }
513
+ for (let attempt = 0; attempt < 3; attempt += 1) {
514
+ const result = exactWhere(where, ["id"])
515
+ ? await this.client.findSessionById({
516
+ organization_id: this.organizationId,
517
+ id: whereValue(where, "id", "update session"),
518
+ })
519
+ : await this.client.findSessionByToken({
520
+ organization_id: this.organizationId,
521
+ token: whereValue(where, "token", "update session"),
522
+ });
523
+ const row = result.value.sessions[0];
524
+ if (row === undefined)
525
+ return null;
526
+ const current = mapSession(row);
527
+ const successor = { ...current, ...update };
528
+ const updated = await this.client.updateSession({
529
+ request_id: "",
530
+ organization_id: this.organizationId,
531
+ user_id: row.user_id,
532
+ session_id: row.id,
533
+ expected_revision: row.revision,
534
+ expires_at: toTimestamp(requiredDate(successor, "expiresAt", "update session")),
535
+ ip_address: nullableString(successor, "ipAddress", "update session"),
536
+ user_agent: nullableString(successor, "userAgent", "update session"),
537
+ updated_at: toTimestamp(requiredDate(successor, "updatedAt", "update session")),
538
+ });
539
+ if (updated.outcome.outcome === "SessionUpdateStale")
540
+ continue;
541
+ if (updated.outcome.outcome === "SessionUpdateMissing")
542
+ return null;
543
+ requireOutcome(updated, "update session", "SessionUpdated");
544
+ return successor;
545
+ }
546
+ return fail("declared_outcome", "update session", "SessionUpdateStale");
547
+ }
548
+ if (model !== "user" ||
549
+ (!exactWhere(where, ["id"]) && !exactWhere(where, ["email"]))) {
550
+ if (model !== "verification" || !exactWhere(where, ["identifier"])) {
551
+ return fail("unsupported_call_shape", `update ${model}`);
552
+ }
553
+ const update = asRecord(updateValue, "update verification");
554
+ const allowed = new Set(["value", "expiresAt", "updatedAt"]);
555
+ const fields = Object.keys(update);
556
+ if (fields.length === 0 || fields.some((field) => !allowed.has(field))) {
557
+ fail("unsupported_call_shape", "update verification");
558
+ }
559
+ const identifier = whereValue(where, "identifier", "update verification");
560
+ for (let attempt = 0; attempt < 3; attempt += 1) {
561
+ const found = await this.client.findLatestVerification({
562
+ organization_id: this.organizationId,
563
+ identifier,
564
+ });
565
+ const row = found.value.verifications[0];
566
+ if (row === undefined)
567
+ return null;
568
+ const successor = { ...mapVerification(row), ...update };
569
+ const result = await this.client.updateVerification({
570
+ request_id: "",
571
+ organization_id: this.organizationId,
572
+ verification_id: row.verification_id,
573
+ expected_revision: row.revision,
574
+ value: requiredString(successor, "value", "update verification"),
575
+ expires_at: toTimestamp(requiredDate(successor, "expiresAt", "update verification")),
576
+ updated_at: toTimestamp(requiredDate(successor, "updatedAt", "update verification")),
577
+ });
578
+ if (result.outcome.outcome === "VerificationUpdateStale")
579
+ continue;
580
+ if (result.outcome.outcome === "VerificationUpdateMissing")
581
+ return null;
582
+ requireOutcome(result, "update verification", "VerificationUpdated");
583
+ return successor;
584
+ }
585
+ return fail("declared_outcome", "update verification", "VerificationUpdateStale");
586
+ }
587
+ const update = asRecord(updateValue, "update user");
588
+ const allowed = new Set(["name", "email", "emailVerified", "image", "updatedAt"]);
589
+ const fields = Object.keys(update);
590
+ if (fields.length === 0 || fields.some((field) => !allowed.has(field))) {
591
+ fail("unsupported_call_shape", "update user");
592
+ }
593
+ let userId = exactWhere(where, ["id"])
594
+ ? whereValue(where, "id", "update user")
595
+ : undefined;
596
+ const initialEmail = exactWhere(where, ["email"])
597
+ ? whereValue(where, "email", "update user")
598
+ : undefined;
599
+ for (let attempt = 0; attempt < 3; attempt += 1) {
600
+ const row = userId === undefined
601
+ ? (await this.client.findUserByEmail({
602
+ organization_id: this.organizationId,
603
+ email: initialEmail,
604
+ })).value.users[0]
605
+ : (await this.client.findUserById({
606
+ organization_id: this.organizationId,
607
+ id: userId,
608
+ })).value;
609
+ if (row === undefined || ("outcome" in row && row.outcome === "Missing"))
610
+ return null;
611
+ const resolved = "outcome" in row ? row.user : row;
612
+ userId = resolved.user_id;
613
+ const current = mapUser(resolved);
614
+ const successor = { ...current, ...update };
615
+ const result = await this.client.updateUser({
616
+ request_id: "",
617
+ organization_id: this.organizationId,
618
+ user_id: userId,
619
+ expected_revision: resolved.revision,
620
+ name: requiredString(successor, "name", "update user"),
621
+ email: requiredString(successor, "email", "update user"),
622
+ email_verified: requiredBoolean(successor, "emailVerified", "update user"),
623
+ image: nullableString(successor, "image", "update user"),
624
+ updated_at: toTimestamp(requiredDate(successor, "updatedAt", "update user")),
625
+ });
626
+ if (result.outcome.outcome === "UserUpdateStale")
627
+ continue;
628
+ if (result.outcome.outcome === "UserUpdateMissing")
629
+ return null;
630
+ requireOutcome(result, "update user", "UserUpdated");
631
+ return successor;
632
+ }
633
+ return fail("declared_outcome", "update user", "UserUpdateStale");
634
+ }
635
+ async findMany(model, where, limit, sortBy, offset, join) {
636
+ assertNoJoin(join, `findMany ${model}`);
637
+ if (!Number.isInteger(limit) || limit < 1)
638
+ fail("unsupported_call_shape", `findMany ${model}`);
639
+ if (model === "user") {
640
+ const window = await this.adminUserWindow(where, limit, sortBy, offset);
641
+ return window.users.map(mapUser);
642
+ }
643
+ if (offset !== undefined && offset !== 0)
644
+ fail("unsupported_call_shape", `findMany ${model}`);
645
+ if (model === "account" && exactWhere(where, ["userId"]) && sortBy === undefined) {
646
+ if (limit > 100)
647
+ fail("result_bound_exceeded", "findMany account");
648
+ const result = await this.client.findAccountsByUserId({
649
+ organization_id: this.organizationId,
650
+ user_id: whereValue(where, "userId", "findMany account"),
651
+ after: null,
652
+ });
653
+ if (result.nextCursor !== undefined)
654
+ fail("result_bound_exceeded", "findMany account");
655
+ return result.value.accounts.slice(0, limit).map(mapAccount);
656
+ }
657
+ if (model === "session" && exactWhere(where, ["userId"]) && sortBy === undefined) {
658
+ const result = await this.client.findSessionsByUserId({
659
+ organization_id: this.organizationId,
660
+ user_id: whereValue(where, "userId", "findMany session"),
661
+ after: null,
662
+ });
663
+ if (result.nextCursor !== undefined)
664
+ fail("result_bound_exceeded", "findMany session");
665
+ return result.value.sessions.slice(0, limit).map(mapSession);
666
+ }
667
+ if (model === "session" &&
668
+ exactWhereShape(where, [
669
+ { field: "userId", operator: "eq" },
670
+ { field: "expiresAt", operator: "gt" },
671
+ ]) &&
672
+ sortBy === undefined) {
673
+ const result = await this.client.findActiveSessionsByUserId({
674
+ organization_id: this.organizationId,
675
+ user_id: whereValue(where, "userId", "findMany active session"),
676
+ expires_after: toTimestamp(whereDate(where, "expiresAt", "findMany active session")),
677
+ after: null,
678
+ });
679
+ if (result.nextCursor !== undefined) {
680
+ fail("result_bound_exceeded", "findMany active session");
681
+ }
682
+ return result.value.sessions.slice(0, limit).map(mapSession);
683
+ }
684
+ const tokenSetShape = exactWhereShape(where, [{ field: "token", operator: "in" }]);
685
+ const activeTokenSetShape = exactWhereShape(where, [
686
+ { field: "token", operator: "in" },
687
+ { field: "expiresAt", operator: "gt" },
688
+ ]);
689
+ if (model === "session" &&
690
+ tokenSetShape &&
691
+ sortBy === undefined) {
692
+ const tokens = whereStringSet(where, "token", "findMany session token set");
693
+ const result = await this.client.findSessionsByTokenSet({
694
+ organization_id: this.organizationId,
695
+ tokens,
696
+ });
697
+ return result.value.sessions.slice(0, limit).map(mapSession);
698
+ }
699
+ if (model === "session" && activeTokenSetShape) {
700
+ return fail("unsupported_call_shape", "findMany session token set with expiry");
701
+ }
702
+ if (model === "verification" &&
703
+ exactWhere(where, ["identifier"]) &&
704
+ sortBy?.field === "createdAt" &&
705
+ sortBy.direction === "desc" &&
706
+ limit === 1) {
707
+ const result = await this.client.findLatestVerification({
708
+ organization_id: this.organizationId,
709
+ identifier: whereValue(where, "identifier", "findMany verification"),
710
+ });
711
+ return result.value.verifications.map(mapVerification);
712
+ }
713
+ if (model === "verification" && exactWhere(where, ["identifier"]) && sortBy !== undefined) {
714
+ return fail("unsupported_call_shape", "findMany verification sort");
715
+ }
716
+ if (model === "verification" && exactWhere(where, ["identifier"]) && limit <= 32) {
717
+ const result = await this.client.findVerificationsByIdentifier({
718
+ organization_id: this.organizationId,
719
+ identifier: whereValue(where, "identifier", "findMany verification"),
720
+ after: null,
721
+ });
722
+ if (result.nextCursor !== undefined)
723
+ fail("result_bound_exceeded", "findMany verification");
724
+ return result.value.verifications.slice(0, limit).map(mapVerification);
725
+ }
726
+ return fail("unsupported_call_shape", `findMany ${model}`);
727
+ }
728
+ async adminUserWindow(where, limit, sortBy, offset = 0) {
729
+ if (limit > 100 || !Number.isSafeInteger(offset) || offset < 0 || offset > 4_096) {
730
+ fail("result_bound_exceeded", "findMany user");
731
+ }
732
+ const selected = adminUserWhere(where, "findMany user");
733
+ const parameters = {
734
+ organization_id: this.organizationId,
735
+ user_id: selected.userId,
736
+ limit,
737
+ offset: BigInt(offset),
738
+ };
739
+ if (selected.operator === undefined) {
740
+ if (sortBy !== undefined && (sortBy.field !== "email" || sortBy.direction !== "asc")) {
741
+ fail("unsupported_call_shape", "findMany user sort");
742
+ }
743
+ const result = await this.client.adminUsersAllEmailAsc({
744
+ ...parameters,
745
+ minimum_email: "",
746
+ });
747
+ return result.value;
748
+ }
749
+ const direction = sortBy?.direction ?? "asc";
750
+ const field = sortBy?.field ?? "email";
751
+ const searchParameters = { ...parameters, needle: selected.needle };
752
+ if (field === "createdAt" && direction === "desc" && selected.operator === "contains") {
753
+ return (await this.client.adminUsersEmailContainsCreatedDesc(searchParameters)).value;
754
+ }
755
+ if (field === "image" && direction === "asc" && selected.operator === "contains") {
756
+ return (await this.client.adminUsersEmailContainsImageAsc(searchParameters)).value;
757
+ }
758
+ if (field !== "email")
759
+ fail("unsupported_call_shape", "findMany user sort");
760
+ if (selected.operator === "contains") {
761
+ return direction === "asc"
762
+ ? (await this.client.adminUsersContainsAsc(searchParameters)).value
763
+ : (await this.client.adminUsersContainsDesc(searchParameters)).value;
764
+ }
765
+ if (selected.operator === "starts_with") {
766
+ return direction === "asc"
767
+ ? (await this.client.adminUsersStartsWithAsc(searchParameters)).value
768
+ : (await this.client.adminUsersStartsWithDesc(searchParameters)).value;
769
+ }
770
+ return direction === "asc"
771
+ ? (await this.client.adminUsersEndsWithAsc(searchParameters)).value
772
+ : (await this.client.adminUsersEndsWithDesc(searchParameters)).value;
773
+ }
774
+ async count(model, where) {
775
+ if (model !== "user")
776
+ fail("unsupported_call_shape", `count ${model}`);
777
+ const window = await this.adminUserWindow(where, 1, undefined, 0);
778
+ const total = window.total.value;
779
+ if (total > BigInt(Number.MAX_SAFE_INTEGER))
780
+ fail("result_bound_exceeded", "count user");
781
+ return Number(total);
782
+ }
783
+ async delete(model, where) {
784
+ if (model === "user" && exactWhere(where, ["id"])) {
785
+ const result = await this.client.deleteUser({
786
+ request_id: "",
787
+ organization_id: this.organizationId,
788
+ user_ids: [whereValue(where, "id", "delete user")],
789
+ });
790
+ if (result.outcome.outcome === "DeleteUserCascadeLimitExceeded") {
791
+ fail("result_bound_exceeded", "delete user", result.outcome.outcome);
792
+ }
793
+ if (!new Set(["UserDeleted", "DeleteUserMissing"]).has(result.outcome.outcome)) {
794
+ fail("declared_outcome", "delete user", result.outcome.outcome);
795
+ }
796
+ return;
797
+ }
798
+ if (model === "account" && exactWhere(where, ["id"])) {
799
+ const account = await this.findOne(model, where);
800
+ if (account === null)
801
+ return;
802
+ const result = await this.client.deleteAccount({
803
+ request_id: "",
804
+ organization_id: this.organizationId,
805
+ user_id: requiredString(account, "userId", "delete account"),
806
+ account_ids: [requiredString(account, "id", "delete account")],
807
+ });
808
+ if (!new Set(["AccountDeleted", "AccountMissing"]).has(result.outcome.outcome)) {
809
+ fail("declared_outcome", "delete account", result.outcome.outcome);
810
+ }
811
+ return;
812
+ }
813
+ if (model === "verification" && exactWhere(where, ["id"])) {
814
+ const result = await this.client.deleteVerification({
815
+ request_id: "",
816
+ organization_id: this.organizationId,
817
+ verification_ids: [whereValue(where, "id", "delete verification")],
818
+ });
819
+ if (!new Set(["VerificationDeleted", "VerificationMissing"]).has(result.outcome.outcome)) {
820
+ fail("declared_outcome", "delete verification", result.outcome.outcome);
821
+ }
822
+ return;
823
+ }
824
+ if (model === "verification" && exactWhere(where, ["identifier"])) {
825
+ const rows = await this.findMany("verification", where, 32);
826
+ if (rows.length === 0)
827
+ return;
828
+ const result = await this.client.deleteVerification({
829
+ request_id: "",
830
+ organization_id: this.organizationId,
831
+ verification_ids: rows.map((row) => requiredString(row, "id", "delete verification by identifier")),
832
+ });
833
+ requireOutcome(result, "delete verification by identifier", "VerificationDeleted");
834
+ return;
835
+ }
836
+ if (model === "session" &&
837
+ (exactWhere(where, ["id"]) || exactWhere(where, ["token"]))) {
838
+ const session = await this.findOne("session", where);
839
+ if (session === null)
840
+ return;
841
+ const result = await this.client.deleteSession({
842
+ request_id: "",
843
+ organization_id: this.organizationId,
844
+ user_id: requiredString(session, "userId", "delete session"),
845
+ session_ids: [requiredString(session, "id", "delete session")],
846
+ });
847
+ if (!new Set(["SessionDeleted", "SessionMissing"]).has(result.outcome.outcome)) {
848
+ fail("declared_outcome", "delete session", result.outcome.outcome);
849
+ }
850
+ return;
851
+ }
852
+ fail("unsupported_call_shape", `delete ${model}`);
853
+ }
854
+ async deleteMany(model, where) {
855
+ if (where.length === 0 && this.resetModelForTesting !== undefined) {
856
+ return this.resetModelForTesting(model);
857
+ }
858
+ if (model === "session" && exactWhere(where, ["userId"])) {
859
+ const rows = await this.findMany(model, where, 32);
860
+ if (rows.length === 0)
861
+ return 0;
862
+ const result = await this.client.deleteSession({
863
+ request_id: "",
864
+ organization_id: this.organizationId,
865
+ user_id: whereValue(where, "userId", "deleteMany session"),
866
+ session_ids: rows.map((row) => requiredString(row, "id", "deleteMany session")),
867
+ });
868
+ requireOutcome(result, "deleteMany session", "SessionDeleted");
869
+ return rows.length;
870
+ }
871
+ if (model === "session" &&
872
+ exactWhereShape(where, [{ field: "token", operator: "in" }])) {
873
+ const rows = await this.findMany(model, where, 32);
874
+ const byUser = new Map();
875
+ for (const row of rows) {
876
+ const userId = requiredString(row, "userId", "deleteMany session token set");
877
+ const ids = byUser.get(userId) ?? [];
878
+ ids.push(requiredString(row, "id", "deleteMany session token set"));
879
+ byUser.set(userId, ids);
880
+ }
881
+ for (const [userId, sessionIds] of byUser) {
882
+ const result = await this.client.deleteSession({
883
+ request_id: "",
884
+ organization_id: this.organizationId,
885
+ user_id: userId,
886
+ session_ids: sessionIds,
887
+ });
888
+ requireOutcome(result, "deleteMany session token set", "SessionDeleted");
889
+ }
890
+ return rows.length;
891
+ }
892
+ if (model === "account" && exactWhere(where, ["userId"])) {
893
+ const rows = await this.findMany(model, where, 100);
894
+ if (rows.length === 0)
895
+ return 0;
896
+ if (rows.length > 32)
897
+ fail("result_bound_exceeded", "deleteMany account");
898
+ const result = await this.client.deleteAccount({
899
+ request_id: "",
900
+ organization_id: this.organizationId,
901
+ user_id: whereValue(where, "userId", "deleteMany account"),
902
+ account_ids: rows.map((row) => requiredString(row, "id", "deleteMany account")),
903
+ });
904
+ requireOutcome(result, "deleteMany account", "AccountDeleted");
905
+ return rows.length;
906
+ }
907
+ if (model === "verification" &&
908
+ exactWhereShape(where, [{ field: "expiresAt", operator: "lt" }])) {
909
+ const result = await this.client.findExpiredVerificationIds({
910
+ organization_id: this.organizationId,
911
+ expires_before: toTimestamp(whereDate(where, "expiresAt", "deleteMany expired verification")),
912
+ after: null,
913
+ });
914
+ if (result.nextCursor !== undefined) {
915
+ fail("result_bound_exceeded", "deleteMany expired verification");
916
+ }
917
+ const ids = result.value.verifications.map((row) => row.verification_id);
918
+ if (ids.length === 0)
919
+ return 0;
920
+ const deleted = await this.client.deleteVerification({
921
+ request_id: "",
922
+ organization_id: this.organizationId,
923
+ verification_ids: ids,
924
+ });
925
+ requireOutcome(deleted, "deleteMany expired verification", "VerificationDeleted");
926
+ return ids.length;
927
+ }
928
+ if (model !== "verification" || !exactWhere(where, ["identifier"])) {
929
+ return fail("unsupported_call_shape", `deleteMany ${model}`);
930
+ }
931
+ const rows = await this.findMany("verification", where, 32);
932
+ if (rows.length === 0)
933
+ return 0;
934
+ const result = await this.client.consumeVerificationSet({
935
+ request_id: "",
936
+ organization_id: this.organizationId,
937
+ verification_ids: rows.map((row) => requiredString(row, "id", "deleteMany verification")),
938
+ });
939
+ requireOutcome(result, "deleteMany verification", "VerificationSetConsumed");
940
+ return rows.length;
941
+ }
942
+ async consumeOne(model, where) {
943
+ if (model !== "verification" || !exactWhere(where, ["id"])) {
944
+ return fail("unsupported_call_shape", `consumeOne ${model}`);
945
+ }
946
+ const result = await this.client.consumeVerification({
947
+ request_id: "",
948
+ organization_id: this.organizationId,
949
+ verification_id: whereValue(where, "id", "consumeOne verification"),
950
+ });
951
+ if (result.outcome.outcome === "VerificationConsumeMissing")
952
+ return null;
953
+ requireOutcome(result, "consumeOne verification", "VerificationConsumed");
954
+ return mapVerification(result.outcome);
955
+ }
956
+ async updateMany(model, where, updateValue) {
957
+ if (model !== "account" || !exactWhere(where, ["userId", "providerId"])) {
958
+ return fail("unsupported_call_shape", `updateMany ${model}`);
959
+ }
960
+ const update = asRecord(updateValue, "updateMany account");
961
+ const updateFields = Object.keys(update).sort();
962
+ if (JSON.stringify(updateFields) !== JSON.stringify(["password", "updatedAt"])) {
963
+ fail("unsupported_call_shape", "updateMany account");
964
+ }
965
+ const userId = whereValue(where, "userId", "updateMany account");
966
+ const providerId = whereValue(where, "providerId", "updateMany account");
967
+ const updatedAt = requiredDate(update, "updatedAt", "updateMany account");
968
+ for (let attempt = 0; attempt < 3; attempt += 1) {
969
+ const found = await this.client.findAccountsByUserProvider({
970
+ organization_id: this.organizationId,
971
+ user_id: userId,
972
+ provider_id: providerId,
973
+ after: null,
974
+ });
975
+ if (found.nextCursor !== undefined)
976
+ fail("result_bound_exceeded", "updateMany account");
977
+ const accounts = found.value.accounts;
978
+ if (accounts.length === 0)
979
+ return 0;
980
+ const result = await this.client.updateAccountPasswords({
981
+ request_id: "",
982
+ organization_id: this.organizationId,
983
+ user_id: userId,
984
+ updates: accounts.map((account) => ({
985
+ organization_id: this.organizationId,
986
+ user_id: userId,
987
+ account_id: account.id,
988
+ provider_id: providerId,
989
+ password: nullableString(update, "password", "updateMany account"),
990
+ updated_at: toTimestamp(updatedAt),
991
+ expected_revision: account.revision,
992
+ })),
993
+ });
994
+ if (result.outcome.outcome === "PasswordAccountStale" ||
995
+ result.outcome.outcome === "PasswordAccountMissing" ||
996
+ result.outcome.outcome === "PasswordProviderMismatch") {
997
+ continue;
998
+ }
999
+ requireOutcome(result, "updateMany account", "AccountPasswordsUpdated");
1000
+ return accounts.length;
1001
+ }
1002
+ return fail("declared_outcome", "updateMany account", "PasswordAccountStale");
1003
+ }
1004
+ async commitSignup(records) {
1005
+ if (records.length !== 3)
1006
+ fail("unsupported_transaction_shape", "transaction signup");
1007
+ const byModel = new Map(records.map((record) => [record.model, record.data]));
1008
+ if (byModel.size !== 3 || !["user", "account", "session"].every((name) => byModel.has(name))) {
1009
+ fail("unsupported_transaction_shape", "transaction signup");
1010
+ }
1011
+ const user = byModel.get("user");
1012
+ const account = byModel.get("account");
1013
+ const session = byModel.get("session");
1014
+ const userId = requiredString(user, "id", "transaction signup");
1015
+ const sessionToken = requiredString(session, "token", "transaction signup");
1016
+ if (requiredString(account, "userId", "transaction signup") !== userId ||
1017
+ requiredString(session, "userId", "transaction signup") !== userId) {
1018
+ fail("unsupported_transaction_shape", "transaction signup");
1019
+ }
1020
+ const result = await this.client.createSignupGraph({
1021
+ request_id: "",
1022
+ organization_id: this.organizationId,
1023
+ user_id: userId,
1024
+ user_name: requiredString(user, "name", "transaction signup"),
1025
+ user_email: requiredString(user, "email", "transaction signup"),
1026
+ user_email_verified: requiredBoolean(user, "emailVerified", "transaction signup"),
1027
+ user_image: nullableString(user, "image", "transaction signup"),
1028
+ user_created_at: toTimestamp(requiredDate(user, "createdAt", "transaction signup")),
1029
+ user_updated_at: toTimestamp(requiredDate(user, "updatedAt", "transaction signup")),
1030
+ account_id: requiredString(account, "id", "transaction signup"),
1031
+ external_account_id: requiredString(account, "accountId", "transaction signup"),
1032
+ provider_id: requiredString(account, "providerId", "transaction signup"),
1033
+ account_password: nullableString(account, "password", "transaction signup"),
1034
+ account_created_at: toTimestamp(requiredDate(account, "createdAt", "transaction signup")),
1035
+ account_updated_at: toTimestamp(requiredDate(account, "updatedAt", "transaction signup")),
1036
+ session_id: requiredString(session, "id", "transaction signup"),
1037
+ session_token: sessionToken,
1038
+ session_expires_at: toTimestamp(requiredDate(session, "expiresAt", "transaction signup")),
1039
+ session_ip_address: nullableString(session, "ipAddress", "transaction signup"),
1040
+ session_user_agent: nullableString(session, "userAgent", "transaction signup"),
1041
+ session_created_at: toTimestamp(requiredDate(session, "createdAt", "transaction signup")),
1042
+ session_updated_at: toTimestamp(requiredDate(session, "updatedAt", "transaction signup")),
1043
+ });
1044
+ requireOutcome(result, "transaction signup", "SignupGraphCreated");
1045
+ }
1046
+ async commitOAuthUser(records) {
1047
+ if (records.length !== 2)
1048
+ fail("unsupported_transaction_shape", "transaction OAuth user");
1049
+ const byModel = new Map(records.map((record) => [record.model, record.data]));
1050
+ if (byModel.size !== 2 || !["user", "account"].every((name) => byModel.has(name))) {
1051
+ fail("unsupported_transaction_shape", "transaction OAuth user");
1052
+ }
1053
+ const user = byModel.get("user");
1054
+ const account = byModel.get("account");
1055
+ const userId = requiredString(user, "id", "transaction OAuth user");
1056
+ if (requiredString(account, "userId", "transaction OAuth user") !== userId) {
1057
+ fail("unsupported_transaction_shape", "transaction OAuth user");
1058
+ }
1059
+ const result = await this.client.createOAuthUserGraph({
1060
+ request_id: "",
1061
+ organization_id: this.organizationId,
1062
+ user_id: userId,
1063
+ user_name: requiredString(user, "name", "transaction OAuth user"),
1064
+ user_email: requiredString(user, "email", "transaction OAuth user"),
1065
+ user_email_verified: requiredBoolean(user, "emailVerified", "transaction OAuth user"),
1066
+ user_image: nullableString(user, "image", "transaction OAuth user"),
1067
+ user_created_at: toTimestamp(requiredDate(user, "createdAt", "transaction OAuth user")),
1068
+ user_updated_at: toTimestamp(requiredDate(user, "updatedAt", "transaction OAuth user")),
1069
+ account_id: requiredString(account, "id", "transaction OAuth user"),
1070
+ external_account_id: requiredString(account, "accountId", "transaction OAuth user"),
1071
+ provider_id: requiredString(account, "providerId", "transaction OAuth user"),
1072
+ access_token: nullableString(account, "accessToken", "transaction OAuth user"),
1073
+ refresh_token: nullableString(account, "refreshToken", "transaction OAuth user"),
1074
+ access_token_expires_at: timestampOrNull(nullableDate(account, "accessTokenExpiresAt", "transaction OAuth user")),
1075
+ refresh_token_expires_at: timestampOrNull(nullableDate(account, "refreshTokenExpiresAt", "transaction OAuth user")),
1076
+ scope: nullableString(account, "scope", "transaction OAuth user"),
1077
+ id_token: nullableString(account, "idToken", "transaction OAuth user"),
1078
+ password: nullableString(account, "password", "transaction OAuth user"),
1079
+ account_created_at: toTimestamp(requiredDate(account, "createdAt", "transaction OAuth user")),
1080
+ account_updated_at: toTimestamp(requiredDate(account, "updatedAt", "transaction OAuth user")),
1081
+ });
1082
+ requireOutcome(result, "transaction OAuth user", "OAuthUserGraphCreated");
1083
+ }
1084
+ async commitVerificationConsume(rows) {
1085
+ if (rows.length < 1 || rows.length > 32) {
1086
+ fail("unsupported_transaction_shape", "transaction verification consume");
1087
+ }
1088
+ const result = await this.client.consumeVerificationSet({
1089
+ request_id: "",
1090
+ organization_id: this.organizationId,
1091
+ verification_ids: rows.map((row) => requiredString(row, "id", "transaction verification consume")),
1092
+ });
1093
+ requireOutcome(result, "transaction verification consume", "VerificationSetConsumed");
1094
+ }
1095
+ }
1096
+ function genericCreate(implementation) {
1097
+ return implementation;
1098
+ }
1099
+ function genericFindOne(implementation) {
1100
+ return implementation;
1101
+ }
1102
+ function genericFindMany(implementation) {
1103
+ return implementation;
1104
+ }
1105
+ function genericConsumeOne(implementation) {
1106
+ return implementation;
1107
+ }
1108
+ function genericUpdate(implementation) {
1109
+ return implementation;
1110
+ }
1111
+ class TransactionRecorder {
1112
+ dispatcher;
1113
+ creates = [];
1114
+ consumeAttempted = false;
1115
+ consumedVerification;
1116
+ verificationDeleteRows;
1117
+ verificationDeleteIdentifier;
1118
+ constructor(dispatcher) {
1119
+ this.dispatcher = dispatcher;
1120
+ }
1121
+ customAdapter() {
1122
+ return {
1123
+ create: genericCreate(async ({ model, data }) => {
1124
+ const copied = { ...data };
1125
+ this.creates.push({ model, data: copied });
1126
+ return copied;
1127
+ }),
1128
+ findOne: genericFindOne(async ({ model, where, join }) => this.dispatcher.findOne(model, where, join)),
1129
+ findMany: genericFindMany(async ({ model, where, limit, sortBy, offset, join }) => this.dispatcher.findMany(model, where, limit, sortBy, offset, join)),
1130
+ count: async () => fail("unsupported_transaction_shape", "transaction count"),
1131
+ update: async () => fail("unsupported_transaction_shape", "transaction update"),
1132
+ updateMany: async () => fail("unsupported_transaction_shape", "transaction updateMany"),
1133
+ delete: async () => fail("unsupported_transaction_shape", "transaction delete"),
1134
+ deleteMany: async ({ model, where }) => {
1135
+ if (model !== "verification" ||
1136
+ !exactWhere(where, ["identifier"]) ||
1137
+ !this.consumeAttempted ||
1138
+ this.verificationDeleteRows !== undefined) {
1139
+ return fail("unsupported_transaction_shape", "transaction deleteMany");
1140
+ }
1141
+ this.verificationDeleteIdentifier = whereValue(where, "identifier", "transaction deleteMany verification");
1142
+ this.verificationDeleteRows = await this.dispatcher.findMany("verification", where, 32);
1143
+ return this.verificationDeleteRows.length;
1144
+ },
1145
+ consumeOne: genericConsumeOne(async ({ model, where }) => {
1146
+ if (model !== "verification" ||
1147
+ !exactWhere(where, ["id"]) ||
1148
+ this.consumeAttempted) {
1149
+ return fail("unsupported_transaction_shape", "transaction consumeOne");
1150
+ }
1151
+ this.consumeAttempted = true;
1152
+ const row = await this.dispatcher.consumeOne(model, where);
1153
+ this.consumedVerification = row;
1154
+ return row;
1155
+ }),
1156
+ incrementOne: async () => fail("unsupported_transaction_shape", "transaction incrementOne"),
1157
+ };
1158
+ }
1159
+ async commit() {
1160
+ const hasVerificationMutation = this.consumeAttempted || this.verificationDeleteRows !== undefined;
1161
+ if (this.creates.length > 0 && hasVerificationMutation) {
1162
+ fail("unsupported_transaction_shape", "mixed transaction");
1163
+ }
1164
+ if (this.creates.length > 0) {
1165
+ if (this.creates.length === 2) {
1166
+ await this.dispatcher.commitOAuthUser(this.creates);
1167
+ }
1168
+ else {
1169
+ await this.dispatcher.commitSignup(this.creates);
1170
+ }
1171
+ return;
1172
+ }
1173
+ if (hasVerificationMutation) {
1174
+ if (!this.consumeAttempted || this.consumedVerification === undefined) {
1175
+ fail("unsupported_transaction_shape", "transaction verification consume");
1176
+ }
1177
+ if (this.verificationDeleteRows === undefined)
1178
+ return;
1179
+ if (this.consumedVerification !== null &&
1180
+ this.consumedVerification.identifier !== this.verificationDeleteIdentifier) {
1181
+ fail("unsupported_transaction_shape", "transaction verification consume");
1182
+ }
1183
+ if (this.verificationDeleteRows.length > 0) {
1184
+ await this.dispatcher.commitVerificationConsume(this.verificationDeleteRows);
1185
+ }
1186
+ }
1187
+ }
1188
+ }
1189
+ function customAdapter(dispatcher) {
1190
+ return {
1191
+ create: genericCreate(async ({ model, data }) => dispatcher.create(model, data)),
1192
+ findOne: genericFindOne(async ({ model, where, join }) => dispatcher.findOne(model, where, join)),
1193
+ findMany: genericFindMany(async ({ model, where, limit, sortBy, offset, join }) => dispatcher.findMany(model, where, limit, sortBy, offset, join)),
1194
+ count: async ({ model, where }) => dispatcher.count(model, where),
1195
+ update: genericUpdate(async ({ model, where, update }) => dispatcher.update(model, where, update)),
1196
+ updateMany: async ({ model, where, update }) => dispatcher.updateMany(model, where, update),
1197
+ delete: async ({ model, where }) => dispatcher.delete(model, where),
1198
+ deleteMany: async ({ model, where }) => dispatcher.deleteMany(model, where),
1199
+ consumeOne: genericConsumeOne(async ({ model, where }) => dispatcher.consumeOne(model, where)),
1200
+ incrementOne: async ({ model }) => fail("unsupported_call_shape", `incrementOne ${model}`),
1201
+ };
1202
+ }
1203
+ const BASE_FACTORY_CONFIG = {
1204
+ adapterId: "riffdb",
1205
+ adapterName: "RiffDB Better Auth Core",
1206
+ usePlural: false,
1207
+ supportsNumericIds: false,
1208
+ supportsUUIDs: false,
1209
+ supportsJSON: false,
1210
+ supportsDates: true,
1211
+ supportsBooleans: true,
1212
+ supportsArrays: false,
1213
+ };
1214
+ export function buildRiffDBBetterAuthAdapter(config) {
1215
+ assertSupportedBetterAuthInstallation();
1216
+ validateConfiguration(config);
1217
+ return (options) => {
1218
+ const dispatcher = new CoreDispatcher(config.client.causalSession(), config.organizationId, config.resetModelForTesting);
1219
+ const factory = createAdapterFactory({
1220
+ config: {
1221
+ ...BASE_FACTORY_CONFIG,
1222
+ debugLogs: false,
1223
+ transaction: async (callback) => {
1224
+ const recorder = new TransactionRecorder(dispatcher);
1225
+ const transactionFactory = createAdapterFactory({
1226
+ config: {
1227
+ ...BASE_FACTORY_CONFIG,
1228
+ debugLogs: false,
1229
+ transaction: false,
1230
+ },
1231
+ adapter: ({ options: transactionOptions, schema }) => {
1232
+ validateResolvedSchema(transactionOptions, schema);
1233
+ return recorder.customAdapter();
1234
+ },
1235
+ });
1236
+ const transactionAdapter = transactionFactory(options);
1237
+ const result = await callback(transactionAdapter);
1238
+ await recorder.commit();
1239
+ return result;
1240
+ },
1241
+ },
1242
+ adapter: ({ options: resolvedOptions, schema }) => {
1243
+ validateResolvedSchema(resolvedOptions, schema);
1244
+ return customAdapter(dispatcher);
1245
+ },
1246
+ });
1247
+ return factory(options);
1248
+ };
1249
+ }
1250
+ export function riffdbAdapter(config) {
1251
+ const commandAttemptBudget = config.commandAttemptBudget ?? 3;
1252
+ if (!Number.isInteger(commandAttemptBudget) || commandAttemptBudget < 1 || commandAttemptBudget > 10) {
1253
+ fail("invalid_configuration", "commandAttemptBudget");
1254
+ }
1255
+ const transport = new DriverGeneratedApplicationTransport(config.driver);
1256
+ return buildRiffDBBetterAuthAdapter({
1257
+ client: new RiffDBBetterAuthCoreClient(transport, commandAttemptBudget),
1258
+ organizationId: config.organizationId,
1259
+ ...(config.debugLogs === undefined ? {} : { debugLogs: config.debugLogs }),
1260
+ });
1261
+ }
1262
+ //# sourceMappingURL=adapter.js.map