@better-auth/core 1.5.5 → 1.5.7-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/dist/api/index.d.mts +41 -14
  2. package/dist/api/index.mjs +1 -2
  3. package/dist/api/index.mjs.map +1 -1
  4. package/dist/async_hooks/index.mjs +1 -1
  5. package/dist/async_hooks/pure.index.mjs +1 -1
  6. package/dist/async_hooks/pure.index.mjs.map +1 -1
  7. package/dist/context/endpoint-context.d.mts +1 -2
  8. package/dist/context/endpoint-context.mjs +1 -2
  9. package/dist/context/endpoint-context.mjs.map +1 -1
  10. package/dist/context/global.mjs +2 -2
  11. package/dist/context/global.mjs.map +1 -1
  12. package/dist/context/index.mjs +1 -2
  13. package/dist/context/request-state.mjs +1 -2
  14. package/dist/context/request-state.mjs.map +1 -1
  15. package/dist/context/transaction.mjs +1 -2
  16. package/dist/context/transaction.mjs.map +1 -1
  17. package/dist/db/adapter/factory.d.mts +0 -2
  18. package/dist/db/adapter/factory.mjs +54 -22
  19. package/dist/db/adapter/factory.mjs.map +1 -1
  20. package/dist/db/adapter/get-default-field-name.mjs +1 -2
  21. package/dist/db/adapter/get-default-field-name.mjs.map +1 -1
  22. package/dist/db/adapter/get-default-model-name.mjs +1 -2
  23. package/dist/db/adapter/get-default-model-name.mjs.map +1 -1
  24. package/dist/db/adapter/get-field-attributes.d.mts +0 -3
  25. package/dist/db/adapter/get-field-attributes.mjs +1 -2
  26. package/dist/db/adapter/get-field-attributes.mjs.map +1 -1
  27. package/dist/db/adapter/get-field-name.mjs +1 -2
  28. package/dist/db/adapter/get-field-name.mjs.map +1 -1
  29. package/dist/db/adapter/get-id-field.d.mts +0 -3
  30. package/dist/db/adapter/get-id-field.mjs +3 -4
  31. package/dist/db/adapter/get-id-field.mjs.map +1 -1
  32. package/dist/db/adapter/get-model-name.mjs +1 -2
  33. package/dist/db/adapter/get-model-name.mjs.map +1 -1
  34. package/dist/db/adapter/index.d.mts +0 -2
  35. package/dist/db/adapter/index.mjs +1 -2
  36. package/dist/db/adapter/index.mjs.map +1 -1
  37. package/dist/db/adapter/types.d.mts +0 -2
  38. package/dist/db/adapter/utils.mjs +1 -1
  39. package/dist/db/adapter/utils.mjs.map +1 -1
  40. package/dist/db/get-tables.d.mts +0 -2
  41. package/dist/db/get-tables.mjs +1 -1
  42. package/dist/db/index.mjs +1 -2
  43. package/dist/db/schema/account.d.mts +0 -1
  44. package/dist/db/schema/account.mjs +1 -2
  45. package/dist/db/schema/account.mjs.map +1 -1
  46. package/dist/db/schema/rate-limit.d.mts +0 -1
  47. package/dist/db/schema/rate-limit.mjs +1 -2
  48. package/dist/db/schema/rate-limit.mjs.map +1 -1
  49. package/dist/db/schema/session.d.mts +0 -1
  50. package/dist/db/schema/session.mjs +1 -2
  51. package/dist/db/schema/session.mjs.map +1 -1
  52. package/dist/db/schema/shared.mjs +1 -2
  53. package/dist/db/schema/shared.mjs.map +1 -1
  54. package/dist/db/schema/user.d.mts +0 -1
  55. package/dist/db/schema/user.mjs +1 -2
  56. package/dist/db/schema/user.mjs.map +1 -1
  57. package/dist/db/schema/verification.d.mts +0 -1
  58. package/dist/db/schema/verification.mjs +1 -2
  59. package/dist/db/schema/verification.mjs.map +1 -1
  60. package/dist/db/type.d.mts +0 -1
  61. package/dist/env/color-depth.mjs +1 -2
  62. package/dist/env/color-depth.mjs.map +1 -1
  63. package/dist/env/env-impl.mjs +1 -1
  64. package/dist/env/env-impl.mjs.map +1 -1
  65. package/dist/env/index.mjs +1 -2
  66. package/dist/env/logger.mjs +1 -2
  67. package/dist/env/logger.mjs.map +1 -1
  68. package/dist/error/codes.mjs +1 -2
  69. package/dist/error/codes.mjs.map +1 -1
  70. package/dist/error/index.mjs +1 -2
  71. package/dist/error/index.mjs.map +1 -1
  72. package/dist/index.mjs +1 -1
  73. package/dist/instrumentation/attributes.d.mts +12 -0
  74. package/dist/instrumentation/attributes.mjs +12 -0
  75. package/dist/instrumentation/attributes.mjs.map +1 -0
  76. package/dist/instrumentation/index.d.mts +3 -0
  77. package/dist/instrumentation/index.mjs +3 -0
  78. package/dist/instrumentation/tracer.d.mts +14 -0
  79. package/dist/instrumentation/tracer.mjs +36 -0
  80. package/dist/instrumentation/tracer.mjs.map +1 -0
  81. package/dist/oauth2/client-credentials-token.d.mts +0 -1
  82. package/dist/oauth2/client-credentials-token.mjs +1 -2
  83. package/dist/oauth2/client-credentials-token.mjs.map +1 -1
  84. package/dist/oauth2/create-authorization-url.d.mts +0 -3
  85. package/dist/oauth2/create-authorization-url.mjs +1 -2
  86. package/dist/oauth2/create-authorization-url.mjs.map +1 -1
  87. package/dist/oauth2/index.mjs +1 -2
  88. package/dist/oauth2/oauth-provider.d.mts +0 -2
  89. package/dist/oauth2/refresh-access-token.d.mts +0 -1
  90. package/dist/oauth2/refresh-access-token.mjs +1 -2
  91. package/dist/oauth2/refresh-access-token.mjs.map +1 -1
  92. package/dist/oauth2/utils.mjs +1 -2
  93. package/dist/oauth2/utils.mjs.map +1 -1
  94. package/dist/oauth2/validate-authorization-code.d.mts +0 -2
  95. package/dist/oauth2/validate-authorization-code.mjs +1 -2
  96. package/dist/oauth2/validate-authorization-code.mjs.map +1 -1
  97. package/dist/oauth2/verify.mjs +1 -2
  98. package/dist/oauth2/verify.mjs.map +1 -1
  99. package/dist/social-providers/apple.d.mts +0 -2
  100. package/dist/social-providers/apple.mjs +2 -3
  101. package/dist/social-providers/apple.mjs.map +1 -1
  102. package/dist/social-providers/atlassian.d.mts +0 -2
  103. package/dist/social-providers/atlassian.mjs +4 -4
  104. package/dist/social-providers/atlassian.mjs.map +1 -1
  105. package/dist/social-providers/cognito.d.mts +0 -2
  106. package/dist/social-providers/cognito.mjs +1 -2
  107. package/dist/social-providers/cognito.mjs.map +1 -1
  108. package/dist/social-providers/discord.d.mts +0 -2
  109. package/dist/social-providers/discord.mjs +4 -4
  110. package/dist/social-providers/discord.mjs.map +1 -1
  111. package/dist/social-providers/dropbox.d.mts +0 -2
  112. package/dist/social-providers/dropbox.mjs +1 -2
  113. package/dist/social-providers/dropbox.mjs.map +1 -1
  114. package/dist/social-providers/facebook.d.mts +0 -2
  115. package/dist/social-providers/facebook.mjs +1 -2
  116. package/dist/social-providers/facebook.mjs.map +1 -1
  117. package/dist/social-providers/figma.d.mts +0 -2
  118. package/dist/social-providers/figma.mjs +4 -4
  119. package/dist/social-providers/figma.mjs.map +1 -1
  120. package/dist/social-providers/github.d.mts +0 -2
  121. package/dist/social-providers/github.mjs +2 -3
  122. package/dist/social-providers/github.mjs.map +1 -1
  123. package/dist/social-providers/gitlab.d.mts +0 -2
  124. package/dist/social-providers/gitlab.mjs +1 -2
  125. package/dist/social-providers/gitlab.mjs.map +1 -1
  126. package/dist/social-providers/google.d.mts +0 -2
  127. package/dist/social-providers/google.mjs +1 -2
  128. package/dist/social-providers/google.mjs.map +1 -1
  129. package/dist/social-providers/huggingface.d.mts +0 -2
  130. package/dist/social-providers/huggingface.mjs +4 -4
  131. package/dist/social-providers/huggingface.mjs.map +1 -1
  132. package/dist/social-providers/index.d.mts +61 -3
  133. package/dist/social-providers/index.mjs +5 -4
  134. package/dist/social-providers/index.mjs.map +1 -1
  135. package/dist/social-providers/kakao.d.mts +0 -2
  136. package/dist/social-providers/kakao.mjs +4 -4
  137. package/dist/social-providers/kakao.mjs.map +1 -1
  138. package/dist/social-providers/kick.d.mts +0 -2
  139. package/dist/social-providers/kick.mjs +1 -2
  140. package/dist/social-providers/kick.mjs.map +1 -1
  141. package/dist/social-providers/line.d.mts +0 -2
  142. package/dist/social-providers/line.mjs +1 -2
  143. package/dist/social-providers/line.mjs.map +1 -1
  144. package/dist/social-providers/linear.d.mts +0 -2
  145. package/dist/social-providers/linear.mjs +1 -2
  146. package/dist/social-providers/linear.mjs.map +1 -1
  147. package/dist/social-providers/linkedin.d.mts +0 -2
  148. package/dist/social-providers/linkedin.mjs +1 -2
  149. package/dist/social-providers/linkedin.mjs.map +1 -1
  150. package/dist/social-providers/microsoft-entra-id.d.mts +0 -2
  151. package/dist/social-providers/microsoft-entra-id.mjs +1 -2
  152. package/dist/social-providers/microsoft-entra-id.mjs.map +1 -1
  153. package/dist/social-providers/naver.d.mts +0 -2
  154. package/dist/social-providers/naver.mjs +4 -4
  155. package/dist/social-providers/naver.mjs.map +1 -1
  156. package/dist/social-providers/notion.d.mts +0 -2
  157. package/dist/social-providers/notion.mjs +1 -2
  158. package/dist/social-providers/notion.mjs.map +1 -1
  159. package/dist/social-providers/paybin.d.mts +0 -2
  160. package/dist/social-providers/paybin.mjs +1 -2
  161. package/dist/social-providers/paybin.mjs.map +1 -1
  162. package/dist/social-providers/paypal.d.mts +0 -2
  163. package/dist/social-providers/paypal.mjs +1 -2
  164. package/dist/social-providers/paypal.mjs.map +1 -1
  165. package/dist/social-providers/polar.d.mts +0 -2
  166. package/dist/social-providers/polar.mjs +4 -4
  167. package/dist/social-providers/polar.mjs.map +1 -1
  168. package/dist/social-providers/railway.d.mts +0 -2
  169. package/dist/social-providers/railway.mjs +1 -2
  170. package/dist/social-providers/railway.mjs.map +1 -1
  171. package/dist/social-providers/reddit.d.mts +0 -2
  172. package/dist/social-providers/reddit.mjs +1 -2
  173. package/dist/social-providers/reddit.mjs.map +1 -1
  174. package/dist/social-providers/roblox.d.mts +0 -2
  175. package/dist/social-providers/roblox.mjs +4 -4
  176. package/dist/social-providers/roblox.mjs.map +1 -1
  177. package/dist/social-providers/salesforce.d.mts +0 -2
  178. package/dist/social-providers/salesforce.mjs +1 -2
  179. package/dist/social-providers/salesforce.mjs.map +1 -1
  180. package/dist/social-providers/slack.d.mts +0 -2
  181. package/dist/social-providers/slack.mjs +4 -4
  182. package/dist/social-providers/slack.mjs.map +1 -1
  183. package/dist/social-providers/spotify.d.mts +0 -2
  184. package/dist/social-providers/spotify.mjs +4 -4
  185. package/dist/social-providers/spotify.mjs.map +1 -1
  186. package/dist/social-providers/tiktok.d.mts +0 -2
  187. package/dist/social-providers/tiktok.mjs +4 -4
  188. package/dist/social-providers/tiktok.mjs.map +1 -1
  189. package/dist/social-providers/twitch.d.mts +0 -2
  190. package/dist/social-providers/twitch.mjs +4 -4
  191. package/dist/social-providers/twitch.mjs.map +1 -1
  192. package/dist/social-providers/twitter.d.mts +0 -2
  193. package/dist/social-providers/twitter.mjs +4 -4
  194. package/dist/social-providers/twitter.mjs.map +1 -1
  195. package/dist/social-providers/vercel.d.mts +0 -2
  196. package/dist/social-providers/vercel.mjs +1 -2
  197. package/dist/social-providers/vercel.mjs.map +1 -1
  198. package/dist/social-providers/vk.d.mts +0 -2
  199. package/dist/social-providers/vk.mjs +4 -4
  200. package/dist/social-providers/vk.mjs.map +1 -1
  201. package/dist/social-providers/wechat.d.mts +114 -0
  202. package/dist/social-providers/wechat.mjs +83 -0
  203. package/dist/social-providers/wechat.mjs.map +1 -0
  204. package/dist/social-providers/zoom.d.mts +0 -2
  205. package/dist/social-providers/zoom.mjs +1 -2
  206. package/dist/social-providers/zoom.mjs.map +1 -1
  207. package/dist/types/context.d.mts +1 -5
  208. package/dist/types/init-options.d.mts +0 -1
  209. package/dist/types/plugin.d.mts +4 -2
  210. package/dist/utils/db.d.mts +0 -2
  211. package/dist/utils/db.mjs +1 -1
  212. package/dist/utils/deprecate.mjs +1 -1
  213. package/dist/utils/error-codes.mjs +1 -1
  214. package/dist/utils/fetch-metadata.mjs +1 -1
  215. package/dist/utils/id.mjs +1 -2
  216. package/dist/utils/id.mjs.map +1 -1
  217. package/dist/utils/ip.mjs +1 -2
  218. package/dist/utils/ip.mjs.map +1 -1
  219. package/dist/utils/json.mjs +1 -2
  220. package/dist/utils/json.mjs.map +1 -1
  221. package/dist/utils/string.mjs +1 -1
  222. package/dist/utils/url.mjs +1 -1
  223. package/package.json +18 -5
  224. package/src/api/index.ts +151 -41
  225. package/src/context/endpoint-context.ts +2 -1
  226. package/src/db/adapter/factory.ts +119 -47
  227. package/src/db/adapter/get-id-field.test.ts +222 -0
  228. package/src/db/adapter/get-id-field.ts +15 -4
  229. package/src/instrumentation/attributes.ts +22 -0
  230. package/src/instrumentation/index.ts +2 -0
  231. package/src/instrumentation/instrumentation.test.ts +139 -0
  232. package/src/instrumentation/tracer.ts +62 -0
  233. package/src/social-providers/apple.ts +1 -1
  234. package/src/social-providers/atlassian.ts +3 -2
  235. package/src/social-providers/discord.ts +3 -2
  236. package/src/social-providers/figma.ts +3 -2
  237. package/src/social-providers/github.ts +1 -1
  238. package/src/social-providers/huggingface.ts +3 -2
  239. package/src/social-providers/index.ts +3 -0
  240. package/src/social-providers/kakao.ts +3 -2
  241. package/src/social-providers/naver.ts +3 -2
  242. package/src/social-providers/polar.ts +3 -2
  243. package/src/social-providers/roblox.ts +3 -2
  244. package/src/social-providers/slack.ts +3 -2
  245. package/src/social-providers/spotify.ts +3 -2
  246. package/src/social-providers/tiktok.ts +3 -2
  247. package/src/social-providers/twitch.ts +3 -2
  248. package/src/social-providers/twitter.ts +3 -2
  249. package/src/social-providers/vk.ts +3 -2
  250. package/src/social-providers/wechat.ts +213 -0
  251. package/src/types/context.ts +1 -3
  252. package/src/types/plugin.ts +14 -1
@@ -1,5 +1,10 @@
1
1
  import { createLogger, getColorDepth, TTY_COLORS } from "../../env";
2
2
  import { BetterAuthError } from "../../error";
3
+ import {
4
+ ATTR_DB_COLLECTION_NAME,
5
+ ATTR_DB_OPERATION_NAME,
6
+ withSpan,
7
+ } from "../../instrumentation";
3
8
  import type { BetterAuthOptions } from "../../types";
4
9
  import { safeJSONParse } from "../../utils/json";
5
10
  import { getAuthTables } from "../get-tables";
@@ -765,20 +770,36 @@ export const createAdapterFactory =
765
770
  });
766
771
  try {
767
772
  if (joinConfig.relation === "one-to-one") {
768
- result = await adapterInstance.findOne<Record<string, any>>({
769
- model: modelName,
770
- where: where,
771
- });
773
+ result = await withSpan(
774
+ `db findOne ${modelName}`,
775
+ {
776
+ [ATTR_DB_OPERATION_NAME]: "findOne",
777
+ [ATTR_DB_COLLECTION_NAME]: modelName,
778
+ },
779
+ () =>
780
+ adapterInstance.findOne<Record<string, any>>({
781
+ model: modelName,
782
+ where: where,
783
+ }),
784
+ );
772
785
  } else {
773
786
  const limit =
774
787
  joinConfig.limit ??
775
788
  options.advanced?.database?.defaultFindManyLimit ??
776
789
  100;
777
- result = await adapterInstance.findMany<Record<string, any>>({
778
- model: modelName,
779
- where: where,
780
- limit,
781
- });
790
+ result = await withSpan(
791
+ `db findMany ${modelName}`,
792
+ {
793
+ [ATTR_DB_OPERATION_NAME]: "findMany",
794
+ [ATTR_DB_COLLECTION_NAME]: modelName,
795
+ },
796
+ () =>
797
+ adapterInstance.findMany<Record<string, any>>({
798
+ model: modelName,
799
+ where: where,
800
+ limit,
801
+ }),
802
+ );
782
803
  }
783
804
  } catch (error) {
784
805
  logger.error(`Failed to query fallback join for model ${modelName}:`, {
@@ -879,7 +900,14 @@ export const createAdapterFactory =
879
900
  `${formatMethod("create")} ${formatAction("Parsed Input")}:`,
880
901
  { model, data },
881
902
  );
882
- const res = await adapterInstance.create<T>({ data, model });
903
+ const res = await withSpan(
904
+ `db create ${model}`,
905
+ {
906
+ [ATTR_DB_OPERATION_NAME]: "create",
907
+ [ATTR_DB_COLLECTION_NAME]: model,
908
+ },
909
+ () => adapterInstance.create<T>({ data, model }),
910
+ );
883
911
  debugLog(
884
912
  { method: "create" },
885
913
  `${formatTransactionId(thisTransactionId)} ${formatStep(3, 4)}`,
@@ -937,11 +965,19 @@ export const createAdapterFactory =
937
965
  `${formatMethod("update")} ${formatAction("Parsed Input")}:`,
938
966
  { model, data },
939
967
  );
940
- const res = await adapterInstance.update<T>({
941
- model,
942
- where,
943
- update: data,
944
- });
968
+ const res = await withSpan(
969
+ `db update ${model}`,
970
+ {
971
+ [ATTR_DB_OPERATION_NAME]: "update",
972
+ [ATTR_DB_COLLECTION_NAME]: model,
973
+ },
974
+ () =>
975
+ adapterInstance.update<T>({
976
+ model,
977
+ where,
978
+ update: data,
979
+ }),
980
+ );
945
981
  debugLog(
946
982
  { method: "update" },
947
983
  `${formatTransactionId(thisTransactionId)} ${formatStep(3, 4)}`,
@@ -1000,11 +1036,19 @@ export const createAdapterFactory =
1000
1036
  { model, data },
1001
1037
  );
1002
1038
 
1003
- const updatedCount = await adapterInstance.updateMany({
1004
- model,
1005
- where,
1006
- update: data,
1007
- });
1039
+ const updatedCount = await withSpan(
1040
+ `db updateMany ${model}`,
1041
+ {
1042
+ [ATTR_DB_OPERATION_NAME]: "updateMany",
1043
+ [ATTR_DB_COLLECTION_NAME]: model,
1044
+ },
1045
+ () =>
1046
+ adapterInstance.updateMany({
1047
+ model,
1048
+ where,
1049
+ update: data,
1050
+ }),
1051
+ );
1008
1052
  debugLog(
1009
1053
  { method: "updateMany" },
1010
1054
  `${formatTransactionId(thisTransactionId)} ${formatStep(3, 4)}`,
@@ -1063,12 +1107,20 @@ export const createAdapterFactory =
1063
1107
  { model, where, select, join },
1064
1108
  );
1065
1109
 
1066
- const res = await adapterInstance.findOne<T>({
1067
- model,
1068
- where,
1069
- select,
1070
- join: passJoinToAdapter ? join : undefined,
1071
- });
1110
+ const res = await withSpan(
1111
+ `db findOne ${model}`,
1112
+ {
1113
+ [ATTR_DB_OPERATION_NAME]: "findOne",
1114
+ [ATTR_DB_COLLECTION_NAME]: model,
1115
+ },
1116
+ () =>
1117
+ adapterInstance.findOne<T>({
1118
+ model,
1119
+ where,
1120
+ select,
1121
+ join: passJoinToAdapter ? join : undefined,
1122
+ }),
1123
+ );
1072
1124
  debugLog(
1073
1125
  { method: "findOne" },
1074
1126
  `${formatTransactionId(thisTransactionId)} ${formatStep(2, 3)}`,
@@ -1142,15 +1194,23 @@ export const createAdapterFactory =
1142
1194
  `${formatMethod("findMany")}:`,
1143
1195
  { model, where, limit, sortBy, offset, join },
1144
1196
  );
1145
- const res = await adapterInstance.findMany<T>({
1146
- model,
1147
- where,
1148
- limit: limit,
1149
- select,
1150
- sortBy,
1151
- offset,
1152
- join: passJoinToAdapter ? join : undefined,
1153
- });
1197
+ const res = await withSpan(
1198
+ `db findMany ${model}`,
1199
+ {
1200
+ [ATTR_DB_OPERATION_NAME]: "findMany",
1201
+ [ATTR_DB_COLLECTION_NAME]: model,
1202
+ },
1203
+ () =>
1204
+ adapterInstance.findMany<T>({
1205
+ model,
1206
+ where,
1207
+ limit: limit,
1208
+ select,
1209
+ sortBy,
1210
+ offset,
1211
+ join: passJoinToAdapter ? join : undefined,
1212
+ }),
1213
+ );
1154
1214
  debugLog(
1155
1215
  { method: "findMany" },
1156
1216
  `${formatTransactionId(thisTransactionId)} ${formatStep(2, 3)}`,
@@ -1197,10 +1257,14 @@ export const createAdapterFactory =
1197
1257
  `${formatMethod("delete")}:`,
1198
1258
  { model, where },
1199
1259
  );
1200
- await adapterInstance.delete({
1201
- model,
1202
- where,
1203
- });
1260
+ await withSpan(
1261
+ `db delete ${model}`,
1262
+ {
1263
+ [ATTR_DB_OPERATION_NAME]: "delete",
1264
+ [ATTR_DB_COLLECTION_NAME]: model,
1265
+ },
1266
+ () => adapterInstance.delete({ model, where }),
1267
+ );
1204
1268
  debugLog(
1205
1269
  { method: "delete" },
1206
1270
  `${formatTransactionId(thisTransactionId)} ${formatStep(2, 2)}`,
@@ -1230,10 +1294,14 @@ export const createAdapterFactory =
1230
1294
  `${formatMethod("deleteMany")} ${formatAction("DeleteMany")}:`,
1231
1295
  { model, where },
1232
1296
  );
1233
- const res = await adapterInstance.deleteMany({
1234
- model,
1235
- where,
1236
- });
1297
+ const res = await withSpan(
1298
+ `db deleteMany ${model}`,
1299
+ {
1300
+ [ATTR_DB_OPERATION_NAME]: "deleteMany",
1301
+ [ATTR_DB_COLLECTION_NAME]: model,
1302
+ },
1303
+ () => adapterInstance.deleteMany({ model, where }),
1304
+ );
1237
1305
  debugLog(
1238
1306
  { method: "deleteMany" },
1239
1307
  `${formatTransactionId(thisTransactionId)} ${formatStep(2, 2)}`,
@@ -1267,10 +1335,14 @@ export const createAdapterFactory =
1267
1335
  where,
1268
1336
  },
1269
1337
  );
1270
- const res = await adapterInstance.count({
1271
- model,
1272
- where,
1273
- });
1338
+ const res = await withSpan(
1339
+ `db count ${model}`,
1340
+ {
1341
+ [ATTR_DB_OPERATION_NAME]: "count",
1342
+ [ATTR_DB_COLLECTION_NAME]: model,
1343
+ },
1344
+ () => adapterInstance.count({ model, where }),
1345
+ );
1274
1346
  debugLog(
1275
1347
  { method: "count" },
1276
1348
  `${formatTransactionId(thisTransactionId)} ${formatStep(2, 2)}`,
@@ -0,0 +1,222 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import type { BetterAuthOptions } from "../../types";
3
+ import type { BetterAuthDBSchema } from "../type";
4
+ import { initGetIdField } from "./get-id-field";
5
+
6
+ const minimalSchema: BetterAuthDBSchema = {
7
+ user: {
8
+ modelName: "user",
9
+ fields: {
10
+ name: { type: "string" },
11
+ email: { type: "string" },
12
+ },
13
+ },
14
+ };
15
+
16
+ const uuidRegex =
17
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
18
+
19
+ function getField(
20
+ options: BetterAuthOptions,
21
+ initExtra?: Partial<Parameters<typeof initGetIdField>[0]>,
22
+ fieldExtra?: { customModelName?: string; forceAllowId?: boolean },
23
+ ) {
24
+ const idField = initGetIdField({
25
+ schema: minimalSchema,
26
+ options,
27
+ ...initExtra,
28
+ });
29
+ return idField({
30
+ customModelName: fieldExtra?.customModelName ?? "user",
31
+ forceAllowId: fieldExtra?.forceAllowId,
32
+ });
33
+ }
34
+
35
+ describe("defaultValue priority", () => {
36
+ it("should return undefined when disableIdGeneration is true", () => {
37
+ const field = getField(
38
+ { database: {} as any },
39
+ { disableIdGeneration: true },
40
+ );
41
+ expect(field.defaultValue).toBeUndefined();
42
+ });
43
+
44
+ it("should return undefined when generateId is false", () => {
45
+ const value = getField({
46
+ database: {} as any,
47
+ advanced: { database: { generateId: false } },
48
+ }).defaultValue?.();
49
+ expect(value).toBeUndefined();
50
+ });
51
+
52
+ it("should return undefined when generateId is 'serial'", () => {
53
+ const value = getField({
54
+ database: {} as any,
55
+ advanced: { database: { generateId: "serial" } },
56
+ }).defaultValue?.();
57
+ expect(value).toBeUndefined();
58
+ });
59
+
60
+ it("should use generateId function over 'uuid' and customIdGenerator", () => {
61
+ const value = getField(
62
+ {
63
+ database: {} as any,
64
+ advanced: { database: { generateId: () => "fn-id" } },
65
+ },
66
+ { customIdGenerator: () => "adapter-id" },
67
+ ).defaultValue?.();
68
+ expect(value).toBe("fn-id");
69
+ });
70
+
71
+ it("should use 'uuid' over customIdGenerator", () => {
72
+ const value = getField(
73
+ {
74
+ database: {} as any,
75
+ advanced: { database: { generateId: "uuid" } },
76
+ },
77
+ { customIdGenerator: () => "adapter-id", supportsUUIDs: false },
78
+ ).defaultValue?.();
79
+ expect(value).toMatch(uuidRegex);
80
+ });
81
+
82
+ it("should use customIdGenerator when generateId is not set", () => {
83
+ const value = getField(
84
+ { database: {} as any },
85
+ { customIdGenerator: () => "adapter-id" },
86
+ ).defaultValue?.();
87
+ expect(value).toBe("adapter-id");
88
+ });
89
+
90
+ it("should fall back to default id generation", () => {
91
+ const value = getField({ database: {} as any }).defaultValue?.();
92
+ expect(typeof value).toBe("string");
93
+ expect(value).not.toMatch(uuidRegex);
94
+ });
95
+ });
96
+
97
+ describe("type and required", () => {
98
+ it("should have type 'number' when generateId is 'serial'", () => {
99
+ const field = getField({
100
+ database: {} as any,
101
+ advanced: { database: { generateId: "serial" } },
102
+ });
103
+ expect(field.type).toBe("number");
104
+ expect(field.required).toBe(false);
105
+ });
106
+
107
+ it("should have type 'string' by default", () => {
108
+ const field = getField({ database: {} as any });
109
+ expect(field.type).toBe("string");
110
+ expect(field.required).toBe(true);
111
+ });
112
+
113
+ it("should not generate id when useUUIDs and supportsUUIDs", () => {
114
+ const field = getField(
115
+ {
116
+ database: {} as any,
117
+ advanced: { database: { generateId: "uuid" } },
118
+ },
119
+ { supportsUUIDs: true },
120
+ );
121
+ expect(field.required).toBe(false);
122
+ expect(field.defaultValue).toBeUndefined();
123
+ });
124
+ });
125
+
126
+ describe("transform.input", () => {
127
+ it("should return undefined for falsy value", () => {
128
+ const field = getField({ database: {} as any });
129
+ expect(field.transform.input(undefined)).toBeUndefined();
130
+ expect(field.transform.input(null)).toBeUndefined();
131
+ expect(field.transform.input("")).toBeUndefined();
132
+ });
133
+
134
+ it("should return value as-is by default", () => {
135
+ const field = getField({ database: {} as any });
136
+ expect(field.transform.input("some-id")).toBe("some-id");
137
+ });
138
+
139
+ describe("serial", () => {
140
+ it("should convert string to number", () => {
141
+ const field = getField({
142
+ database: {} as any,
143
+ advanced: { database: { generateId: "serial" } },
144
+ });
145
+ expect(field.transform.input("42")).toBe(42);
146
+ });
147
+
148
+ it("should return undefined for non-numeric string", () => {
149
+ const field = getField({
150
+ database: {} as any,
151
+ advanced: { database: { generateId: "serial" } },
152
+ });
153
+ expect(field.transform.input("not-a-number")).toBeUndefined();
154
+ });
155
+ });
156
+
157
+ describe("uuid", () => {
158
+ it("should return value as-is when shouldGenerateId and not forceAllowId", () => {
159
+ const field = getField(
160
+ {
161
+ database: {} as any,
162
+ advanced: { database: { generateId: "uuid" } },
163
+ },
164
+ { supportsUUIDs: false },
165
+ );
166
+ const uuid = crypto.randomUUID();
167
+ expect(field.transform.input(uuid)).toBe(uuid);
168
+ });
169
+
170
+ it("should return undefined when supportsUUIDs (DB handles it)", () => {
171
+ const field = getField(
172
+ {
173
+ database: {} as any,
174
+ advanced: { database: { generateId: "uuid" } },
175
+ },
176
+ { supportsUUIDs: true },
177
+ );
178
+ expect(field.transform.input("some-value")).toBeUndefined();
179
+ });
180
+
181
+ it("should accept valid UUID when forceAllowId is true", () => {
182
+ const uuid = crypto.randomUUID();
183
+ const field = getField(
184
+ {
185
+ database: {} as any,
186
+ advanced: { database: { generateId: "uuid" } },
187
+ },
188
+ { supportsUUIDs: false },
189
+ { forceAllowId: true },
190
+ );
191
+ expect(field.transform.input(uuid)).toBe(uuid);
192
+ });
193
+
194
+ it("should generate new UUID for non-string value when DB doesn't support UUIDs", () => {
195
+ const field = getField(
196
+ {
197
+ database: {} as any,
198
+ advanced: { database: { generateId: "uuid" } },
199
+ },
200
+ { supportsUUIDs: false },
201
+ { forceAllowId: true },
202
+ );
203
+ const result = field.transform.input(123);
204
+ expect(result).toMatch(uuidRegex);
205
+ });
206
+ });
207
+ });
208
+
209
+ describe("transform.output", () => {
210
+ it("should return undefined for falsy value", () => {
211
+ const field = getField({ database: {} as any });
212
+ expect(field.transform.output(undefined)).toBeUndefined();
213
+ expect(field.transform.output(null)).toBeUndefined();
214
+ expect(field.transform.output("")).toBeUndefined();
215
+ });
216
+
217
+ it("should convert value to string", () => {
218
+ const field = getField({ database: {} as any });
219
+ expect(field.transform.output(123)).toBe("123");
220
+ expect(field.transform.output("abc")).toBe("abc");
221
+ });
222
+ });
@@ -57,18 +57,29 @@ export const initGetIdField = ({
57
57
  defaultValue() {
58
58
  if (disableIdGeneration) return undefined;
59
59
  const generateId = options.advanced?.database?.generateId;
60
- if (generateId === false || useNumberId) return undefined;
60
+
61
+ // let the database handle id generation
62
+ if (generateId === false || generateId === "serial")
63
+ return undefined;
64
+
65
+ // user-provided function takes highest priority
61
66
  if (typeof generateId === "function") {
62
67
  return generateId({
63
68
  model,
64
69
  });
65
70
  }
66
- if (customIdGenerator) {
67
- return customIdGenerator({ model });
68
- }
71
+
72
+ // user-provided "uuid" option
69
73
  if (generateId === "uuid") {
70
74
  return crypto.randomUUID();
71
75
  }
76
+
77
+ // database adapter-level custom id generator
78
+ if (customIdGenerator) {
79
+ return customIdGenerator({ model });
80
+ }
81
+
82
+ // fallback to default id generation
72
83
  return defaultGenerateId();
73
84
  },
74
85
  }
@@ -0,0 +1,22 @@
1
+ import {
2
+ ATTR_DB_COLLECTION_NAME,
3
+ ATTR_DB_OPERATION_NAME,
4
+ ATTR_HTTP_RESPONSE_STATUS_CODE,
5
+ ATTR_HTTP_ROUTE,
6
+ } from "@opentelemetry/semantic-conventions";
7
+
8
+ export {
9
+ ATTR_DB_COLLECTION_NAME,
10
+ ATTR_DB_OPERATION_NAME,
11
+ ATTR_HTTP_RESPONSE_STATUS_CODE,
12
+ ATTR_HTTP_ROUTE,
13
+ };
14
+
15
+ /** Operation identifier (e.g. getSession, signUpWithEmailAndPassword). Uses endpoint operationId when set, otherwise the endpoint key. */
16
+ export const ATTR_OPERATION_ID = "better_auth.operation_id" as const;
17
+
18
+ /** Hook type (e.g. before, after, create.before). */
19
+ export const ATTR_HOOK_TYPE = "better_auth.hook.type" as const;
20
+
21
+ /** Execution context (e.g. user, plugin:id). */
22
+ export const ATTR_CONTEXT = "better_auth.context" as const;
@@ -0,0 +1,2 @@
1
+ export * from "./attributes";
2
+ export * from "./tracer";
@@ -0,0 +1,139 @@
1
+ import { trace } from "@opentelemetry/api";
2
+ import {
3
+ InMemorySpanExporter,
4
+ SimpleSpanProcessor,
5
+ } from "@opentelemetry/sdk-trace-base";
6
+ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
7
+ import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
8
+ import { withSpan } from ".";
9
+ import { ATTR_DB_COLLECTION_NAME, ATTR_DB_OPERATION_NAME } from "./attributes";
10
+
11
+ describe("instrumentation", () => {
12
+ let provider: NodeTracerProvider;
13
+ let exporter: InMemorySpanExporter;
14
+
15
+ beforeAll(async () => {
16
+ exporter = new InMemorySpanExporter();
17
+ provider = new NodeTracerProvider({
18
+ spanProcessors: [new SimpleSpanProcessor(exporter)],
19
+ });
20
+ trace.setGlobalTracerProvider(provider);
21
+ });
22
+
23
+ afterAll(async () => {
24
+ await provider.shutdown();
25
+ });
26
+
27
+ beforeEach(() => {
28
+ exporter.reset();
29
+ });
30
+
31
+ it("creates a span with name and attributes for sync function", () => {
32
+ const result = withSpan(
33
+ "test.sync",
34
+ {
35
+ [ATTR_DB_OPERATION_NAME]: "findOne",
36
+ [ATTR_DB_COLLECTION_NAME]: "user",
37
+ },
38
+ () => 42,
39
+ );
40
+
41
+ expect(result).toBe(42);
42
+
43
+ const spans = exporter.getFinishedSpans();
44
+ expect(spans).toHaveLength(1);
45
+ expect(spans[0]?.name).toBe("test.sync");
46
+ expect(spans[0]?.attributes).toMatchObject({
47
+ [ATTR_DB_OPERATION_NAME]: "findOne",
48
+ [ATTR_DB_COLLECTION_NAME]: "user",
49
+ });
50
+ });
51
+
52
+ it("creates a span for async function", async () => {
53
+ const result = await withSpan(
54
+ "test.async",
55
+ { endpoint: "getSession" },
56
+ async () => {
57
+ await new Promise((r) => setTimeout(r, 5));
58
+ return "session-id";
59
+ },
60
+ );
61
+
62
+ expect(result).toBe("session-id");
63
+
64
+ const spans = exporter.getFinishedSpans();
65
+ expect(spans).toHaveLength(1);
66
+ expect(spans[0]?.name).toBe("test.async");
67
+ expect(spans[0]?.attributes).toMatchObject({ endpoint: "getSession" });
68
+ });
69
+
70
+ it("records error status and exception when sync function throws", () => {
71
+ const err = new Error("sync failure");
72
+
73
+ expect(() =>
74
+ withSpan("test.sync.error", { foo: "bar" }, () => {
75
+ throw err;
76
+ }),
77
+ ).toThrow("sync failure");
78
+
79
+ const spans = exporter.getFinishedSpans();
80
+ expect(spans).toHaveLength(1);
81
+ expect(spans[0]?.name).toBe("test.sync.error");
82
+ expect(spans[0]?.status).toMatchObject({
83
+ code: 2,
84
+ message: "sync failure",
85
+ });
86
+ });
87
+
88
+ it("records error status and exception when async function rejects", async () => {
89
+ const err = new Error("async failure");
90
+
91
+ await expect(
92
+ withSpan("test.async.error", { baz: 1 }, async () => {
93
+ await Promise.resolve();
94
+ throw err;
95
+ }),
96
+ ).rejects.toThrow("async failure");
97
+
98
+ const spans = exporter.getFinishedSpans();
99
+ expect(spans).toHaveLength(1);
100
+ expect(spans[0]?.name).toBe("test.async.error");
101
+ expect(spans[0]?.status).toMatchObject({
102
+ code: 2,
103
+ message: "async failure",
104
+ });
105
+ });
106
+
107
+ it("creates multiple sequential spans", () => {
108
+ void withSpan("first", { order: 1 }, () => 1);
109
+ void withSpan("second", { order: 2 }, () => 2);
110
+
111
+ const spans = exporter.getFinishedSpans();
112
+ expect(spans).toHaveLength(2);
113
+ expect(spans.map((s) => s.name)).toEqual(
114
+ expect.arrayContaining(["first", "second"]),
115
+ );
116
+ });
117
+
118
+ it("creates nested spans when withSpan is composed", () => {
119
+ const result = withSpan("outer", { depth: 0 }, () => {
120
+ return withSpan("inner", { depth: 1 }, () => "ok");
121
+ });
122
+
123
+ expect(result).toBe("ok");
124
+
125
+ const spans = exporter.getFinishedSpans();
126
+ expect(spans).toHaveLength(2);
127
+ expect(spans.map((s) => s.name).sort()).toEqual(["inner", "outer"]);
128
+ });
129
+
130
+ it("uses better-auth instrumentation scope", () => {
131
+ void withSpan("scope.check", {}, () => undefined);
132
+
133
+ const spans = exporter.getFinishedSpans();
134
+ expect(spans).toHaveLength(1);
135
+
136
+ const span = spans[0];
137
+ expect(span?.instrumentationLibrary?.name).toBe("better-auth");
138
+ });
139
+ });