@classytic/arc 2.3.0 → 2.4.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 (175) hide show
  1. package/README.md +187 -18
  2. package/bin/arc.js +11 -3
  3. package/dist/BaseController-CkM5dUh_.mjs +1031 -0
  4. package/dist/{EventTransport-BkUDYZEb.d.mts → EventTransport-wc5hSLik.d.mts} +1 -1
  5. package/dist/{HookSystem-BsGV-j2l.mjs → HookSystem-COkyWztM.mjs} +2 -3
  6. package/dist/{ResourceRegistry-7Ic20ZMw.mjs → ResourceRegistry-DeCIFlix.mjs} +8 -5
  7. package/dist/adapters/index.d.mts +3 -5
  8. package/dist/adapters/index.mjs +2 -3
  9. package/dist/{prisma-DJbMt3yf.mjs → adapters-DTC4Ug66.mjs} +45 -12
  10. package/dist/audit/index.d.mts +4 -7
  11. package/dist/audit/index.mjs +2 -29
  12. package/dist/audit/mongodb.d.mts +1 -4
  13. package/dist/audit/mongodb.mjs +2 -3
  14. package/dist/auth/index.d.mts +7 -9
  15. package/dist/auth/index.mjs +65 -63
  16. package/dist/auth/redis-session.d.mts +1 -1
  17. package/dist/auth/redis-session.mjs +1 -2
  18. package/dist/{betterAuthOpenApi-DjWDddNc.mjs → betterAuthOpenApi-lz0IRbXJ.mjs} +4 -6
  19. package/dist/cache/index.d.mts +23 -23
  20. package/dist/cache/index.mjs +4 -6
  21. package/dist/{caching-GSDJcA6-.mjs → caching-BSXB-Xr7.mjs} +2 -24
  22. package/dist/chunk-BpYLSNr0.mjs +14 -0
  23. package/dist/circuitBreaker-BOBOpN2w.mjs +284 -0
  24. package/dist/circuitBreaker-JP2GdJ4b.d.mts +206 -0
  25. package/dist/cli/commands/describe.mjs +24 -7
  26. package/dist/cli/commands/docs.mjs +6 -7
  27. package/dist/cli/commands/doctor.d.mts +10 -0
  28. package/dist/cli/commands/doctor.mjs +156 -0
  29. package/dist/cli/commands/generate.mjs +66 -17
  30. package/dist/cli/commands/init.mjs +315 -45
  31. package/dist/cli/commands/introspect.mjs +2 -4
  32. package/dist/cli/index.d.mts +1 -10
  33. package/dist/cli/index.mjs +4 -153
  34. package/dist/{constants-DdXFXQtN.mjs → constants-Cxde4rpC.mjs} +1 -2
  35. package/dist/core/index.d.mts +3 -5
  36. package/dist/core/index.mjs +5 -4
  37. package/dist/core-C1XCMtqM.mjs +185 -0
  38. package/dist/{createApp-CgKOPhA4.mjs → createApp-ByWNRsZj.mjs} +64 -35
  39. package/dist/{defineResource-DWbpJYtm.mjs → defineResource-D9aY5Cy6.mjs} +108 -1157
  40. package/dist/discovery/index.mjs +37 -5
  41. package/dist/docs/index.d.mts +6 -9
  42. package/dist/docs/index.mjs +3 -21
  43. package/dist/dynamic/index.d.mts +93 -0
  44. package/dist/dynamic/index.mjs +122 -0
  45. package/dist/{elevation-DSTbVvYj.mjs → elevation-BEdACOLB.mjs} +5 -36
  46. package/dist/{elevation-DGo5shaX.d.mts → elevation-Ca_yveIO.d.mts} +41 -7
  47. package/dist/{errorHandler-C3GY3_ow.mjs → errorHandler--zp54tGc.mjs} +3 -5
  48. package/dist/errorHandler-Do4vVQ1f.d.mts +139 -0
  49. package/dist/{errors-DBANPbGr.mjs → errors-rxhfP7Hf.mjs} +1 -2
  50. package/dist/{eventPlugin-BEOvaDqo.mjs → eventPlugin-Ba00swHF.mjs} +25 -27
  51. package/dist/{eventPlugin-H6wDDjGO.d.mts → eventPlugin-iGrSEmwJ.d.mts} +105 -5
  52. package/dist/events/index.d.mts +72 -7
  53. package/dist/events/index.mjs +216 -4
  54. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  55. package/dist/events/transports/redis-stream-entry.mjs +19 -7
  56. package/dist/events/transports/redis.d.mts +1 -1
  57. package/dist/events/transports/redis.mjs +3 -4
  58. package/dist/factory/index.d.mts +23 -9
  59. package/dist/factory/index.mjs +48 -3
  60. package/dist/{fields-Bi_AVKSo.d.mts → fields-DFwdaWCq.d.mts} +1 -1
  61. package/dist/{fields-CTd_CrKr.mjs → fields-ipsbIRPK.mjs} +1 -2
  62. package/dist/hooks/index.d.mts +1 -3
  63. package/dist/hooks/index.mjs +2 -3
  64. package/dist/idempotency/index.d.mts +5 -5
  65. package/dist/idempotency/index.mjs +3 -7
  66. package/dist/idempotency/mongodb.d.mts +1 -1
  67. package/dist/idempotency/mongodb.mjs +4 -5
  68. package/dist/idempotency/redis.d.mts +1 -1
  69. package/dist/idempotency/redis.mjs +2 -5
  70. package/dist/{fastifyAdapter-6b_eRDBw.d.mts → index-BL8CaQih.d.mts} +56 -57
  71. package/dist/index-Diqcm14c.d.mts +369 -0
  72. package/dist/{prisma-Dy5S5F5i.d.mts → index-yhxyjqNb.d.mts} +4 -5
  73. package/dist/index.d.mts +100 -105
  74. package/dist/index.mjs +85 -58
  75. package/dist/integrations/event-gateway.d.mts +1 -1
  76. package/dist/integrations/event-gateway.mjs +8 -4
  77. package/dist/integrations/index.d.mts +4 -2
  78. package/dist/integrations/index.mjs +1 -1
  79. package/dist/integrations/jobs.d.mts +2 -2
  80. package/dist/integrations/jobs.mjs +63 -14
  81. package/dist/integrations/mcp/index.d.mts +219 -0
  82. package/dist/integrations/mcp/index.mjs +572 -0
  83. package/dist/integrations/mcp/testing.d.mts +53 -0
  84. package/dist/integrations/mcp/testing.mjs +104 -0
  85. package/dist/integrations/streamline.mjs +39 -19
  86. package/dist/integrations/webhooks.d.mts +56 -0
  87. package/dist/integrations/webhooks.mjs +139 -0
  88. package/dist/integrations/websocket-redis.d.mts +46 -0
  89. package/dist/integrations/websocket-redis.mjs +50 -0
  90. package/dist/integrations/websocket.d.mts +68 -2
  91. package/dist/integrations/websocket.mjs +96 -13
  92. package/dist/{interface-CSNjltAc.d.mts → interface-B4awm1RJ.d.mts} +2 -2
  93. package/dist/interface-DGmPxakH.d.mts +2213 -0
  94. package/dist/{keys-DhqDRxv3.mjs → keys-qcD-TVJl.mjs} +3 -4
  95. package/dist/{logger-ByrvQWZO.mjs → logger-Dz3j1ItV.mjs} +2 -4
  96. package/dist/{memory-B2v7KrCB.mjs → memory-Cb_7iy9e.mjs} +2 -4
  97. package/dist/metrics-Csh4nsvv.mjs +224 -0
  98. package/dist/migrations/index.d.mts +113 -44
  99. package/dist/migrations/index.mjs +84 -102
  100. package/dist/{mongodb-DNKEExbf.mjs → mongodb-BuQ7fNTg.mjs} +1 -4
  101. package/dist/{mongodb-ClykrfGo.d.mts → mongodb-CUpYfxfD.d.mts} +2 -3
  102. package/dist/{mongodb-Dg8O_gvd.d.mts → mongodb-bga9AbkD.d.mts} +2 -2
  103. package/dist/{openapi-9nB_kiuR.mjs → openapi-CBmZ6EQN.mjs} +4 -21
  104. package/dist/org/index.d.mts +12 -14
  105. package/dist/org/index.mjs +92 -119
  106. package/dist/org/types.d.mts +2 -2
  107. package/dist/org/types.mjs +1 -1
  108. package/dist/permissions/index.d.mts +4 -278
  109. package/dist/permissions/index.mjs +4 -579
  110. package/dist/permissions-CA5zg0yK.mjs +751 -0
  111. package/dist/plugins/index.d.mts +104 -107
  112. package/dist/plugins/index.mjs +203 -313
  113. package/dist/plugins/response-cache.mjs +4 -69
  114. package/dist/plugins/tracing-entry.d.mts +1 -1
  115. package/dist/plugins/tracing-entry.mjs +24 -11
  116. package/dist/{pluralize-CM-jZg7p.mjs → pluralize-CcT6qF0a.mjs} +12 -13
  117. package/dist/policies/index.d.mts +2 -2
  118. package/dist/policies/index.mjs +80 -83
  119. package/dist/presets/index.d.mts +26 -19
  120. package/dist/presets/index.mjs +2 -142
  121. package/dist/presets/multiTenant.d.mts +1 -4
  122. package/dist/presets/multiTenant.mjs +4 -6
  123. package/dist/presets-C9QXJV1u.mjs +422 -0
  124. package/dist/{queryCachePlugin-B6R0d4av.mjs → queryCachePlugin-ClosZdNS.mjs} +6 -27
  125. package/dist/{queryCachePlugin-Q6SYuHZ6.d.mts → queryCachePlugin-DcmETvcB.d.mts} +3 -3
  126. package/dist/queryParser-CgCtsjti.mjs +352 -0
  127. package/dist/{redis-UwjEp8Ea.d.mts → redis-CQ5YxMC5.d.mts} +2 -2
  128. package/dist/{redis-stream-CBg0upHI.d.mts → redis-stream-BW9UKLZM.d.mts} +9 -2
  129. package/dist/registry/index.d.mts +1 -4
  130. package/dist/registry/index.mjs +3 -4
  131. package/dist/{introspectionPlugin-B3JkrjwU.mjs → registry-I-ogLgL9.mjs} +1 -8
  132. package/dist/{requestContext-xi6OKBL-.mjs → requestContext-DYtmNpm5.mjs} +1 -3
  133. package/dist/resourceToTools-PMFE8HIv.mjs +533 -0
  134. package/dist/rpc/index.d.mts +90 -0
  135. package/dist/rpc/index.mjs +248 -0
  136. package/dist/{schemaConverter-Dtg0Kt9T.mjs → schemaConverter-DjzHpFam.mjs} +1 -2
  137. package/dist/schemas/index.d.mts +30 -30
  138. package/dist/schemas/index.mjs +2 -4
  139. package/dist/scope/index.d.mts +13 -2
  140. package/dist/scope/index.mjs +18 -5
  141. package/dist/{sessionManager-D_iEHjQl.d.mts → sessionManager-wbkYj2HL.d.mts} +2 -2
  142. package/dist/{sse-DkqQ1uxb.mjs → sse-BkViJPlT.mjs} +4 -25
  143. package/dist/testing/index.d.mts +551 -567
  144. package/dist/testing/index.mjs +1744 -1799
  145. package/dist/{tracing-8CEbhF0w.d.mts → tracing-bz_U4EM1.d.mts} +6 -1
  146. package/dist/{typeGuards-DwxA1t_L.mjs → typeGuards-Cj5Rgvlg.mjs} +1 -2
  147. package/dist/types/index.d.mts +4 -946
  148. package/dist/types/index.mjs +2 -4
  149. package/dist/types-BJmgxNbF.d.mts +275 -0
  150. package/dist/{types-RLkFVgaw.d.mts → types-BNUccdcf.d.mts} +2 -2
  151. package/dist/{types-Beqn1Un7.mjs → types-C6TQjtdi.mjs} +30 -2
  152. package/dist/{types-tKwaViYB.d.mts → types-Dt0-AI6E.d.mts} +68 -27
  153. package/dist/{types-DelU6kln.mjs → types-ZUu_h0jp.mjs} +1 -2
  154. package/dist/utils/index.d.mts +254 -351
  155. package/dist/utils/index.mjs +7 -6
  156. package/dist/utils-Dc0WhlIl.mjs +594 -0
  157. package/dist/versioning-BzfeHmhj.mjs +37 -0
  158. package/package.json +44 -10
  159. package/skills/arc/SKILL.md +518 -0
  160. package/skills/arc/references/auth.md +250 -0
  161. package/skills/arc/references/events.md +272 -0
  162. package/skills/arc/references/integrations.md +385 -0
  163. package/skills/arc/references/mcp.md +431 -0
  164. package/skills/arc/references/production.md +610 -0
  165. package/skills/arc/references/testing.md +183 -0
  166. package/dist/audited-CGdLiSlE.mjs +0 -140
  167. package/dist/chunk-C7Uep-_p.mjs +0 -20
  168. package/dist/circuitBreaker-CSS2VvL6.mjs +0 -1109
  169. package/dist/errorHandler-CW3OOeYq.d.mts +0 -72
  170. package/dist/interface-BtdYtQUA.d.mts +0 -1114
  171. package/dist/presets-BTeYbw7h.d.mts +0 -57
  172. package/dist/presets-CeFtfDR8.mjs +0 -119
  173. /package/dist/{errors-DAWRdiYP.d.mts → errors-CPpvPHT0.d.mts} +0 -0
  174. /package/dist/{externalPaths-SyPF2tgK.d.mts → externalPaths-DpO-s7r8.d.mts} +0 -0
  175. /package/dist/{interface-DTbsvIWe.d.mts → interface-D_BWALyZ.d.mts} +0 -0
@@ -1,5 +1,4 @@
1
- import { a as getTeamId, c as isElevated, i as getOrgRoles, l as isMember, n as PUBLIC_SCOPE, o as hasOrgAccess, r as getOrgId, s as isAuthenticated, t as AUTHENTICATED_SCOPE } from "../types-Beqn1Un7.mjs";
2
-
1
+ import { a as getTeamId, c as hasOrgAccess, d as isMember, i as getOrgRoles, l as isAuthenticated, n as PUBLIC_SCOPE, r as getOrgId, t as AUTHENTICATED_SCOPE, u as isElevated } from "../types-C6TQjtdi.mjs";
3
2
  //#region src/types/index.ts
4
3
  /**
5
4
  * Extract user ID from a user object (supports both id and _id)
@@ -9,6 +8,5 @@ function getUserId(user) {
9
8
  const id = user.id ?? user._id;
10
9
  return id ? String(id) : void 0;
11
10
  }
12
-
13
11
  //#endregion
14
- export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, getOrgId, getOrgRoles, getTeamId, getUserId, hasOrgAccess, isAuthenticated, isElevated, isMember };
12
+ export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, getOrgId, getOrgRoles, getTeamId, getUserId, hasOrgAccess, isAuthenticated, isElevated, isMember };
@@ -0,0 +1,275 @@
1
+ import { Lt as ResourceDefinition } from "./interface-DGmPxakH.mjs";
2
+ import { z } from "zod";
3
+
4
+ //#region src/integrations/mcp/types.d.ts
5
+ /** Behavioral hints for MCP clients (tool annotations per MCP spec) */
6
+ interface ToolAnnotations {
7
+ /** Tool only reads data, no side effects */
8
+ readOnlyHint?: boolean;
9
+ /** Tool may perform destructive/irreversible actions */
10
+ destructiveHint?: boolean;
11
+ /** Tool can be safely retried with same input */
12
+ idempotentHint?: boolean;
13
+ /** Tool interacts with external systems beyond this server */
14
+ openWorldHint?: boolean;
15
+ }
16
+ /** Context passed to tool handlers at invocation time */
17
+ interface ToolContext {
18
+ /** Session identity — null in no-auth mode */
19
+ session: McpAuthResult | null;
20
+ /** Log to MCP client (best-effort, non-blocking) */
21
+ log: (level: "info" | "warning" | "error" | "debug", message: string) => Promise<void>;
22
+ /** Raw MCP SDK extra context (for advanced use) */
23
+ extra: Record<string, unknown>;
24
+ }
25
+ /**
26
+ * MCP CallToolResult — return type from tool handlers.
27
+ *
28
+ * `content` follows the MCP spec: at minimum a text item, optionally other
29
+ * resource/image content types. The fallback union member allows future
30
+ * MCP spec additions without breaking existing handlers.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * return { content: [{ type: 'text', text: JSON.stringify(result) }] };
35
+ * return { content: [{ type: 'text', text: 'Not found' }], isError: true };
36
+ * ```
37
+ */
38
+ interface CallToolResult {
39
+ content: Array<{
40
+ type: "text";
41
+ text: string;
42
+ } | {
43
+ type: "resource";
44
+ resource: {
45
+ uri: string;
46
+ mimeType?: string;
47
+ text?: string;
48
+ };
49
+ } | {
50
+ type: string;
51
+ [key: string]: unknown;
52
+ }>;
53
+ isError?: boolean;
54
+ /** Structured output for typed tool results (MCP spec extension) */
55
+ structuredContent?: unknown;
56
+ }
57
+ /**
58
+ * Output of defineTool() — plain data, not yet registered on a server.
59
+ *
60
+ * `inputSchema` is a flat Zod shape `{ name: z.string(), age: z.number() }` —
61
+ * the SDK wraps it in z.object() internally. Do NOT pass z.object() here.
62
+ */
63
+ interface ToolDefinition {
64
+ name: string;
65
+ description: string;
66
+ title?: string;
67
+ /** Flat Zod shape: `{ field: z.string() }`. NOT z.object(). */
68
+ inputSchema?: Record<string, z.ZodTypeAny>;
69
+ /** Flat Zod shape for structured output validation */
70
+ outputSchema?: Record<string, z.ZodTypeAny>;
71
+ annotations?: ToolAnnotations;
72
+ handler: (input: Record<string, unknown>, ctx: ToolContext) => Promise<CallToolResult>;
73
+ }
74
+ /** Output of definePrompt() — plain data, not yet registered */
75
+ interface PromptDefinition {
76
+ name: string;
77
+ description: string;
78
+ title?: string;
79
+ /** Flat Zod shape for prompt arguments */
80
+ argsSchema?: Record<string, z.ZodTypeAny>;
81
+ handler: (args: Record<string, unknown>) => PromptResult;
82
+ }
83
+ /** Prompt handler return type */
84
+ interface PromptResult {
85
+ messages: Array<{
86
+ role: "user" | "assistant";
87
+ content: {
88
+ type: "text";
89
+ text: string;
90
+ } | {
91
+ type: string;
92
+ [key: string]: unknown;
93
+ };
94
+ }>;
95
+ }
96
+ /** Per-resource MCP configuration overrides */
97
+ interface McpResourceConfig {
98
+ /** Which CRUD operations to expose (default: all enabled on the resource) */
99
+ operations?: CrudOperation[];
100
+ /** Override tool descriptions per operation */
101
+ descriptions?: Partial<Record<CrudOperation, string>>;
102
+ /** Fields to hide from MCP tool schemas (beyond schemaOptions.hiddenFields) */
103
+ hideFields?: string[];
104
+ /** Per-operation tool name overrides: `{ get: 'get_job_by_id' }` */
105
+ names?: Partial<Record<CrudOperation, string>>;
106
+ /** Per-resource tool name prefix (overrides global `toolNamePrefix`): `'db'` → `db_list_jobs` */
107
+ toolNamePrefix?: string;
108
+ }
109
+ /**
110
+ * Auth resolver function — user provides their own auth logic.
111
+ * Receives request headers, returns identity or null (unauthorized).
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * // API key auth
116
+ * auth: async (headers) => {
117
+ * if (headers['x-api-key'] !== process.env.MCP_API_KEY) return null;
118
+ * return { userId: 'service-account', organizationId: 'org-123' };
119
+ * },
120
+ *
121
+ * // Static org (trusted internal network)
122
+ * auth: async () => ({ userId: 'internal', organizationId: 'org-main' }),
123
+ *
124
+ * // Gateway-validated JWT (token already verified by API gateway)
125
+ * auth: async (headers) => {
126
+ * const userId = headers['x-user-id'];
127
+ * const orgId = headers['x-org-id'];
128
+ * return userId ? { userId, organizationId: orgId } : null;
129
+ * },
130
+ * ```
131
+ */
132
+ type McpAuthResolver = (headers: Record<string, string | undefined>) => Promise<McpAuthResult | null> | McpAuthResult | null;
133
+ /**
134
+ * mcpPlugin() options — Fastify plugin config.
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * // No auth (dev/testing)
139
+ * await app.register(mcpPlugin, { resources, auth: false });
140
+ *
141
+ * // Better Auth OAuth 2.1
142
+ * await app.register(mcpPlugin, { resources, auth: getAuth() });
143
+ *
144
+ * // Custom auth function (API key, gateway headers, etc.)
145
+ * await app.register(mcpPlugin, {
146
+ * resources,
147
+ * auth: async (headers) => {
148
+ * if (headers['x-api-key'] !== process.env.MCP_KEY) return null;
149
+ * return { userId: 'bot', organizationId: 'org-1' };
150
+ * },
151
+ * });
152
+ * ```
153
+ */
154
+ interface McpPluginOptions {
155
+ /** Arc resources to expose as MCP tools */
156
+ resources: ResourceDefinition[];
157
+ /**
158
+ * Auth mode:
159
+ * - `false` — no auth, anonymous access (default)
160
+ * - `BetterAuthHandler` — OAuth 2.1 via Better Auth's mcp() plugin
161
+ * - `McpAuthResolver` — custom function that resolves identity from headers
162
+ */
163
+ auth?: BetterAuthHandler | McpAuthResolver | false;
164
+ /** MCP endpoint path (default: '/mcp') */
165
+ prefix?: string;
166
+ /** Server identity */
167
+ serverName?: string;
168
+ serverVersion?: string;
169
+ /** Instructions for the LLM — guidance on tool usage, constraints */
170
+ instructions?: string;
171
+ /** Resources to exclude by name (ignored if `include` is set) */
172
+ exclude?: string[];
173
+ /** Resources to include by name — only these get MCP tools. Takes priority over `exclude`. */
174
+ include?: string[];
175
+ /** Tool name prefix: 'crm' → 'crm_list_products' */
176
+ toolNamePrefix?: string;
177
+ /** Per-resource overrides (operations, descriptions, hideFields, names, toolNamePrefix) */
178
+ overrides?: Record<string, McpResourceConfig>;
179
+ /** Hand-written tools added alongside auto-generated ones */
180
+ extraTools?: ToolDefinition[];
181
+ /** Custom prompts */
182
+ extraPrompts?: PromptDefinition[];
183
+ /**
184
+ * Session mode:
185
+ * - `false` (default) — stateless, fresh server per request. Best for production, scaling, serverless.
186
+ * - `true` — stateful, sessions cached with TTL. Use for server-initiated notifications or long-lived connections.
187
+ */
188
+ stateful?: boolean;
189
+ /** Session TTL in ms (default: 1800000 = 30 min). Only used when stateful: true. */
190
+ sessionTtlMs?: number;
191
+ /** Max concurrent sessions (default: 1000). Only used when stateful: true. */
192
+ maxSessions?: number;
193
+ /**
194
+ * Auth cache TTL in ms for stateless mode (default: 5000 = 5 sec).
195
+ * Caches auth resolver results briefly to avoid redundant DB lookups
196
+ * across initialize → tools/list → tools/call sequences.
197
+ * Set to `0` to disable.
198
+ */
199
+ authCacheTtlMs?: number;
200
+ }
201
+ /** Minimal Better Auth handler interface for MCP session validation */
202
+ interface BetterAuthHandler {
203
+ api: {
204
+ getMcpSession: (opts: {
205
+ headers: Record<string, string | undefined>;
206
+ }) => Promise<McpSession | null>;
207
+ };
208
+ handler: (request: Request) => Promise<Response>;
209
+ }
210
+ /** Session from Better Auth's getMcpSession() */
211
+ interface McpSession {
212
+ userId: string;
213
+ clientId: string;
214
+ scopes: string;
215
+ activeOrganizationId?: string;
216
+ accessToken: string;
217
+ accessTokenExpiresAt: string;
218
+ refreshToken: string;
219
+ refreshTokenExpiresAt: string;
220
+ }
221
+ /** Resolved auth identity for a single MCP request */
222
+ interface McpAuthResult {
223
+ userId: string;
224
+ organizationId?: string;
225
+ /** User roles (global) — used by guard helpers like requireRole() */
226
+ roles?: string[];
227
+ /** Org-level roles — used by guard helpers */
228
+ orgRoles?: string[];
229
+ /** Any extra metadata from the auth resolver */
230
+ [key: string]: unknown;
231
+ }
232
+ /** Internal session entry */
233
+ interface SessionEntry {
234
+ transport: {
235
+ handleRequest: (req: unknown, res: unknown, body?: unknown) => Promise<void>;
236
+ close: () => void;
237
+ };
238
+ lastAccessed: number;
239
+ organizationId: string;
240
+ auth: McpAuthResult | null;
241
+ /** Mutable ref updated per-request — tool handler closures read from this */
242
+ authRef: {
243
+ current: McpAuthResult | null;
244
+ };
245
+ }
246
+ /**
247
+ * Configuration for createMcpServer() — Level 2 declarative factory.
248
+ *
249
+ * @example
250
+ * ```ts
251
+ * const server = await createMcpServer({
252
+ * name: 'my-api',
253
+ * version: '1.0.0',
254
+ * instructions: 'Use list_users to browse users.',
255
+ * tools: [
256
+ * defineTool('greet', {
257
+ * description: 'Say hello',
258
+ * input: { name: z.string() },
259
+ * handler: async ({ name }) => ({ content: [{ type: 'text', text: `Hello ${name}` }] }),
260
+ * }),
261
+ * ],
262
+ * });
263
+ * ```
264
+ */
265
+ interface CreateMcpServerConfig {
266
+ name: string;
267
+ version?: string;
268
+ instructions?: string;
269
+ tools?: ToolDefinition[];
270
+ prompts?: PromptDefinition[];
271
+ }
272
+ /** CRUD operation type */
273
+ type CrudOperation = "list" | "get" | "create" | "update" | "delete";
274
+ //#endregion
275
+ export { McpAuthResolver as a, McpResourceConfig as c, SessionEntry as d, ToolAnnotations as f, CrudOperation as i, PromptDefinition as l, ToolDefinition as m, CallToolResult as n, McpAuthResult as o, ToolContext as p, CreateMcpServerConfig as r, McpPluginOptions as s, BetterAuthHandler as t, PromptResult as u };
@@ -29,7 +29,7 @@ declare function getUserRoles(user: UserBase | null | undefined): string[];
29
29
  /**
30
30
  * Context passed to permission check functions
31
31
  */
32
- interface PermissionContext<TDoc = any> {
32
+ interface PermissionContext<TDoc = Record<string, unknown>> {
33
33
  /** Authenticated user or null if unauthenticated */
34
34
  user: UserBase | null;
35
35
  /** Fastify request object */
@@ -80,7 +80,7 @@ interface PermissionResult {
80
80
  * };
81
81
  * ```
82
82
  */
83
- type PermissionCheck<TDoc = any> = ((context: PermissionContext<TDoc>) => boolean | PermissionResult | Promise<boolean | PermissionResult>) & PermissionCheckMeta;
83
+ type PermissionCheck<TDoc = Record<string, unknown>> = ((context: PermissionContext<TDoc>) => boolean | PermissionResult | Promise<boolean | PermissionResult>) & PermissionCheckMeta;
84
84
  /**
85
85
  * Optional metadata attached to permission check functions.
86
86
  * Used for OpenAPI docs, introspection, and route-level auth decisions.
@@ -29,10 +29,38 @@ function getOrgRoles(scope) {
29
29
  function getTeamId(scope) {
30
30
  if (scope.kind === "member") return scope.teamId;
31
31
  }
32
+ /**
33
+ * Get userId from scope (available on authenticated, member, elevated).
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * import { getUserId } from '@classytic/arc/scope';
38
+ * const userId = getUserId(request.scope);
39
+ * ```
40
+ */
41
+ function getUserId(scope) {
42
+ if (scope.kind === "public") return void 0;
43
+ return scope.userId;
44
+ }
45
+ /**
46
+ * Get global user roles from scope (available on authenticated and member).
47
+ * These are user-level roles (e.g. superadmin, finance-admin) distinct from
48
+ * org-level roles (scope.orgRoles).
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * import { getUserRoles } from '@classytic/arc/scope';
53
+ * const globalRoles = getUserRoles(request.scope);
54
+ * ```
55
+ */
56
+ function getUserRoles(scope) {
57
+ if (scope.kind === "authenticated") return scope.userRoles ?? [];
58
+ if (scope.kind === "member") return scope.userRoles;
59
+ return [];
60
+ }
32
61
  /** Default public scope — used as initial decoration value */
33
62
  const PUBLIC_SCOPE = Object.freeze({ kind: "public" });
34
63
  /** Default authenticated scope — used when user is logged in but no org */
35
64
  const AUTHENTICATED_SCOPE = Object.freeze({ kind: "authenticated" });
36
-
37
65
  //#endregion
38
- export { getTeamId as a, isElevated as c, getOrgRoles as i, isMember as l, PUBLIC_SCOPE as n, hasOrgAccess as o, getOrgId as r, isAuthenticated as s, AUTHENTICATED_SCOPE as t };
66
+ export { getTeamId as a, hasOrgAccess as c, isMember as d, getOrgRoles as i, isAuthenticated as l, PUBLIC_SCOPE as n, getUserId as o, getOrgId as r, getUserRoles as s, AUTHENTICATED_SCOPE as t, isElevated as u };
@@ -1,18 +1,26 @@
1
- import { n as ElevationOptions } from "./elevation-DGo5shaX.mjs";
2
- import { Authenticator } from "./types/index.mjs";
3
- import { t as ExternalOpenApiPaths } from "./externalPaths-SyPF2tgK.mjs";
4
- import { i as CacheStore } from "./interface-DTbsvIWe.mjs";
5
- import { r as QueryCachePluginOptions } from "./queryCachePlugin-Q6SYuHZ6.mjs";
6
- import { i as EventTransport } from "./EventTransport-BkUDYZEb.mjs";
7
- import { t as EventPluginOptions } from "./eventPlugin-H6wDDjGO.mjs";
8
- import { o as CachingOptions, r as SSEOptions, t as ErrorHandlerOptions } from "./errorHandler-CW3OOeYq.mjs";
9
- import { r as IdempotencyStore } from "./interface-CSNjltAc.mjs";
1
+ import { n as ElevationOptions } from "./elevation-Ca_yveIO.mjs";
2
+ import { h as Authenticator } from "./interface-DGmPxakH.mjs";
3
+ import { t as ExternalOpenApiPaths } from "./externalPaths-DpO-s7r8.mjs";
4
+ import { i as CacheStore } from "./interface-D_BWALyZ.mjs";
5
+ import { r as QueryCachePluginOptions } from "./queryCachePlugin-DcmETvcB.mjs";
6
+ import { i as EventTransport } from "./EventTransport-wc5hSLik.mjs";
7
+ import { t as EventPluginOptions } from "./eventPlugin-iGrSEmwJ.mjs";
8
+ import { c as MetricsOptions, d as SSEOptions, m as CachingOptions, r as VersioningOptions, t as ErrorHandlerOptions } from "./errorHandler-Do4vVQ1f.mjs";
9
+ import { r as IdempotencyStore } from "./interface-B4awm1RJ.mjs";
10
10
  import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from "fastify";
11
- import { FastifyCorsOptions } from "@fastify/cors";
12
- import { FastifyHelmetOptions } from "@fastify/helmet";
13
- import { RateLimitOptions } from "@fastify/rate-limit";
14
11
 
15
12
  //#region src/factory/types.d.ts
13
+ type CorsOptions = Record<string, unknown> & {
14
+ origin?: unknown;
15
+ credentials?: boolean;
16
+ methods?: string[];
17
+ allowedHeaders?: string[];
18
+ };
19
+ type HelmetOptions = Record<string, unknown>;
20
+ type RateLimitOpts = Record<string, unknown> & {
21
+ max?: number;
22
+ timeWindow?: string | number;
23
+ };
16
24
  /**
17
25
  * Arc's built-in JWT auth
18
26
  *
@@ -44,7 +52,7 @@ import { RateLimitOptions } from "@fastify/rate-limit";
44
52
  * ```
45
53
  */
46
54
  interface JwtAuthOption {
47
- type: 'jwt';
55
+ type: "jwt";
48
56
  /**
49
57
  * JWT configuration (optional but recommended)
50
58
  * If provided, jwt utilities are available in authenticator context
@@ -82,6 +90,18 @@ interface JwtAuthOption {
82
90
  * Property name to store user on request (default: 'user')
83
91
  */
84
92
  userProperty?: string;
93
+ /**
94
+ * Token revocation check — called after JWT verification.
95
+ * Return `true` to reject the token (fail-closed: errors also reject).
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * isRevoked: async (decoded) => {
100
+ * return revokedTokens.has(decoded.jti as string);
101
+ * },
102
+ * ```
103
+ */
104
+ isRevoked?: (decoded: Record<string, unknown>) => boolean | Promise<boolean>;
85
105
  }
86
106
  /**
87
107
  * Better Auth adapter integration
@@ -100,7 +120,7 @@ interface JwtAuthOption {
100
120
  * ```
101
121
  */
102
122
  interface BetterAuthOption {
103
- type: 'betterAuth';
123
+ type: "betterAuth";
104
124
  /** Better Auth adapter — pass the result of createBetterAuthAdapter() */
105
125
  betterAuth: {
106
126
  plugin: FastifyPluginAsync;
@@ -126,7 +146,7 @@ interface BetterAuthOption {
126
146
  * ```
127
147
  */
128
148
  interface CustomPluginAuthOption {
129
- type: 'custom';
149
+ type: "custom";
130
150
  /** Custom Fastify plugin that sets up authentication */
131
151
  plugin: FastifyPluginAsync;
132
152
  }
@@ -151,7 +171,7 @@ interface CustomPluginAuthOption {
151
171
  * ```
152
172
  */
153
173
  interface CustomAuthenticatorOption {
154
- type: 'authenticator';
174
+ type: "authenticator";
155
175
  /** Authenticate function — decorates fastify.authenticate directly */
156
176
  authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
157
177
  /**
@@ -215,14 +235,17 @@ type AuthOption = false | JwtAuthOption | BetterAuthOption | CustomPluginAuthOpt
215
235
  */
216
236
  interface CreateAppOptions {
217
237
  /** Environment preset: 'production', 'development', 'testing', or 'edge' */
218
- preset?: 'production' | 'development' | 'testing' | 'edge';
238
+ preset?: "production" | "development" | "testing" | "edge";
219
239
  /**
220
240
  * Runtime profile for store backends.
221
241
  * - 'memory' (default): Uses in-memory stores. Suitable for single-instance deployments.
222
- * - 'distributed': Requires Redis-compatible adapters for cache, events, idempotency.
223
- * Startup fails fast if any required distributed adapter is missing.
242
+ * - 'distributed': Requires durable adapters for events, and for any enabled
243
+ * shared subsystems such as caching/queryCache/rate limiting.
244
+ * Idempotency remains per-resource opt-in: memory-backed stores are rejected,
245
+ * while a missing idempotency store emits a startup warning because dedupe
246
+ * would be instance-local.
224
247
  */
225
- runtime?: 'memory' | 'distributed';
248
+ runtime?: "memory" | "distributed";
226
249
  /**
227
250
  * Store and transport instances for runtime profile validation.
228
251
  * When `runtime` is `'distributed'`, Arc validates that these are
@@ -235,7 +258,7 @@ interface CreateAppOptions {
235
258
  queryCache?: CacheStore;
236
259
  };
237
260
  /** Fastify logger configuration */
238
- logger?: FastifyServerOptions['logger'];
261
+ logger?: FastifyServerOptions["logger"];
239
262
  /**
240
263
  * Enable Arc debug logging.
241
264
  *
@@ -332,11 +355,11 @@ interface CreateAppOptions {
332
355
  */
333
356
  elevation?: ElevationOptions | false;
334
357
  /** Helmet security headers. Set to false to disable. */
335
- helmet?: FastifyHelmetOptions | false;
358
+ helmet?: HelmetOptions | false;
336
359
  /** CORS configuration. Set to false to disable. */
337
- cors?: FastifyCorsOptions | false;
360
+ cors?: CorsOptions | false;
338
361
  /** Rate limiting. Set to false to disable. */
339
- rateLimit?: RateLimitOptions | false;
362
+ rateLimit?: RateLimitOpts | false;
340
363
  /** Under pressure health monitoring. Set to false to disable. */
341
364
  underPressure?: UnderPressureOptions | false;
342
365
  /** @fastify/sensible (HTTP helpers). Set to false to disable. */
@@ -378,7 +401,7 @@ interface CreateAppOptions {
378
401
  * });
379
402
  * ```
380
403
  */
381
- events?: Omit<EventPluginOptions, 'transport'> | boolean;
404
+ events?: Omit<EventPluginOptions, "transport"> | boolean;
382
405
  /**
383
406
  * Caching headers (ETag + Cache-Control). Default: false (opt-in).
384
407
  * Set to true for defaults, or pass CachingOptions for fine control.
@@ -396,6 +419,16 @@ interface CreateAppOptions {
396
419
  * Requires per-resource `cache` config on defineResource().
397
420
  */
398
421
  queryCache?: QueryCachePluginOptions | boolean;
422
+ /**
423
+ * Metrics endpoint (Prometheus-compatible). Default: false (opt-in).
424
+ * Set to true for defaults (/_metrics), or pass MetricsOptions for custom path/prefix.
425
+ */
426
+ metrics?: MetricsOptions | boolean;
427
+ /**
428
+ * API versioning (header or prefix-based). Default: false (opt-in).
429
+ * Pass VersioningOptions to enable.
430
+ */
431
+ versioning?: VersioningOptions;
399
432
  };
400
433
  /**
401
434
  * Type provider for schema inference.
@@ -416,7 +449,7 @@ interface CreateAppOptions {
416
449
  * // Now route schemas built with Type.* give full TS inference
417
450
  * ```
418
451
  */
419
- typeProvider?: 'typebox';
452
+ typeProvider?: "typebox";
420
453
  /**
421
454
  * Error handler plugin. Normalizes AJV, Mongoose, and ArcError responses
422
455
  * into a consistent JSON envelope. Enabled by default.
@@ -448,21 +481,29 @@ interface CreateAppOptions {
448
481
  onClose?: (fastify: FastifyInstance) => void | Promise<void>;
449
482
  }
450
483
  interface UnderPressureOptions {
484
+ /** Expose `/_status` route for health checks (default: false) */
451
485
  exposeStatusRoute?: boolean;
486
+ /** Event loop lag threshold in ms — requests rejected above this (default: 1000) */
452
487
  maxEventLoopDelay?: number;
488
+ /** V8 heap usage threshold in bytes — requests rejected above this */
453
489
  maxHeapUsedBytes?: number;
490
+ /** RSS memory threshold in bytes — requests rejected above this */
454
491
  maxRssBytes?: number;
455
492
  }
456
493
  interface MultipartOptions {
457
494
  limits?: {
458
- fileSize?: number;
495
+ /** Max file size in bytes (default: Fastify default ~1MB) */fileSize?: number; /** Max number of files per request */
459
496
  files?: number;
460
497
  };
461
498
  }
462
499
  interface RawBodyOptions {
500
+ /** Body field name to store raw body on (default: 'rawBody') */
463
501
  field?: string;
502
+ /** Apply to all routes globally (default: false) */
464
503
  global?: boolean;
504
+ /** Encoding for raw body string (default: 'utf8') */
465
505
  encoding?: string;
506
+ /** Parse raw body before other parsers (default: false) */
466
507
  runFirst?: boolean;
467
508
  }
468
509
  //#endregion
@@ -20,6 +20,5 @@ function getUserRoles(user) {
20
20
  if (!user) return [];
21
21
  return normalizeRoles(user.role);
22
22
  }
23
-
24
23
  //#endregion
25
- export { normalizeRoles as n, getUserRoles as t };
24
+ export { normalizeRoles as n, getUserRoles as t };