@riffdb/better-auth 0.2.0 → 0.2.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 (176) hide show
  1. package/DESIGN.md +90 -14
  2. package/README.md +157 -25
  3. package/dist/adapter.d.ts +7 -1
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js +385 -47
  6. package/dist/adapter.js.map +1 -1
  7. package/dist/generated/client.d.ts +267 -26
  8. package/dist/generated/client.d.ts.map +1 -1
  9. package/dist/generated/client.js +479 -69
  10. package/dist/generated/client.js.map +1 -1
  11. package/dist/index.d.ts +3 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +3 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/plugin-dispatch.d.ts +37 -0
  16. package/dist/plugin-dispatch.d.ts.map +1 -0
  17. package/dist/plugin-dispatch.js +293 -0
  18. package/dist/plugin-dispatch.js.map +1 -0
  19. package/dist/profile-contract.d.ts +3 -0
  20. package/dist/profile-contract.d.ts.map +1 -1
  21. package/dist/profile-contract.js +8 -0
  22. package/dist/profile-contract.js.map +1 -1
  23. package/dist/profile-plan.js +1 -1
  24. package/dist/profile-plan.js.map +1 -1
  25. package/dist/profile.d.ts +2 -1
  26. package/dist/profile.d.ts.map +1 -1
  27. package/dist/profile.js +2 -1
  28. package/dist/profile.js.map +1 -1
  29. package/dist/profiles/custom-fields/profile.d.ts +3 -0
  30. package/dist/profiles/custom-fields/profile.d.ts.map +1 -0
  31. package/dist/profiles/custom-fields/profile.js +475 -0
  32. package/dist/profiles/custom-fields/profile.js.map +1 -0
  33. package/dist/profiles/custom-fields/src/generated/client.d.ts +2710 -0
  34. package/dist/profiles/custom-fields/src/generated/client.d.ts.map +1 -0
  35. package/dist/profiles/custom-fields/src/generated/client.js +2663 -0
  36. package/dist/profiles/custom-fields/src/generated/client.js.map +1 -0
  37. package/dist/profiles/runtime/profile.d.ts +3 -0
  38. package/dist/profiles/runtime/profile.d.ts.map +1 -0
  39. package/dist/profiles/runtime/profile.js +1065 -0
  40. package/dist/profiles/runtime/profile.js.map +1 -0
  41. package/dist/profiles/runtime/src/generated/client.d.ts +4274 -0
  42. package/dist/profiles/runtime/src/generated/client.d.ts.map +1 -0
  43. package/dist/profiles/runtime/src/generated/client.js +4607 -0
  44. package/dist/profiles/runtime/src/generated/client.js.map +1 -0
  45. package/dist/runtime-materialize.d.ts +10 -0
  46. package/dist/runtime-materialize.d.ts.map +1 -0
  47. package/dist/runtime-materialize.js +344 -0
  48. package/dist/runtime-materialize.js.map +1 -0
  49. package/dist/runtime-profile.d.ts +34 -0
  50. package/dist/runtime-profile.d.ts.map +1 -0
  51. package/dist/runtime-profile.js +203 -0
  52. package/dist/runtime-profile.js.map +1 -0
  53. package/generated/go/client.go +350 -96
  54. package/generated/mcp/tools.json +3338 -1768
  55. package/generated/python/client.py +603 -26
  56. package/generated/riffdb.application.exact.json +1 -1
  57. package/generated/riffdb.contract.bundle +0 -0
  58. package/generated/rust/client.rs +522 -26
  59. package/package.json +34 -13
  60. package/profiles/custom-fields/generated/go/client.go +2578 -0
  61. package/profiles/custom-fields/generated/mcp/tools.json +15998 -0
  62. package/profiles/custom-fields/generated/python/client.py +5141 -0
  63. package/profiles/custom-fields/generated/riffdb.application.exact.json +1 -0
  64. package/profiles/custom-fields/generated/riffdb.contract.bundle +0 -0
  65. package/profiles/custom-fields/generated/rust/client.rs +7980 -0
  66. package/profiles/custom-fields/profile.ts +474 -0
  67. package/profiles/custom-fields/riffdb/contract.riff +568 -0
  68. package/profiles/custom-fields/riffdb/queries/admin_users_all_email_asc.riffq +24 -0
  69. package/profiles/custom-fields/riffdb/queries/admin_users_contains_asc.riffq +24 -0
  70. package/profiles/custom-fields/riffdb/queries/admin_users_contains_desc.riffq +24 -0
  71. package/profiles/custom-fields/riffdb/queries/admin_users_email_contains_created_desc.riffq +24 -0
  72. package/profiles/custom-fields/riffdb/queries/admin_users_email_contains_image_asc.riffq +24 -0
  73. package/profiles/custom-fields/riffdb/queries/admin_users_ends_with_asc.riffq +24 -0
  74. package/profiles/custom-fields/riffdb/queries/admin_users_ends_with_desc.riffq +24 -0
  75. package/profiles/custom-fields/riffdb/queries/admin_users_starts_with_asc.riffq +24 -0
  76. package/profiles/custom-fields/riffdb/queries/admin_users_starts_with_desc.riffq +24 -0
  77. package/profiles/custom-fields/riffdb/queries/find_account_by_id.riffq +24 -0
  78. package/profiles/custom-fields/riffdb/queries/find_account_by_provider.riffq +26 -0
  79. package/profiles/custom-fields/riffdb/queries/find_accounts_by_user_id.riffq +25 -0
  80. package/profiles/custom-fields/riffdb/queries/find_accounts_by_user_provider.riffq +27 -0
  81. package/profiles/custom-fields/riffdb/queries/find_active_sessions_by_user_id.riffq +23 -0
  82. package/profiles/custom-fields/riffdb/queries/find_credential_account.riffq +25 -0
  83. package/profiles/custom-fields/riffdb/queries/find_expired_verification_ids.riffq +17 -0
  84. package/profiles/custom-fields/riffdb/queries/find_latest_verification.riffq +20 -0
  85. package/profiles/custom-fields/riffdb/queries/find_session_by_id.riffq +20 -0
  86. package/profiles/custom-fields/riffdb/queries/find_session_by_token.riffq +20 -0
  87. package/profiles/custom-fields/riffdb/queries/find_sessions_by_token_set.riffq +20 -0
  88. package/profiles/custom-fields/riffdb/queries/find_sessions_by_user_id.riffq +21 -0
  89. package/profiles/custom-fields/riffdb/queries/find_user_by_email.riffq +16 -0
  90. package/profiles/custom-fields/riffdb/queries/find_user_by_id.riffq +15 -0
  91. package/profiles/custom-fields/riffdb/queries/find_verification_by_id.riffq +19 -0
  92. package/profiles/custom-fields/riffdb/queries/find_verifications_by_identifier.riffq +21 -0
  93. package/profiles/custom-fields/riffdb/queries/profile_account_with_user_by_provider.riffq +31 -0
  94. package/profiles/custom-fields/riffdb/queries/profile_session_with_user_by_id.riffq +14 -0
  95. package/profiles/custom-fields/riffdb/queries/profile_session_with_user_by_token.riffq +14 -0
  96. package/profiles/custom-fields/riffdb/queries/profile_sessions_with_users_by_token_set.riffq +25 -0
  97. package/profiles/custom-fields/riffdb/queries/profile_user_with_accounts_by_email.riffq +19 -0
  98. package/profiles/custom-fields/riffdb/queries/profile_user_with_accounts_by_id.riffq +19 -0
  99. package/profiles/custom-fields/riffdb.application.json +217 -0
  100. package/profiles/custom-fields/riffdb.application.lock.json +1 -0
  101. package/profiles/custom-fields/riffdb.toml +3 -0
  102. package/profiles/custom-fields/src/generated/client.ts +3096 -0
  103. package/profiles/organization-security/materialization.json +3 -0
  104. package/profiles/organization-security/riffdb/contract.riff +12 -0
  105. package/profiles/organization-security/riffdb/queries/find_team_by_id.riffq +1 -1
  106. package/profiles/organization-security/riffdb/queries/find_team_member_by_id.riffq +1 -1
  107. package/profiles/organization-security/riffdb/queries/find_team_member_by_membership_key.riffq +16 -0
  108. package/profiles/organization-security/riffdb/queries/find_team_member_by_team_id.riffq +1 -1
  109. package/profiles/organization-security/riffdb.application.json +5 -0
  110. package/profiles/organization-security.profile.json +27 -1
  111. package/profiles/runtime/generated/go/client.go +4511 -0
  112. package/profiles/runtime/generated/mcp/tools.json +30004 -0
  113. package/profiles/runtime/generated/python/client.py +9337 -0
  114. package/profiles/runtime/generated/riffdb.application.exact.json +1 -0
  115. package/profiles/runtime/generated/riffdb.contract.bundle +0 -0
  116. package/profiles/runtime/generated/rust/client.rs +13297 -0
  117. package/profiles/runtime/profile.ts +1064 -0
  118. package/profiles/runtime/riffdb/contract.riff +1053 -0
  119. package/profiles/runtime/riffdb/queries/admin_users_all_email_asc.riffq +24 -0
  120. package/profiles/runtime/riffdb/queries/admin_users_contains_asc.riffq +24 -0
  121. package/profiles/runtime/riffdb/queries/admin_users_contains_desc.riffq +24 -0
  122. package/profiles/runtime/riffdb/queries/admin_users_email_contains_created_desc.riffq +24 -0
  123. package/profiles/runtime/riffdb/queries/admin_users_email_contains_image_asc.riffq +24 -0
  124. package/profiles/runtime/riffdb/queries/admin_users_ends_with_asc.riffq +24 -0
  125. package/profiles/runtime/riffdb/queries/admin_users_ends_with_desc.riffq +24 -0
  126. package/profiles/runtime/riffdb/queries/admin_users_starts_with_asc.riffq +24 -0
  127. package/profiles/runtime/riffdb/queries/admin_users_starts_with_desc.riffq +24 -0
  128. package/profiles/runtime/riffdb/queries/find_account_by_id.riffq +24 -0
  129. package/profiles/runtime/riffdb/queries/find_account_by_provider.riffq +26 -0
  130. package/profiles/runtime/riffdb/queries/find_accounts_by_user_id.riffq +25 -0
  131. package/profiles/runtime/riffdb/queries/find_accounts_by_user_provider.riffq +27 -0
  132. package/profiles/runtime/riffdb/queries/find_active_sessions_by_user_id.riffq +23 -0
  133. package/profiles/runtime/riffdb/queries/find_credential_account.riffq +25 -0
  134. package/profiles/runtime/riffdb/queries/find_expired_verification_ids.riffq +17 -0
  135. package/profiles/runtime/riffdb/queries/find_latest_verification.riffq +20 -0
  136. package/profiles/runtime/riffdb/queries/find_session_by_id.riffq +20 -0
  137. package/profiles/runtime/riffdb/queries/find_session_by_token.riffq +20 -0
  138. package/profiles/runtime/riffdb/queries/find_sessions_by_token_set.riffq +20 -0
  139. package/profiles/runtime/riffdb/queries/find_sessions_by_user_id.riffq +21 -0
  140. package/profiles/runtime/riffdb/queries/find_user_by_email.riffq +16 -0
  141. package/profiles/runtime/riffdb/queries/find_user_by_id.riffq +15 -0
  142. package/profiles/runtime/riffdb/queries/find_verification_by_id.riffq +19 -0
  143. package/profiles/runtime/riffdb/queries/find_verifications_by_identifier.riffq +21 -0
  144. package/profiles/runtime/riffdb/queries/profile_account_with_user_by_provider.riffq +31 -0
  145. package/profiles/runtime/riffdb/queries/profile_invitation_by_email.riffq +17 -0
  146. package/profiles/runtime/riffdb/queries/profile_invitation_by_id.riffq +17 -0
  147. package/profiles/runtime/riffdb/queries/profile_invitation_by_organization_email_status.riffq +19 -0
  148. package/profiles/runtime/riffdb/queries/profile_invitation_by_organization_id.riffq +17 -0
  149. package/profiles/runtime/riffdb/queries/profile_invitation_by_organization_status.riffq +18 -0
  150. package/profiles/runtime/riffdb/queries/profile_member_by_id.riffq +27 -0
  151. package/profiles/runtime/riffdb/queries/profile_member_by_organization.riffq +20 -0
  152. package/profiles/runtime/riffdb/queries/profile_member_by_organization_created_at_asc.riffq +20 -0
  153. package/profiles/runtime/riffdb/queries/profile_member_by_organization_created_at_desc.riffq +20 -0
  154. package/profiles/runtime/riffdb/queries/profile_member_by_organization_role_asc.riffq +20 -0
  155. package/profiles/runtime/riffdb/queries/profile_member_by_organization_role_desc.riffq +20 -0
  156. package/profiles/runtime/riffdb/queries/profile_member_by_organization_user.riffq +28 -0
  157. package/profiles/runtime/riffdb/queries/profile_member_by_user.riffq +27 -0
  158. package/profiles/runtime/riffdb/queries/profile_organization_by_id.riffq +27 -0
  159. package/profiles/runtime/riffdb/queries/profile_organization_by_slug.riffq +27 -0
  160. package/profiles/runtime/riffdb/queries/profile_session_with_user_by_id.riffq +14 -0
  161. package/profiles/runtime/riffdb/queries/profile_session_with_user_by_token.riffq +14 -0
  162. package/profiles/runtime/riffdb/queries/profile_sessions_with_users_by_token_set.riffq +25 -0
  163. package/profiles/runtime/riffdb/queries/profile_two_factor_by_id.riffq +17 -0
  164. package/profiles/runtime/riffdb/queries/profile_two_factor_by_user.riffq +17 -0
  165. package/profiles/runtime/riffdb/queries/profile_user_with_accounts_by_email.riffq +19 -0
  166. package/profiles/runtime/riffdb/queries/profile_user_with_accounts_by_id.riffq +19 -0
  167. package/profiles/runtime/riffdb/queries/profile_users_by_id_set.riffq +8 -0
  168. package/profiles/runtime/riffdb.application.json +324 -0
  169. package/profiles/runtime/riffdb.application.lock.json +1 -0
  170. package/profiles/runtime/riffdb.toml +3 -0
  171. package/profiles/runtime/src/generated/client.ts +5186 -0
  172. package/riffdb/contract.riff +58 -0
  173. package/riffdb/queries/find_credential_account.riffq +25 -0
  174. package/riffdb.application.json +106 -26
  175. package/riffdb.application.lock.json +1 -1
  176. package/src/generated/client.ts +2746 -0
package/dist/adapter.js CHANGED
@@ -4,7 +4,9 @@ import { createRequire } from "node:module";
4
4
  import { DriverGeneratedApplicationTransport, } from "@riffdb/client";
5
5
  import { createAdapterFactory, } from "better-auth/adapters";
6
6
  import { RiffDBBetterAuthCoreClient, } from "./generated/client.js";
7
- import { BETTER_AUTH_PINNED_VERSION } from "./profile.js";
7
+ import { BETTER_AUTH_PINNED_VERSION, SUPPORTED_BETTER_AUTH_VERSIONS } from "./profile.js";
8
+ import { validateRuntimeProfile, instantiateProfileClient, encodeProfileField, decodeProfileField, storageField } from "./runtime-profile.js";
9
+ import { ProfilePluginDispatcher } from "./plugin-dispatch.js";
8
10
  const CORE_SCHEMA_FIELDS = {
9
11
  user: ["name", "email", "emailVerified", "image", "createdAt", "updatedAt"],
10
12
  session: [
@@ -60,7 +62,7 @@ function validateConfiguration(config) {
60
62
  }
61
63
  }
62
64
  export function assertSupportedBetterAuthInstallation(installedVersion = resolveInstalledBetterAuthVersion()) {
63
- if (installedVersion !== BETTER_AUTH_PINNED_VERSION) {
65
+ if (!SUPPORTED_BETTER_AUTH_VERSIONS.some((version) => version === installedVersion)) {
64
66
  fail("invalid_configuration", "betterAuthVersion");
65
67
  }
66
68
  }
@@ -84,12 +86,27 @@ function resolveInstalledBetterAuthVersion() {
84
86
  }
85
87
  fail("invalid_configuration", "betterAuthVersion");
86
88
  }
87
- function validateResolvedSchema(options, schema) {
89
+ function validateResolvedSchema(options, schema, profile) {
88
90
  if (options.advanced?.database?.generateId !== "uuid") {
89
91
  fail("invalid_configuration", "generateId");
90
92
  }
91
- if ((options.plugins?.length ?? 0) !== 0)
92
- fail("invalid_configuration", "plugins");
93
+ if (options.databaseHooks !== undefined || options.emailAndPassword?.revokeSessionsOnPasswordReset) {
94
+ fail("invalid_configuration", "transactionConfiguration");
95
+ }
96
+ if (profile !== undefined) {
97
+ try {
98
+ validateRuntimeProfile(profile, options, schema);
99
+ }
100
+ catch {
101
+ fail("invalid_configuration", "compiledProfile");
102
+ }
103
+ return;
104
+ }
105
+ for (const plugin of options.plugins ?? []) {
106
+ if (plugin.id !== "next-cookies" || plugin.version !== BETTER_AUTH_PINNED_VERSION || plugin.schema !== undefined) {
107
+ fail("invalid_configuration", "plugins");
108
+ }
109
+ }
93
110
  const modelNames = Object.keys(schema).sort();
94
111
  const expectedModels = Object.keys(CORE_SCHEMA_FIELDS).sort();
95
112
  if (JSON.stringify(modelNames) !== JSON.stringify(expectedModels)) {
@@ -308,15 +325,41 @@ class CoreDispatcher {
308
325
  client;
309
326
  organizationId;
310
327
  resetModelForTesting;
311
- constructor(client, organizationId, resetModelForTesting) {
328
+ profile;
329
+ plugins;
330
+ constructor(client, organizationId, resetModelForTesting, profile) {
312
331
  this.client = client;
313
332
  this.organizationId = organizationId;
314
333
  this.resetModelForTesting = resetModelForTesting;
334
+ this.profile = profile;
335
+ this.plugins = profile === undefined ? undefined : new ProfilePluginDispatcher(profile, client, organizationId);
336
+ }
337
+ fields(model) {
338
+ return this.profile?.plan.models.find((entry) => entry.name === model)?.fields
339
+ .filter((field) => !CORE_SCHEMA_FIELDS[model].includes(field.name)) ?? [];
340
+ }
341
+ encodeExtra(model, data, prefix = "") {
342
+ return Object.fromEntries(this.fields(model).map((field) => [prefix + storageField(field.name), encodeProfileField(field, data[field.name])]));
343
+ }
344
+ decodeExtra(model, row) {
345
+ return Object.fromEntries(this.fields(model).map((field) => [field.name, decodeProfileField(field, row[storageField(field.name)])]));
346
+ }
347
+ mapUser = (row) => ({ ...mapUser(row), ...this.decodeExtra("user", row) });
348
+ mapSession = (row) => ({ ...mapSession(row), ...this.decodeExtra("session", row) });
349
+ mapAccount = (row) => ({ ...mapAccount(row), ...this.decodeExtra("account", row) });
350
+ mapVerification = (row) => ({ ...mapVerification(row), ...this.decodeExtra("verification", row) });
351
+ normalizeCreate(model, data) {
352
+ if (!(model in CORE_SCHEMA_FIELDS))
353
+ return { ...data };
354
+ return { ...data, ...this.decodeExtra(model, this.encodeExtra(model, data)) };
315
355
  }
316
356
  async create(model, dataValue) {
317
357
  const data = asRecord(dataValue, `create ${model}`);
358
+ if (this.plugins?.supports(model))
359
+ return this.plugins.create(model, data);
318
360
  if (model === "user") {
319
361
  const result = await this.client.createUser({
362
+ ...this.encodeExtra("user", data),
320
363
  request_id: "",
321
364
  organization_id: this.organizationId,
322
365
  id: requiredString(data, "id", "create user"),
@@ -328,10 +371,11 @@ class CoreDispatcher {
328
371
  updated_at: toTimestamp(requiredDate(data, "updatedAt", "create user")),
329
372
  });
330
373
  requireOutcome(result, "create user", "UserCreated");
331
- return { ...data, image: data.image ?? null };
374
+ return { ...this.normalizeCreate(model, data), image: data.image ?? null };
332
375
  }
333
376
  if (model === "account") {
334
377
  const result = await this.client.createAccount({
378
+ ...this.encodeExtra("account", data),
335
379
  request_id: "",
336
380
  organization_id: this.organizationId,
337
381
  id: requiredString(data, "id", "create account"),
@@ -349,11 +393,12 @@ class CoreDispatcher {
349
393
  updated_at: toTimestamp(requiredDate(data, "updatedAt", "create account")),
350
394
  });
351
395
  requireOutcome(result, "create account", "AccountCreated");
352
- return { ...data };
396
+ return this.normalizeCreate(model, data);
353
397
  }
354
398
  if (model === "session") {
355
399
  const token = requiredString(data, "token", "create session");
356
400
  const result = await this.client.createSession({
401
+ ...this.encodeExtra("session", data),
357
402
  request_id: "",
358
403
  organization_id: this.organizationId,
359
404
  id: requiredString(data, "id", "create session"),
@@ -366,10 +411,11 @@ class CoreDispatcher {
366
411
  updated_at: toTimestamp(requiredDate(data, "updatedAt", "create session")),
367
412
  });
368
413
  requireOutcome(result, "create session", "SessionCreated");
369
- return { ...data };
414
+ return this.normalizeCreate(model, data);
370
415
  }
371
416
  if (model === "verification") {
372
417
  const result = await this.client.createVerification({
418
+ ...this.encodeExtra("verification", data),
373
419
  request_id: "",
374
420
  organization_id: this.organizationId,
375
421
  id: requiredString(data, "id", "create verification"),
@@ -380,11 +426,25 @@ class CoreDispatcher {
380
426
  updated_at: toTimestamp(requiredDate(data, "updatedAt", "create verification")),
381
427
  });
382
428
  requireOutcome(result, "create verification", "VerificationCreated");
383
- return { ...data };
429
+ return this.normalizeCreate(model, data);
384
430
  }
385
431
  return fail("unsupported_model", `create ${model}`);
386
432
  }
387
433
  async findOne(model, where, join) {
434
+ if (this.plugins?.supports(model))
435
+ return (await this.plugins.read(model, where, 1, undefined, 0, join)).rows[0] ?? null;
436
+ if (this.plugins !== undefined && join !== undefined && Object.keys(join).length > 0) {
437
+ const value = await this.plugins.joinedCore(model, where, join, false);
438
+ if (value.outcome === "Missing")
439
+ return null;
440
+ if (model === "user" && value.accounts.length > 32)
441
+ fail("result_bound_exceeded", "user accounts");
442
+ if (model === "user")
443
+ return { ...this.mapUser(value.user),
444
+ account: value.accounts.slice(0, join.account?.limit ?? 100).map(this.mapAccount) };
445
+ return { ...this.mapSession(value.session),
446
+ user: this.mapUser(value.user) };
447
+ }
388
448
  assertNoJoin(join, `findOne ${model}`);
389
449
  if (model === "user" && exactWhere(where, ["email"])) {
390
450
  const result = await this.client.findUserByEmail({
@@ -392,14 +452,14 @@ class CoreDispatcher {
392
452
  email: whereValue(where, "email", "findOne user"),
393
453
  });
394
454
  const row = result.value.users[0];
395
- return row === undefined ? null : mapUser(row);
455
+ return row === undefined ? null : this.mapUser(row);
396
456
  }
397
457
  if (model === "user" && exactWhere(where, ["id"])) {
398
458
  const result = await this.client.findUserById({
399
459
  organization_id: this.organizationId,
400
460
  id: whereValue(where, "id", "findOne user"),
401
461
  });
402
- return result.value.outcome === "Missing" ? null : mapUser(result.value.user);
462
+ return result.value.outcome === "Missing" ? null : this.mapUser(result.value.user);
403
463
  }
404
464
  if (model === "session" && exactWhere(where, ["token"])) {
405
465
  const token = whereValue(where, "token", "findOne session");
@@ -408,7 +468,7 @@ class CoreDispatcher {
408
468
  token,
409
469
  });
410
470
  const row = result.value.sessions[0];
411
- return row === undefined ? null : mapSession(row);
471
+ return row === undefined ? null : this.mapSession(row);
412
472
  }
413
473
  if (model === "session" && exactWhere(where, ["id"])) {
414
474
  const result = await this.client.findSessionById({
@@ -416,7 +476,7 @@ class CoreDispatcher {
416
476
  id: whereValue(where, "id", "findOne session"),
417
477
  });
418
478
  const row = result.value.sessions[0];
419
- return row === undefined ? null : mapSession(row);
479
+ return row === undefined ? null : this.mapSession(row);
420
480
  }
421
481
  if (model === "account" && exactWhere(where, ["id"])) {
422
482
  const result = await this.client.findAccountById({
@@ -424,7 +484,7 @@ class CoreDispatcher {
424
484
  id: whereValue(where, "id", "findOne account"),
425
485
  });
426
486
  const row = result.value.accounts[0];
427
- return row === undefined ? null : mapAccount(row);
487
+ return row === undefined ? null : this.mapAccount(row);
428
488
  }
429
489
  if (model === "account" && exactWhere(where, ["accountId", "providerId"])) {
430
490
  const result = await this.client.findAccountByProvider({
@@ -433,7 +493,17 @@ class CoreDispatcher {
433
493
  provider_id: whereValue(where, "providerId", "findOne account"),
434
494
  });
435
495
  const row = result.value.accounts[0];
436
- return row === undefined ? null : mapAccount(row);
496
+ return row === undefined ? null : this.mapAccount(row);
497
+ }
498
+ if (model === "account" && exactWhere(where, ["userId", "accountId", "providerId"])) {
499
+ const result = await this.client.findCredentialAccount({
500
+ organization_id: this.organizationId,
501
+ user_id: whereValue(where, "userId", "findCredentialAccount"),
502
+ account_id: whereValue(where, "accountId", "findCredentialAccount"),
503
+ provider_id: whereValue(where, "providerId", "findCredentialAccount"),
504
+ });
505
+ const row = result.value.accounts[0];
506
+ return row === undefined ? null : this.mapAccount(row);
437
507
  }
438
508
  if (model === "verification" && exactWhere(where, ["id"])) {
439
509
  const result = await this.client.findVerificationById({
@@ -443,14 +513,17 @@ class CoreDispatcher {
443
513
  const value = result.value;
444
514
  return value.outcome === "Missing"
445
515
  ? null
446
- : mapVerification(value.verification);
516
+ : this.mapVerification(value.verification);
447
517
  }
448
518
  return fail("unsupported_call_shape", `findOne ${model}`);
449
519
  }
450
520
  async update(model, where, updateValue) {
521
+ if (this.plugins?.supports(model))
522
+ return this.plugins.update(model, where, asRecord(updateValue, "update"));
451
523
  if (model === "account" && exactWhere(where, ["id"])) {
452
524
  const update = asRecord(updateValue, "update account");
453
525
  const allowed = new Set([
526
+ ...this.fields("account").map((field) => field.name),
454
527
  "accountId",
455
528
  "providerId",
456
529
  "accessToken",
@@ -477,6 +550,7 @@ class CoreDispatcher {
477
550
  return null;
478
551
  const successor = { ...mapAccount(row), ...update };
479
552
  const updated = await this.client.updateAccount({
553
+ ...this.encodeExtra("account", successor),
480
554
  request_id: "",
481
555
  organization_id: this.organizationId,
482
556
  user_id: row.user_id,
@@ -505,7 +579,7 @@ class CoreDispatcher {
505
579
  if (model === "session" &&
506
580
  (exactWhere(where, ["id"]) || exactWhere(where, ["token"]))) {
507
581
  const update = asRecord(updateValue, "update session");
508
- const allowed = new Set(["expiresAt", "ipAddress", "userAgent", "updatedAt"]);
582
+ const allowed = new Set(["expiresAt", "ipAddress", "userAgent", "updatedAt", ...this.fields("session").map((field) => field.name)]);
509
583
  const fields = Object.keys(update);
510
584
  if (fields.length === 0 || fields.some((field) => !allowed.has(field))) {
511
585
  fail("unsupported_call_shape", "update session");
@@ -523,9 +597,10 @@ class CoreDispatcher {
523
597
  const row = result.value.sessions[0];
524
598
  if (row === undefined)
525
599
  return null;
526
- const current = mapSession(row);
600
+ const current = this.mapSession(row);
527
601
  const successor = { ...current, ...update };
528
602
  const updated = await this.client.updateSession({
603
+ ...this.encodeExtra("session", successor),
529
604
  request_id: "",
530
605
  organization_id: this.organizationId,
531
606
  user_id: row.user_id,
@@ -551,7 +626,7 @@ class CoreDispatcher {
551
626
  return fail("unsupported_call_shape", `update ${model}`);
552
627
  }
553
628
  const update = asRecord(updateValue, "update verification");
554
- const allowed = new Set(["value", "expiresAt", "updatedAt"]);
629
+ const allowed = new Set(["value", "expiresAt", "updatedAt", ...this.fields("verification").map((field) => field.name)]);
555
630
  const fields = Object.keys(update);
556
631
  if (fields.length === 0 || fields.some((field) => !allowed.has(field))) {
557
632
  fail("unsupported_call_shape", "update verification");
@@ -567,6 +642,7 @@ class CoreDispatcher {
567
642
  return null;
568
643
  const successor = { ...mapVerification(row), ...update };
569
644
  const result = await this.client.updateVerification({
645
+ ...this.encodeExtra("verification", successor),
570
646
  request_id: "",
571
647
  organization_id: this.organizationId,
572
648
  verification_id: row.verification_id,
@@ -585,7 +661,7 @@ class CoreDispatcher {
585
661
  return fail("declared_outcome", "update verification", "VerificationUpdateStale");
586
662
  }
587
663
  const update = asRecord(updateValue, "update user");
588
- const allowed = new Set(["name", "email", "emailVerified", "image", "updatedAt"]);
664
+ const allowed = new Set(["name", "email", "emailVerified", "image", "updatedAt", ...this.fields("user").map((field) => field.name)]);
589
665
  const fields = Object.keys(update);
590
666
  if (fields.length === 0 || fields.some((field) => !allowed.has(field))) {
591
667
  fail("unsupported_call_shape", "update user");
@@ -610,9 +686,10 @@ class CoreDispatcher {
610
686
  return null;
611
687
  const resolved = "outcome" in row ? row.user : row;
612
688
  userId = resolved.user_id;
613
- const current = mapUser(resolved);
689
+ const current = this.mapUser(resolved);
614
690
  const successor = { ...current, ...update };
615
691
  const result = await this.client.updateUser({
692
+ ...this.encodeExtra("user", successor),
616
693
  request_id: "",
617
694
  organization_id: this.organizationId,
618
695
  user_id: userId,
@@ -633,12 +710,33 @@ class CoreDispatcher {
633
710
  return fail("declared_outcome", "update user", "UserUpdateStale");
634
711
  }
635
712
  async findMany(model, where, limit, sortBy, offset, join) {
713
+ if (this.plugins?.supports(model))
714
+ return (await this.plugins.read(model, where, limit, sortBy, offset, join)).rows;
715
+ if (this.plugins !== undefined && join !== undefined && Object.keys(join).length > 0) {
716
+ if (sortBy !== undefined || (offset !== undefined && offset !== 0))
717
+ fail("unsupported_call_shape", "core join window");
718
+ const value = await this.plugins.joinedCore(model, where ?? [], join, true, limit);
719
+ const rows = value[model === "account" ? "accounts" : "sessions"];
720
+ const users = value.users;
721
+ if (rows.length > 32 || users.length > 32)
722
+ fail("result_bound_exceeded", "core join");
723
+ return rows.slice(0, limit).map((row) => ({
724
+ ...(model === "account" ? this.mapAccount(row) : this.mapSession(row)),
725
+ user: this.mapUser(users.find((user) => user.user_id === row.user_id) ?? fail("invalid_record_shape", "core join user")),
726
+ }));
727
+ }
728
+ if (model === "user" && this.profile?.plan.plugins.includes("organization") && exactWhereShape(where, [{ field: "id", operator: "in" }])) {
729
+ assertNoJoin(join, "user ID set");
730
+ if (sortBy !== undefined || (offset !== undefined && offset !== 0))
731
+ fail("unsupported_call_shape", "user ID set");
732
+ return this.plugins.usersById(where, limit);
733
+ }
636
734
  assertNoJoin(join, `findMany ${model}`);
637
735
  if (!Number.isInteger(limit) || limit < 1)
638
736
  fail("unsupported_call_shape", `findMany ${model}`);
639
737
  if (model === "user") {
640
738
  const window = await this.adminUserWindow(where, limit, sortBy, offset);
641
- return window.users.map(mapUser);
739
+ return window.users.map(this.mapUser);
642
740
  }
643
741
  if (offset !== undefined && offset !== 0)
644
742
  fail("unsupported_call_shape", `findMany ${model}`);
@@ -652,7 +750,15 @@ class CoreDispatcher {
652
750
  });
653
751
  if (result.nextCursor !== undefined)
654
752
  fail("result_bound_exceeded", "findMany account");
655
- return result.value.accounts.slice(0, limit).map(mapAccount);
753
+ return result.value.accounts.slice(0, limit).map(this.mapAccount);
754
+ }
755
+ if (model === "account" && exactWhere(where, ["providerId", "accountId"]) && sortBy === undefined) {
756
+ if (limit > 100)
757
+ fail("result_bound_exceeded", "findMany provider account");
758
+ const result = await this.client.findAccountByProvider({ organization_id: this.organizationId,
759
+ account_id: whereValue(where, "accountId", "findMany provider account"),
760
+ provider_id: whereValue(where, "providerId", "findMany provider account") });
761
+ return result.value.accounts.map(this.mapAccount);
656
762
  }
657
763
  if (model === "session" && exactWhere(where, ["userId"]) && sortBy === undefined) {
658
764
  const result = await this.client.findSessionsByUserId({
@@ -662,7 +768,7 @@ class CoreDispatcher {
662
768
  });
663
769
  if (result.nextCursor !== undefined)
664
770
  fail("result_bound_exceeded", "findMany session");
665
- return result.value.sessions.slice(0, limit).map(mapSession);
771
+ return result.value.sessions.slice(0, limit).map(this.mapSession);
666
772
  }
667
773
  if (model === "session" &&
668
774
  exactWhereShape(where, [
@@ -679,7 +785,7 @@ class CoreDispatcher {
679
785
  if (result.nextCursor !== undefined) {
680
786
  fail("result_bound_exceeded", "findMany active session");
681
787
  }
682
- return result.value.sessions.slice(0, limit).map(mapSession);
788
+ return result.value.sessions.slice(0, limit).map(this.mapSession);
683
789
  }
684
790
  const tokenSetShape = exactWhereShape(where, [{ field: "token", operator: "in" }]);
685
791
  const activeTokenSetShape = exactWhereShape(where, [
@@ -694,7 +800,7 @@ class CoreDispatcher {
694
800
  organization_id: this.organizationId,
695
801
  tokens,
696
802
  });
697
- return result.value.sessions.slice(0, limit).map(mapSession);
803
+ return result.value.sessions.slice(0, limit).map(this.mapSession);
698
804
  }
699
805
  if (model === "session" && activeTokenSetShape) {
700
806
  return fail("unsupported_call_shape", "findMany session token set with expiry");
@@ -708,7 +814,7 @@ class CoreDispatcher {
708
814
  organization_id: this.organizationId,
709
815
  identifier: whereValue(where, "identifier", "findMany verification"),
710
816
  });
711
- return result.value.verifications.map(mapVerification);
817
+ return result.value.verifications.map(this.mapVerification);
712
818
  }
713
819
  if (model === "verification" && exactWhere(where, ["identifier"]) && sortBy !== undefined) {
714
820
  return fail("unsupported_call_shape", "findMany verification sort");
@@ -721,7 +827,7 @@ class CoreDispatcher {
721
827
  });
722
828
  if (result.nextCursor !== undefined)
723
829
  fail("result_bound_exceeded", "findMany verification");
724
- return result.value.verifications.slice(0, limit).map(mapVerification);
830
+ return result.value.verifications.slice(0, limit).map(this.mapVerification);
725
831
  }
726
832
  return fail("unsupported_call_shape", `findMany ${model}`);
727
833
  }
@@ -772,6 +878,11 @@ class CoreDispatcher {
772
878
  : (await this.client.adminUsersEndsWithDesc(searchParameters)).value;
773
879
  }
774
880
  async count(model, where) {
881
+ if (this.plugins?.supports(model)) {
882
+ if (model !== "member" || !exactWhere(where, ["organizationId"]))
883
+ fail("unsupported_call_shape", "plugin count");
884
+ return (await this.plugins.read(model, where, 1)).total;
885
+ }
775
886
  if (model !== "user")
776
887
  fail("unsupported_call_shape", `count ${model}`);
777
888
  const window = await this.adminUserWindow(where, 1, undefined, 0);
@@ -781,6 +892,13 @@ class CoreDispatcher {
781
892
  return Number(total);
782
893
  }
783
894
  async delete(model, where) {
895
+ if (this.plugins?.supports(model)) {
896
+ if (!exactWhere(where, ["id"]))
897
+ fail("unsupported_call_shape", "plugin delete");
898
+ return this.plugins.deleteIds(model, [whereValue(where, "id", "plugin delete")]);
899
+ }
900
+ if (model === "user" && this.profile?.plan.plugins.includes("organization"))
901
+ fail("unsupported_call_shape", "delete user in organization profile");
784
902
  if (model === "user" && exactWhere(where, ["id"])) {
785
903
  const result = await this.client.deleteUser({
786
904
  request_id: "",
@@ -852,6 +970,16 @@ class CoreDispatcher {
852
970
  fail("unsupported_call_shape", `delete ${model}`);
853
971
  }
854
972
  async deleteMany(model, where) {
973
+ if (this.plugins?.supports(model) && where.length > 0) {
974
+ if (!(model === "twoFactor" && (exactWhere(where, ["userId"]) || exactWhere(where, ["id"]))) &&
975
+ !(model === "member" && exactWhere(where, ["id"])))
976
+ fail("unsupported_call_shape", "plugin deleteMany");
977
+ const result = await this.plugins.read(model, where, 100);
978
+ if (result.total > 100)
979
+ fail("result_bound_exceeded", "plugin deleteMany");
980
+ await this.plugins.deleteIds(model, result.rows.map((row) => requiredString(row, "id", "plugin deleteMany")));
981
+ return result.rows.length;
982
+ }
855
983
  if (where.length === 0 && this.resetModelForTesting !== undefined) {
856
984
  return this.resetModelForTesting(model);
857
985
  }
@@ -951,9 +1079,86 @@ class CoreDispatcher {
951
1079
  if (result.outcome.outcome === "VerificationConsumeMissing")
952
1080
  return null;
953
1081
  requireOutcome(result, "consumeOne verification", "VerificationConsumed");
954
- return mapVerification(result.outcome);
1082
+ return this.mapVerification(result.outcome);
1083
+ }
1084
+ async prepareConsume(id) {
1085
+ const { value } = await this.client.findVerificationById({
1086
+ organization_id: this.organizationId, id,
1087
+ });
1088
+ return value.outcome === "Missing" ? null : {
1089
+ row: this.mapVerification(value.verification), revision: value.verification.revision,
1090
+ };
1091
+ }
1092
+ async commitConsume(snapshot) {
1093
+ const result = await this.client.consumeVerificationSnapshot({
1094
+ request_id: "", organization_id: this.organizationId,
1095
+ verification_id: requiredString(snapshot.row, "id", "consumeOne"),
1096
+ expected_revision: snapshot.revision,
1097
+ });
1098
+ if (result.outcome.outcome === "VerificationConsumeMissing")
1099
+ return false;
1100
+ requireOutcome(result, "consumeOne", "VerificationConsumed");
1101
+ return true;
1102
+ }
1103
+ async commitPasswordReset(snapshot, where, update, cleanup) {
1104
+ const userId = whereValue(where, "userId", "resetPassword");
1105
+ if (!exactWhere(where, ["userId", "providerId", "accountId"]) ||
1106
+ whereValue(where, "providerId", "resetPassword") !== "credential" ||
1107
+ whereValue(where, "accountId", "resetPassword") !== userId ||
1108
+ snapshot.row.value !== userId || cleanup.length !== 0 ||
1109
+ Object.keys(update).some((field) => field !== "password" && field !== "updatedAt")) {
1110
+ fail("unsupported_transaction_shape", "resetPassword");
1111
+ }
1112
+ const password = requiredString(update, "password", "resetPassword");
1113
+ const updatedAt = toTimestamp(requiredDate(update, "updatedAt", "resetPassword"));
1114
+ for (let attempt = 0; attempt < 3; attempt += 1) {
1115
+ const { value } = await this.client.findCredentialAccount({
1116
+ organization_id: this.organizationId, user_id: userId, account_id: userId, provider_id: "credential",
1117
+ });
1118
+ const account = value.accounts[0];
1119
+ if (account === undefined)
1120
+ fail("unsupported_transaction_shape", "resetPassword create account");
1121
+ const result = await this.client.resetCredentialPassword({
1122
+ request_id: "", organization_id: this.organizationId,
1123
+ verification_ids: [requiredString(snapshot.row, "id", "resetPassword")],
1124
+ verification_revision: snapshot.revision,
1125
+ user_id: userId, external_account_id: userId, account_id: account.id,
1126
+ account_revision: account.revision, password, updated_at: updatedAt,
1127
+ });
1128
+ if (result.outcome.outcome === "PasswordAccountStale")
1129
+ continue;
1130
+ requireOutcome(result, "resetPassword", "CredentialPasswordReset");
1131
+ return;
1132
+ }
1133
+ fail("declared_outcome", "resetPassword", "PasswordAccountStale");
955
1134
  }
956
1135
  async updateMany(model, where, updateValue) {
1136
+ if (model === "account" && exactWhere(where, ["userId", "providerId", "accountId"])) {
1137
+ const userId = whereValue(where, "userId", "update credential password");
1138
+ if (whereValue(where, "providerId", "update credential password") !== "credential" ||
1139
+ whereValue(where, "accountId", "update credential password") !== userId ||
1140
+ Object.keys(updateValue).some((field) => field !== "password" && field !== "updatedAt"))
1141
+ fail("unsupported_call_shape", "update credential password");
1142
+ const password = requiredString(updateValue, "password", "update credential password");
1143
+ const updatedAt = toTimestamp(requiredDate(updateValue, "updatedAt", "update credential password"));
1144
+ for (let attempt = 0; attempt < 3; attempt += 1) {
1145
+ const { value } = await this.client.findCredentialAccount({ organization_id: this.organizationId,
1146
+ user_id: userId, account_id: userId, provider_id: "credential" });
1147
+ const account = value.accounts[0];
1148
+ if (account === undefined)
1149
+ return 0;
1150
+ const result = await this.client.updateCredentialPassword({ request_id: "", organization_id: this.organizationId,
1151
+ user_id: userId, account_id: account.id, external_account_id: userId, expected_revision: account.revision,
1152
+ password, updated_at: updatedAt });
1153
+ if (result.outcome.outcome === "PasswordAccountStale")
1154
+ continue;
1155
+ if (result.outcome.outcome === "PasswordAccountMissing")
1156
+ return 0;
1157
+ requireOutcome(result, "update credential password", "CredentialPasswordUpdated");
1158
+ return 1;
1159
+ }
1160
+ fail("declared_outcome", "update credential password", "PasswordAccountStale");
1161
+ }
957
1162
  if (model !== "account" || !exactWhere(where, ["userId", "providerId"])) {
958
1163
  return fail("unsupported_call_shape", `updateMany ${model}`);
959
1164
  }
@@ -1018,6 +1223,9 @@ class CoreDispatcher {
1018
1223
  fail("unsupported_transaction_shape", "transaction signup");
1019
1224
  }
1020
1225
  const result = await this.client.createSignupGraph({
1226
+ ...this.encodeExtra("user", user, "user_"),
1227
+ ...this.encodeExtra("session", session, "session_"),
1228
+ ...this.encodeExtra("account", account, "account_"),
1021
1229
  request_id: "",
1022
1230
  organization_id: this.organizationId,
1023
1231
  user_id: userId,
@@ -1057,6 +1265,8 @@ class CoreDispatcher {
1057
1265
  fail("unsupported_transaction_shape", "transaction OAuth user");
1058
1266
  }
1059
1267
  const result = await this.client.createOAuthUserGraph({
1268
+ ...this.encodeExtra("user", user, "user_"),
1269
+ ...this.encodeExtra("account", account, "account_"),
1060
1270
  request_id: "",
1061
1271
  organization_id: this.organizationId,
1062
1272
  user_id: userId,
@@ -1092,6 +1302,38 @@ class CoreDispatcher {
1092
1302
  });
1093
1303
  requireOutcome(result, "transaction verification consume", "VerificationSetConsumed");
1094
1304
  }
1305
+ async increment(model, where, increment, set) {
1306
+ if (!this.plugins?.supports(model))
1307
+ fail("unsupported_call_shape", `incrementOne ${model}`);
1308
+ return this.plugins.increment(model, where, { ...increment }, set === undefined ? {} : { ...set });
1309
+ }
1310
+ async commitMemberActivation(data, where, update) {
1311
+ if (this.plugins === undefined || !exactWhere(where, ["token"]))
1312
+ fail("unsupported_transaction_shape", "member activation");
1313
+ for (let attempt = 0; attempt < 3; attempt += 1) {
1314
+ const { value } = await this.client.findSessionByToken({ organization_id: this.organizationId,
1315
+ token: whereValue(where, "token", "member activation") });
1316
+ const raw = value.sessions[0];
1317
+ if (raw === undefined)
1318
+ fail("declared_outcome", "member activation", "SessionMissing");
1319
+ await this.plugins.createMemberAndActivate(data, this.mapSession(raw), raw, update);
1320
+ return;
1321
+ }
1322
+ }
1323
+ async commitPasswordChange(where, update) {
1324
+ const account = await this.findOne("account", where);
1325
+ if (account === null || account.providerId !== "credential" || account.accountId !== account.userId) {
1326
+ fail("declared_outcome", "changePassword", "PasswordAccountMissing");
1327
+ }
1328
+ const userId = requiredString(account, "userId", "changePassword");
1329
+ const affected = await this.updateMany("account", [
1330
+ { field: "userId", value: userId, operator: "eq", connector: "AND", mode: "sensitive" },
1331
+ { field: "accountId", value: userId, operator: "eq", connector: "AND", mode: "sensitive" },
1332
+ { field: "providerId", value: "credential", operator: "eq", connector: "AND", mode: "sensitive" },
1333
+ ], update);
1334
+ if (affected !== 1)
1335
+ fail("declared_outcome", "changePassword", "PasswordAccountMissing");
1336
+ }
1095
1337
  }
1096
1338
  function genericCreate(implementation) {
1097
1339
  return implementation;
@@ -1115,23 +1357,80 @@ class TransactionRecorder {
1115
1357
  consumedVerification;
1116
1358
  verificationDeleteRows;
1117
1359
  verificationDeleteIdentifier;
1360
+ verificationSnapshot;
1361
+ passwordReset;
1362
+ lostConsume = false;
1363
+ organizationDeletion;
1364
+ memberDeletion;
1365
+ sessionActivation;
1366
+ passwordChange;
1118
1367
  constructor(dispatcher) {
1119
1368
  this.dispatcher = dispatcher;
1120
1369
  }
1121
1370
  customAdapter() {
1122
1371
  return {
1123
1372
  create: genericCreate(async ({ model, data }) => {
1124
- const copied = { ...data };
1373
+ const copied = this.dispatcher.normalizeCreate(model, data);
1125
1374
  this.creates.push({ model, data: copied });
1126
1375
  return copied;
1127
1376
  }),
1128
1377
  findOne: genericFindOne(async ({ model, where, join }) => this.dispatcher.findOne(model, where, join)),
1129
1378
  findMany: genericFindMany(async ({ model, where, limit, sortBy, offset, join }) => this.dispatcher.findMany(model, where, limit, sortBy, offset, join)),
1130
1379
  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"),
1380
+ update: genericUpdate(async ({ model, where, update }) => {
1381
+ if (model === "account" && exactWhere(where, ["id"]) && this.passwordChange === undefined &&
1382
+ typeof update.password === "string" && Object.keys(update).every((field) => field === "password" || field === "updatedAt")) {
1383
+ requiredString(update, "password", "changePassword");
1384
+ requiredDate(update, "updatedAt", "changePassword");
1385
+ const current = await this.dispatcher.findOne("account", where);
1386
+ if (current === null)
1387
+ fail("declared_outcome", "changePassword", "PasswordAccountMissing");
1388
+ this.passwordChange = { where, update };
1389
+ return { ...current, ...update };
1390
+ }
1391
+ if (this.dispatcher.plugins === undefined || model !== "session" || !exactWhere(where, ["token"]) ||
1392
+ this.sessionActivation !== undefined || this.creates.length !== 1 || this.creates[0].model !== "member" ||
1393
+ Object.keys(update).some((field) => field !== "activeOrganizationId" && field !== "updatedAt"))
1394
+ fail("unsupported_transaction_shape", "member activation");
1395
+ const current = await this.dispatcher.findOne("session", where);
1396
+ if (current === null)
1397
+ return null;
1398
+ this.sessionActivation = { where, update };
1399
+ return { ...current, ...update };
1400
+ }),
1401
+ updateMany: async ({ model, where, update }) => {
1402
+ if (model !== "account" || !exactWhere(where, ["userId", "providerId", "accountId"]) ||
1403
+ !this.consumeAttempted || this.passwordReset !== undefined ||
1404
+ Object.keys(update).some((field) => field !== "password" && field !== "updatedAt")) {
1405
+ fail("unsupported_transaction_shape", "transaction updateMany");
1406
+ }
1407
+ this.passwordReset = { where, update };
1408
+ return 1;
1409
+ },
1410
+ delete: async ({ model, where }) => {
1411
+ if (this.dispatcher.plugins === undefined || !exactWhere(where, ["id"]))
1412
+ fail("unsupported_transaction_shape", "transaction delete");
1413
+ const id = whereValue(where, "id", "transaction delete");
1414
+ if (model === "organization" && this.organizationDeletion?.id === id && this.organizationDeletion.models.join(",") === "member,invitation") {
1415
+ this.organizationDeletion.deleted = true;
1416
+ return;
1417
+ }
1418
+ if (model === "member" && this.memberDeletion === undefined && this.organizationDeletion === undefined) {
1419
+ this.memberDeletion = id;
1420
+ return;
1421
+ }
1422
+ fail("unsupported_transaction_shape", "transaction delete");
1423
+ },
1134
1424
  deleteMany: async ({ model, where }) => {
1425
+ if (this.dispatcher.plugins !== undefined && ["member", "invitation"].includes(model) && exactWhere(where, ["organizationId"])) {
1426
+ const id = whereValue(where, "organizationId", "organization delete");
1427
+ this.organizationDeletion ??= { id, models: [], deleted: false };
1428
+ if (this.organizationDeletion.id !== id || this.organizationDeletion.deleted ||
1429
+ model !== (this.organizationDeletion.models.length === 0 ? "member" : "invitation") || this.organizationDeletion.models.length >= 2)
1430
+ fail("unsupported_transaction_shape", "organization delete");
1431
+ this.organizationDeletion.models.push(model);
1432
+ return 0; // The upstream graph ignores these counts; one cascade commits them together.
1433
+ }
1135
1434
  if (model !== "verification" ||
1136
1435
  !exactWhere(where, ["identifier"]) ||
1137
1436
  !this.consumeAttempted ||
@@ -1139,7 +1438,8 @@ class TransactionRecorder {
1139
1438
  return fail("unsupported_transaction_shape", "transaction deleteMany");
1140
1439
  }
1141
1440
  this.verificationDeleteIdentifier = whereValue(where, "identifier", "transaction deleteMany verification");
1142
- this.verificationDeleteRows = await this.dispatcher.findMany("verification", where, 32);
1441
+ this.verificationDeleteRows = (await this.dispatcher.findMany("verification", where, 32))
1442
+ .filter((row) => row.id !== this.consumedVerification?.id);
1143
1443
  return this.verificationDeleteRows.length;
1144
1444
  },
1145
1445
  consumeOne: genericConsumeOne(async ({ model, where }) => {
@@ -1149,19 +1449,43 @@ class TransactionRecorder {
1149
1449
  return fail("unsupported_transaction_shape", "transaction consumeOne");
1150
1450
  }
1151
1451
  this.consumeAttempted = true;
1152
- const row = await this.dispatcher.consumeOne(model, where);
1153
- this.consumedVerification = row;
1154
- return row;
1452
+ this.verificationSnapshot = await this.dispatcher.prepareConsume(whereValue(where, "id", "consumeOne"));
1453
+ this.consumedVerification = this.verificationSnapshot?.row ?? null;
1454
+ return this.consumedVerification;
1155
1455
  }),
1156
1456
  incrementOne: async () => fail("unsupported_transaction_shape", "transaction incrementOne"),
1157
1457
  };
1158
1458
  }
1159
1459
  async commit() {
1160
1460
  const hasVerificationMutation = this.consumeAttempted || this.verificationDeleteRows !== undefined;
1461
+ const hasOrganizationMutation = this.organizationDeletion !== undefined || this.memberDeletion !== undefined;
1462
+ if (this.passwordChange !== undefined) {
1463
+ if (hasVerificationMutation || hasOrganizationMutation || this.creates.length > 0 || this.sessionActivation !== undefined)
1464
+ fail("unsupported_transaction_shape", "mixed password change");
1465
+ await this.dispatcher.commitPasswordChange(this.passwordChange.where, this.passwordChange.update);
1466
+ return;
1467
+ }
1468
+ if (hasOrganizationMutation) {
1469
+ if (this.creates.length > 0 || hasVerificationMutation || this.sessionActivation !== undefined ||
1470
+ (this.organizationDeletion !== undefined && this.memberDeletion !== undefined))
1471
+ fail("unsupported_transaction_shape", "mixed organization transaction");
1472
+ if (this.organizationDeletion !== undefined) {
1473
+ if (!this.organizationDeletion.deleted)
1474
+ fail("unsupported_transaction_shape", "incomplete organization deletion");
1475
+ await this.dispatcher.plugins.deleteIds("organization", [this.organizationDeletion.id]);
1476
+ }
1477
+ else
1478
+ await this.dispatcher.plugins.deleteIds("member", [this.memberDeletion]);
1479
+ return;
1480
+ }
1161
1481
  if (this.creates.length > 0 && hasVerificationMutation) {
1162
1482
  fail("unsupported_transaction_shape", "mixed transaction");
1163
1483
  }
1164
1484
  if (this.creates.length > 0) {
1485
+ if (this.creates.length === 1 && this.creates[0].model === "member" && this.sessionActivation !== undefined) {
1486
+ await this.dispatcher.commitMemberActivation(this.creates[0].data, this.sessionActivation.where, this.sessionActivation.update);
1487
+ return;
1488
+ }
1165
1489
  if (this.creates.length === 2) {
1166
1490
  await this.dispatcher.commitOAuthUser(this.creates);
1167
1491
  }
@@ -1174,13 +1498,21 @@ class TransactionRecorder {
1174
1498
  if (!this.consumeAttempted || this.consumedVerification === undefined) {
1175
1499
  fail("unsupported_transaction_shape", "transaction verification consume");
1176
1500
  }
1177
- if (this.verificationDeleteRows === undefined)
1501
+ if (this.verificationSnapshot == null)
1178
1502
  return;
1179
- if (this.consumedVerification !== null &&
1503
+ if (this.verificationDeleteRows !== undefined && this.consumedVerification !== null &&
1180
1504
  this.consumedVerification.identifier !== this.verificationDeleteIdentifier) {
1181
1505
  fail("unsupported_transaction_shape", "transaction verification consume");
1182
1506
  }
1183
- if (this.verificationDeleteRows.length > 0) {
1507
+ if (this.passwordReset !== undefined) {
1508
+ await this.dispatcher.commitPasswordReset(this.verificationSnapshot, this.passwordReset.where, this.passwordReset.update, this.verificationDeleteRows ?? []);
1509
+ return;
1510
+ }
1511
+ if (!await this.dispatcher.commitConsume(this.verificationSnapshot)) {
1512
+ this.lostConsume = true;
1513
+ return;
1514
+ }
1515
+ if (this.verificationDeleteRows !== undefined && this.verificationDeleteRows.length > 0) {
1184
1516
  await this.dispatcher.commitVerificationConsume(this.verificationDeleteRows);
1185
1517
  }
1186
1518
  }
@@ -1197,7 +1529,7 @@ function customAdapter(dispatcher) {
1197
1529
  delete: async ({ model, where }) => dispatcher.delete(model, where),
1198
1530
  deleteMany: async ({ model, where }) => dispatcher.deleteMany(model, where),
1199
1531
  consumeOne: genericConsumeOne(async ({ model, where }) => dispatcher.consumeOne(model, where)),
1200
- incrementOne: async ({ model }) => fail("unsupported_call_shape", `incrementOne ${model}`),
1532
+ incrementOne: (async ({ model, where, increment, set }) => dispatcher.increment(model, where, increment, set)),
1201
1533
  };
1202
1534
  }
1203
1535
  const BASE_FACTORY_CONFIG = {
@@ -1215,10 +1547,14 @@ export function buildRiffDBBetterAuthAdapter(config) {
1215
1547
  assertSupportedBetterAuthInstallation();
1216
1548
  validateConfiguration(config);
1217
1549
  return (options) => {
1218
- const dispatcher = new CoreDispatcher(config.client.causalSession(), config.organizationId, config.resetModelForTesting);
1550
+ if (config.profile !== undefined)
1551
+ options = { ...options, advanced: { ...options.advanced,
1552
+ database: { ...options.advanced?.database, joins: true } } };
1553
+ const dispatcher = new CoreDispatcher(config.client.causalSession(), config.organizationId, config.resetModelForTesting, config.profile);
1219
1554
  const factory = createAdapterFactory({
1220
1555
  config: {
1221
1556
  ...BASE_FACTORY_CONFIG,
1557
+ adapterName: config.profile?.plan.name ?? BASE_FACTORY_CONFIG.adapterName,
1222
1558
  debugLogs: false,
1223
1559
  transaction: async (callback) => {
1224
1560
  const recorder = new TransactionRecorder(dispatcher);
@@ -1229,18 +1565,18 @@ export function buildRiffDBBetterAuthAdapter(config) {
1229
1565
  transaction: false,
1230
1566
  },
1231
1567
  adapter: ({ options: transactionOptions, schema }) => {
1232
- validateResolvedSchema(transactionOptions, schema);
1568
+ validateResolvedSchema(transactionOptions, schema, config.profile);
1233
1569
  return recorder.customAdapter();
1234
1570
  },
1235
1571
  });
1236
1572
  const transactionAdapter = transactionFactory(options);
1237
1573
  const result = await callback(transactionAdapter);
1238
1574
  await recorder.commit();
1239
- return result;
1575
+ return recorder.lostConsume ? null : result;
1240
1576
  },
1241
1577
  },
1242
1578
  adapter: ({ options: resolvedOptions, schema }) => {
1243
- validateResolvedSchema(resolvedOptions, schema);
1579
+ validateResolvedSchema(resolvedOptions, schema, config.profile);
1244
1580
  return customAdapter(dispatcher);
1245
1581
  },
1246
1582
  });
@@ -1254,9 +1590,11 @@ export function riffdbAdapter(config) {
1254
1590
  }
1255
1591
  const transport = new DriverGeneratedApplicationTransport(config.driver);
1256
1592
  return buildRiffDBBetterAuthAdapter({
1257
- client: new RiffDBBetterAuthCoreClient(transport, commandAttemptBudget),
1593
+ client: config.profile === undefined ? new RiffDBBetterAuthCoreClient(transport, commandAttemptBudget) :
1594
+ instantiateProfileClient(config.profile, transport, commandAttemptBudget),
1258
1595
  organizationId: config.organizationId,
1259
1596
  ...(config.debugLogs === undefined ? {} : { debugLogs: config.debugLogs }),
1597
+ ...(config.profile === undefined ? {} : { profile: config.profile }),
1260
1598
  });
1261
1599
  }
1262
1600
  //# sourceMappingURL=adapter.js.map