@classytic/arc 1.1.0 → 2.1.2

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 (322) hide show
  1. package/README.md +247 -794
  2. package/bin/arc.js +91 -52
  3. package/dist/EventTransport-BD2U0BTc.d.mts +100 -0
  4. package/dist/EventTransport-BD2U0BTc.d.mts.map +1 -0
  5. package/dist/HookSystem-BsGV-j2l.mjs +405 -0
  6. package/dist/HookSystem-BsGV-j2l.mjs.map +1 -0
  7. package/dist/ResourceRegistry-DsN4KJjV.mjs +250 -0
  8. package/dist/ResourceRegistry-DsN4KJjV.mjs.map +1 -0
  9. package/dist/adapters/index.d.mts +5 -0
  10. package/dist/adapters/index.mjs +3 -0
  11. package/dist/audit/index.d.mts +82 -0
  12. package/dist/audit/index.d.mts.map +1 -0
  13. package/dist/audit/index.mjs +276 -0
  14. package/dist/audit/index.mjs.map +1 -0
  15. package/dist/audit/mongodb.d.mts +5 -0
  16. package/dist/audit/mongodb.mjs +3 -0
  17. package/dist/audited-C3T5DTUx.mjs +141 -0
  18. package/dist/audited-C3T5DTUx.mjs.map +1 -0
  19. package/dist/auth/index.d.mts +189 -0
  20. package/dist/auth/index.d.mts.map +1 -0
  21. package/dist/auth/index.mjs +1102 -0
  22. package/dist/auth/index.mjs.map +1 -0
  23. package/dist/auth/redis-session.d.mts +44 -0
  24. package/dist/auth/redis-session.d.mts.map +1 -0
  25. package/dist/auth/redis-session.mjs +76 -0
  26. package/dist/auth/redis-session.mjs.map +1 -0
  27. package/dist/betterAuthOpenApi-BrHKeSAx.mjs +250 -0
  28. package/dist/betterAuthOpenApi-BrHKeSAx.mjs.map +1 -0
  29. package/dist/cache/index.d.mts +146 -0
  30. package/dist/cache/index.d.mts.map +1 -0
  31. package/dist/cache/index.mjs +92 -0
  32. package/dist/cache/index.mjs.map +1 -0
  33. package/dist/caching-Bl28lYsR.mjs +94 -0
  34. package/dist/caching-Bl28lYsR.mjs.map +1 -0
  35. package/dist/chunk-C7Uep-_p.mjs +20 -0
  36. package/dist/circuitBreaker-DeY4FCjs.mjs +1097 -0
  37. package/dist/circuitBreaker-DeY4FCjs.mjs.map +1 -0
  38. package/dist/cli/commands/describe.d.mts +19 -0
  39. package/dist/cli/commands/describe.d.mts.map +1 -0
  40. package/dist/cli/commands/describe.mjs +239 -0
  41. package/dist/cli/commands/describe.mjs.map +1 -0
  42. package/dist/cli/commands/docs.d.mts +14 -0
  43. package/dist/cli/commands/docs.d.mts.map +1 -0
  44. package/dist/cli/commands/docs.mjs +53 -0
  45. package/dist/cli/commands/docs.mjs.map +1 -0
  46. package/dist/cli/commands/{generate.d.ts → generate.d.mts} +3 -1
  47. package/dist/cli/commands/generate.d.mts.map +1 -0
  48. package/dist/cli/commands/generate.mjs +358 -0
  49. package/dist/cli/commands/generate.mjs.map +1 -0
  50. package/dist/cli/commands/{init.d.ts → init.d.mts} +12 -8
  51. package/dist/cli/commands/init.d.mts.map +1 -0
  52. package/dist/cli/commands/{init.js → init.mjs} +807 -616
  53. package/dist/cli/commands/init.mjs.map +1 -0
  54. package/dist/cli/commands/introspect.d.mts +11 -0
  55. package/dist/cli/commands/introspect.d.mts.map +1 -0
  56. package/dist/cli/commands/introspect.mjs +76 -0
  57. package/dist/cli/commands/introspect.mjs.map +1 -0
  58. package/dist/cli/index.d.mts +17 -0
  59. package/dist/cli/index.d.mts.map +1 -0
  60. package/dist/cli/index.mjs +157 -0
  61. package/dist/cli/index.mjs.map +1 -0
  62. package/dist/constants-DdXFXQtN.mjs +85 -0
  63. package/dist/constants-DdXFXQtN.mjs.map +1 -0
  64. package/dist/core/index.d.mts +5 -0
  65. package/dist/core/index.mjs +4 -0
  66. package/dist/createApp-CUgNqegw.mjs +560 -0
  67. package/dist/createApp-CUgNqegw.mjs.map +1 -0
  68. package/dist/defineResource-k0_BDn8v.mjs +2197 -0
  69. package/dist/defineResource-k0_BDn8v.mjs.map +1 -0
  70. package/dist/discovery/index.d.mts +47 -0
  71. package/dist/discovery/index.d.mts.map +1 -0
  72. package/dist/discovery/index.mjs +110 -0
  73. package/dist/discovery/index.mjs.map +1 -0
  74. package/dist/docs/index.d.mts +163 -0
  75. package/dist/docs/index.d.mts.map +1 -0
  76. package/dist/docs/index.mjs +73 -0
  77. package/dist/docs/index.mjs.map +1 -0
  78. package/dist/elevation-BRy3yFWT.mjs +113 -0
  79. package/dist/elevation-BRy3yFWT.mjs.map +1 -0
  80. package/dist/elevation-B_2dRLVP.d.mts +88 -0
  81. package/dist/elevation-B_2dRLVP.d.mts.map +1 -0
  82. package/dist/errorHandler-BbcgBmIH.d.mts +73 -0
  83. package/dist/errorHandler-BbcgBmIH.d.mts.map +1 -0
  84. package/dist/errorHandler-C1okiriz.mjs +109 -0
  85. package/dist/errorHandler-C1okiriz.mjs.map +1 -0
  86. package/dist/errors-B9bZok84.mjs +212 -0
  87. package/dist/errors-B9bZok84.mjs.map +1 -0
  88. package/dist/errors-ChKiFz62.d.mts +125 -0
  89. package/dist/errors-ChKiFz62.d.mts.map +1 -0
  90. package/dist/eventPlugin-CTrLH3mt.d.mts +125 -0
  91. package/dist/eventPlugin-CTrLH3mt.d.mts.map +1 -0
  92. package/dist/eventPlugin-DGR_B2on.mjs +230 -0
  93. package/dist/eventPlugin-DGR_B2on.mjs.map +1 -0
  94. package/dist/events/index.d.mts +54 -0
  95. package/dist/events/index.d.mts.map +1 -0
  96. package/dist/events/index.mjs +52 -0
  97. package/dist/events/index.mjs.map +1 -0
  98. package/dist/events/transports/redis-stream-entry.d.mts +2 -0
  99. package/dist/events/transports/redis-stream-entry.mjs +178 -0
  100. package/dist/events/transports/redis-stream-entry.mjs.map +1 -0
  101. package/dist/events/transports/redis.d.mts +77 -0
  102. package/dist/events/transports/redis.d.mts.map +1 -0
  103. package/dist/events/transports/redis.mjs +125 -0
  104. package/dist/events/transports/redis.mjs.map +1 -0
  105. package/dist/externalPaths-DlINfKbP.d.mts +51 -0
  106. package/dist/externalPaths-DlINfKbP.d.mts.map +1 -0
  107. package/dist/factory/index.d.mts +64 -0
  108. package/dist/factory/index.d.mts.map +1 -0
  109. package/dist/factory/index.mjs +3 -0
  110. package/dist/fastifyAdapter-BkrGrlFi.d.mts +217 -0
  111. package/dist/fastifyAdapter-BkrGrlFi.d.mts.map +1 -0
  112. package/dist/fields-DyaDVX4J.d.mts +110 -0
  113. package/dist/fields-DyaDVX4J.d.mts.map +1 -0
  114. package/dist/fields-iagOozy0.mjs +115 -0
  115. package/dist/fields-iagOozy0.mjs.map +1 -0
  116. package/dist/hooks/index.d.mts +4 -0
  117. package/dist/hooks/index.mjs +3 -0
  118. package/dist/idempotency/index.d.mts +97 -0
  119. package/dist/idempotency/index.d.mts.map +1 -0
  120. package/dist/idempotency/index.mjs +320 -0
  121. package/dist/idempotency/index.mjs.map +1 -0
  122. package/dist/idempotency/mongodb.d.mts +2 -0
  123. package/dist/idempotency/mongodb.mjs +115 -0
  124. package/dist/idempotency/mongodb.mjs.map +1 -0
  125. package/dist/idempotency/redis.d.mts +2 -0
  126. package/dist/idempotency/redis.mjs +104 -0
  127. package/dist/idempotency/redis.mjs.map +1 -0
  128. package/dist/index.d.mts +261 -0
  129. package/dist/index.d.mts.map +1 -0
  130. package/dist/index.mjs +105 -0
  131. package/dist/index.mjs.map +1 -0
  132. package/dist/integrations/event-gateway.d.mts +47 -0
  133. package/dist/integrations/event-gateway.d.mts.map +1 -0
  134. package/dist/integrations/event-gateway.mjs +44 -0
  135. package/dist/integrations/event-gateway.mjs.map +1 -0
  136. package/dist/integrations/index.d.mts +5 -0
  137. package/dist/integrations/index.mjs +1 -0
  138. package/dist/integrations/jobs.d.mts +104 -0
  139. package/dist/integrations/jobs.d.mts.map +1 -0
  140. package/dist/integrations/jobs.mjs +124 -0
  141. package/dist/integrations/jobs.mjs.map +1 -0
  142. package/dist/integrations/streamline.d.mts +61 -0
  143. package/dist/integrations/streamline.d.mts.map +1 -0
  144. package/dist/integrations/streamline.mjs +126 -0
  145. package/dist/integrations/streamline.mjs.map +1 -0
  146. package/dist/integrations/websocket.d.mts +83 -0
  147. package/dist/integrations/websocket.d.mts.map +1 -0
  148. package/dist/integrations/websocket.mjs +289 -0
  149. package/dist/integrations/websocket.mjs.map +1 -0
  150. package/dist/interface-B01JvPVc.d.mts +78 -0
  151. package/dist/interface-B01JvPVc.d.mts.map +1 -0
  152. package/dist/interface-CZe8IkMf.d.mts +55 -0
  153. package/dist/interface-CZe8IkMf.d.mts.map +1 -0
  154. package/dist/interface-Ch8HU9uM.d.mts +1098 -0
  155. package/dist/interface-Ch8HU9uM.d.mts.map +1 -0
  156. package/dist/introspectionPlugin-rFdO8ZUa.mjs +54 -0
  157. package/dist/introspectionPlugin-rFdO8ZUa.mjs.map +1 -0
  158. package/dist/keys-BqNejWup.mjs +43 -0
  159. package/dist/keys-BqNejWup.mjs.map +1 -0
  160. package/dist/logger-Df2O2WsW.mjs +79 -0
  161. package/dist/logger-Df2O2WsW.mjs.map +1 -0
  162. package/dist/memory-cQgelFOj.mjs +144 -0
  163. package/dist/memory-cQgelFOj.mjs.map +1 -0
  164. package/dist/migrations/index.d.mts +157 -0
  165. package/dist/migrations/index.d.mts.map +1 -0
  166. package/dist/migrations/index.mjs +261 -0
  167. package/dist/migrations/index.mjs.map +1 -0
  168. package/dist/mongodb-BfJVlUJH.mjs +94 -0
  169. package/dist/mongodb-BfJVlUJH.mjs.map +1 -0
  170. package/dist/mongodb-CGzRbfAK.d.mts +119 -0
  171. package/dist/mongodb-CGzRbfAK.d.mts.map +1 -0
  172. package/dist/mongodb-JN-9JA7K.d.mts +72 -0
  173. package/dist/mongodb-JN-9JA7K.d.mts.map +1 -0
  174. package/dist/openapi-G3Cw7XuM.mjs +524 -0
  175. package/dist/openapi-G3Cw7XuM.mjs.map +1 -0
  176. package/dist/org/index.d.mts +69 -0
  177. package/dist/org/index.d.mts.map +1 -0
  178. package/dist/org/index.mjs +514 -0
  179. package/dist/org/index.mjs.map +1 -0
  180. package/dist/org/types.d.mts +83 -0
  181. package/dist/org/types.d.mts.map +1 -0
  182. package/dist/org/types.mjs +1 -0
  183. package/dist/permissions/index.d.mts +279 -0
  184. package/dist/permissions/index.d.mts.map +1 -0
  185. package/dist/permissions/index.mjs +579 -0
  186. package/dist/permissions/index.mjs.map +1 -0
  187. package/dist/plugins/index.d.mts +173 -0
  188. package/dist/plugins/index.d.mts.map +1 -0
  189. package/dist/plugins/index.mjs +523 -0
  190. package/dist/plugins/index.mjs.map +1 -0
  191. package/dist/plugins/response-cache.d.mts +88 -0
  192. package/dist/plugins/response-cache.d.mts.map +1 -0
  193. package/dist/plugins/response-cache.mjs +284 -0
  194. package/dist/plugins/response-cache.mjs.map +1 -0
  195. package/dist/plugins/tracing-entry.d.mts +2 -0
  196. package/dist/plugins/tracing-entry.mjs +186 -0
  197. package/dist/plugins/tracing-entry.mjs.map +1 -0
  198. package/dist/pluralize-CEweyOEm.mjs +87 -0
  199. package/dist/pluralize-CEweyOEm.mjs.map +1 -0
  200. package/dist/policies/{index.d.ts → index.d.mts} +204 -169
  201. package/dist/policies/index.d.mts.map +1 -0
  202. package/dist/policies/index.mjs +322 -0
  203. package/dist/policies/index.mjs.map +1 -0
  204. package/dist/presets/{index.d.ts → index.d.mts} +63 -131
  205. package/dist/presets/index.d.mts.map +1 -0
  206. package/dist/presets/index.mjs +144 -0
  207. package/dist/presets/index.mjs.map +1 -0
  208. package/dist/presets/multiTenant.d.mts +25 -0
  209. package/dist/presets/multiTenant.d.mts.map +1 -0
  210. package/dist/presets/multiTenant.mjs +114 -0
  211. package/dist/presets/multiTenant.mjs.map +1 -0
  212. package/dist/presets-BITljm96.mjs +120 -0
  213. package/dist/presets-BITljm96.mjs.map +1 -0
  214. package/dist/presets-DzSMwlKj.d.mts +58 -0
  215. package/dist/presets-DzSMwlKj.d.mts.map +1 -0
  216. package/dist/prisma-DJbMt3yf.mjs +628 -0
  217. package/dist/prisma-DJbMt3yf.mjs.map +1 -0
  218. package/dist/prisma-Dg9GoVdj.d.mts +275 -0
  219. package/dist/prisma-Dg9GoVdj.d.mts.map +1 -0
  220. package/dist/queryCachePlugin-7THaI5mt.d.mts +72 -0
  221. package/dist/queryCachePlugin-7THaI5mt.d.mts.map +1 -0
  222. package/dist/queryCachePlugin-DMBnp2Q0.mjs +139 -0
  223. package/dist/queryCachePlugin-DMBnp2Q0.mjs.map +1 -0
  224. package/dist/redis-D-JAeLtm.d.mts +50 -0
  225. package/dist/redis-D-JAeLtm.d.mts.map +1 -0
  226. package/dist/redis-stream-Bdh_vUU8.d.mts +104 -0
  227. package/dist/redis-stream-Bdh_vUU8.d.mts.map +1 -0
  228. package/dist/registry/index.d.mts +12 -0
  229. package/dist/registry/index.d.mts.map +1 -0
  230. package/dist/registry/index.mjs +4 -0
  231. package/dist/requestContext-QQD6ROJc.mjs +56 -0
  232. package/dist/requestContext-QQD6ROJc.mjs.map +1 -0
  233. package/dist/schemaConverter-BwrmWroW.mjs +99 -0
  234. package/dist/schemaConverter-BwrmWroW.mjs.map +1 -0
  235. package/dist/schemas/index.d.mts +64 -0
  236. package/dist/schemas/index.d.mts.map +1 -0
  237. package/dist/schemas/index.mjs +83 -0
  238. package/dist/schemas/index.mjs.map +1 -0
  239. package/dist/scope/index.d.mts +22 -0
  240. package/dist/scope/index.d.mts.map +1 -0
  241. package/dist/scope/index.mjs +66 -0
  242. package/dist/scope/index.mjs.map +1 -0
  243. package/dist/sessionManager-jPKLbHE0.d.mts +187 -0
  244. package/dist/sessionManager-jPKLbHE0.d.mts.map +1 -0
  245. package/dist/sse-B3c3_yZp.mjs +124 -0
  246. package/dist/sse-B3c3_yZp.mjs.map +1 -0
  247. package/dist/testing/index.d.mts +908 -0
  248. package/dist/testing/index.d.mts.map +1 -0
  249. package/dist/testing/index.mjs +1977 -0
  250. package/dist/testing/index.mjs.map +1 -0
  251. package/dist/tracing-Cc7vVQPp.d.mts +71 -0
  252. package/dist/tracing-Cc7vVQPp.d.mts.map +1 -0
  253. package/dist/typeGuards-DhMNLuvU.mjs +10 -0
  254. package/dist/typeGuards-DhMNLuvU.mjs.map +1 -0
  255. package/dist/types/index.d.mts +947 -0
  256. package/dist/types/index.d.mts.map +1 -0
  257. package/dist/types/index.mjs +15 -0
  258. package/dist/types/index.mjs.map +1 -0
  259. package/dist/types-Beqn1Un7.mjs +39 -0
  260. package/dist/types-Beqn1Un7.mjs.map +1 -0
  261. package/dist/types-CIgB7UUl.d.mts +446 -0
  262. package/dist/types-CIgB7UUl.d.mts.map +1 -0
  263. package/dist/types-aYB4V7uN.d.mts +87 -0
  264. package/dist/types-aYB4V7uN.d.mts.map +1 -0
  265. package/dist/utils/index.d.mts +748 -0
  266. package/dist/utils/index.d.mts.map +1 -0
  267. package/dist/utils/index.mjs +6 -0
  268. package/package.json +194 -68
  269. package/dist/BaseController-DVAiHxEQ.d.ts +0 -233
  270. package/dist/adapters/index.d.ts +0 -237
  271. package/dist/adapters/index.js +0 -668
  272. package/dist/arcCorePlugin-CsShQdyP.d.ts +0 -273
  273. package/dist/audit/index.d.ts +0 -195
  274. package/dist/audit/index.js +0 -319
  275. package/dist/auth/index.d.ts +0 -47
  276. package/dist/auth/index.js +0 -174
  277. package/dist/cli/commands/docs.d.ts +0 -11
  278. package/dist/cli/commands/docs.js +0 -474
  279. package/dist/cli/commands/generate.js +0 -334
  280. package/dist/cli/commands/introspect.d.ts +0 -8
  281. package/dist/cli/commands/introspect.js +0 -338
  282. package/dist/cli/index.d.ts +0 -4
  283. package/dist/cli/index.js +0 -3269
  284. package/dist/core/index.d.ts +0 -220
  285. package/dist/core/index.js +0 -2786
  286. package/dist/createApp-Ce9wl8W9.d.ts +0 -77
  287. package/dist/docs/index.d.ts +0 -166
  288. package/dist/docs/index.js +0 -658
  289. package/dist/errors-8WIxGS_6.d.ts +0 -122
  290. package/dist/events/index.d.ts +0 -117
  291. package/dist/events/index.js +0 -89
  292. package/dist/factory/index.d.ts +0 -38
  293. package/dist/factory/index.js +0 -1652
  294. package/dist/hooks/index.d.ts +0 -4
  295. package/dist/hooks/index.js +0 -199
  296. package/dist/idempotency/index.d.ts +0 -323
  297. package/dist/idempotency/index.js +0 -500
  298. package/dist/index-B4t03KQ0.d.ts +0 -1366
  299. package/dist/index.d.ts +0 -135
  300. package/dist/index.js +0 -4756
  301. package/dist/migrations/index.d.ts +0 -185
  302. package/dist/migrations/index.js +0 -274
  303. package/dist/org/index.d.ts +0 -129
  304. package/dist/org/index.js +0 -220
  305. package/dist/permissions/index.d.ts +0 -144
  306. package/dist/permissions/index.js +0 -103
  307. package/dist/plugins/index.d.ts +0 -46
  308. package/dist/plugins/index.js +0 -1069
  309. package/dist/policies/index.js +0 -196
  310. package/dist/presets/index.js +0 -384
  311. package/dist/presets/multiTenant.d.ts +0 -39
  312. package/dist/presets/multiTenant.js +0 -112
  313. package/dist/registry/index.d.ts +0 -16
  314. package/dist/registry/index.js +0 -253
  315. package/dist/testing/index.d.ts +0 -618
  316. package/dist/testing/index.js +0 -48020
  317. package/dist/types/index.d.ts +0 -4
  318. package/dist/types/index.js +0 -8
  319. package/dist/types-B99TBmFV.d.ts +0 -76
  320. package/dist/types-BvckRbs2.d.ts +0 -143
  321. package/dist/utils/index.d.ts +0 -679
  322. package/dist/utils/index.js +0 -931
@@ -0,0 +1,69 @@
1
+ import "../elevation-B_2dRLVP.mjs";
2
+ import { S as RouteHandler } from "../interface-Ch8HU9uM.mjs";
3
+ import { i as UserBase } from "../types-aYB4V7uN.mjs";
4
+ import "../types/index.mjs";
5
+ import { InvitationAdapter, InvitationDoc, MemberDoc, OrgAdapter, OrgDoc, OrgPermissionStatement, OrgRole, OrganizationPluginOptions } from "./types.mjs";
6
+ import { FastifyPluginAsync, RouteHandlerMethod } from "fastify";
7
+
8
+ //#region src/org/orgGuard.d.ts
9
+ interface OrgGuardOptions {
10
+ /** Require organization context (default: true) */
11
+ requireOrgContext?: boolean;
12
+ /** Required org-level roles */
13
+ roles?: string[];
14
+ }
15
+ /**
16
+ * Create org guard middleware.
17
+ * Reads `request.scope` for org context and roles.
18
+ * Elevated scope always passes.
19
+ */
20
+ declare function orgGuard(options?: OrgGuardOptions): RouteHandler;
21
+ /**
22
+ * Shorthand for requiring org context
23
+ */
24
+ declare function requireOrg(): RouteHandler;
25
+ /**
26
+ * Require org context with specific roles
27
+ */
28
+ declare function requireOrgRole(...roles: string[]): RouteHandler;
29
+ //#endregion
30
+ //#region src/org/orgMembership.d.ts
31
+ interface OrgMembershipOptions {
32
+ /** Path to user's organizations array */
33
+ userOrgsPath?: string;
34
+ /** Optional DB lookup function */
35
+ validateFromDb?: (userId: string, orgId: string) => Promise<boolean>;
36
+ }
37
+ interface OrgRolesOptions {
38
+ /** Path to user's organizations array */
39
+ userOrgsPath?: string;
40
+ }
41
+ /**
42
+ * Check if user is member of organization.
43
+ * This is a low-level utility for checking membership from user object data.
44
+ * For request-level checks, use `request.scope` (isMember/isElevated guards).
45
+ */
46
+ declare function orgMembershipCheck(user: UserBase | undefined | null, orgId: string | undefined | null, options?: OrgMembershipOptions): Promise<boolean>;
47
+ /**
48
+ * Get user's role in organization from user object data.
49
+ * For request-level role checks, use `request.scope.orgRoles` (when scope is 'member').
50
+ */
51
+ declare function getUserOrgRoles(user: UserBase | undefined | null, orgId: string | undefined | null, options?: OrgRolesOptions): string[];
52
+ /**
53
+ * Check if user has specific role in organization from user object data.
54
+ * For request-level role checks, use `requireOrgRole()` permission or `request.scope`.
55
+ */
56
+ declare function hasOrgRole(user: UserBase | undefined | null, orgId: string | undefined | null, roles: string | string[], options?: OrgRolesOptions): boolean;
57
+ //#endregion
58
+ //#region src/org/organizationPlugin.d.ts
59
+ declare module 'fastify' {
60
+ interface FastifyInstance {
61
+ /** Middleware: require the caller to hold one of the listed org roles */
62
+ requireOrgRole: (roles: string[]) => RouteHandlerMethod;
63
+ }
64
+ }
65
+ declare const organizationPlugin: FastifyPluginAsync<OrganizationPluginOptions>;
66
+ declare const _default: FastifyPluginAsync<OrganizationPluginOptions>;
67
+ //#endregion
68
+ export { type InvitationAdapter, type InvitationDoc, type MemberDoc, type OrgAdapter, type OrgDoc, type OrgGuardOptions, type OrgMembershipOptions, type OrgPermissionStatement, type OrgRole, type OrgRolesOptions, type OrganizationPluginOptions, getUserOrgRoles, hasOrgRole, orgGuard, orgMembershipCheck, _default as organizationPlugin, organizationPlugin as organizationPluginFn, requireOrg, requireOrgRole };
69
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/org/orgGuard.ts","../../src/org/orgMembership.ts","../../src/org/organizationPlugin.ts"],"mappings":";;;;;;;;UAsBiB,eAAA;EAYQ;EAVvB,iBAAA;EAUmE;EARnE,KAAA;AAAA;;;;;AAkEF;iBA1DgB,QAAA,CAAS,OAAA,GAAS,eAAA,GAAuB,YAAA;;;;iBAmDzC,UAAA,CAAA,GAAc,YAAA;;;AC7E9B;iBDoFgB,cAAA,CAAA,GAAkB,KAAA,aAAkB,YAAA;;;UCpFnC,oBAAA;;EAEf,YAAA;EDY8B;ECV9B,cAAA,IAAkB,MAAA,UAAgB,KAAA,aAAkB,OAAA;AAAA;AAAA,UAGrC,eAAA;EDmBD;ECjBd,YAAA;AAAA;;;;;;iBAQoB,kBAAA,CACpB,IAAA,EAAM,QAAA,qBACN,KAAA,6BACA,OAAA,GAAS,oBAAA,GACR,OAAA;ADwDH;;;;AAAA,iBCxBgB,eAAA,CACd,IAAA,EAAM,QAAA,qBACN,KAAA,6BACA,OAAA,GAAS,eAAA;AD4BX;;;;AAAA,iBCRgB,UAAA,CACd,IAAA,EAAM,QAAA,qBACN,KAAA,6BACA,KAAA,qBACA,OAAA,GAAS,eAAA;;;;YC3CC,eAAA;IF+CI;IE7CZ,cAAA,GAAiB,KAAA,eAAoB,kBAAA;EAAA;AAAA;AAAA,cA0DnC,kBAAA,EAAoB,kBAAA,CAAmB,yBAAA;AAAA,cAAyB,QAAA"}
@@ -0,0 +1,514 @@
1
+ import { c as isElevated, i as getOrgRoles, l as isMember, n as PUBLIC_SCOPE, o as hasOrgAccess } from "../types-Beqn1Un7.mjs";
2
+ import fp from "fastify-plugin";
3
+
4
+ //#region src/org/orgGuard.ts
5
+ /**
6
+ * Create org guard middleware.
7
+ * Reads `request.scope` for org context and roles.
8
+ * Elevated scope always passes.
9
+ */
10
+ function orgGuard(options = {}) {
11
+ const { requireOrgContext = true, roles = [] } = options;
12
+ return async function orgGuardMiddleware(request, reply) {
13
+ const scope = request.scope ?? PUBLIC_SCOPE;
14
+ if (isElevated(scope)) return;
15
+ if (requireOrgContext && !hasOrgAccess(scope)) {
16
+ reply.code(403).send({
17
+ success: false,
18
+ error: "Organization context required",
19
+ code: "ORG_CONTEXT_REQUIRED",
20
+ message: "This endpoint requires an organization context. Please specify organization via x-organization-id header."
21
+ });
22
+ return;
23
+ }
24
+ if (roles.length > 0 && isMember(scope)) {
25
+ const userOrgRoles = getOrgRoles(scope);
26
+ if (!roles.some((role) => userOrgRoles.includes(role))) {
27
+ reply.code(403).send({
28
+ success: false,
29
+ error: "Insufficient organization permissions",
30
+ code: "ORG_ROLE_REQUIRED",
31
+ message: `This action requires one of these organization roles: ${roles.join(", ")}`,
32
+ required: roles,
33
+ current: userOrgRoles
34
+ });
35
+ return;
36
+ }
37
+ }
38
+ };
39
+ }
40
+ /**
41
+ * Shorthand for requiring org context
42
+ */
43
+ function requireOrg() {
44
+ return orgGuard({ requireOrgContext: true });
45
+ }
46
+ /**
47
+ * Require org context with specific roles
48
+ */
49
+ function requireOrgRole(...roles) {
50
+ return orgGuard({
51
+ requireOrgContext: true,
52
+ roles
53
+ });
54
+ }
55
+
56
+ //#endregion
57
+ //#region src/org/orgMembership.ts
58
+ /**
59
+ * Check if user is member of organization.
60
+ * This is a low-level utility for checking membership from user object data.
61
+ * For request-level checks, use `request.scope` (isMember/isElevated guards).
62
+ */
63
+ async function orgMembershipCheck(user, orgId, options = {}) {
64
+ const { userOrgsPath = "organizations", validateFromDb } = options;
65
+ if (!user || !orgId) return false;
66
+ if ((user[userOrgsPath] ?? []).some((o) => {
67
+ return (o.organizationId?.toString() ?? String(o)) === orgId.toString();
68
+ })) return true;
69
+ if (validateFromDb) {
70
+ const userId = (user._id ?? user.id)?.toString();
71
+ if (userId) return validateFromDb(userId, orgId);
72
+ }
73
+ return false;
74
+ }
75
+ /**
76
+ * Get user's role in organization from user object data.
77
+ * For request-level role checks, use `request.scope.orgRoles` (when scope is 'member').
78
+ */
79
+ function getUserOrgRoles(user, orgId, options = {}) {
80
+ const { userOrgsPath = "organizations" } = options;
81
+ if (!user || !orgId) return [];
82
+ return (user[userOrgsPath] ?? []).find((o) => {
83
+ return (o.organizationId?.toString() ?? String(o)) === orgId.toString();
84
+ })?.roles ?? [];
85
+ }
86
+ /**
87
+ * Check if user has specific role in organization from user object data.
88
+ * For request-level role checks, use `requireOrgRole()` permission or `request.scope`.
89
+ */
90
+ function hasOrgRole(user, orgId, roles, options = {}) {
91
+ const userOrgRoles = getUserOrgRoles(user, orgId, options);
92
+ return (Array.isArray(roles) ? roles : [roles]).some((role) => userOrgRoles.includes(role));
93
+ }
94
+
95
+ //#endregion
96
+ //#region src/org/organizationPlugin.ts
97
+ /**
98
+ * Organization Plugin -- Full org management with REST endpoints
99
+ *
100
+ * Creates these routes:
101
+ * - POST /api/organizations -- Create org
102
+ * - GET /api/organizations -- List user's orgs
103
+ * - GET /api/organizations/:orgId -- Get org
104
+ * - PATCH /api/organizations/:orgId -- Update org
105
+ * - DELETE /api/organizations/:orgId -- Delete org
106
+ * - GET /api/organizations/:orgId/members -- List members
107
+ * - POST /api/organizations/:orgId/members -- Add member
108
+ * - PATCH /api/organizations/:orgId/members/:userId -- Update role
109
+ * - DELETE /api/organizations/:orgId/members/:userId -- Remove member
110
+ *
111
+ * @example
112
+ * import { organizationPlugin } from '@classytic/arc/org';
113
+ *
114
+ * await fastify.register(organizationPlugin, {
115
+ * adapter: myMongooseOrgAdapter,
116
+ * basePath: '/api/organizations',
117
+ * enableInvitations: false,
118
+ * });
119
+ */
120
+ const DEFAULT_ROLES = [
121
+ {
122
+ name: "owner",
123
+ permissions: [{
124
+ resource: "*",
125
+ action: ["*"]
126
+ }]
127
+ },
128
+ {
129
+ name: "admin",
130
+ permissions: [{
131
+ resource: "org",
132
+ action: ["read", "update"]
133
+ }, {
134
+ resource: "members",
135
+ action: ["*"]
136
+ }]
137
+ },
138
+ {
139
+ name: "member",
140
+ permissions: [{
141
+ resource: "org",
142
+ action: ["read"]
143
+ }, {
144
+ resource: "members",
145
+ action: ["read"]
146
+ }]
147
+ }
148
+ ];
149
+ /** Extract a UserBase from the request (set by auth plugin). */
150
+ function getUser(request) {
151
+ return request.user;
152
+ }
153
+ /** Get user id (supports both `id` and `_id`). */
154
+ function getUserId(user) {
155
+ const raw = user.id ?? user._id;
156
+ return raw ? String(raw) : void 0;
157
+ }
158
+ /** Standard JSON error reply. */
159
+ function sendError(reply, statusCode, code, message) {
160
+ reply.code(statusCode).send({
161
+ success: false,
162
+ code,
163
+ error: message
164
+ });
165
+ }
166
+ const organizationPlugin = async (fastify, opts) => {
167
+ const { adapter, roles = DEFAULT_ROLES, basePath = "/api/organizations", enableInvitations = false } = opts;
168
+ const validRoleNames = new Set(roles.map((r) => r.name));
169
+ /**
170
+ * Create a preHandler that:
171
+ * 1. Ensures the request is authenticated
172
+ * 2. Looks up the caller's membership in the org identified by `:orgId`
173
+ * 3. Verifies the caller holds one of the required roles
174
+ */
175
+ fastify.decorate("requireOrgRole", function requireOrgRole(requiredRoles) {
176
+ return async function requireOrgRoleHandler(request, reply) {
177
+ const user = getUser(request);
178
+ if (!user) {
179
+ sendError(reply, 401, "UNAUTHORIZED", "Authentication required");
180
+ return;
181
+ }
182
+ const userId = getUserId(user);
183
+ if (!userId) {
184
+ sendError(reply, 401, "UNAUTHORIZED", "Unable to determine user identity");
185
+ return;
186
+ }
187
+ const { orgId } = request.params;
188
+ if (!orgId) {
189
+ sendError(reply, 400, "MISSING_ORG_ID", "Organization ID is required");
190
+ return;
191
+ }
192
+ const member = await adapter.getMember(orgId, userId);
193
+ if (!member) {
194
+ sendError(reply, 403, "NOT_A_MEMBER", "You are not a member of this organization");
195
+ return;
196
+ }
197
+ if (!requiredRoles.includes(member.role)) {
198
+ sendError(reply, 403, "INSUFFICIENT_ROLE", `This action requires one of these roles: ${requiredRoles.join(", ")}`);
199
+ return;
200
+ }
201
+ };
202
+ });
203
+ /** Wrap preHandlers so that authenticate is called first (if available). */
204
+ function withAuth(...extra) {
205
+ const handlers = [];
206
+ const inst = fastify;
207
+ if (typeof inst.authenticate === "function") handlers.push(inst.authenticate);
208
+ handlers.push(...extra);
209
+ return handlers;
210
+ }
211
+ function generateSlug(name) {
212
+ return name.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
213
+ }
214
+ /**
215
+ * POST / -- Create organization
216
+ *
217
+ * Body: { name: string; slug?: string; [key: string]: unknown }
218
+ * The authenticated user becomes the owner.
219
+ */
220
+ fastify.post(basePath, { preHandler: withAuth() }, async (request, reply) => {
221
+ const user = getUser(request);
222
+ if (!user) {
223
+ sendError(reply, 401, "UNAUTHORIZED", "Authentication required");
224
+ return;
225
+ }
226
+ const userId = getUserId(user);
227
+ if (!userId) {
228
+ sendError(reply, 401, "UNAUTHORIZED", "Unable to determine user identity");
229
+ return;
230
+ }
231
+ const body = request.body;
232
+ if (!body?.name) {
233
+ sendError(reply, 400, "VALIDATION_ERROR", "Organization name is required");
234
+ return;
235
+ }
236
+ const slug = body.slug ?? generateSlug(body.name);
237
+ if (await adapter.getOrgBySlug(slug)) {
238
+ sendError(reply, 409, "SLUG_TAKEN", `An organization with slug '${slug}' already exists`);
239
+ return;
240
+ }
241
+ const org = await adapter.createOrg({
242
+ ...body,
243
+ name: body.name,
244
+ slug,
245
+ ownerId: userId
246
+ });
247
+ await adapter.addMember(org.id, userId, "owner");
248
+ reply.code(201).send({
249
+ success: true,
250
+ data: org
251
+ });
252
+ });
253
+ /**
254
+ * GET / -- List the authenticated user's organizations
255
+ */
256
+ fastify.get(basePath, { preHandler: withAuth() }, async (request, reply) => {
257
+ const user = getUser(request);
258
+ if (!user) {
259
+ sendError(reply, 401, "UNAUTHORIZED", "Authentication required");
260
+ return;
261
+ }
262
+ const userId = getUserId(user);
263
+ if (!userId) {
264
+ sendError(reply, 401, "UNAUTHORIZED", "Unable to determine user identity");
265
+ return;
266
+ }
267
+ const orgs = await adapter.listUserOrgs(userId);
268
+ reply.send({
269
+ success: true,
270
+ data: orgs
271
+ });
272
+ });
273
+ /**
274
+ * GET /:orgId -- Get a single organization
275
+ */
276
+ fastify.get(`${basePath}/:orgId`, { preHandler: withAuth(fastify.requireOrgRole([
277
+ "owner",
278
+ "admin",
279
+ "member"
280
+ ])) }, async (request, reply) => {
281
+ const { orgId } = request.params;
282
+ const org = await adapter.getOrg(orgId);
283
+ if (!org) {
284
+ sendError(reply, 404, "NOT_FOUND", "Organization not found");
285
+ return;
286
+ }
287
+ reply.send({
288
+ success: true,
289
+ data: org
290
+ });
291
+ });
292
+ /**
293
+ * PATCH /:orgId -- Update organization
294
+ */
295
+ fastify.patch(`${basePath}/:orgId`, { preHandler: withAuth(fastify.requireOrgRole(["owner", "admin"])) }, async (request, reply) => {
296
+ const { orgId } = request.params;
297
+ const body = request.body;
298
+ if (!body || Object.keys(body).length === 0) {
299
+ sendError(reply, 400, "VALIDATION_ERROR", "Request body must not be empty");
300
+ return;
301
+ }
302
+ const { ownerId: _ownerId, id: _id, ...updates } = body;
303
+ const org = await adapter.updateOrg(orgId, updates);
304
+ if (!org) {
305
+ sendError(reply, 404, "NOT_FOUND", "Organization not found");
306
+ return;
307
+ }
308
+ reply.send({
309
+ success: true,
310
+ data: org
311
+ });
312
+ });
313
+ /**
314
+ * DELETE /:orgId -- Delete organization (owner only)
315
+ */
316
+ fastify.delete(`${basePath}/:orgId`, { preHandler: withAuth(fastify.requireOrgRole(["owner"])) }, async (request, reply) => {
317
+ const { orgId } = request.params;
318
+ if (!await adapter.getOrg(orgId)) {
319
+ sendError(reply, 404, "NOT_FOUND", "Organization not found");
320
+ return;
321
+ }
322
+ await adapter.deleteOrg(orgId);
323
+ reply.send({
324
+ success: true,
325
+ message: "Organization deleted"
326
+ });
327
+ });
328
+ /**
329
+ * GET /:orgId/members -- List members
330
+ */
331
+ fastify.get(`${basePath}/:orgId/members`, { preHandler: withAuth(fastify.requireOrgRole([
332
+ "owner",
333
+ "admin",
334
+ "member"
335
+ ])) }, async (request, reply) => {
336
+ const { orgId } = request.params;
337
+ const members = await adapter.listMembers(orgId);
338
+ reply.send({
339
+ success: true,
340
+ data: members
341
+ });
342
+ });
343
+ /**
344
+ * POST /:orgId/members -- Add a member
345
+ *
346
+ * Body: { userId: string; role: string }
347
+ */
348
+ fastify.post(`${basePath}/:orgId/members`, { preHandler: withAuth(fastify.requireOrgRole(["owner", "admin"])) }, async (request, reply) => {
349
+ const { orgId } = request.params;
350
+ const body = request.body;
351
+ if (!body?.userId || !body.role) {
352
+ sendError(reply, 400, "VALIDATION_ERROR", "userId and role are required");
353
+ return;
354
+ }
355
+ if (!validRoleNames.has(body.role)) {
356
+ sendError(reply, 400, "INVALID_ROLE", `Invalid role '${body.role}'. Valid roles: ${[...validRoleNames].join(", ")}`);
357
+ return;
358
+ }
359
+ if (await adapter.getMember(orgId, body.userId)) {
360
+ sendError(reply, 409, "ALREADY_MEMBER", "User is already a member of this organization");
361
+ return;
362
+ }
363
+ const member = await adapter.addMember(orgId, body.userId, body.role);
364
+ reply.code(201).send({
365
+ success: true,
366
+ data: member
367
+ });
368
+ });
369
+ /**
370
+ * PATCH /:orgId/members/:userId -- Update a member's role
371
+ *
372
+ * Body: { role: string }
373
+ */
374
+ fastify.patch(`${basePath}/:orgId/members/:userId`, { preHandler: withAuth(fastify.requireOrgRole(["owner", "admin"])) }, async (request, reply) => {
375
+ const { orgId, userId } = request.params;
376
+ const body = request.body;
377
+ if (!body?.role) {
378
+ sendError(reply, 400, "VALIDATION_ERROR", "role is required");
379
+ return;
380
+ }
381
+ if (!validRoleNames.has(body.role)) {
382
+ sendError(reply, 400, "INVALID_ROLE", `Invalid role '${body.role}'. Valid roles: ${[...validRoleNames].join(", ")}`);
383
+ return;
384
+ }
385
+ const currentMember = await adapter.getMember(orgId, userId);
386
+ if (!currentMember) {
387
+ sendError(reply, 404, "NOT_FOUND", "Member not found");
388
+ return;
389
+ }
390
+ if (currentMember.role === "owner" && body.role !== "owner") {
391
+ if ((await adapter.listMembers(orgId)).filter((m) => m.role === "owner").length <= 1) {
392
+ sendError(reply, 400, "LAST_OWNER", "Cannot change the role of the last owner. Transfer ownership first.");
393
+ return;
394
+ }
395
+ }
396
+ const member = await adapter.updateMemberRole(orgId, userId, body.role);
397
+ if (!member) {
398
+ sendError(reply, 404, "NOT_FOUND", "Member not found");
399
+ return;
400
+ }
401
+ reply.send({
402
+ success: true,
403
+ data: member
404
+ });
405
+ });
406
+ /**
407
+ * DELETE /:orgId/members/:userId -- Remove a member
408
+ */
409
+ fastify.delete(`${basePath}/:orgId/members/:userId`, { preHandler: withAuth(fastify.requireOrgRole(["owner", "admin"])) }, async (request, reply) => {
410
+ const { orgId, userId } = request.params;
411
+ const member = await adapter.getMember(orgId, userId);
412
+ if (!member) {
413
+ sendError(reply, 404, "NOT_FOUND", "Member not found");
414
+ return;
415
+ }
416
+ if (member.role === "owner") {
417
+ if ((await adapter.listMembers(orgId)).filter((m) => m.role === "owner").length <= 1) {
418
+ sendError(reply, 400, "LAST_OWNER", "Cannot remove the last owner. Transfer ownership or delete the organization.");
419
+ return;
420
+ }
421
+ }
422
+ await adapter.removeMember(orgId, userId);
423
+ reply.send({
424
+ success: true,
425
+ message: "Member removed"
426
+ });
427
+ });
428
+ if (enableInvitations && adapter.invitations) {
429
+ const inv = adapter.invitations;
430
+ /**
431
+ * POST /:orgId/invitations -- Create invitation
432
+ *
433
+ * Body: { email: string; role: string; expiresAt?: string }
434
+ */
435
+ fastify.post(`${basePath}/:orgId/invitations`, { preHandler: withAuth(fastify.requireOrgRole(["owner", "admin"])) }, async (request, reply) => {
436
+ const user = getUser(request);
437
+ const userId = user ? getUserId(user) : void 0;
438
+ if (!userId) {
439
+ sendError(reply, 401, "UNAUTHORIZED", "Authentication required");
440
+ return;
441
+ }
442
+ const { orgId } = request.params;
443
+ const body = request.body;
444
+ if (!body?.email || !body.role) {
445
+ sendError(reply, 400, "VALIDATION_ERROR", "email and role are required");
446
+ return;
447
+ }
448
+ if (!validRoleNames.has(body.role)) {
449
+ sendError(reply, 400, "INVALID_ROLE", `Invalid role '${body.role}'. Valid roles: ${[...validRoleNames].join(", ")}`);
450
+ return;
451
+ }
452
+ const defaultExpiry = new Date(Date.now() + 10080 * 60 * 1e3);
453
+ const expiresAt = body.expiresAt ? new Date(body.expiresAt) : defaultExpiry;
454
+ const invitation = await inv.create({
455
+ orgId,
456
+ email: body.email,
457
+ role: body.role,
458
+ invitedBy: userId,
459
+ status: "pending",
460
+ expiresAt
461
+ });
462
+ reply.code(201).send({
463
+ success: true,
464
+ data: invitation
465
+ });
466
+ });
467
+ /**
468
+ * GET /:orgId/invitations -- List pending invitations
469
+ */
470
+ fastify.get(`${basePath}/:orgId/invitations`, { preHandler: withAuth(fastify.requireOrgRole(["owner", "admin"])) }, async (request, reply) => {
471
+ const { orgId } = request.params;
472
+ const invitations = await inv.listPending(orgId);
473
+ reply.send({
474
+ success: true,
475
+ data: invitations
476
+ });
477
+ });
478
+ /**
479
+ * POST /invitations/:invitationId/accept -- Accept invitation
480
+ */
481
+ fastify.post(`${basePath}/invitations/:invitationId/accept`, { preHandler: withAuth() }, async (request, reply) => {
482
+ const { invitationId } = request.params;
483
+ await inv.accept(invitationId);
484
+ reply.send({
485
+ success: true,
486
+ message: "Invitation accepted"
487
+ });
488
+ });
489
+ /**
490
+ * POST /invitations/:invitationId/reject -- Reject invitation
491
+ */
492
+ fastify.post(`${basePath}/invitations/:invitationId/reject`, { preHandler: withAuth() }, async (request, reply) => {
493
+ const { invitationId } = request.params;
494
+ await inv.reject(invitationId);
495
+ reply.send({
496
+ success: true,
497
+ message: "Invitation rejected"
498
+ });
499
+ });
500
+ }
501
+ fastify.log?.debug?.({
502
+ basePath,
503
+ roles: [...validRoleNames],
504
+ invitations: enableInvitations
505
+ }, "Organization plugin registered");
506
+ };
507
+ var organizationPlugin_default = fp(organizationPlugin, {
508
+ name: "arc-organization",
509
+ fastify: "5.x"
510
+ });
511
+
512
+ //#endregion
513
+ export { getUserOrgRoles, hasOrgRole, orgGuard, orgMembershipCheck, organizationPlugin_default as organizationPlugin, organizationPlugin as organizationPluginFn, requireOrg, requireOrgRole };
514
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/org/orgGuard.ts","../../src/org/orgMembership.ts","../../src/org/organizationPlugin.ts"],"sourcesContent":["/**\n * Organization Guard Middleware\n *\n * Ensures organization context is present before handler execution.\n *\n * @example\n * // Require org context\n * fastify.get('/invoices', {\n * preHandler: [fastify.authenticate, orgGuard()]\n * }, handler);\n *\n * // Require specific org roles\n * fastify.post('/invoices', {\n * preHandler: [fastify.authenticate, orgGuard({ roles: ['admin', 'accountant'] })]\n * }, handler);\n */\n\nimport type { FastifyReply } from 'fastify';\nimport type { RequestWithExtras, RouteHandler } from '../types/index.js';\nimport type { RequestScope } from '../scope/types.js';\nimport { isMember, isElevated, hasOrgAccess, getOrgRoles, PUBLIC_SCOPE } from '../scope/types.js';\n\nexport interface OrgGuardOptions {\n /** Require organization context (default: true) */\n requireOrgContext?: boolean;\n /** Required org-level roles */\n roles?: string[];\n}\n\n/**\n * Create org guard middleware.\n * Reads `request.scope` for org context and roles.\n * Elevated scope always passes.\n */\nexport function orgGuard(options: OrgGuardOptions = {}): RouteHandler {\n const {\n requireOrgContext = true,\n roles = [],\n } = options;\n\n return async function orgGuardMiddleware(\n request: RequestWithExtras,\n reply: FastifyReply\n ): Promise<void> {\n const scope = request.scope ?? PUBLIC_SCOPE;\n\n // Elevated scope always passes\n if (isElevated(scope)) return;\n\n // Check org context exists\n if (requireOrgContext && !hasOrgAccess(scope)) {\n reply.code(403).send({\n success: false,\n error: 'Organization context required',\n code: 'ORG_CONTEXT_REQUIRED',\n message:\n 'This endpoint requires an organization context. ' +\n 'Please specify organization via x-organization-id header.',\n });\n return;\n }\n\n // Check org-level roles if specified\n if (roles.length > 0 && isMember(scope)) {\n const userOrgRoles = getOrgRoles(scope);\n const hasRequiredRole = roles.some((role) => userOrgRoles.includes(role));\n\n if (!hasRequiredRole) {\n reply.code(403).send({\n success: false,\n error: 'Insufficient organization permissions',\n code: 'ORG_ROLE_REQUIRED',\n message: `This action requires one of these organization roles: ${roles.join(', ')}`,\n required: roles,\n current: userOrgRoles,\n });\n return;\n }\n }\n };\n}\n\n/**\n * Shorthand for requiring org context\n */\nexport function requireOrg(): RouteHandler {\n return orgGuard({ requireOrgContext: true });\n}\n\n/**\n * Require org context with specific roles\n */\nexport function requireOrgRole(...roles: string[]): RouteHandler {\n return orgGuard({ requireOrgContext: true, roles });\n}\n\nexport default orgGuard;\n","/**\n * Organization Membership Utilities\n *\n * Server-side membership validation.\n */\n\nimport type { UserBase, UserOrganization } from '../types/index.js';\n\nexport interface OrgMembershipOptions {\n /** Path to user's organizations array */\n userOrgsPath?: string;\n /** Optional DB lookup function */\n validateFromDb?: (userId: string, orgId: string) => Promise<boolean>;\n}\n\nexport interface OrgRolesOptions {\n /** Path to user's organizations array */\n userOrgsPath?: string;\n}\n\n/**\n * Check if user is member of organization.\n * This is a low-level utility for checking membership from user object data.\n * For request-level checks, use `request.scope` (isMember/isElevated guards).\n */\nexport async function orgMembershipCheck(\n user: UserBase | undefined | null,\n orgId: string | undefined | null,\n options: OrgMembershipOptions = {}\n): Promise<boolean> {\n const {\n userOrgsPath = 'organizations',\n validateFromDb,\n } = options;\n\n if (!user || !orgId) return false;\n\n // Check from user object\n const userOrgs = ((user as UserBase & { [key: string]: unknown })[userOrgsPath] ?? []) as UserOrganization[];\n const isMemberFromUser = userOrgs.some((o) => {\n const memberOrgId = o.organizationId?.toString() ?? String(o);\n return memberOrgId === orgId.toString();\n });\n\n if (isMemberFromUser) return true;\n\n // Optional: validate from database\n if (validateFromDb) {\n const userId = (user._id ?? user.id)?.toString();\n if (userId) {\n return validateFromDb(userId, orgId);\n }\n }\n\n return false;\n}\n\n/**\n * Get user's role in organization from user object data.\n * For request-level role checks, use `request.scope.orgRoles` (when scope is 'member').\n */\nexport function getUserOrgRoles(\n user: UserBase | undefined | null,\n orgId: string | undefined | null,\n options: OrgRolesOptions = {}\n): string[] {\n const { userOrgsPath = 'organizations' } = options;\n\n if (!user || !orgId) return [];\n\n const userOrgs = ((user as UserBase & { [key: string]: unknown })[userOrgsPath] ?? []) as UserOrganization[];\n const membership = userOrgs.find((o) => {\n const memberOrgId = o.organizationId?.toString() ?? String(o);\n return memberOrgId === orgId.toString();\n });\n\n const membershipRoles = membership as { roles?: string[] } | undefined;\n return membershipRoles?.roles ?? [];\n}\n\n/**\n * Check if user has specific role in organization from user object data.\n * For request-level role checks, use `requireOrgRole()` permission or `request.scope`.\n */\nexport function hasOrgRole(\n user: UserBase | undefined | null,\n orgId: string | undefined | null,\n roles: string | string[],\n options: OrgRolesOptions = {}\n): boolean {\n const userOrgRoles = getUserOrgRoles(user, orgId, options);\n const requiredRoles = Array.isArray(roles) ? roles : [roles];\n\n return requiredRoles.some((role) => userOrgRoles.includes(role));\n}\n\nexport default { orgMembershipCheck, getUserOrgRoles, hasOrgRole };\n","/**\n * Organization Plugin -- Full org management with REST endpoints\n *\n * Creates these routes:\n * - POST /api/organizations -- Create org\n * - GET /api/organizations -- List user's orgs\n * - GET /api/organizations/:orgId -- Get org\n * - PATCH /api/organizations/:orgId -- Update org\n * - DELETE /api/organizations/:orgId -- Delete org\n * - GET /api/organizations/:orgId/members -- List members\n * - POST /api/organizations/:orgId/members -- Add member\n * - PATCH /api/organizations/:orgId/members/:userId -- Update role\n * - DELETE /api/organizations/:orgId/members/:userId -- Remove member\n *\n * @example\n * import { organizationPlugin } from '@classytic/arc/org';\n *\n * await fastify.register(organizationPlugin, {\n * adapter: myMongooseOrgAdapter,\n * basePath: '/api/organizations',\n * enableInvitations: false,\n * });\n */\n\nimport fp from 'fastify-plugin';\nimport type {\n FastifyInstance,\n FastifyPluginAsync,\n FastifyReply,\n FastifyRequest,\n RouteHandlerMethod,\n} from 'fastify';\nimport type {\n OrgAdapter,\n OrgRole,\n OrganizationPluginOptions,\n MemberDoc,\n} from './types.js';\nimport type { UserBase } from '../permissions/types.js';\n\n// ---------------------------------------------------------------------------\n// Fastify type augmentations\n// ---------------------------------------------------------------------------\n\ndeclare module 'fastify' {\n interface FastifyInstance {\n /** Middleware: require the caller to hold one of the listed org roles */\n requireOrgRole: (roles: string[]) => RouteHandlerMethod;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Default roles\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_ROLES: OrgRole[] = [\n {\n name: 'owner',\n permissions: [{ resource: '*', action: ['*'] }],\n },\n {\n name: 'admin',\n permissions: [\n { resource: 'org', action: ['read', 'update'] },\n { resource: 'members', action: ['*'] },\n ],\n },\n {\n name: 'member',\n permissions: [\n { resource: 'org', action: ['read'] },\n { resource: 'members', action: ['read'] },\n ],\n },\n];\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Extract a UserBase from the request (set by auth plugin). */\nfunction getUser(request: FastifyRequest): UserBase | undefined {\n return (request as FastifyRequest & { user?: UserBase }).user;\n}\n\n/** Get user id (supports both `id` and `_id`). */\nfunction getUserId(user: UserBase): string | undefined {\n const raw = user.id ?? user._id;\n return raw ? String(raw) : undefined;\n}\n\n/** Standard JSON error reply. */\nfunction sendError(\n reply: FastifyReply,\n statusCode: number,\n code: string,\n message: string,\n): void {\n void reply.code(statusCode).send({ success: false, code, error: message });\n}\n\n// ---------------------------------------------------------------------------\n// Plugin implementation\n// ---------------------------------------------------------------------------\n\nconst organizationPlugin: FastifyPluginAsync<OrganizationPluginOptions> = async (\n fastify: FastifyInstance,\n opts: OrganizationPluginOptions,\n) => {\n const {\n adapter,\n roles = DEFAULT_ROLES,\n basePath = '/api/organizations',\n enableInvitations = false,\n } = opts;\n\n // Collect valid role names for quick validation\n const validRoleNames = new Set(roles.map((r) => r.name));\n\n // --------------------------------------------------\n // requireOrgRole decorator\n // --------------------------------------------------\n\n /**\n * Create a preHandler that:\n * 1. Ensures the request is authenticated\n * 2. Looks up the caller's membership in the org identified by `:orgId`\n * 3. Verifies the caller holds one of the required roles\n */\n fastify.decorate(\n 'requireOrgRole',\n function requireOrgRole(requiredRoles: string[]): RouteHandlerMethod {\n return async function requireOrgRoleHandler(\n request: FastifyRequest,\n reply: FastifyReply,\n ): Promise<void> {\n const user = getUser(request);\n if (!user) {\n sendError(reply, 401, 'UNAUTHORIZED', 'Authentication required');\n return;\n }\n\n const userId = getUserId(user);\n if (!userId) {\n sendError(reply, 401, 'UNAUTHORIZED', 'Unable to determine user identity');\n return;\n }\n\n const { orgId } = request.params as { orgId?: string };\n if (!orgId) {\n sendError(reply, 400, 'MISSING_ORG_ID', 'Organization ID is required');\n return;\n }\n\n const member = await adapter.getMember(orgId, userId);\n if (!member) {\n sendError(reply, 403, 'NOT_A_MEMBER', 'You are not a member of this organization');\n return;\n }\n\n const hasRole = requiredRoles.includes(member.role);\n if (!hasRole) {\n sendError(\n reply,\n 403,\n 'INSUFFICIENT_ROLE',\n `This action requires one of these roles: ${requiredRoles.join(', ')}`,\n );\n return;\n }\n };\n },\n );\n\n // --------------------------------------------------\n // Auth helper -- optional authenticate decorator\n // --------------------------------------------------\n\n /** Wrap preHandlers so that authenticate is called first (if available). */\n function withAuth(...extra: RouteHandlerMethod[]): RouteHandlerMethod[] {\n const handlers: RouteHandlerMethod[] = [];\n const inst = fastify as FastifyInstance & { authenticate?: RouteHandlerMethod };\n if (typeof inst.authenticate === 'function') {\n handlers.push(inst.authenticate);\n }\n handlers.push(...extra);\n return handlers;\n }\n\n // --------------------------------------------------\n // Slug helper\n // --------------------------------------------------\n\n function generateSlug(name: string): string {\n return name\n .toLowerCase()\n .trim()\n .replace(/[^\\w\\s-]/g, '')\n .replace(/[\\s_]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n }\n\n // --------------------------------------------------\n // Organization routes\n // --------------------------------------------------\n\n /**\n * POST / -- Create organization\n *\n * Body: { name: string; slug?: string; [key: string]: unknown }\n * The authenticated user becomes the owner.\n */\n fastify.post(\n basePath,\n { preHandler: withAuth() },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const user = getUser(request);\n if (!user) {\n sendError(reply, 401, 'UNAUTHORIZED', 'Authentication required');\n return;\n }\n\n const userId = getUserId(user);\n if (!userId) {\n sendError(reply, 401, 'UNAUTHORIZED', 'Unable to determine user identity');\n return;\n }\n\n const body = request.body as { name?: string; slug?: string; [key: string]: unknown } | undefined;\n if (!body?.name) {\n sendError(reply, 400, 'VALIDATION_ERROR', 'Organization name is required');\n return;\n }\n\n const slug = body.slug ?? generateSlug(body.name);\n\n // Check slug uniqueness\n const existing = await adapter.getOrgBySlug(slug);\n if (existing) {\n sendError(reply, 409, 'SLUG_TAKEN', `An organization with slug '${slug}' already exists`);\n return;\n }\n\n const org = await adapter.createOrg({ ...body, name: body.name, slug, ownerId: userId });\n\n // Auto-add creator as owner\n await adapter.addMember(org.id, userId, 'owner');\n\n void reply.code(201).send({ success: true, data: org });\n },\n );\n\n /**\n * GET / -- List the authenticated user's organizations\n */\n fastify.get(\n basePath,\n { preHandler: withAuth() },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const user = getUser(request);\n if (!user) {\n sendError(reply, 401, 'UNAUTHORIZED', 'Authentication required');\n return;\n }\n\n const userId = getUserId(user);\n if (!userId) {\n sendError(reply, 401, 'UNAUTHORIZED', 'Unable to determine user identity');\n return;\n }\n\n const orgs = await adapter.listUserOrgs(userId);\n void reply.send({ success: true, data: orgs });\n },\n );\n\n /**\n * GET /:orgId -- Get a single organization\n */\n fastify.get(\n `${basePath}/:orgId`,\n { preHandler: withAuth(fastify.requireOrgRole(['owner', 'admin', 'member'])) },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const { orgId } = request.params as { orgId: string };\n const org = await adapter.getOrg(orgId);\n\n if (!org) {\n sendError(reply, 404, 'NOT_FOUND', 'Organization not found');\n return;\n }\n\n void reply.send({ success: true, data: org });\n },\n );\n\n /**\n * PATCH /:orgId -- Update organization\n */\n fastify.patch(\n `${basePath}/:orgId`,\n { preHandler: withAuth(fastify.requireOrgRole(['owner', 'admin'])) },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const { orgId } = request.params as { orgId: string };\n const body = request.body as Partial<Record<string, unknown>> | undefined;\n\n if (!body || Object.keys(body).length === 0) {\n sendError(reply, 400, 'VALIDATION_ERROR', 'Request body must not be empty');\n return;\n }\n\n // Prevent changing ownerId or id through PATCH\n const { ownerId: _ownerId, id: _id, ...updates } = body;\n\n const org = await adapter.updateOrg(orgId, updates);\n if (!org) {\n sendError(reply, 404, 'NOT_FOUND', 'Organization not found');\n return;\n }\n\n void reply.send({ success: true, data: org });\n },\n );\n\n /**\n * DELETE /:orgId -- Delete organization (owner only)\n */\n fastify.delete(\n `${basePath}/:orgId`,\n { preHandler: withAuth(fastify.requireOrgRole(['owner'])) },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const { orgId } = request.params as { orgId: string };\n\n const org = await adapter.getOrg(orgId);\n if (!org) {\n sendError(reply, 404, 'NOT_FOUND', 'Organization not found');\n return;\n }\n\n await adapter.deleteOrg(orgId);\n void reply.send({ success: true, message: 'Organization deleted' });\n },\n );\n\n // --------------------------------------------------\n // Member routes\n // --------------------------------------------------\n\n /**\n * GET /:orgId/members -- List members\n */\n fastify.get(\n `${basePath}/:orgId/members`,\n { preHandler: withAuth(fastify.requireOrgRole(['owner', 'admin', 'member'])) },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const { orgId } = request.params as { orgId: string };\n const members = await adapter.listMembers(orgId);\n void reply.send({ success: true, data: members });\n },\n );\n\n /**\n * POST /:orgId/members -- Add a member\n *\n * Body: { userId: string; role: string }\n */\n fastify.post(\n `${basePath}/:orgId/members`,\n { preHandler: withAuth(fastify.requireOrgRole(['owner', 'admin'])) },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const { orgId } = request.params as { orgId: string };\n const body = request.body as { userId?: string; role?: string } | undefined;\n\n if (!body?.userId || !body.role) {\n sendError(reply, 400, 'VALIDATION_ERROR', 'userId and role are required');\n return;\n }\n\n if (!validRoleNames.has(body.role)) {\n sendError(\n reply,\n 400,\n 'INVALID_ROLE',\n `Invalid role '${body.role}'. Valid roles: ${[...validRoleNames].join(', ')}`,\n );\n return;\n }\n\n // Prevent duplicate membership\n const existing = await adapter.getMember(orgId, body.userId);\n if (existing) {\n sendError(reply, 409, 'ALREADY_MEMBER', 'User is already a member of this organization');\n return;\n }\n\n const member = await adapter.addMember(orgId, body.userId, body.role);\n void reply.code(201).send({ success: true, data: member });\n },\n );\n\n /**\n * PATCH /:orgId/members/:userId -- Update a member's role\n *\n * Body: { role: string }\n */\n fastify.patch(\n `${basePath}/:orgId/members/:userId`,\n { preHandler: withAuth(fastify.requireOrgRole(['owner', 'admin'])) },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const { orgId, userId } = request.params as { orgId: string; userId: string };\n const body = request.body as { role?: string } | undefined;\n\n if (!body?.role) {\n sendError(reply, 400, 'VALIDATION_ERROR', 'role is required');\n return;\n }\n\n if (!validRoleNames.has(body.role)) {\n sendError(\n reply,\n 400,\n 'INVALID_ROLE',\n `Invalid role '${body.role}'. Valid roles: ${[...validRoleNames].join(', ')}`,\n );\n return;\n }\n\n // Prevent demoting the last owner\n const currentMember = await adapter.getMember(orgId, userId);\n if (!currentMember) {\n sendError(reply, 404, 'NOT_FOUND', 'Member not found');\n return;\n }\n\n if (currentMember.role === 'owner' && body.role !== 'owner') {\n const members = await adapter.listMembers(orgId);\n const ownerCount = members.filter((m: MemberDoc) => m.role === 'owner').length;\n if (ownerCount <= 1) {\n sendError(\n reply,\n 400,\n 'LAST_OWNER',\n 'Cannot change the role of the last owner. Transfer ownership first.',\n );\n return;\n }\n }\n\n const member = await adapter.updateMemberRole(orgId, userId, body.role);\n if (!member) {\n sendError(reply, 404, 'NOT_FOUND', 'Member not found');\n return;\n }\n\n void reply.send({ success: true, data: member });\n },\n );\n\n /**\n * DELETE /:orgId/members/:userId -- Remove a member\n */\n fastify.delete(\n `${basePath}/:orgId/members/:userId`,\n { preHandler: withAuth(fastify.requireOrgRole(['owner', 'admin'])) },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const { orgId, userId } = request.params as { orgId: string; userId: string };\n\n const member = await adapter.getMember(orgId, userId);\n if (!member) {\n sendError(reply, 404, 'NOT_FOUND', 'Member not found');\n return;\n }\n\n // Prevent removing the last owner\n if (member.role === 'owner') {\n const members = await adapter.listMembers(orgId);\n const ownerCount = members.filter((m: MemberDoc) => m.role === 'owner').length;\n if (ownerCount <= 1) {\n sendError(\n reply,\n 400,\n 'LAST_OWNER',\n 'Cannot remove the last owner. Transfer ownership or delete the organization.',\n );\n return;\n }\n }\n\n await adapter.removeMember(orgId, userId);\n void reply.send({ success: true, message: 'Member removed' });\n },\n );\n\n // --------------------------------------------------\n // Invitation routes (optional)\n // --------------------------------------------------\n\n if (enableInvitations && adapter.invitations) {\n const inv = adapter.invitations;\n\n /**\n * POST /:orgId/invitations -- Create invitation\n *\n * Body: { email: string; role: string; expiresAt?: string }\n */\n fastify.post(\n `${basePath}/:orgId/invitations`,\n { preHandler: withAuth(fastify.requireOrgRole(['owner', 'admin'])) },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const user = getUser(request);\n const userId = user ? getUserId(user) : undefined;\n if (!userId) {\n sendError(reply, 401, 'UNAUTHORIZED', 'Authentication required');\n return;\n }\n\n const { orgId } = request.params as { orgId: string };\n const body = request.body as { email?: string; role?: string; expiresAt?: string } | undefined;\n\n if (!body?.email || !body.role) {\n sendError(reply, 400, 'VALIDATION_ERROR', 'email and role are required');\n return;\n }\n\n if (!validRoleNames.has(body.role)) {\n sendError(\n reply,\n 400,\n 'INVALID_ROLE',\n `Invalid role '${body.role}'. Valid roles: ${[...validRoleNames].join(', ')}`,\n );\n return;\n }\n\n const defaultExpiry = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days\n const expiresAt = body.expiresAt ? new Date(body.expiresAt) : defaultExpiry;\n\n const invitation = await inv.create({\n orgId,\n email: body.email,\n role: body.role,\n invitedBy: userId,\n status: 'pending',\n expiresAt,\n });\n\n void reply.code(201).send({ success: true, data: invitation });\n },\n );\n\n /**\n * GET /:orgId/invitations -- List pending invitations\n */\n fastify.get(\n `${basePath}/:orgId/invitations`,\n { preHandler: withAuth(fastify.requireOrgRole(['owner', 'admin'])) },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const { orgId } = request.params as { orgId: string };\n const invitations = await inv.listPending(orgId);\n void reply.send({ success: true, data: invitations });\n },\n );\n\n /**\n * POST /invitations/:invitationId/accept -- Accept invitation\n */\n fastify.post(\n `${basePath}/invitations/:invitationId/accept`,\n { preHandler: withAuth() },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const { invitationId } = request.params as { invitationId: string };\n await inv.accept(invitationId);\n void reply.send({ success: true, message: 'Invitation accepted' });\n },\n );\n\n /**\n * POST /invitations/:invitationId/reject -- Reject invitation\n */\n fastify.post(\n `${basePath}/invitations/:invitationId/reject`,\n { preHandler: withAuth() },\n async (request: FastifyRequest, reply: FastifyReply) => {\n const { invitationId } = request.params as { invitationId: string };\n await inv.reject(invitationId);\n void reply.send({ success: true, message: 'Invitation rejected' });\n },\n );\n }\n\n fastify.log?.debug?.(\n { basePath, roles: [...validRoleNames], invitations: enableInvitations },\n 'Organization plugin registered',\n );\n};\n\nexport default fp(organizationPlugin, {\n name: 'arc-organization',\n fastify: '5.x',\n});\n\nexport { organizationPlugin };\n"],"mappings":"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"}