@nextsparkjs/core 0.1.0-beta.195 → 0.1.0-beta.197

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 (283) hide show
  1. package/README.md +2 -0
  2. package/dist/components/auth/forms/SignupForm.d.ts.map +1 -1
  3. package/dist/components/auth/forms/SignupForm.js +55 -45
  4. package/dist/components/billing/InvoicesPagination.js +2 -2
  5. package/dist/components/dashboard/block-editor/config-panel.d.ts.map +1 -1
  6. package/dist/components/dashboard/block-editor/config-panel.js +3 -2
  7. package/dist/components/dashboard/block-editor/entity-fields-sidebar.d.ts.map +1 -1
  8. package/dist/components/dashboard/block-editor/entity-fields-sidebar.js +3 -2
  9. package/dist/components/dashboard/navigation/DynamicNavigation.js +2 -2
  10. package/dist/components/settings/layouts/SettingsSidebar.d.ts.map +1 -1
  11. package/dist/components/settings/layouts/SettingsSidebar.js +0 -1
  12. package/dist/components/teams/CreateTeamButton.js +2 -2
  13. package/dist/components/teams/CreateTeamDialog.js +1 -1
  14. package/dist/components/teams/InviteMemberButton.js +3 -3
  15. package/dist/components/teams/InviteMemberDialog.js +1 -1
  16. package/dist/components/teams/TeamPendingInvitations.js +2 -2
  17. package/dist/components/users/tables/UsersTable.d.ts.map +1 -1
  18. package/dist/components/users/tables/UsersTable.js +3 -4
  19. package/dist/hooks/useAuthReadiness.d.ts +1 -0
  20. package/dist/hooks/useAuthReadiness.d.ts.map +1 -1
  21. package/dist/hooks/useAuthReadiness.js +3 -1
  22. package/dist/lib/actions/entity.actions.js +2 -2
  23. package/dist/lib/actions/team.actions.js +2 -2
  24. package/dist/lib/actions/user.actions.js +4 -4
  25. package/dist/lib/api/api-error.d.ts +2 -0
  26. package/dist/lib/api/api-error.d.ts.map +1 -1
  27. package/dist/lib/api/api-error.js +3 -1
  28. package/dist/lib/api/auth/dual-auth.d.ts +2 -1
  29. package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
  30. package/dist/lib/api/auth/dual-auth.js +25 -6
  31. package/dist/lib/api/auth/scope-policy.d.ts +6 -3
  32. package/dist/lib/api/auth/scope-policy.d.ts.map +1 -1
  33. package/dist/lib/api/auth.d.ts.map +1 -1
  34. package/dist/lib/api/auth.js +7 -0
  35. package/dist/lib/api/cors-response.d.ts +3 -2
  36. package/dist/lib/api/cors-response.d.ts.map +1 -1
  37. package/dist/lib/api/cors-response.js +8 -6
  38. package/dist/lib/api/entity/generic-handler.d.ts.map +1 -1
  39. package/dist/lib/api/entity/generic-handler.js +66 -15
  40. package/dist/lib/api/entity-handler.js +2 -2
  41. package/dist/lib/api/helpers.d.ts.map +1 -1
  42. package/dist/lib/api/helpers.js +9 -24
  43. package/dist/lib/api/permission-middleware.d.ts.map +1 -1
  44. package/dist/lib/api/permission-middleware.js +0 -2
  45. package/dist/lib/api/post-categories.d.ts +29 -0
  46. package/dist/lib/api/post-categories.d.ts.map +1 -0
  47. package/dist/lib/api/post-categories.js +33 -0
  48. package/dist/lib/api/rate-limit.d.ts +8 -0
  49. package/dist/lib/api/rate-limit.d.ts.map +1 -1
  50. package/dist/lib/api/rate-limit.js +34 -3
  51. package/dist/lib/api/request-origin.d.ts.map +1 -1
  52. package/dist/lib/api/request-origin.js +1 -1
  53. package/dist/lib/auth/auth-methods.d.ts +5 -2
  54. package/dist/lib/auth/auth-methods.d.ts.map +1 -1
  55. package/dist/lib/auth/auth-methods.js +4 -2
  56. package/dist/lib/auth/authorization-session.d.ts +14 -0
  57. package/dist/lib/auth/authorization-session.d.ts.map +1 -0
  58. package/dist/lib/auth/authorization-session.js +10 -0
  59. package/dist/lib/auth/request-session.d.ts +5 -0
  60. package/dist/lib/auth/request-session.d.ts.map +1 -1
  61. package/dist/lib/auth/request-session.js +3 -0
  62. package/dist/lib/auth/runtime-readiness.d.ts +7 -4
  63. package/dist/lib/auth/runtime-readiness.d.ts.map +1 -1
  64. package/dist/lib/auth/runtime-readiness.js +9 -6
  65. package/dist/lib/auth/sample-accounts.d.ts +18 -0
  66. package/dist/lib/auth/sample-accounts.d.ts.map +1 -0
  67. package/dist/lib/auth/sample-accounts.js +25 -0
  68. package/dist/lib/auth/suspension.d.ts +22 -0
  69. package/dist/lib/auth/suspension.d.ts.map +1 -0
  70. package/dist/lib/auth/suspension.js +43 -0
  71. package/dist/lib/auth.d.ts.map +1 -1
  72. package/dist/lib/auth.js +20 -17
  73. package/dist/lib/cache/public-entity-cache.d.ts +21 -0
  74. package/dist/lib/cache/public-entity-cache.d.ts.map +1 -0
  75. package/dist/lib/cache/public-entity-cache.js +25 -0
  76. package/dist/lib/config/app.config.d.ts.map +1 -1
  77. package/dist/lib/config/app.config.js +6 -7
  78. package/dist/lib/config/types.d.ts +8 -6
  79. package/dist/lib/config/types.d.ts.map +1 -1
  80. package/dist/lib/db-pool.d.ts +31 -0
  81. package/dist/lib/db-pool.d.ts.map +1 -0
  82. package/dist/lib/db-pool.js +75 -0
  83. package/dist/lib/db.d.ts +1 -1
  84. package/dist/lib/db.d.ts.map +1 -1
  85. package/dist/lib/db.js +11 -14
  86. package/dist/lib/entities/api-generator.js +5 -5
  87. package/dist/lib/entities/entity-hooks.d.ts.map +1 -1
  88. package/dist/lib/entities/entity-hooks.js +4 -0
  89. package/dist/lib/mcp/errors.d.ts +2 -0
  90. package/dist/lib/mcp/errors.d.ts.map +1 -1
  91. package/dist/lib/mcp/errors.js +3 -1
  92. package/dist/lib/mcp/index.d.ts +1 -1
  93. package/dist/lib/mcp/index.d.ts.map +1 -1
  94. package/dist/lib/mcp/index.js +2 -1
  95. package/dist/lib/oauth/token-refresh.d.ts.map +1 -1
  96. package/dist/lib/oauth/token-refresh.js +3 -6
  97. package/dist/lib/permissions/check.d.ts +9 -0
  98. package/dist/lib/permissions/check.d.ts.map +1 -1
  99. package/dist/lib/permissions/check.js +11 -1
  100. package/dist/lib/services/generic-entity.service.d.ts.map +1 -1
  101. package/dist/lib/services/generic-entity.service.js +41 -7
  102. package/dist/lib/services/transactional-meta.service.d.ts.map +1 -1
  103. package/dist/lib/services/transactional-meta.service.js +7 -8
  104. package/dist/lib/teams/actions.d.ts.map +1 -1
  105. package/dist/lib/teams/actions.js +0 -1
  106. package/dist/lib/validation.d.ts +5 -0
  107. package/dist/lib/validation.d.ts.map +1 -1
  108. package/dist/lib/validation.js +2 -0
  109. package/dist/messages/de/billing.json +12 -1
  110. package/dist/messages/de/common.json +2 -0
  111. package/dist/messages/de/docs.json +1 -0
  112. package/dist/messages/de/features.json +16 -4
  113. package/dist/messages/de/home.json +5 -0
  114. package/dist/messages/de/index.d.ts +68 -25
  115. package/dist/messages/de/index.d.ts.map +1 -1
  116. package/dist/messages/de/index.js +2 -0
  117. package/dist/messages/de/index.ts +2 -0
  118. package/dist/messages/de/settings.json +1 -0
  119. package/dist/messages/de/teams.json +3 -18
  120. package/dist/messages/en/billing.json +1 -1
  121. package/dist/messages/en/common.json +2 -0
  122. package/dist/messages/en/docs.json +1 -0
  123. package/dist/messages/en/features.json +16 -4
  124. package/dist/messages/en/home.json +5 -0
  125. package/dist/messages/en/index.d.ts +58 -25
  126. package/dist/messages/en/index.d.ts.map +1 -1
  127. package/dist/messages/en/index.js +2 -0
  128. package/dist/messages/en/index.ts +2 -0
  129. package/dist/messages/en/settings.json +1 -0
  130. package/dist/messages/en/teams.json +3 -18
  131. package/dist/messages/es/billing.json +1 -1
  132. package/dist/messages/es/common.json +2 -0
  133. package/dist/messages/es/docs.json +1 -0
  134. package/dist/messages/es/features.json +16 -4
  135. package/dist/messages/es/home.json +5 -0
  136. package/dist/messages/es/index.d.ts +41 -8
  137. package/dist/messages/es/index.d.ts.map +1 -1
  138. package/dist/messages/es/index.js +2 -0
  139. package/dist/messages/es/index.ts +2 -0
  140. package/dist/messages/es/settings.json +1 -0
  141. package/dist/messages/es/teams.json +3 -0
  142. package/dist/messages/fr/billing.json +12 -1
  143. package/dist/messages/fr/common.json +2 -0
  144. package/dist/messages/fr/docs.json +1 -0
  145. package/dist/messages/fr/features.json +16 -4
  146. package/dist/messages/fr/home.json +5 -0
  147. package/dist/messages/fr/index.d.ts +68 -25
  148. package/dist/messages/fr/index.d.ts.map +1 -1
  149. package/dist/messages/fr/index.js +2 -0
  150. package/dist/messages/fr/index.ts +2 -0
  151. package/dist/messages/fr/settings.json +1 -0
  152. package/dist/messages/fr/teams.json +3 -18
  153. package/dist/messages/it/billing.json +12 -1
  154. package/dist/messages/it/common.json +2 -0
  155. package/dist/messages/it/docs.json +1 -0
  156. package/dist/messages/it/features.json +16 -4
  157. package/dist/messages/it/home.json +5 -0
  158. package/dist/messages/it/index.d.ts +68 -25
  159. package/dist/messages/it/index.d.ts.map +1 -1
  160. package/dist/messages/it/index.js +2 -0
  161. package/dist/messages/it/index.ts +2 -0
  162. package/dist/messages/it/settings.json +1 -0
  163. package/dist/messages/it/teams.json +3 -18
  164. package/dist/messages/pt/billing.json +12 -1
  165. package/dist/messages/pt/common.json +2 -0
  166. package/dist/messages/pt/docs.json +1 -0
  167. package/dist/messages/pt/features.json +16 -4
  168. package/dist/messages/pt/home.json +5 -0
  169. package/dist/messages/pt/index.d.ts +68 -25
  170. package/dist/messages/pt/index.d.ts.map +1 -1
  171. package/dist/messages/pt/index.js +2 -0
  172. package/dist/messages/pt/index.ts +2 -0
  173. package/dist/messages/pt/settings.json +1 -0
  174. package/dist/messages/pt/teams.json +3 -18
  175. package/dist/migrations/029_sample_accounts_outside_development.sql +55 -0
  176. package/dist/migrations/030_taxonomies_team_writes.sql +29 -0
  177. package/dist/migrations/031_users_role_suspended.sql +41 -0
  178. package/dist/migrations/090_sample_data.sql +10 -0
  179. package/dist/routes/(auth)/signup/page.cc.d.ts +3 -1
  180. package/dist/routes/(auth)/signup/page.cc.d.ts.map +1 -1
  181. package/dist/routes/(auth)/signup/page.cc.js +2 -2
  182. package/dist/routes/(auth)/signup/page.d.ts +3 -1
  183. package/dist/routes/(auth)/signup/page.d.ts.map +1 -1
  184. package/dist/routes/(auth)/signup/page.js +4 -3
  185. package/dist/routes/_internal/area-access.js +2 -2
  186. package/dist/routes/_internal/entity-permission-layout.d.ts.map +1 -1
  187. package/dist/routes/_internal/entity-permission-layout.js +0 -7
  188. package/dist/routes/api/devtools/config/entities/route.js +2 -2
  189. package/dist/routes/api/devtools/config/theme/route.js +2 -2
  190. package/dist/routes/api/devtools/tests/[...path]/route.js +2 -2
  191. package/dist/routes/api/devtools/tests/route.js +2 -2
  192. package/dist/routes/api/mcp/route.d.ts.map +1 -1
  193. package/dist/routes/api/mcp/route.js +7 -1
  194. package/dist/routes/api/superadmin/subscriptions/route.js +2 -2
  195. package/dist/routes/api/superadmin/teams/[teamId]/route.js +2 -2
  196. package/dist/routes/api/superadmin/teams/route.js +2 -2
  197. package/dist/routes/api/superadmin/users/[userId]/route.d.ts +5 -3
  198. package/dist/routes/api/superadmin/users/[userId]/route.d.ts.map +1 -1
  199. package/dist/routes/api/superadmin/users/[userId]/route.js +46 -23
  200. package/dist/routes/api/superadmin/users/route.js +2 -2
  201. package/dist/routes/api/user/delete-account/route.d.ts.map +1 -1
  202. package/dist/routes/api/user/delete-account/route.js +2 -3
  203. package/dist/routes/api/user/plan-flags/route.d.ts.map +1 -1
  204. package/dist/routes/api/user/plan-flags/route.js +5 -5
  205. package/dist/routes/api/user/profile/route.js +3 -3
  206. package/dist/routes/api/v1/auth/signup-with-invite/route.d.ts.map +1 -1
  207. package/dist/routes/api/v1/auth/signup-with-invite/route.js +80 -44
  208. package/dist/routes/api/v1/media/upload/route.d.ts +2 -1
  209. package/dist/routes/api/v1/media/upload/route.d.ts.map +1 -1
  210. package/dist/routes/api/v1/media/upload/route.js +10 -2
  211. package/dist/routes/api/v1/post-categories/[id]/route.d.ts.map +1 -1
  212. package/dist/routes/api/v1/post-categories/[id]/route.js +39 -34
  213. package/dist/routes/api/v1/post-categories/route.d.ts.map +1 -1
  214. package/dist/routes/api/v1/post-categories/route.js +18 -14
  215. package/dist/routes/api/v1/team-invitations/route.d.ts.map +1 -1
  216. package/dist/routes/api/v1/team-invitations/route.js +12 -5
  217. package/dist/routes/api/v1/teams/[teamId]/members/route.d.ts.map +1 -1
  218. package/dist/routes/api/v1/teams/[teamId]/members/route.js +12 -10
  219. package/dist/routes/api/v1/teams/switch/route.d.ts.map +1 -1
  220. package/dist/routes/api/v1/teams/switch/route.js +4 -1
  221. package/dist/routes/dashboard/settings/password/page.d.ts.map +1 -1
  222. package/dist/routes/dashboard/settings/password/page.js +10 -12
  223. package/dist/routes/devtools/config/page.js +1 -1
  224. package/dist/routes/devtools/page.js +1 -1
  225. package/dist/routes/devtools/scheduled-actions/page.js +1 -1
  226. package/dist/routes/devtools/tests/[[...path]]/page.js +1 -1
  227. package/dist/routes/superadmin/users/page.js +2 -2
  228. package/dist/templates/ci-workflows/github/cypress-regression.yml +1 -1
  229. package/dist/templates/ci-workflows/github/cypress-smoke.yml +2 -1
  230. package/dist/templates/next.config.mjs +11 -31
  231. package/dist/templates/projects/blog/package.json +1 -1
  232. package/dist/templates/projects/crm/package.json +1 -1
  233. package/dist/templates/projects/productivity/package.json +1 -1
  234. package/dist/templates/projects/starter/README.md +2 -0
  235. package/dist/templates/projects/starter/config/app.config.ts +8 -0
  236. package/dist/templates/projects/starter/docs/public/01-overview/01-introduction.md +2 -0
  237. package/dist/templates/projects/starter/templates/dashboard/analytics/layout.tsx +14 -0
  238. package/dist/templates/proxy.ts +18 -8
  239. package/dist/templates/public/uploads/README.md +5 -94
  240. package/migrations/029_sample_accounts_outside_development.sql +55 -0
  241. package/migrations/030_taxonomies_team_writes.sql +29 -0
  242. package/migrations/031_users_role_suspended.sql +41 -0
  243. package/migrations/090_sample_data.sql +10 -0
  244. package/package.json +3 -3
  245. package/scripts/build/registry/__tests__/build-output.test.mjs +1 -1
  246. package/scripts/build/registry/__tests__/project-mode-root-first.test.mjs +6 -6
  247. package/scripts/build/registry/__tests__/write-places.test.mjs +1 -1
  248. package/scripts/build/registry/contracts/__tests__/contracts-wire.test.mjs +1 -1
  249. package/scripts/build/registry/contracts/__tests__/contracts.test.mjs +1 -1
  250. package/scripts/build/registry/host/README.md +2 -2
  251. package/scripts/build/registry/host/__tests__/facade-emitter.test.mjs +14 -13
  252. package/scripts/build/registry/host/__tests__/host-mode.test.mjs +1 -1
  253. package/scripts/build/registry/host/__tests__/host-project-composition.test.mjs +1 -1
  254. package/scripts/build/registry/host/__tests__/host-project.test.mjs +2 -2
  255. package/scripts/build/registry/host/__tests__/host-proxy-areas.test.mjs +99 -1
  256. package/scripts/build/registry/host/__tests__/host-robustness.test.mjs +1 -1
  257. package/scripts/build/registry/host/facade-emitter.mjs +1 -1
  258. package/scripts/build/registry/host/next-route-exports.json +2 -2
  259. package/scripts/build/registry/host/proxy-areas.mjs +92 -2
  260. package/scripts/db/run-migrations.mjs +30 -10
  261. package/scripts/db/sample-data.mjs +44 -0
  262. package/scripts/db/verify-theme-migrations.mjs +7 -1
  263. package/templates/ci-workflows/github/cypress-regression.yml +1 -1
  264. package/templates/ci-workflows/github/cypress-smoke.yml +2 -1
  265. package/templates/next.config.mjs +11 -31
  266. package/templates/projects/blog/package.json +1 -1
  267. package/templates/projects/crm/package.json +1 -1
  268. package/templates/projects/productivity/package.json +1 -1
  269. package/templates/projects/starter/README.md +2 -0
  270. package/templates/projects/starter/config/app.config.ts +8 -0
  271. package/templates/projects/starter/docs/public/01-overview/01-introduction.md +2 -0
  272. package/templates/projects/starter/templates/dashboard/analytics/layout.tsx +14 -0
  273. package/templates/proxy.ts +18 -8
  274. package/templates/public/uploads/README.md +5 -94
  275. package/tests/jest/__mocks__/@nextsparkjs/registries/api-presets-registry.ts +2 -0
  276. package/tests/jest/__mocks__/@nextsparkjs/registries/mcp-registry.ts +2 -0
  277. package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +5 -4
  278. package/dist/templates/projects/starter/public/brand/.gitkeep +0 -0
  279. package/dist/templates/public/uploads/.gitignore +0 -6
  280. package/dist/templates/public/uploads/temp/.gitkeep +0 -1
  281. package/templates/projects/starter/public/brand/.gitkeep +0 -0
  282. package/templates/public/uploads/.gitignore +0 -6
  283. package/templates/public/uploads/temp/.gitkeep +0 -1
@@ -43,6 +43,34 @@ function checkRateLimit(identifier, limit = 1e3, windowMs = 6e4) {
43
43
  limit
44
44
  };
45
45
  }
46
+ const apiKeyChecks = /* @__PURE__ */ new WeakMap();
47
+ function apiKeyRateLimitResponse(request, auth) {
48
+ let result = apiKeyChecks.get(request);
49
+ if (!result) {
50
+ result = checkRateLimit(auth.keyId);
51
+ apiKeyChecks.set(request, result);
52
+ }
53
+ if (result.allowed) return null;
54
+ const retryAfter = Math.ceil((result.resetTime - Date.now()) / 1e3);
55
+ return NextResponse.json(
56
+ {
57
+ success: false,
58
+ error: "Rate limit exceeded",
59
+ message: `Too many requests. Limit: ${result.limit} requests per minute`,
60
+ code: "RATE_LIMIT_EXCEEDED",
61
+ retryAfter
62
+ },
63
+ {
64
+ status: 429,
65
+ headers: {
66
+ "X-RateLimit-Limit": result.limit.toString(),
67
+ "X-RateLimit-Remaining": "0",
68
+ "X-RateLimit-Reset": result.resetTime.toString(),
69
+ "Retry-After": retryAfter.toString()
70
+ }
71
+ }
72
+ );
73
+ }
46
74
  async function checkDistributedRateLimit(identifier, type = "api") {
47
75
  if (isRateLimitingDisabled()) {
48
76
  return { allowed: true, remaining: 999, resetTime: Date.now() + 6e4, limit: 999 };
@@ -225,9 +253,11 @@ function withRateLimitTier(handler, tier = "api") {
225
253
  return createRateLimitErrorResponse(rateLimitResult);
226
254
  }
227
255
  const response = await handler(request, ...args);
228
- response.headers.set("X-RateLimit-Limit", rateLimitResult.limit.toString());
229
- response.headers.set("X-RateLimit-Remaining", Math.max(0, rateLimitResult.remaining).toString());
230
- response.headers.set("X-RateLimit-Reset", rateLimitResult.resetTime.toString());
256
+ if (response.status !== 429) {
257
+ response.headers.set("X-RateLimit-Limit", rateLimitResult.limit.toString());
258
+ response.headers.set("X-RateLimit-Remaining", Math.max(0, rateLimitResult.remaining).toString());
259
+ response.headers.set("X-RateLimit-Reset", rateLimitResult.resetTime.toString());
260
+ }
231
261
  return response;
232
262
  };
233
263
  return async (request, ...args) => {
@@ -237,6 +267,7 @@ function withRateLimitTier(handler, tier = "api") {
237
267
  }
238
268
  export {
239
269
  addRateLimitHeaders,
270
+ apiKeyRateLimitResponse,
240
271
  applyRateLimit,
241
272
  checkDistributedRateLimit,
242
273
  checkRateLimit,
@@ -1 +1 @@
1
- {"version":3,"file":"request-origin.d.ts","sourceRoot":"","sources":["../../../src/lib/api/request-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AA+DvD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY,GAAG,WAAW,CAoBnF"}
1
+ {"version":3,"file":"request-origin.d.ts","sourceRoot":"","sources":["../../../src/lib/api/request-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAgEvD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY,GAAG,WAAW,CAoBnF"}
@@ -36,7 +36,7 @@ function withoutSessionCookies(request) {
36
36
  }
37
37
  function isTrustedWriteOrigin(request, origin) {
38
38
  const path = requestPathname(request);
39
- if (path !== void 0 && isAppOnlyCorsPath(path)) {
39
+ if (path === void 0 || isAppOnlyCorsPath(path)) {
40
40
  const lan = normalizeCorsEnvironment(process.env.NODE_ENV || "development") !== "production" && isPrivateLanOrigin(origin);
41
41
  return lan || appOrigins().includes(origin);
42
42
  }
@@ -46,7 +46,10 @@ export declare function getPrimaryEmailMethod(methods: readonly AuthLoginMethod[
46
46
  export declare function matchAuthPreset(methods: readonly AuthLoginMethod[]): AuthPreset | null;
47
47
  /**
48
48
  * Server-side: should Better Auth's email + password endpoints be enabled?
49
- * Defaults to true regardless of the UI preset (see `AuthConfig.emailAndPassword`).
49
+ * An explicit `emailAndPassword.enabled` decides. Otherwise they are on when
50
+ * `methods` lists `'email-password'`, and outside production (so the dev
51
+ * keyring, `db:seed` users and Cypress sign in with a password); a production
52
+ * app on the passwordless preset serves no password endpoint.
50
53
  */
51
- export declare function isPasswordLoginEnabled(authConfig?: Pick<AuthConfig, 'emailAndPassword'> | null): boolean;
54
+ export declare function isPasswordLoginEnabled(authConfig?: Pick<AuthConfig, 'methods' | 'emailAndPassword'> | null, nodeEnv?: string | undefined): boolean;
52
55
  //# sourceMappingURL=auth-methods.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth-methods.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/auth-methods.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE9E,uDAAuD;AACvD,eAAO,MAAM,kBAAkB,EAAE,SAAS,eAAe,EAA8C,CAAA;AAEvG,uEAAuE;AACvE,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC,CAKjF,CAAA;AAED,mEAAmE;AACnE,eAAO,MAAM,oBAAoB,EAAE,SAAS,eAAe,EAA8B,CAAA;AAEzF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,GAChF,eAAe,EAAE,CAkBnB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAEzG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,OAAO,CAEjF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,eAAe,EAAE,GAClC,OAAO,CAAC,eAAe,EAAE,WAAW,GAAG,gBAAgB,CAAC,GAAG,IAAI,CAKjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,UAAU,GAAG,IAAI,CAMtF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC,GAAG,IAAI,GACvD,OAAO,CAET"}
1
+ {"version":3,"file":"auth-methods.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/auth-methods.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE9E,uDAAuD;AACvD,eAAO,MAAM,kBAAkB,EAAE,SAAS,eAAe,EAA8C,CAAA;AAEvG,uEAAuE;AACvE,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC,CAKjF,CAAA;AAED,mEAAmE;AACnE,eAAO,MAAM,oBAAoB,EAAE,SAAS,eAAe,EAA8B,CAAA;AAEzF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,GAChF,eAAe,EAAE,CAkBnB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAEzG;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,OAAO,CAEjF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,eAAe,EAAE,GAClC,OAAO,CAAC,eAAe,EAAE,WAAW,GAAG,gBAAgB,CAAC,GAAG,IAAI,CAKjE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,UAAU,GAAG,IAAI,CAMtF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,kBAAkB,CAAC,GAAG,IAAI,EACpE,OAAO,GAAE,MAAM,GAAG,SAAgC,GACjD,OAAO,CAIT"}
@@ -45,9 +45,11 @@ function matchAuthPreset(methods) {
45
45
  }
46
46
  return null;
47
47
  }
48
- function isPasswordLoginEnabled(authConfig) {
48
+ function isPasswordLoginEnabled(authConfig, nodeEnv = process.env.NODE_ENV) {
49
49
  var _a;
50
- return ((_a = authConfig == null ? void 0 : authConfig.emailAndPassword) == null ? void 0 : _a.enabled) !== false;
50
+ const declared = (_a = authConfig == null ? void 0 : authConfig.emailAndPassword) == null ? void 0 : _a.enabled;
51
+ if (typeof declared === "boolean") return declared;
52
+ return ((authConfig == null ? void 0 : authConfig.methods) ?? []).includes("email-password") || nodeEnv !== "production";
51
53
  }
52
54
  export {
53
55
  AUTH_LOGIN_METHODS,
@@ -0,0 +1,14 @@
1
+ import { type TypedSession } from '../auth';
2
+ /**
3
+ * The request's session as the database has it now, for authorization reads: the API entry points (dual-auth,
4
+ * `validateAndAuthenticateRequest`), the /api/user routes, the server actions, generateEntityAPI, the /superadmin and
5
+ * /devtools API and page checks, and the proxy template's gate.
6
+ *
7
+ * Better Auth's cookie cache (`session.cookieCache`, on by default for 5 minutes) answers `getSession` from the
8
+ * signed `session_data` cookie without reading the database, so a signed-out or revoked session, or a role changed
9
+ * since sign-in, would keep its old answer until that cookie expires. `disableCookieCache` reads the session and its
10
+ * user from the database instead (one query). Reads that only display data keep the cache. A suspended account's
11
+ * session is no session (lib/auth/suspension.ts).
12
+ */
13
+ export declare function getAuthorizationSession(headers: Headers): Promise<TypedSession | null>;
14
+ //# sourceMappingURL=authorization-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorization-session.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/authorization-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,YAAY,EAAE,MAAM,SAAS,CAAA;AAGjD;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAG5F"}
@@ -0,0 +1,10 @@
1
+ import { auth } from "../auth.js";
2
+ import { isSuspendedRole } from "./suspension.js";
3
+ async function getAuthorizationSession(headers) {
4
+ var _a;
5
+ const session = await auth.api.getSession({ headers, query: { disableCookieCache: true } });
6
+ return session && !isSuspendedRole((_a = session.user) == null ? void 0 : _a.role) ? session : null;
7
+ }
8
+ export {
9
+ getAuthorizationSession
10
+ };
@@ -1,6 +1,11 @@
1
1
  import { type TypedSession } from '../auth';
2
2
  /** The request's verified session, or null; one lookup per request however many callers ask. */
3
3
  export declare const getRequestSession: () => Promise<TypedSession | null>;
4
+ /**
5
+ * The same, read from the database instead of better-auth's cookie cache, once per request: the /superadmin and
6
+ * /devtools checks use it, so a sign-out or a role change counts at once (lib/auth/authorization-session.ts).
7
+ */
8
+ export declare const getAuthorizationRequestSession: () => Promise<TypedSession | null>;
4
9
  /** The team the session chose (the activeTeamId cookie, only when this session wrote it), or null. */
5
10
  export declare function getSessionActiveTeamId(session: TypedSession): Promise<string | null>;
6
11
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"request-session.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/request-session.ts"],"names":[],"mappings":"AAWA,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,SAAS,CAAA;AAI5D,gGAAgG;AAChG,eAAO,MAAM,iBAAiB,QAAmB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAsC,CAAA;AAElH,sGAAsG;AACtG,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAE1F;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAK/G"}
1
+ {"version":3,"file":"request-session.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/request-session.ts"],"names":[],"mappings":"AAWA,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,SAAS,CAAA;AAK5D,gGAAgG;AAChG,eAAO,MAAM,iBAAiB,QAAmB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAsC,CAAA;AAElH;;;GAGG;AACH,eAAO,MAAM,8BAA8B,QAAmB,OAAO,CAAC,YAAY,GAAG,IAAI,CAA8C,CAAA;AAEvI,sGAAsG;AACtG,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAE1F;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAK/G"}
@@ -1,9 +1,11 @@
1
1
  import { cache } from "react";
2
2
  import { cookies, headers } from "next/headers";
3
3
  import { getTypedSession } from "../auth.js";
4
+ import { getAuthorizationSession } from "./authorization-session.js";
4
5
  import { ACTIVE_TEAM_COOKIE, activeTeamIdForSession } from "../teams/active-team-cookie.js";
5
6
  import { getDashboardTeamId } from "../teams/dashboard-team.js";
6
7
  const getRequestSession = cache(async () => getTypedSession(await headers()));
8
+ const getAuthorizationRequestSession = cache(async () => getAuthorizationSession(await headers()));
7
9
  async function getSessionActiveTeamId(session) {
8
10
  var _a, _b;
9
11
  return activeTeamIdForSession((_a = (await cookies()).get(ACTIVE_TEAM_COOKIE)) == null ? void 0 : _a.value, (_b = session.session) == null ? void 0 : _b.id);
@@ -16,6 +18,7 @@ async function getDashboardPermissionContext() {
16
18
  return { userId, teamId: await getDashboardTeamId(userId, await getSessionActiveTeamId(session)) };
17
19
  }
18
20
  export {
21
+ getAuthorizationRequestSession,
19
22
  getDashboardPermissionContext,
20
23
  getRequestSession,
21
24
  getSessionActiveTeamId
@@ -8,12 +8,15 @@ export interface RuntimeAuthReadinessOptions {
8
8
  }
9
9
  /**
10
10
  * Server-derived flows that do not follow the login UI intent (`auth.methods`).
11
- * The password backend (`auth.emailAndPassword.enabled`) stays on under the
12
- * passwordless preset, so invitation registration and password recovery must
13
- * not be inferred from `availableMethods`.
11
+ * The password backend (`isPasswordLoginEnabled`) can be on without
12
+ * `'email-password'` in `methods` (outside production, or declared), so
13
+ * invitation registration and password recovery are not inferred from
14
+ * `availableMethods`.
14
15
  */
15
16
  export interface PublicAuthCapabilities {
16
- /** `/api/v1/auth/signup-with-invite` accepts a password (backend switch only). */
17
+ /** `/api/v1/auth/signup-with-invite` creates the invited account: always, with or without a password. */
18
+ invitationSignup: boolean;
19
+ /** `/api/v1/auth/signup-with-invite` takes a password (backend switch only); otherwise the account signs in by code. */
17
20
  invitationPasswordSignup: boolean;
18
21
  /** `/request-password-reset` can run: password backend + usable email delivery. */
19
22
  passwordRecovery: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-readiness.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/runtime-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAGpB,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAGlE,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,aAAa,CAAA;AAEpB,KAAK,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAE5D,MAAM,WAAW,2BAA2B;IAC1C,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,WAAW,GAAG,kBAAkB,CAAC,GAAG,IAAI,CAAA;IAClF,GAAG,CAAC,EAAE,kBAAkB,CAAA;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,kFAAkF;IAClF,wBAAwB,EAAE,OAAO,CAAA;IACjC,mFAAmF;IACnF,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,OAAO,GAAG,aAAa,CAAA;IAC/B,gBAAgB,EAAE,eAAe,EAAE,CAAA;IACnC,YAAY,EAAE,sBAAsB,CAAA;CACrC;AAaD,qEAAqE;AACrE,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,2BAAgC,GAAG,mBAAmB,CAEtG;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,2BAAgC,GAAG,IAAI,CAezF;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,2BAAgC,GAAG,mBAAmB,CAYrG;AAiBD,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAE1F;AAED,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAE3F;AAkDD;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA4E1B;AAED,sFAAsF;AACtF,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,EAChC,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,QAAQ,CAAC,CAGnB"}
1
+ {"version":3,"file":"runtime-readiness.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/runtime-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAGpB,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAIlE,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,aAAa,CAAA;AAEpB,KAAK,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAE5D,MAAM,WAAW,2BAA2B;IAC1C,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,WAAW,GAAG,kBAAkB,CAAC,GAAG,IAAI,CAAA;IAClF,GAAG,CAAC,EAAE,kBAAkB,CAAA;CACzB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,yGAAyG;IACzG,gBAAgB,EAAE,OAAO,CAAA;IACzB,wHAAwH;IACxH,wBAAwB,EAAE,OAAO,CAAA;IACjC,mFAAmF;IACnF,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,OAAO,GAAG,aAAa,CAAA;IAC/B,gBAAgB,EAAE,eAAe,EAAE,CAAA;IACnC,YAAY,EAAE,sBAAsB,CAAA;CACrC;AAaD,qEAAqE;AACrE,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,2BAAgC,GAAG,mBAAmB,CAEtG;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,2BAAgC,GAAG,IAAI,CAgBzF;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,2BAAgC,GAAG,mBAAmB,CAarG;AAiBD,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAE1F;AAED,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAE3F;AAmDD;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA4E1B;AAED,sFAAsF;AACtF,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,EAChC,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,QAAQ,CAAC,CAGnB"}
@@ -2,6 +2,7 @@ import "server-only";
2
2
  import { AUTH_CONFIG } from "../config/index.js";
3
3
  import { isPasswordLoginEnabled } from "./auth-methods.js";
4
4
  import { warnClientIpSourceAtStartup } from "../api/client-ip.js";
5
+ import { warnSampleAccountsAtStartup } from "./sample-accounts.js";
5
6
  import {
6
7
  authReadinessConfigurationFromEnv,
7
8
  evaluateAuthReadiness
@@ -21,6 +22,7 @@ function getRuntimeAuthReadiness(options = {}) {
21
22
  }
22
23
  function logAuthReadinessAtStartup(options = {}) {
23
24
  warnClientIpSourceAtStartup(options.env ?? process.env);
25
+ void warnSampleAccountsAtStartup(options.env ?? process.env);
24
26
  try {
25
27
  const env = options.env ?? process.env;
26
28
  if (env.NODE_ENV !== "production") return;
@@ -37,11 +39,12 @@ function logAuthReadinessAtStartup(options = {}) {
37
39
  function getPublicAuthReadiness(options = {}) {
38
40
  const result = getRuntimeAuthReadiness(options);
39
41
  const authConfig = options.authConfig === void 0 ? AUTH_CONFIG : options.authConfig;
40
- const passwordBackend = isPasswordLoginEnabled(authConfig);
42
+ const passwordBackend = isPasswordLoginEnabled(authConfig, (options.env ?? process.env).NODE_ENV);
41
43
  return {
42
44
  status: result.outcome === "ready" ? "ready" : "unavailable",
43
45
  availableMethods: result.availableMethods,
44
46
  capabilities: {
47
+ invitationSignup: true,
45
48
  invitationPasswordSignup: passwordBackend,
46
49
  passwordRecovery: passwordBackend && isRuntimeEmailAvailable(options)
47
50
  }
@@ -68,7 +71,7 @@ function authRoutePath(pathname) {
68
71
  const marker = "/api/auth";
69
72
  const index = pathname.lastIndexOf(marker);
70
73
  if (index === -1) return null;
71
- const suffix = pathname.slice(index + marker.length);
74
+ const suffix = pathname.slice(index + marker.length).replace(/\/+$/, "");
72
75
  return suffix || "/";
73
76
  }
74
77
  function jsonResponse(body, status = 200) {
@@ -103,7 +106,7 @@ async function requestedSocialProvider(request) {
103
106
  }
104
107
  }
105
108
  async function getAuthReadinessResponse(request, options = {}) {
106
- var _a, _b, _c;
109
+ var _a, _b;
107
110
  const pathname = new URL(request.url).pathname;
108
111
  const route = authRoutePath(pathname);
109
112
  if (!route) return null;
@@ -138,10 +141,10 @@ async function getAuthReadinessResponse(request, options = {}) {
138
141
  return unavailableResponse(method, diagnostics, pathname);
139
142
  }
140
143
  }
141
- if (isPasswordOperation && ((_a = authConfig == null ? void 0 : authConfig.emailAndPassword) == null ? void 0 : _a.enabled) === false) {
144
+ if (isPasswordOperation && !isPasswordLoginEnabled(authConfig, (options.env ?? process.env).NODE_ENV)) {
142
145
  return unavailableResponse("email-password", [{
143
146
  code: "EMAIL_PASSWORD_POLICY_DISABLED",
144
- message: "Password authentication is disabled by auth.emailAndPassword.enabled."
147
+ message: "Password authentication is off: auth.methods has no email-password and auth.emailAndPassword.enabled is not true."
145
148
  }], pathname);
146
149
  }
147
150
  if (isPasswordEmailDeliveryOperation || isGeneralEmailDeliveryOperation) {
@@ -158,7 +161,7 @@ async function getAuthReadinessResponse(request, options = {}) {
158
161
  method = "google";
159
162
  const readiness = evaluateProvider("google", options);
160
163
  diagnostics = readiness.diagnostics;
161
- if (((_c = (_b = authConfig == null ? void 0 : authConfig.providers) == null ? void 0 : _b.google) == null ? void 0 : _c.enabled) === false) {
164
+ if (((_b = (_a = authConfig == null ? void 0 : authConfig.providers) == null ? void 0 : _a.google) == null ? void 0 : _b.enabled) === false) {
162
165
  return unavailableResponse(method, [{
163
166
  code: "GOOGLE_POLICY_DISABLED",
164
167
  message: "Google authentication is disabled by auth.providers.google.enabled."
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The password hashes the sample data ships with (core's 090_sample_data.sql and
3
+ * the project templates' sample data). A "credential" account that still holds
4
+ * one of them can be signed in to with a documented password.
5
+ * Keep in sync with migrations/029_sample_accounts_outside_development.sql.
6
+ */
7
+ export declare const SAMPLE_PASSWORD_HASHES: readonly ["22de14d5472248ed0bece911df908b2a:d29576424798ba6845d348a3767c0b0f38a00f2aca461b3b1d34b99a93cab06c86774c6edb183e6d6ec47457649b032a49a7b60a48f6f4f7fbbc4ea40258f19f", "3db9e98e2b4d3caca97fdf2783791cbc:34b293de615caf277a237773208858e960ea8aa10f1f5c5c309b632f192cac34d52ceafbd338385616f4929e4b1b6c055b67429c6722ffdb80b01d9bf4764866"];
8
+ type QueryRows = (text: string, params?: unknown[]) => Promise<Array<{
9
+ count: number;
10
+ }>>;
11
+ /**
12
+ * In production, logs one warning when accounts with a sample password exist.
13
+ * Never throws. When the query fails it logs one line with the error code or class name only
14
+ * (never the error object, which can carry the connection string).
15
+ */
16
+ export declare function warnSampleAccountsAtStartup(env?: Record<string, string | undefined>, queryRows?: QueryRows): Promise<void>;
17
+ export {};
18
+ //# sourceMappingURL=sample-accounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sample-accounts.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/sample-accounts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,qVAGzB,CAAA;AAEV,KAAK,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAAA;AAExF;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,EACrD,SAAS,CAAC,EAAE,SAAS,GACpB,OAAO,CAAC,IAAI,CAAC,CAgBf"}
@@ -0,0 +1,25 @@
1
+ const SAMPLE_PASSWORD_HASHES = [
2
+ "22de14d5472248ed0bece911df908b2a:d29576424798ba6845d348a3767c0b0f38a00f2aca461b3b1d34b99a93cab06c86774c6edb183e6d6ec47457649b032a49a7b60a48f6f4f7fbbc4ea40258f19f",
3
+ "3db9e98e2b4d3caca97fdf2783791cbc:34b293de615caf277a237773208858e960ea8aa10f1f5c5c309b632f192cac34d52ceafbd338385616f4929e4b1b6c055b67429c6722ffdb80b01d9bf4764866"
4
+ ];
5
+ async function warnSampleAccountsAtStartup(env = process.env, queryRows) {
6
+ if (env.NODE_ENV !== "production") return;
7
+ try {
8
+ const run = queryRows ?? (await import("../db.js")).queryRows;
9
+ const [row] = await run(
10
+ `SELECT count(*)::int AS count FROM "account" WHERE "providerId" = 'credential' AND "password" = ANY($1)`,
11
+ [SAMPLE_PASSWORD_HASHES]
12
+ );
13
+ if (row && row.count > 0) {
14
+ console.warn(`[auth-readiness] ${row.count} account(s) still have a sample-data password; pnpm db:migrate disables them once; if it already ran, see the CHANGELOG upgrade notes`);
15
+ }
16
+ } catch (error) {
17
+ const safe = (value) => typeof value === "string" && /^[A-Za-z0-9_]{1,40}$/.test(value) ? value : void 0;
18
+ const reason = safe(error == null ? void 0 : error.code) ?? safe(error == null ? void 0 : error.name) ?? "unknown error";
19
+ console.warn(`[auth-readiness] could not check for sample-data passwords at startup (query failed: ${reason}); check DATABASE_URL, then run the read-only query in the 0.1.0-beta.196 CHANGELOG upgrade notes`);
20
+ }
21
+ }
22
+ export {
23
+ SAMPLE_PASSWORD_HASHES,
24
+ warnSampleAccountsAtStartup
25
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A suspended account: `users.role = 'suspended'` (allowed by `check_users_role` since migration 031). It gets no new
3
+ * session (the session create hook in lib/auth.ts), and its existing session and its API keys are refused where core
4
+ * authenticates: dual-auth, `validateAndAuthenticateRequest`, getAuthorizationSession, validateApiKey and the proxy
5
+ * template.
6
+ */
7
+ export declare const SUSPENDED_ROLE = "suspended";
8
+ export declare function isSuspendedRole(role: unknown): boolean;
9
+ /**
10
+ * Suspends `userId` in one statement: sets the role, deletes the user's sessions and deactivates their active API keys
11
+ * (`status = 'inactive'`, so the audit log keeps the rows; unsuspending does not bring them back). A superadmin is never
12
+ * suspended. Returns null when no user was suspended (unknown id, or a superadmin).
13
+ */
14
+ export declare function suspendUser(userId: string, actorId: string): Promise<{
15
+ sessions: number;
16
+ apiKeys: number;
17
+ } | null>;
18
+ /** Gives a suspended user back the `member` role. Their deactivated API keys stay deactivated. Null if not suspended. */
19
+ export declare function unsuspendUser(userId: string, actorId: string): Promise<{
20
+ id: string;
21
+ } | null>;
22
+ //# sourceMappingURL=suspension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suspension.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/suspension.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,eAAO,MAAM,cAAc,cAAc,CAAA;AAEzC,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAEtD;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAuBxH;AAED,yHAAyH;AACzH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAOnG"}
@@ -0,0 +1,43 @@
1
+ import { queryWithRLS } from "../db.js";
2
+ import { invalidateApiKeyCache } from "../api/auth.js";
3
+ const SUSPENDED_ROLE = "suspended";
4
+ function isSuspendedRole(role) {
5
+ return role === SUSPENDED_ROLE;
6
+ }
7
+ async function suspendUser(userId, actorId) {
8
+ const [result] = await queryWithRLS(
9
+ `WITH suspended AS (
10
+ UPDATE "users" SET role = '${SUSPENDED_ROLE}', "updatedAt" = NOW()
11
+ WHERE id = $1 AND role <> 'superadmin'
12
+ RETURNING id
13
+ ), sessions AS (
14
+ DELETE FROM "session" WHERE "userId" IN (SELECT id FROM suspended) RETURNING id
15
+ ), keys AS (
16
+ UPDATE "api_key" SET status = 'inactive'
17
+ WHERE "userId" IN (SELECT id FROM suspended) AND status = 'active'
18
+ RETURNING "keyHash"
19
+ )
20
+ SELECT (SELECT COUNT(*) FROM suspended)::int AS suspended,
21
+ (SELECT COUNT(*) FROM sessions)::int AS sessions,
22
+ COALESCE((SELECT json_agg("keyHash") FROM keys), '[]'::json) AS "keyHashes"`,
23
+ [userId],
24
+ actorId
25
+ );
26
+ if (!(result == null ? void 0 : result.suspended)) return null;
27
+ for (const keyHash of result.keyHashes) invalidateApiKeyCache(keyHash);
28
+ return { sessions: result.sessions, apiKeys: result.keyHashes.length };
29
+ }
30
+ async function unsuspendUser(userId, actorId) {
31
+ const [row] = await queryWithRLS(
32
+ `UPDATE "users" SET role = 'member', "updatedAt" = NOW() WHERE id = $1 AND role = '${SUSPENDED_ROLE}' RETURNING id`,
33
+ [userId],
34
+ actorId
35
+ );
36
+ return row ?? null;
37
+ }
38
+ export {
39
+ SUSPENDED_ROLE,
40
+ isSuspendedRole,
41
+ suspendUser,
42
+ unsuspendUser
43
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAUA,OAAO,EAA0F,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAmBjI;;;;;;;;;;;;;GAaG;AACH,wBAAsB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQlF;AAED;;;;;;;;;GASG;AACH,wBAAsB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnF;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAuHD,eAAO,MAAM,IAAI,qEAiXf,CAAC;AAEH,MAAM,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;AACjD,MAAM,MAAM,WAAW,GAAG,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,kBAAkB,EAAE,QAAQ,EAAE,CAAC;CAC/C,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC5B,IAAI,EAAE,WAAW,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAO9B"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAWA,OAAO,EAA0F,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAmBjI;;;;;;;;;;;;;GAaG;AACH,wBAAsB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQlF;AAED;;;;;;;;;GASG;AACH,wBAAsB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnF;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyHD,eAAO,MAAM,IAAI,qEAsXf,CAAC;AAEH,MAAM,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;AACjD,MAAM,MAAM,WAAW,GAAG,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,kBAAkB,EAAE,QAAQ,EAAE,CAAC;CAC/C,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC5B,IAAI,EAAE,WAAW,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAO9B"}
package/dist/lib/auth.js CHANGED
@@ -1,5 +1,5 @@
1
- import { betterAuth } from "better-auth";
2
- import { Pool } from "pg";
1
+ import { betterAuth, APIError } from "better-auth";
2
+ import { createPool } from "./db-pool.js";
3
3
  import { nextCookies } from "better-auth/next-js";
4
4
  import { emailOTP } from "better-auth/plugins";
5
5
  import { parseSSLConfig, stripSSLParams, queryOne } from "./db.js";
@@ -42,13 +42,13 @@ const databaseUrl = process.env.DATABASE_SERVICE_URL || process.env.DATABASE_URL
42
42
  const cleanUrl = stripSSLParams(databaseUrl);
43
43
  const isPoolerUrl = /:6543(\/|$|\?)/.test(databaseUrl) || /pooler/i.test(databaseUrl);
44
44
  const connectionString = isPoolerUrl ? cleanUrl.includes("?") ? `${cleanUrl}&pgbouncer=true` : `${cleanUrl}?pgbouncer=true` : cleanUrl;
45
- const pool = new Pool({
45
+ const AUTH_POOLS = /* @__PURE__ */ Symbol.for("nextspark.auth.pools");
46
+ const authPools = globalThis[AUTH_POOLS] ??= /* @__PURE__ */ new Map();
47
+ const pool = authPools.get(databaseUrl) ?? createPool("auth", {
46
48
  connectionString,
47
- ssl: parseSSLConfig(databaseUrl),
48
- connectionTimeoutMillis: 1e4,
49
- idleTimeoutMillis: 3e4,
50
- max: 20
49
+ ssl: parseSSLConfig(databaseUrl)
51
50
  });
51
+ authPools.set(databaseUrl, pool);
52
52
  const sessionConfig = resolveSessionConfig(AUTH_CONFIG);
53
53
  const otpConfig = resolveOtpConfig(AUTH_CONFIG);
54
54
  function resolveNonOwnerSignupRole() {
@@ -137,10 +137,9 @@ const auth = betterAuth({
137
137
  }
138
138
  },
139
139
  emailAndPassword: {
140
- // Enabled by default even under the passwordless preset (AUTH_CONFIG.methods
141
- // only shapes the login UI) so password accounts, seeded test users and
142
- // API logins keep working. A theme hard-disables it with
143
- // `auth.emailAndPassword.enabled: false`.
140
+ // On when AUTH_CONFIG.methods lists 'email-password', and outside production
141
+ // (seeded test users, the dev keyring). A production passwordless app opts
142
+ // in with `auth.emailAndPassword.enabled: true` (API logins, test users).
144
143
  enabled: isPasswordLoginEnabled(AUTH_CONFIG),
145
144
  requireEmailVerification: true,
146
145
  minPasswordLength: 8,
@@ -277,7 +276,7 @@ const auth = betterAuth({
277
276
  if (registrationMode === "domain-restricted" || registrationMode === "domain-open") {
278
277
  const allowedDomains = ((_d = (_c = AUTH_CONFIG) == null ? void 0 : _c.registration) == null ? void 0 : _d.allowedDomains) ?? [];
279
278
  if (allowedDomains.length > 0 && !isDomainAllowed(user.email, allowedDomains)) {
280
- console.log(`[Auth] Blocked registration for ${user.email}: domain not in allowedDomains (allowed: ${allowedDomains.join(", ")})`);
279
+ console.log(`[Auth] Blocked registration for a user at ${user.email.split("@").pop()}: domain not in allowedDomains (allowed: ${allowedDomains.join(", ")})`);
281
280
  throw new Error(`DOMAIN_NOT_ALLOWED: Email domain not authorized. Please use an email from: ${allowedDomains.join(", ")}`);
282
281
  }
283
282
  }
@@ -333,18 +332,22 @@ const auth = betterAuth({
333
332
  // Enforce domain restrictions on EVERY login (not just signup)
334
333
  // @ts-expect-error — pre-existing type error, tracked in https://github.com/NextSpark-js/nextspark/issues/131
335
334
  before: async (session) => {
336
- var _a, _b, _c, _d, _e;
337
- const registrationMode = ((_b = (_a = AUTH_CONFIG) == null ? void 0 : _a.registration) == null ? void 0 : _b.mode) ?? "open";
335
+ var _a, _b, _c, _d, _e, _f;
336
+ const owner = await pool.query("SELECT role FROM users WHERE id = $1 LIMIT 1", [session.userId]);
337
+ if (((_a = owner.rows[0]) == null ? void 0 : _a.role) === "suspended") {
338
+ throw new APIError("FORBIDDEN", { message: "This account is suspended", code: "ACCOUNT_SUSPENDED" });
339
+ }
340
+ const registrationMode = ((_c = (_b = AUTH_CONFIG) == null ? void 0 : _b.registration) == null ? void 0 : _c.mode) ?? "open";
338
341
  if (registrationMode === "domain-restricted" || registrationMode === "domain-open") {
339
- const allowedDomains = ((_d = (_c = AUTH_CONFIG) == null ? void 0 : _c.registration) == null ? void 0 : _d.allowedDomains) ?? [];
342
+ const allowedDomains = ((_e = (_d = AUTH_CONFIG) == null ? void 0 : _d.registration) == null ? void 0 : _e.allowedDomains) ?? [];
340
343
  if (allowedDomains.length > 0) {
341
344
  const result = await pool.query(
342
345
  "SELECT email FROM users WHERE id = $1 LIMIT 1",
343
346
  [session.userId]
344
347
  );
345
- const email = (_e = result.rows[0]) == null ? void 0 : _e.email;
348
+ const email = (_f = result.rows[0]) == null ? void 0 : _f.email;
346
349
  if (email && !isDomainAllowed(email, allowedDomains)) {
347
- console.log(`[Auth] Blocked sign-in for ${email}: domain not in allowedDomains`);
350
+ console.log(`[Auth] Blocked sign-in for a user at ${email.split("@").pop()}: domain not in allowedDomains`);
348
351
  return false;
349
352
  }
350
353
  }
@@ -0,0 +1,21 @@
1
+ import type { EntityConfig } from '../entities/types';
2
+ /**
3
+ * The public pages of an entity are cached (`'use cache'` tagged `entity:<slug>` in public-item-route.cc; ISR
4
+ * `revalidate` in the other host), so a write has to expire them. Every writer of an entity ends here: the
5
+ * generic REST handler, the entity handler, GenericEntityService and the server actions reach it through the
6
+ * `afterEntity*` hooks (entity-hooks.ts), and the one write that skips hooks (`deleteMany`) calls it itself.
7
+ *
8
+ * - Cache Components: `entity:<slug>` is on every cached read of the entity's items (found or not found), so
9
+ * one tag covers the old and the new slug of a rename, an unpublished item and a deleted one. It also
10
+ * drops the sibling items' entries; they are read again by their next visit.
11
+ * - ISR: Next tags a page by its route file path, route group included, so the path is `/(public)<basePath>` with
12
+ * 'layout' (item pages and archive of a base path) or the root item route with 'page'. `(public)` is the group the
13
+ * host generator puts those routes in (scripts/build/registry/host/entity-routes.mjs); a test keeps them equal.
14
+ * - Patterns are tagged `patterns` and are expired by their own writes.
15
+ *
16
+ * Outside a request (a script, a test) Next refuses the call; there is no cache to expire there.
17
+ */
18
+ /** Route group of the generated public routes. Keep equal to the one in scripts/build/registry/host/entity-routes.mjs. */
19
+ export declare const PUBLIC_ROUTE_GROUP = "(public)";
20
+ export declare function expirePublicEntity(config: Pick<EntityConfig, 'slug' | 'access' | 'builder'> | undefined): void;
21
+ //# sourceMappingURL=public-entity-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-entity-cache.d.ts","sourceRoot":"","sources":["../../../src/lib/cache/public-entity-cache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIrD;;;;;;;;;;;;;;;GAeG;AACH,0HAA0H;AAC1H,eAAO,MAAM,kBAAkB,aAAa,CAAA;AAE5C,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC,GAAG,SAAS,GAAG,IAAI,CAe9G"}
@@ -0,0 +1,25 @@
1
+ import { revalidatePath } from "next/cache";
2
+ import { unstable_rethrow } from "next/navigation";
3
+ import { getEntityBasePath } from "../entities/schema-generator.js";
4
+ import { revalidateTag } from "./revalidate-tag.js";
5
+ const PUBLIC_ROUTE_GROUP = "(public)";
6
+ function expirePublicEntity(config) {
7
+ var _a;
8
+ if (!config) return;
9
+ try {
10
+ if (config.slug === "patterns") revalidateTag("patterns");
11
+ const basePath = getEntityBasePath(config);
12
+ if (!basePath) return;
13
+ revalidateTag(`entity:${config.slug}`);
14
+ const group = `/${PUBLIC_ROUTE_GROUP}`;
15
+ if (basePath === "/") revalidatePath(`${group}${((_a = config.access) == null ? void 0 : _a.allowNestedSlugs) ? "/[...slug]" : "/[slug]"}`, "page");
16
+ else revalidatePath(`${group}${basePath}`, "layout");
17
+ } catch (error) {
18
+ unstable_rethrow(error);
19
+ console.warn(`[public-entity-cache] could not expire the public pages of ${config.slug}: ${error instanceof Error ? error.message : error}`);
20
+ }
21
+ }
22
+ export {
23
+ PUBLIC_ROUTE_GROUP,
24
+ expirePublicEntity
25
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"app.config.d.ts","sourceRoot":"","sources":["../../../src/lib/config/app.config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,SAAS,CAAA;AAOzD,eAAO,MAAM,kBAAkB,EAAE,SA8gBhC,CAAA"}
1
+ {"version":3,"file":"app.config.d.ts","sourceRoot":"","sources":["../../../src/lib/config/app.config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,SAAS,CAAA;AAOzD,eAAO,MAAM,kBAAkB,EAAE,SA6gBhC,CAAA"}
@@ -337,14 +337,13 @@ const DEFAULT_APP_CONFIG = {
337
337
  */
338
338
  methods: ["email-otp", "google"],
339
339
  /**
340
- * Better Auth's email + password endpoints stay enabled by default (even
341
- * under the passwordless preset) so password accounts, seeded test users
342
- * and API-based logins keep working. Set `enabled: false` in a theme to
343
- * hard-disable password auth server-side.
340
+ * Better Auth's email + password endpoints. Left undeclared, they follow
341
+ * `methods`: on when it lists 'email-password', and always outside
342
+ * production (seeded test users, the dev keyring). Set `enabled: true` to
343
+ * keep password logins (API clients, test users) in a passwordless
344
+ * production app, or `false` to turn them off everywhere.
344
345
  */
345
- emailAndPassword: {
346
- enabled: true
347
- },
346
+ emailAndPassword: {},
348
347
  /**
349
348
  * Whether Better Auth automatically sends the verification email on signup.
350
349
  *
@@ -532,18 +532,20 @@ export interface AuthConfig {
532
532
  * auth: { methods: ['email-otp', 'email-password', 'google'] } // both, OTP first
533
533
  * ```
534
534
  *
535
- * This only shapes the UI and the signup page. Better Auth's password
536
- * endpoints stay enabled unless `emailAndPassword.enabled` is set to false.
535
+ * This shapes the UI and the signup page, and decides whether Better Auth's
536
+ * password endpoints run in production (see `emailAndPassword`).
537
537
  */
538
538
  methods?: AuthLoginMethod[];
539
539
  /**
540
540
  * Server-side switch for Better Auth's email + password endpoints
541
541
  * (`sign-in/email`, `sign-up/email`, forget/reset/change-password).
542
542
  *
543
- * Default: `true` — even under the passwordless preset — so existing
544
- * password accounts, seeded test users and API-based logins keep working
545
- * when a theme only changes the login UI. Set `{ enabled: false }` to
546
- * hard-disable password auth for a strictly passwordless app.
543
+ * Default (undeclared): on when `methods` lists `'email-password'`, and
544
+ * outside production (`NODE_ENV !== 'production'`) so the dev keyring,
545
+ * `db:seed` users and Cypress can sign in with a password. A production app
546
+ * on the passwordless preset serves no password endpoint. Set
547
+ * `{ enabled: true }` to keep password logins (API clients, test users) in
548
+ * production, or `{ enabled: false }` to turn them off everywhere.
547
549
  */
548
550
  emailAndPassword?: {
549
551
  enabled?: boolean;