@classytic/arc 2.11.4 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +16 -12
  2. package/dist/{BaseController-swXruJ2_.mjs → BaseController-DX_T-bDB.mjs} +388 -423
  3. package/dist/EventTransport-CT_52aWU.d.mts +34 -0
  4. package/dist/EventTransport-DLWoUMHy.mjs +103 -0
  5. package/dist/{ResourceRegistry-DkAeAuTX.mjs → ResourceRegistry-CTERg_2x.mjs} +139 -66
  6. package/dist/audit/index.d.mts +2 -2
  7. package/dist/audit/index.mjs +1 -1
  8. package/dist/auth/audit.d.mts +199 -0
  9. package/dist/auth/audit.mjs +288 -0
  10. package/dist/auth/index.d.mts +3 -3
  11. package/dist/auth/index.mjs +117 -191
  12. package/dist/{betterAuthOpenApi-DwxtK3uG.mjs → betterAuthOpenApi--M_i87dQ.mjs} +1 -1
  13. package/dist/buildHandler-olo-gt94.mjs +610 -0
  14. package/dist/cache/index.mjs +3 -3
  15. package/dist/cli/commands/describe.d.mts +89 -13
  16. package/dist/cli/commands/describe.mjs +56 -2
  17. package/dist/cli/commands/docs.mjs +2 -2
  18. package/dist/cli/commands/generate.mjs +147 -48
  19. package/dist/cli/commands/init.d.mts +13 -0
  20. package/dist/cli/commands/init.mjs +130 -87
  21. package/dist/cli/commands/introspect.mjs +8 -1
  22. package/dist/context/index.mjs +1 -1
  23. package/dist/core/index.d.mts +3 -3
  24. package/dist/core/index.mjs +5 -5
  25. package/dist/core-DECn6zaU.mjs +1399 -0
  26. package/dist/{createActionRouter-CIKOcNA7.mjs → createActionRouter-CBxLLbn3.mjs} +7 -20
  27. package/dist/createAggregationRouter-CRIBv4sC.mjs +114 -0
  28. package/dist/{createApp-C9bRrqlX.mjs → createApp-XX2-N0Yd.mjs} +28 -22
  29. package/dist/{defineEvent-D1Ky9M1D.mjs → defineEvent-D5h7EvAx.mjs} +1 -1
  30. package/dist/docs/index.d.mts +24 -11
  31. package/dist/docs/index.mjs +2 -2
  32. package/dist/{elevation-DOFoxoDs.mjs → elevation-DgoeTyfX.mjs} +1 -1
  33. package/dist/errorHandler-Bk-AGhkU.mjs +174 -0
  34. package/dist/errorHandler-DFr45ZG4.d.mts +45 -0
  35. package/dist/errors-j4aJm1Wg.mjs +184 -0
  36. package/dist/{eventPlugin-Cts2-Tfj.mjs → eventPlugin-CaKTYkYM.mjs} +28 -4
  37. package/dist/{eventPlugin-DDJoNEPL.d.mts → eventPlugin-qXpqTebY.d.mts} +24 -1
  38. package/dist/events/index.d.mts +6 -6
  39. package/dist/events/index.mjs +11 -35
  40. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  41. package/dist/events/transports/redis.d.mts +1 -1
  42. package/dist/factory/index.d.mts +2 -2
  43. package/dist/factory/index.mjs +2 -2
  44. package/dist/{fields-BRjxOAFp.d.mts → fields-COhcH3fk.d.mts} +23 -2
  45. package/dist/hooks/index.d.mts +1 -1
  46. package/dist/hooks/index.mjs +1 -1
  47. package/dist/idempotency/index.d.mts +1 -1
  48. package/dist/idempotency/index.mjs +1 -20
  49. package/dist/idempotency/redis.mjs +1 -1
  50. package/dist/{index-rHjXmJar.d.mts → index-BTqLEvhu.d.mts} +163 -3
  51. package/dist/{index-CXXRbnf8.d.mts → index-BtW7qYwa.d.mts} +660 -326
  52. package/dist/{index-m8mOOlFW.d.mts → index-Ds61mrJE.d.mts} +50 -4
  53. package/dist/{index-D9t1KNaB.d.mts → index-Dz5IKsrE.d.mts} +360 -219
  54. package/dist/index.d.mts +6 -7
  55. package/dist/index.mjs +9 -10
  56. package/dist/integrations/event-gateway.d.mts +1 -1
  57. package/dist/integrations/event-gateway.mjs +1 -1
  58. package/dist/integrations/index.d.mts +1 -1
  59. package/dist/integrations/mcp/index.d.mts +2 -2
  60. package/dist/integrations/mcp/index.mjs +1 -1
  61. package/dist/integrations/mcp/testing.d.mts +1 -1
  62. package/dist/integrations/mcp/testing.mjs +1 -1
  63. package/dist/integrations/streamline.d.mts +60 -11
  64. package/dist/integrations/streamline.mjs +75 -85
  65. package/dist/integrations/websocket.mjs +2 -8
  66. package/dist/middleware/index.d.mts +1 -1
  67. package/dist/middleware/index.mjs +2 -2
  68. package/dist/migrations/index.d.mts +23 -3
  69. package/dist/migrations/index.mjs +0 -7
  70. package/dist/{multipartBody-CvTR1Un6.mjs → multipartBody-BOvVSVCD.mjs} +11 -8
  71. package/dist/openapi-noXno2CV.mjs +968 -0
  72. package/dist/org/index.d.mts +2 -2
  73. package/dist/org/index.mjs +1 -1
  74. package/dist/permissions/index.d.mts +3 -3
  75. package/dist/permissions/index.mjs +3 -3
  76. package/dist/{permissions-gd_aUWrR.mjs → permissions-ohQyv50e.mjs} +404 -176
  77. package/dist/{pipe-DVoIheVC.mjs → pipe-Zr0KXjQe.mjs} +1 -1
  78. package/dist/pipeline/index.d.mts +1 -1
  79. package/dist/pipeline/index.mjs +1 -1
  80. package/dist/plugins/index.d.mts +16 -31
  81. package/dist/plugins/index.mjs +33 -13
  82. package/dist/plugins/response-cache.mjs +1 -1
  83. package/dist/plugins/tracing-entry.mjs +1 -1
  84. package/dist/presets/filesUpload.d.mts +4 -4
  85. package/dist/presets/filesUpload.mjs +6 -9
  86. package/dist/presets/index.d.mts +1 -1
  87. package/dist/presets/index.mjs +1 -1
  88. package/dist/presets/multiTenant.d.mts +1 -1
  89. package/dist/presets/multiTenant.mjs +2 -2
  90. package/dist/presets/search.d.mts +2 -2
  91. package/dist/presets/search.mjs +6 -8
  92. package/dist/{presets-Z7P5w4gF.mjs → presets-BbkjdPeH.mjs} +6 -28
  93. package/dist/{queryCachePlugin-Bq6bO6vc.mjs → queryCachePlugin-m1XsgAIJ.mjs} +3 -3
  94. package/dist/{redis-stream-xTGxB2bm.d.mts → redis-stream-D6HzR1Z_.d.mts} +1 -1
  95. package/dist/registry/index.d.mts +1 -1
  96. package/dist/registry/index.mjs +2 -2
  97. package/dist/{replyHelpers-ByllIXXV.mjs → replyHelpers-CK-FNO8E.mjs} +3 -21
  98. package/dist/{resourceToTools-CxNmI6xF.mjs → resourceToTools-DLL32us3.mjs} +224 -71
  99. package/dist/{routerShared-BqLRb5l7.mjs → routerShared-DrOa-26E.mjs} +41 -36
  100. package/dist/{schemaIR-Dy2p4MxS.mjs → schemaIR-lYhC2gE5.mjs} +1 -1
  101. package/dist/schemas/index.d.mts +100 -30
  102. package/dist/schemas/index.mjs +86 -29
  103. package/dist/scim/index.d.mts +264 -0
  104. package/dist/scim/index.mjs +963 -0
  105. package/dist/scope/index.d.mts +3 -3
  106. package/dist/scope/index.mjs +4 -4
  107. package/dist/{sse-V7aXc3bW.mjs → sse-Bz-5ZeTt.mjs} +1 -1
  108. package/dist/{store-helpers-Cp4uKC1U.mjs → store-helpers-BkIN9-vu.mjs} +1 -1
  109. package/dist/testing/index.d.mts +2 -8
  110. package/dist/testing/index.mjs +16 -24
  111. package/dist/types/index.d.mts +4 -4
  112. package/dist/{types-D7KpfiL1.d.mts → types-BvqwCCSx.d.mts} +73 -25
  113. package/dist/{types-DDyTPc6y.d.mts → types-CTYvcwHe.d.mts} +195 -1
  114. package/dist/{types-AOD8fxIw.mjs → types-C_s5moIu.mjs} +117 -1
  115. package/dist/{types-BQ9TJQNy.d.mts → types-DQHFc8PM.d.mts} +1 -1
  116. package/dist/utils/index.d.mts +2 -2
  117. package/dist/utils/index.mjs +5 -5
  118. package/dist/{utils-CcYTj09l.mjs → utils-_h9B3c57.mjs} +1269 -1334
  119. package/dist/{versioning-DsglKfM_.d.mts → versioning-DTTvc80y.d.mts} +1 -1
  120. package/package.json +24 -34
  121. package/skills/arc/SKILL.md +147 -51
  122. package/skills/arc/references/agent-auth.md +238 -0
  123. package/skills/arc/references/api-reference.md +187 -0
  124. package/skills/arc/references/auth.md +354 -7
  125. package/skills/arc/references/enterprise-auth.md +94 -0
  126. package/skills/arc/references/events.md +8 -6
  127. package/skills/arc/references/mcp.md +2 -2
  128. package/skills/arc/references/multi-tenancy.md +11 -2
  129. package/skills/arc/references/production.md +10 -9
  130. package/skills/arc/references/scim.md +247 -0
  131. package/skills/arc/references/testing.md +1 -1
  132. package/skills/arc-code-review/SKILL.md +141 -0
  133. package/skills/arc-code-review/references/anti-patterns.md +911 -0
  134. package/skills/arc-code-review/references/arc-cheatsheet.md +380 -0
  135. package/skills/arc-code-review/references/migration-recipes.md +700 -0
  136. package/skills/arc-code-review/references/mongokit-migration.md +386 -0
  137. package/skills/arc-code-review/references/scaffolding.md +230 -0
  138. package/skills/arc-code-review/references/severity.md +127 -0
  139. package/dist/EventTransport-BFQjw9pB.mjs +0 -133
  140. package/dist/EventTransport-CYNUXdCJ.d.mts +0 -293
  141. package/dist/adapters/index.d.mts +0 -3
  142. package/dist/adapters/index.mjs +0 -2
  143. package/dist/adapters-DUUiiimH.mjs +0 -964
  144. package/dist/auth/mongoose.d.mts +0 -191
  145. package/dist/auth/mongoose.mjs +0 -73
  146. package/dist/core-CbcQRIch.mjs +0 -1054
  147. package/dist/errorHandler-BQm8ZxTK.mjs +0 -173
  148. package/dist/errorHandler-DEWmGWPz.d.mts +0 -114
  149. package/dist/errors-D5c-5BJL.mjs +0 -232
  150. package/dist/index-Rg8axYPz.d.mts +0 -370
  151. package/dist/openapi-D7G1V7ex.mjs +0 -557
  152. /package/dist/{HookSystem-CGsMd6oK.mjs → HookSystem-Iiebom92.mjs} +0 -0
  153. /package/dist/{actionPermissions-sUUKDhtP.mjs → actionPermissions-CyUkQu6O.mjs} +0 -0
  154. /package/dist/{caching-CheW3m-S.mjs → caching-SM8gghN6.mjs} +0 -0
  155. /package/dist/{constants-BhY1OHoH.mjs → constants-Cxde4rpC.mjs} +0 -0
  156. /package/dist/{elevation-BQQXZ_VR.d.mts → elevation-BXOWoGCF.d.mts} +0 -0
  157. /package/dist/{keys-CARyUjiR.mjs → keys-CGcCbNyu.mjs} +0 -0
  158. /package/dist/{loadResources-CPpkyKfM.mjs → loadResources-DBMQg_Aj.mjs} +0 -0
  159. /package/dist/{memory-DikHSvWa.mjs → memory-UBydS5ku.mjs} +0 -0
  160. /package/dist/{metrics-Csh4nsvv.mjs → metrics-Qnvwc-LQ.mjs} +0 -0
  161. /package/dist/{pluralize-CWP6MB39.mjs → pluralize-DQgqgifU.mjs} +0 -0
  162. /package/dist/{registry-D63ee7fl.mjs → registry-I-ogLgL9.mjs} +0 -0
  163. /package/dist/{requestContext-C5XeK3VA.mjs → requestContext-SSaaTgW8.mjs} +0 -0
  164. /package/dist/{schemaConverter-B0oKLuqI.mjs → schemaConverter-De34B1ZG.mjs} +0 -0
  165. /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-BzkXkvVv.mjs} +0 -0
  166. /package/dist/{types-DV9WDfeg.mjs → types-D57iXYb8.mjs} +0 -0
  167. /package/dist/{versioning-CGPjkqAg.mjs → versioning-BUrT5aP4.mjs} +0 -0
@@ -1,7 +1,7 @@
1
- import { _ as requireTeamMembership, m as requireOrgRole, p as requireOrgMembership } from "../permissions-gd_aUWrR.mjs";
2
- import { n as normalizeRoles, t as getUserRoles } from "../types-DV9WDfeg.mjs";
3
- import { t as ArcError } from "../errors-D5c-5BJL.mjs";
4
- import { n as extractBetterAuthOpenApi } from "../betterAuthOpenApi-DwxtK3uG.mjs";
1
+ import { d as createDomainError, l as UnauthorizedError, p as isArcError, t as ArcError } from "../errors-j4aJm1Wg.mjs";
2
+ import { _ as requireOrgMembership, v as requireOrgRole, x as requireTeamMembership } from "../permissions-ohQyv50e.mjs";
3
+ import { n as normalizeRoles, t as getUserRoles } from "../types-D57iXYb8.mjs";
4
+ import { n as extractBetterAuthOpenApi } from "../betterAuthOpenApi--M_i87dQ.mjs";
5
5
  import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
6
6
  import fp from "fastify-plugin";
7
7
  //#region src/auth/authPlugin.ts
@@ -88,17 +88,17 @@ const authPlugin = async (fastify, opts = {}) => {
88
88
  const token = resolveToken(request);
89
89
  if (token) {
90
90
  const decoded = jwtContext.verify(token);
91
- if (decoded.type === "refresh") throw new Error("Refresh tokens cannot be used for authentication");
92
- if (strictTokenType && decoded.type !== "access") throw new Error("Invalid token type: expected access token");
91
+ if (decoded.type === "refresh") throw createDomainError("arc.auth.invalid_token_type", "Refresh tokens cannot be used for authentication", 401);
92
+ if (strictTokenType && decoded.type !== "access") throw createDomainError("arc.auth.invalid_token_type", "Invalid token type: expected access token", 401);
93
93
  user = decoded;
94
94
  }
95
- } else throw new Error("No authenticator configured. Provide auth.authenticate function or auth.jwt.secret.");
96
- if (!user) throw new Error("Authentication required");
95
+ } else throw createDomainError("arc.auth.misconfigured", "No authenticator configured. Provide auth.authenticate function or auth.jwt.secret.", 500);
96
+ if (!user) throw new UnauthorizedError("Authentication required");
97
97
  if (isRevoked) try {
98
- if (await isRevoked(user)) throw new Error("Token has been revoked");
98
+ if (await isRevoked(user)) throw createDomainError("arc.auth.token_revoked", "Token has been revoked", 401);
99
99
  } catch (revokeErr) {
100
- if (revokeErr instanceof Error && revokeErr.message === "Token has been revoked") throw revokeErr;
101
- throw new Error("Token revocation check failed");
100
+ if (isArcError(revokeErr) && revokeErr.code === "arc.auth.token_revoked") throw revokeErr;
101
+ throw createDomainError("arc.auth.revocation_check_failed", "Token revocation check failed", 401);
102
102
  }
103
103
  const reqRecord = request;
104
104
  reqRecord.user = user;
@@ -126,11 +126,20 @@ const authPlugin = async (fastify, opts = {}) => {
126
126
  await onFailure(request, reply, error);
127
127
  return;
128
128
  }
129
+ if (isArcError(error)) {
130
+ const message = exposeAuthErrors ? error.message : "Authentication required";
131
+ reply.code(error.statusCode).send({
132
+ code: error.code,
133
+ message,
134
+ status: error.statusCode
135
+ });
136
+ return;
137
+ }
129
138
  const message = exposeAuthErrors ? error.message : "Authentication required";
130
139
  reply.code(401).send({
131
- success: false,
132
- error: "Unauthorized",
133
- message
140
+ code: "arc.unauthorized",
141
+ message,
142
+ status: 401
134
143
  });
135
144
  }
136
145
  };
@@ -193,7 +202,7 @@ const authPlugin = async (fastify, opts = {}) => {
193
202
  * App calls this after validating credentials (login, OAuth, etc.)
194
203
  */
195
204
  const issueTokens = (payload, options) => {
196
- if (!jwtContext) throw new Error("JWT not configured. Provide auth.jwt.secret to use issueTokens.");
205
+ if (!jwtContext) throw createDomainError("arc.auth.misconfigured", "JWT not configured. Provide auth.jwt.secret to use issueTokens.", 500);
197
206
  const accessTtl = options?.expiresIn ?? accessExpiresIn;
198
207
  const refreshTtl = options?.refreshExpiresIn ?? refreshExpiresIn;
199
208
  const accessToken = jwtContext.sign({
@@ -228,9 +237,9 @@ const authPlugin = async (fastify, opts = {}) => {
228
237
  * App calls this in refresh endpoint
229
238
  */
230
239
  const verifyRefreshToken = (token) => {
231
- if (!jwtContext) throw new Error("JWT not configured. Provide auth.jwt.secret to use verifyRefreshToken.");
240
+ if (!jwtContext) throw createDomainError("arc.auth.misconfigured", "JWT not configured. Provide auth.jwt.secret to use verifyRefreshToken.", 500);
232
241
  const decoded = fastify.jwt.verify(token, { ...refreshSecret !== jwtConfig?.secret ? { key: refreshSecret } : {} });
233
- if (decoded.type !== "refresh") throw new Error("Invalid token type: expected refresh token");
242
+ if (decoded.type !== "refresh") throw createDomainError("arc.auth.invalid_token_type", "Invalid token type: expected refresh token", 401);
234
243
  return decoded;
235
244
  };
236
245
  /**
@@ -246,9 +255,9 @@ const authPlugin = async (fastify, opts = {}) => {
246
255
  const user = reqRecord[userProperty] ?? reqRecord.user;
247
256
  if (!user) {
248
257
  reply.code(401).send({
249
- success: false,
250
- error: "Unauthorized",
251
- message: "No user context"
258
+ code: "arc.unauthorized",
259
+ message: "No user context",
260
+ status: 401
252
261
  });
253
262
  return;
254
263
  }
@@ -256,9 +265,9 @@ const authPlugin = async (fastify, opts = {}) => {
256
265
  if (allowedRoles.length === 1 && allowedRoles[0] === "*") return;
257
266
  if (!allowedRoles.some((role) => userRoles.includes(role))) {
258
267
  reply.code(403).send({
259
- success: false,
260
- error: "Forbidden",
261
- message: `Requires one of: ${allowedRoles.join(", ")}`
268
+ code: "arc.forbidden",
269
+ message: `Requires one of: ${allowedRoles.join(", ")}`,
270
+ status: 403
262
271
  });
263
272
  return;
264
273
  }
@@ -334,65 +343,84 @@ async function sendFetchResponse(response, reply) {
334
343
  }
335
344
  }
336
345
  /**
337
- * Try to get session via Better Auth's direct JS API.
338
- * Returns null if the API method is not available (older Better Auth versions).
346
+ * Resolve the current session via Better Auth's direct JS API.
347
+ *
348
+ * Throws `ArcError(BETTER_AUTH_API_MISSING)` when `auth.api.getSession` is
349
+ * absent — this surfaces immediately and clearly when an integrator passes a
350
+ * stub handler instead of a real `betterAuth()` instance.
339
351
  */
340
- async function tryDirectGetSession(auth, headers) {
352
+ async function getSessionDirect(auth, headers) {
341
353
  const api = auth.api;
342
- if (!api || typeof api.getSession !== "function") return null;
343
- try {
344
- const result = await api.getSession({ headers });
345
- if (result?.user) return result;
346
- return null;
347
- } catch {
348
- return null;
349
- }
354
+ if (!api || typeof api.getSession !== "function") throw new ArcError("Better Auth instance is missing `api.getSession` — arc 2.13+ requires the in-process API map. Pass a real `betterAuth()` instance or supply an `api: { getSession }` stub.", {
355
+ code: "BETTER_AUTH_API_MISSING",
356
+ statusCode: 500
357
+ });
358
+ const result = await api.getSession({ headers });
359
+ return result?.user ? result : null;
350
360
  }
351
361
  /**
352
- * Try to get active org member via direct JS API.
353
- * Returns roles array or null if not available.
362
+ * Read a method from `auth.api` supports both the flat shape that real
363
+ * `betterAuth()` instances expose (`api.getActiveMember`) and the nested
364
+ * shape some test mocks / older builds use (`api.organization.getActiveMember`).
365
+ *
366
+ * Real Better Auth 1.6.x flattens every plugin endpoint onto the top-level
367
+ * `api` object. The nested form is kept as a fallback for hand-rolled stubs.
354
368
  */
355
- async function tryDirectGetActiveMember(auth, headers) {
356
- const getActiveMember = auth.api?.organization?.getActiveMember;
357
- if (typeof getActiveMember !== "function") return null;
369
+ function pickApiMethod(auth, name, group) {
370
+ const api = auth.api;
371
+ if (!api) return void 0;
372
+ const flat = api[name];
373
+ if (typeof flat === "function") return flat;
374
+ if (group) {
375
+ const nested = api[group]?.[name];
376
+ if (typeof nested === "function") return nested;
377
+ }
378
+ }
379
+ /** Resolve org roles for the active member (session.activeOrganizationId path). */
380
+ async function getActiveMemberRoles(auth, headers) {
381
+ const fn = pickApiMethod(auth, "getActiveMember", "organization");
382
+ if (!fn) return null;
358
383
  try {
359
- const memberData = await getActiveMember({ headers });
360
- if (memberData) return extractRolesFromMembership(memberData);
361
- return null;
384
+ const memberData = await fn({ headers });
385
+ return memberData ? extractRolesFromMembership(memberData) : null;
362
386
  } catch {
363
387
  return null;
364
388
  }
365
389
  }
366
390
  /**
367
- * Look up member role by explicit organizationId (query param).
391
+ * Resolve org roles for an explicit organizationId.
368
392
  *
369
- * Better Auth's `getActiveMemberRole` endpoint accepts an `organizationId`
370
- * query parameter, bypassing the session's `activeOrganizationId`.
371
- * This is essential for API key auth where the synthetic session has no
372
- * active organization set — callers pass org context via `x-organization-id` header.
393
+ * Required for API key auth where the synthetic session lacks
394
+ * `activeOrganizationId` callers pass org context via the
395
+ * `x-organization-id` header.
373
396
  */
374
- async function tryDirectGetMemberRole(auth, headers, organizationId) {
375
- const getActiveMemberRole = auth.api?.organization?.getActiveMemberRole;
376
- if (typeof getActiveMemberRole !== "function") return null;
397
+ async function getMemberRolesByOrg(auth, headers, organizationId) {
398
+ const fn = pickApiMethod(auth, "getActiveMemberRole", "organization");
399
+ if (!fn) return null;
377
400
  try {
378
- const result = await getActiveMemberRole({
401
+ const result = await fn({
379
402
  headers,
380
403
  query: { organizationId }
381
404
  });
382
- if (result?.role) return normalizeRoles(result.role);
383
- return null;
405
+ return result?.role ? normalizeRoles(result.role) : null;
384
406
  } catch {
385
407
  return null;
386
408
  }
387
409
  }
388
410
  /**
389
- * Try to list teams via direct JS API.
411
+ * List teams the current user is a member of. Used to validate
412
+ * `activeTeamId` against the membership set before binding it to scope.
413
+ *
414
+ * Better Auth 1.6+ exposes this as `auth.api.listUserTeams` (path:
415
+ * `/organization/list-user-teams`). Older 1.5.x exposed
416
+ * `auth.api.listTeams` — kept as a fallback so stubs/older versions still
417
+ * work.
390
418
  */
391
- async function tryDirectListTeams(auth, headers) {
392
- const listTeams = auth.api?.organization?.listTeams;
393
- if (typeof listTeams !== "function") return null;
419
+ async function listTeamsDirect(auth, headers) {
420
+ const fn = pickApiMethod(auth, "listUserTeams", "organization") ?? pickApiMethod(auth, "listTeams", "organization");
421
+ if (!fn) return null;
394
422
  try {
395
- const result = await listTeams({ headers });
423
+ const result = await fn({ headers });
396
424
  const teams = Array.isArray(result) ? result : result?.teams;
397
425
  return Array.isArray(teams) ? teams : null;
398
426
  } catch {
@@ -432,61 +460,6 @@ function extractRolesFromMembership(membership) {
432
460
  }
433
461
  return [];
434
462
  }
435
- /** Match an organization membership entry against the active org id */
436
- function membershipMatchesOrg(membership, activeOrgId) {
437
- return [
438
- normalizeId(membership.organizationId),
439
- normalizeId(membership.orgId),
440
- normalizeId(membership.id),
441
- normalizeId(membership.organization?._id),
442
- normalizeId(membership.organization?.id),
443
- normalizeId(membership.organization?.organizationId)
444
- ].filter(Boolean).includes(activeOrgId);
445
- }
446
- /**
447
- * Resolve org roles with fallback chain:
448
- * 1) GET /organization/get-active-member (requires activeOrganizationId in session)
449
- * 2) GET /organization/get-active-member-role?organizationId=... (explicit org — works for API key auth)
450
- * 3) GET /organization/list (fallback for type mismatch/legacy ID storage)
451
- */
452
- async function resolveOrgRoles(auth, protocol, host, normalizedBase, headers, activeOrgId) {
453
- const memberUrl = `${protocol}://${host}${normalizedBase}/organization/get-active-member`;
454
- const memberRequest = new Request(memberUrl, {
455
- method: "GET",
456
- headers
457
- });
458
- const memberResponse = await auth.handler(memberRequest);
459
- if (memberResponse.ok) {
460
- const memberData = await memberResponse.json();
461
- if (memberData) return extractRolesFromMembership(memberData);
462
- }
463
- const roleUrl = `${protocol}://${host}${normalizedBase}/organization/get-active-member-role?organizationId=${encodeURIComponent(activeOrgId)}`;
464
- const roleRequest = new Request(roleUrl, {
465
- method: "GET",
466
- headers
467
- });
468
- const roleResponse = await auth.handler(roleRequest);
469
- if (roleResponse.ok) {
470
- const roleData = await roleResponse.json();
471
- if (roleData?.role) return normalizeRoles(roleData.role);
472
- }
473
- const listUrl = `${protocol}://${host}${normalizedBase}/organization/list`;
474
- const listRequest = new Request(listUrl, {
475
- method: "GET",
476
- headers
477
- });
478
- const listResponse = await auth.handler(listRequest);
479
- if (!listResponse.ok) return null;
480
- const listData = await listResponse.json();
481
- const memberships = Array.isArray(listData) ? listData : listData?.organizations ?? listData?.data ?? [];
482
- if (!Array.isArray(memberships)) return null;
483
- const target = memberships.find((entry) => {
484
- if (!entry || typeof entry !== "object") return false;
485
- return membershipMatchesOrg(entry, activeOrgId);
486
- });
487
- if (!target) return null;
488
- return extractRolesFromMembership(target);
489
- }
490
463
  /**
491
464
  * Create a Better Auth adapter for Arc/Fastify.
492
465
  *
@@ -527,33 +500,13 @@ function createBetterAuthAdapter(options) {
527
500
  */
528
501
  const authenticate = async (request, reply) => {
529
502
  try {
530
- const protocol = request.protocol ?? "http";
531
- const host = request.hostname ?? "localhost";
532
503
  const headers = buildHeaders(request);
533
- let sessionData = null;
534
- sessionData = await tryDirectGetSession(auth, headers);
535
- if (!sessionData) {
536
- const sessionUrl = `${protocol}://${host}${normalizedBase}/get-session`;
537
- const sessionRequest = new Request(sessionUrl, {
538
- method: "GET",
539
- headers
540
- });
541
- const sessionResponse = await auth.handler(sessionRequest);
542
- if (!sessionResponse.ok) {
543
- reply.code(401).send({
544
- success: false,
545
- error: "Unauthorized",
546
- message: "Invalid or expired session"
547
- });
548
- return;
549
- }
550
- sessionData = await sessionResponse.json();
551
- }
504
+ const sessionData = await getSessionDirect(auth, headers);
552
505
  if (!sessionData?.user) {
553
506
  reply.code(401).send({
554
- success: false,
555
- error: "Unauthorized",
556
- message: "No active session"
507
+ code: "arc.unauthorized",
508
+ message: "Invalid or expired session",
509
+ status: 401
557
510
  });
558
511
  return;
559
512
  }
@@ -572,9 +525,8 @@ function createBetterAuthAdapter(options) {
572
525
  const session = sessionData.session;
573
526
  const activeOrgId = session?.activeOrganizationId || request.headers["x-organization-id"];
574
527
  if (activeOrgId) {
575
- let orgRoles = await tryDirectGetActiveMember(auth, headers);
576
- if (!orgRoles) orgRoles = await tryDirectGetMemberRole(auth, headers, activeOrgId);
577
- if (!orgRoles) orgRoles = await resolveOrgRoles(auth, protocol, host, normalizedBase, headers, activeOrgId);
528
+ let orgRoles = await getActiveMemberRoles(auth, headers);
529
+ if (!orgRoles) orgRoles = await getMemberRolesByOrg(auth, headers, activeOrgId);
578
530
  if (orgRoles) {
579
531
  const scope = {
580
532
  kind: "member",
@@ -585,21 +537,7 @@ function createBetterAuthAdapter(options) {
585
537
  };
586
538
  const activeTeamId = session?.activeTeamId;
587
539
  if (activeTeamId) {
588
- let teams = await tryDirectListTeams(auth, headers);
589
- if (!teams) {
590
- const teamsUrl = `${protocol}://${host}${normalizedBase}/organization/list-teams`;
591
- const teamsRequest = new Request(teamsUrl, {
592
- method: "GET",
593
- headers
594
- });
595
- const teamsResponse = await auth.handler(teamsRequest);
596
- if (teamsResponse.ok) {
597
- const teamsData = await teamsResponse.json();
598
- const teamsList = Array.isArray(teamsData) ? teamsData : teamsData?.teams;
599
- teams = Array.isArray(teamsList) ? teamsList : [];
600
- }
601
- }
602
- if (teams?.some((t) => normalizeId(t.id) === activeTeamId)) scope.teamId = activeTeamId;
540
+ if ((await listTeamsDirect(auth, headers))?.some((t) => normalizeId(t.id) === activeTeamId)) scope.teamId = activeTeamId;
603
541
  }
604
542
  req.scope = scope;
605
543
  }
@@ -608,9 +546,9 @@ function createBetterAuthAdapter(options) {
608
546
  } catch (err) {
609
547
  const message = exposeAuthErrors ? err instanceof Error ? err.message : String(err) : "Authentication required";
610
548
  reply.code(401).send({
611
- success: false,
612
- error: "Unauthorized",
613
- message
549
+ code: "arc.unauthorized",
550
+ message,
551
+ status: 401
614
552
  });
615
553
  }
616
554
  };
@@ -625,17 +563,7 @@ function createBetterAuthAdapter(options) {
625
563
  const optionalAuthenticate = async (request, _reply) => {
626
564
  try {
627
565
  const headers = buildHeaders(request);
628
- let sessionData = null;
629
- sessionData = await tryDirectGetSession(auth, headers);
630
- if (!sessionData) {
631
- const sessionUrl = `${request.protocol ?? "http"}://${request.hostname ?? "localhost"}${normalizedBase}/get-session`;
632
- const sessionRequest = new Request(sessionUrl, {
633
- method: "GET",
634
- headers
635
- });
636
- const sessionResponse = await auth.handler(sessionRequest);
637
- if (sessionResponse.ok) sessionData = await sessionResponse.json();
638
- }
566
+ const sessionData = await getSessionDirect(auth, headers);
639
567
  if (!sessionData?.user) return;
640
568
  const req = request;
641
569
  req.user = sessionData.user;
@@ -651,9 +579,8 @@ function createBetterAuthAdapter(options) {
651
579
  if (orgEnabled) {
652
580
  const activeOrgId = sessionData.session?.activeOrganizationId || request.headers["x-organization-id"];
653
581
  if (activeOrgId) {
654
- let orgRoles = await tryDirectGetActiveMember(auth, headers);
655
- if (!orgRoles) orgRoles = await tryDirectGetMemberRole(auth, headers, activeOrgId);
656
- if (!orgRoles) orgRoles = await resolveOrgRoles(auth, request.protocol ?? "http", request.hostname ?? "localhost", normalizedBase, headers, activeOrgId);
582
+ let orgRoles = await getActiveMemberRoles(auth, headers);
583
+ if (!orgRoles) orgRoles = await getMemberRolesByOrg(auth, headers, activeOrgId);
657
584
  if (orgRoles) req.scope = {
658
585
  kind: "member",
659
586
  userId: optUserId,
@@ -684,7 +611,7 @@ function createBetterAuthAdapter(options) {
684
611
  if (!fastify.hasDecorator("authenticate")) fastify.decorate("authenticate", authenticate);
685
612
  if (!fastify.hasDecorator("optionalAuthenticate")) fastify.decorate("optionalAuthenticate", optionalAuthenticate);
686
613
  if (!extractedOpenApi && openapiOpt !== false && auth.api && typeof auth.api === "object") {
687
- const { extractBetterAuthOpenApi } = await import("../betterAuthOpenApi-DwxtK3uG.mjs").then((n) => n.t);
614
+ const { extractBetterAuthOpenApi } = await import("../betterAuthOpenApi--M_i87dQ.mjs").then((n) => n.t);
688
615
  extractedOpenApi = extractBetterAuthOpenApi(auth.api, {
689
616
  basePath,
690
617
  userFields
@@ -1006,18 +933,17 @@ function createSessionManager(options) {
1006
933
  const session = request.session;
1007
934
  if (!session) {
1008
935
  reply.code(401).send({
1009
- success: false,
1010
- error: "Unauthorized",
1011
- message: "Authentication required"
936
+ code: "arc.unauthorized",
937
+ message: "Authentication required",
938
+ status: 401
1012
939
  });
1013
940
  return;
1014
941
  }
1015
942
  if (Date.now() - session.updatedAt > freshAgeMs) {
1016
943
  reply.code(403).send({
1017
- success: false,
1018
- error: "SessionNotFresh",
944
+ code: "arc.forbidden",
1019
945
  message: "Session is not fresh. Please re-authenticate to perform this action.",
1020
- code: "SESSION_NOT_FRESH"
946
+ status: 403
1021
947
  });
1022
948
  return;
1023
949
  }
@@ -1028,9 +954,9 @@ function createSessionManager(options) {
1028
954
  const signedValue = parseCookies(typeof cookieHeader === "string" ? cookieHeader : void 0).get(cookieName);
1029
955
  if (!signedValue) {
1030
956
  reply.code(401).send({
1031
- success: false,
1032
- error: "Unauthorized",
1033
- message: "No session cookie"
957
+ code: "arc.unauthorized",
958
+ message: "No session cookie",
959
+ status: 401
1034
960
  });
1035
961
  return;
1036
962
  }
@@ -1038,9 +964,9 @@ function createSessionManager(options) {
1038
964
  if (!sessionId) {
1039
965
  reply.header("Set-Cookie", buildClearCookieHeader(cookieName, cookieOptions));
1040
966
  reply.code(401).send({
1041
- success: false,
1042
- error: "Unauthorized",
1043
- message: "Invalid session"
967
+ code: "arc.unauthorized",
968
+ message: "Invalid session",
969
+ status: 401
1044
970
  });
1045
971
  return;
1046
972
  }
@@ -1048,9 +974,9 @@ function createSessionManager(options) {
1048
974
  if (!session) {
1049
975
  reply.header("Set-Cookie", buildClearCookieHeader(cookieName, cookieOptions));
1050
976
  reply.code(401).send({
1051
- success: false,
1052
- error: "Unauthorized",
1053
- message: "Session expired or revoked"
977
+ code: "arc.unauthorized",
978
+ message: "Session expired or revoked",
979
+ status: 401
1054
980
  });
1055
981
  return;
1056
982
  }
@@ -1058,9 +984,9 @@ function createSessionManager(options) {
1058
984
  await store.delete(sessionId);
1059
985
  reply.header("Set-Cookie", buildClearCookieHeader(cookieName, cookieOptions));
1060
986
  reply.code(401).send({
1061
- success: false,
1062
- error: "Unauthorized",
1063
- message: "Session expired"
987
+ code: "arc.unauthorized",
988
+ message: "Session expired",
989
+ status: 401
1064
990
  });
1065
991
  return;
1066
992
  }
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
- import { a as toJsonSchema } from "./schemaConverter-B0oKLuqI.mjs";
2
+ import { a as toJsonSchema } from "./schemaConverter-De34B1ZG.mjs";
3
3
  //#region src/auth/betterAuthOpenApi.ts
4
4
  var betterAuthOpenApi_exports = /* @__PURE__ */ __exportAll({ extractBetterAuthOpenApi: () => extractBetterAuthOpenApi });
5
5
  /**