@classytic/arc 2.3.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) 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.mjs +3 -7
  99. package/dist/{mongodb-DNKEExbf.mjs → mongodb-BuQ7fNTg.mjs} +1 -4
  100. package/dist/{mongodb-ClykrfGo.d.mts → mongodb-CUpYfxfD.d.mts} +2 -3
  101. package/dist/{mongodb-Dg8O_gvd.d.mts → mongodb-bga9AbkD.d.mts} +2 -2
  102. package/dist/{openapi-9nB_kiuR.mjs → openapi-CBmZ6EQN.mjs} +4 -21
  103. package/dist/org/index.d.mts +12 -14
  104. package/dist/org/index.mjs +92 -119
  105. package/dist/org/types.d.mts +2 -2
  106. package/dist/org/types.mjs +1 -1
  107. package/dist/permissions/index.d.mts +4 -278
  108. package/dist/permissions/index.mjs +4 -579
  109. package/dist/permissions-CA5zg0yK.mjs +751 -0
  110. package/dist/plugins/index.d.mts +104 -107
  111. package/dist/plugins/index.mjs +203 -313
  112. package/dist/plugins/response-cache.mjs +4 -69
  113. package/dist/plugins/tracing-entry.d.mts +1 -1
  114. package/dist/plugins/tracing-entry.mjs +24 -11
  115. package/dist/{pluralize-CM-jZg7p.mjs → pluralize-CcT6qF0a.mjs} +12 -13
  116. package/dist/policies/index.d.mts +2 -2
  117. package/dist/policies/index.mjs +80 -83
  118. package/dist/presets/index.d.mts +26 -19
  119. package/dist/presets/index.mjs +2 -142
  120. package/dist/presets/multiTenant.d.mts +1 -4
  121. package/dist/presets/multiTenant.mjs +4 -6
  122. package/dist/presets-C9QXJV1u.mjs +422 -0
  123. package/dist/{queryCachePlugin-B6R0d4av.mjs → queryCachePlugin-ClosZdNS.mjs} +6 -27
  124. package/dist/{queryCachePlugin-Q6SYuHZ6.d.mts → queryCachePlugin-DcmETvcB.d.mts} +3 -3
  125. package/dist/queryParser-CgCtsjti.mjs +352 -0
  126. package/dist/{redis-UwjEp8Ea.d.mts → redis-CQ5YxMC5.d.mts} +2 -2
  127. package/dist/{redis-stream-CBg0upHI.d.mts → redis-stream-BW9UKLZM.d.mts} +9 -2
  128. package/dist/registry/index.d.mts +1 -4
  129. package/dist/registry/index.mjs +3 -4
  130. package/dist/{introspectionPlugin-B3JkrjwU.mjs → registry-I-ogLgL9.mjs} +1 -8
  131. package/dist/{requestContext-xi6OKBL-.mjs → requestContext-DYtmNpm5.mjs} +1 -3
  132. package/dist/resourceToTools-B6ZN9Ing.mjs +489 -0
  133. package/dist/rpc/index.d.mts +90 -0
  134. package/dist/rpc/index.mjs +248 -0
  135. package/dist/{schemaConverter-Dtg0Kt9T.mjs → schemaConverter-DjzHpFam.mjs} +1 -2
  136. package/dist/schemas/index.d.mts +30 -30
  137. package/dist/schemas/index.mjs +2 -4
  138. package/dist/scope/index.d.mts +13 -2
  139. package/dist/scope/index.mjs +18 -5
  140. package/dist/{sessionManager-D_iEHjQl.d.mts → sessionManager-wbkYj2HL.d.mts} +2 -2
  141. package/dist/{sse-DkqQ1uxb.mjs → sse-BkViJPlT.mjs} +4 -25
  142. package/dist/testing/index.d.mts +551 -567
  143. package/dist/testing/index.mjs +1744 -1799
  144. package/dist/{tracing-8CEbhF0w.d.mts → tracing-bz_U4EM1.d.mts} +6 -1
  145. package/dist/{typeGuards-DwxA1t_L.mjs → typeGuards-Cj5Rgvlg.mjs} +1 -2
  146. package/dist/types/index.d.mts +4 -946
  147. package/dist/types/index.mjs +2 -4
  148. package/dist/types-BJmgxNbF.d.mts +275 -0
  149. package/dist/{types-RLkFVgaw.d.mts → types-BNUccdcf.d.mts} +2 -2
  150. package/dist/{types-Beqn1Un7.mjs → types-C6TQjtdi.mjs} +30 -2
  151. package/dist/{types-tKwaViYB.d.mts → types-Dt0-AI6E.d.mts} +68 -27
  152. package/dist/{types-DelU6kln.mjs → types-ZUu_h0jp.mjs} +1 -2
  153. package/dist/utils/index.d.mts +254 -351
  154. package/dist/utils/index.mjs +7 -6
  155. package/dist/utils-Dc0WhlIl.mjs +594 -0
  156. package/dist/versioning-BzfeHmhj.mjs +37 -0
  157. package/package.json +44 -10
  158. package/skills/arc/SKILL.md +506 -0
  159. package/skills/arc/references/auth.md +250 -0
  160. package/skills/arc/references/events.md +272 -0
  161. package/skills/arc/references/integrations.md +385 -0
  162. package/skills/arc/references/mcp.md +386 -0
  163. package/skills/arc/references/production.md +610 -0
  164. package/skills/arc/references/testing.md +183 -0
  165. package/dist/audited-CGdLiSlE.mjs +0 -140
  166. package/dist/chunk-C7Uep-_p.mjs +0 -20
  167. package/dist/circuitBreaker-CSS2VvL6.mjs +0 -1109
  168. package/dist/errorHandler-CW3OOeYq.d.mts +0 -72
  169. package/dist/interface-BtdYtQUA.d.mts +0 -1114
  170. package/dist/presets-BTeYbw7h.d.mts +0 -57
  171. package/dist/presets-CeFtfDR8.mjs +0 -119
  172. /package/dist/{errors-DAWRdiYP.d.mts → errors-CPpvPHT0.d.mts} +0 -0
  173. /package/dist/{externalPaths-SyPF2tgK.d.mts → externalPaths-DpO-s7r8.d.mts} +0 -0
  174. /package/dist/{interface-DTbsvIWe.d.mts → interface-D_BWALyZ.d.mts} +0 -0
package/README.md CHANGED
@@ -81,6 +81,23 @@ auth: false
81
81
 
82
82
  **Decorates:** `app.authenticate`, `app.optionalAuthenticate`, `app.authorize`
83
83
 
84
+ ### Token Revocation
85
+
86
+ Arc provides the `isRevoked` primitive — you implement the store (Redis, DB, Better Auth):
87
+
88
+ ```typescript
89
+ auth: {
90
+ type: 'jwt',
91
+ jwt: { secret: process.env.JWT_SECRET },
92
+ isRevoked: async (decoded) => {
93
+ // Redis set, DB lookup, or any async check
94
+ return await redis.sismember('revoked-tokens', decoded.jti ?? decoded.id);
95
+ },
96
+ }
97
+ ```
98
+
99
+ Fail-closed: if the revocation check throws, the token is rejected.
100
+
84
101
  ## Permissions
85
102
 
86
103
  Function-based, composable:
@@ -221,6 +238,114 @@ await app.events.subscribe('order.*', async (event) => { ... });
221
238
 
222
239
  CRUD events (`product.created`, `product.updated`, `product.deleted`) emit automatically.
223
240
 
241
+ ### defineEvent — Typed Events with Schema Validation
242
+
243
+ Declare events with schemas for runtime validation and introspection:
244
+
245
+ ```typescript
246
+ import { defineEvent, createEventRegistry } from '@classytic/arc/events';
247
+
248
+ // Define typed events
249
+ const OrderCreated = defineEvent({
250
+ name: 'order.created',
251
+ version: 1,
252
+ description: 'Emitted when an order is placed',
253
+ schema: {
254
+ type: 'object',
255
+ properties: {
256
+ orderId: { type: 'string' },
257
+ total: { type: 'number' },
258
+ currency: { type: 'string' },
259
+ },
260
+ required: ['orderId', 'total'],
261
+ },
262
+ });
263
+
264
+ // Type-safe event creation
265
+ const event = OrderCreated.create({ orderId: 'o-1', total: 100 }, { userId: 'user-1' });
266
+ await app.events.publish(event.type, event.payload, event.meta);
267
+ ```
268
+
269
+ **Event Registry** — catalog + auto-validation on publish:
270
+
271
+ ```typescript
272
+ const registry = createEventRegistry();
273
+ registry.register(OrderCreated);
274
+ registry.register(OrderShipped);
275
+
276
+ // Wire into eventPlugin — validates payloads on publish
277
+ const app = await createApp({
278
+ arcPlugins: {
279
+ events: { registry, validateMode: 'warn' },
280
+ // 'warn' (default): log warning, still publish
281
+ // 'reject': throw error, do NOT publish
282
+ // 'off': registry is introspection-only
283
+ },
284
+ });
285
+
286
+ // Introspect at runtime
287
+ app.events.registry?.catalog();
288
+ // → [{ name: 'order.created', version: 1, schema: {...} }, ...]
289
+ ```
290
+
291
+ Export the registry alongside resources for `arc describe` to auto-detect:
292
+
293
+ ```typescript
294
+ // src/events.ts
295
+ export const eventRegistry = createEventRegistry();
296
+ eventRegistry.register(OrderCreated);
297
+ eventRegistry.register(OrderShipped);
298
+ ```
299
+
300
+ ### Event Transports
301
+
302
+ | Transport | Import | Use Case |
303
+ |-----------|--------|----------|
304
+ | `MemoryEventTransport` | `@classytic/arc/events` | Development, testing, single-instance |
305
+ | `RedisEventTransport` | `@classytic/arc/events/redis` | Multi-instance pub/sub (fan-out) |
306
+ | `RedisStreamTransport` | `@classytic/arc/events/redis-stream` | Ordered events with consumer groups |
307
+
308
+ ```typescript
309
+ // Redis Pub/Sub
310
+ import { RedisEventTransport } from '@classytic/arc/events/redis';
311
+ const transport = new RedisEventTransport(redis, { channel: 'arc-events' });
312
+
313
+ // Redis Streams (ordered, durable)
314
+ import { RedisStreamTransport } from '@classytic/arc/events/redis-stream';
315
+ const transport = new RedisStreamTransport(redis, { stream: 'arc-events' });
316
+ ```
317
+
318
+ **Behavioral contract:**
319
+ - **Memory**: Handlers execute sequentially (ordered, awaited)
320
+ - **Redis Pub/Sub**: Handlers fire-and-forget (unordered, fan-out)
321
+ - **Redis Streams**: Ordered delivery with consumer group acknowledgment
322
+
323
+ ### Retry & Dead Letter Queue
324
+
325
+ ```typescript
326
+ import { withRetry, createDeadLetterPublisher } from '@classytic/arc/events';
327
+
328
+ // Per-handler retry with exponential backoff
329
+ await app.events.subscribe('order.created', withRetry(
330
+ async (event) => { await sendConfirmationEmail(event.payload); },
331
+ {
332
+ maxRetries: 3,
333
+ backoffMs: 1000,
334
+ onDead: createDeadLetterPublisher(app.events), // publishes to $deadLetter channel
335
+ },
336
+ ));
337
+
338
+ // Or configure auto-retry for ALL handlers via plugin
339
+ const app = await createApp({
340
+ arcPlugins: {
341
+ events: {
342
+ retry: { maxRetries: 3, backoffMs: 1000 },
343
+ deadLetterQueue: { store: async (event, errors) => { /* custom DLQ */ } },
344
+ },
345
+ },
346
+ });
347
+ ```
348
+
224
349
  ## Factory — createApp()
225
350
 
226
351
  ```typescript
@@ -282,6 +407,7 @@ await app.register(websocketPlugin, {
282
407
  auth: true, // fail-closed: throws if authenticate not registered
283
408
  resources: ['product', 'order'],
284
409
  roomPolicy: (client, room) => ['product', 'order'].includes(room),
410
+ reauthInterval: 300000, // re-validate token every 5 min (0 = disabled)
285
411
  maxMessageBytes: 16384, // 16KB message size cap
286
412
  maxSubscriptionsPerClient: 100, // prevent resource exhaustion
287
413
  });
@@ -296,6 +422,49 @@ await app.register(eventGatewayPlugin, {
296
422
  });
297
423
  ```
298
424
 
425
+ ## Pipeline — Guards, Transforms, Interceptors
426
+
427
+ Functional composition for cross-cutting concerns:
428
+
429
+ ```typescript
430
+ import { pipe, guard, transform, intercept } from '@classytic/arc';
431
+
432
+ const isActive = guard('isActive', (ctx) => ctx.query?.filters?.isActive !== false);
433
+ const slugify = transform('slugify', (ctx) => ({ ...ctx, body: { ...ctx.body, slug: toSlug(ctx.body.name) } }));
434
+ const timing = intercept('timing', async (ctx, next) => {
435
+ const start = Date.now();
436
+ const result = await next();
437
+ console.log(`${ctx.resource}.${ctx.operation}: ${Date.now() - start}ms`);
438
+ return result;
439
+ });
440
+
441
+ defineResource({
442
+ name: 'product',
443
+ pipe: pipe(isActive, slugify, timing),
444
+ // or per-operation: pipe: { create: pipe(slugify), list: pipe(timing) }
445
+ });
446
+ ```
447
+
448
+ ## Utilities
449
+
450
+ ```typescript
451
+ // Circuit Breaker — fault tolerance for external service calls
452
+ import { createCircuitBreaker } from '@classytic/arc/utils';
453
+ const paymentBreaker = createCircuitBreaker(
454
+ async (amount) => stripe.charges.create({ amount }),
455
+ { name: 'stripe', failureThreshold: 5, resetTimeout: 30000, fallback: async () => cached },
456
+ );
457
+
458
+ // State Machine — workflow validation
459
+ import { createStateMachine } from '@classytic/arc/utils';
460
+ const orderState = createStateMachine('Order', {
461
+ approve: ['pending', 'draft'],
462
+ cancel: ['pending', 'approved'],
463
+ fulfill: { from: ['approved'], to: 'fulfilled', guard: ({ data }) => data.paid },
464
+ });
465
+ orderState.assert('approve', currentStatus); // throws if invalid transition
466
+ ```
467
+
299
468
  ## Integrations
300
469
 
301
470
  All separate subpath imports — only loaded when used:
@@ -334,6 +503,19 @@ npx @classytic/arc introspect --entry ./dist/index.js # Show resources
334
503
  npx @classytic/arc doctor # Health check
335
504
  ```
336
505
 
506
+ `arc describe` auto-detects exported `EventRegistry` and includes the event catalog in output:
507
+
508
+ ```json
509
+ {
510
+ "$schema": "arc-describe/v1",
511
+ "resources": [...],
512
+ "eventCatalog": [
513
+ { "name": "order.created", "version": 1, "hasSchema": true, "schemaFields": ["orderId", "total"], "requiredFields": ["orderId", "total"] }
514
+ ],
515
+ "stats": { "totalResources": 5, "totalRoutes": 28, "totalCatalogedEvents": 3 }
516
+ }
517
+ ```
518
+
337
519
  ## Subpath Imports
338
520
 
339
521
  | Import | Purpose |
@@ -342,12 +524,12 @@ npx @classytic/arc doctor # Health check
342
524
  | `@classytic/arc/factory` | `createApp()`, presets |
343
525
  | `@classytic/arc/cache` | `MemoryCacheStore`, `RedisCacheStore`, `QueryCache` |
344
526
  | `@classytic/arc/auth` | Auth plugin, Better Auth adapter, session manager |
345
- | `@classytic/arc/events` | Event plugin, memory transport |
346
- | `@classytic/arc/events/redis` | Redis event transport |
347
- | `@classytic/arc/events/redis-stream` | Redis Streams transport |
527
+ | `@classytic/arc/events` | Event plugin, transports, `defineEvent`, `createEventRegistry` |
528
+ | `@classytic/arc/events/redis` | Redis Pub/Sub event transport |
529
+ | `@classytic/arc/events/redis-stream` | Redis Streams event transport |
348
530
  | `@classytic/arc/plugins` | Health, graceful shutdown, request ID, SSE, caching |
349
531
  | `@classytic/arc/plugins/tracing` | OpenTelemetry |
350
- | `@classytic/arc/permissions` | All permission functions |
532
+ | `@classytic/arc/permissions` | All permission functions, role hierarchy |
351
533
  | `@classytic/arc/scope` | Request scope helpers (`isMember`, `isElevated`, `getOrgId`) |
352
534
  | `@classytic/arc/org` | Organization module |
353
535
  | `@classytic/arc/hooks` | Lifecycle hooks |
@@ -364,20 +546,7 @@ npx @classytic/arc doctor # Health check
364
546
  | `@classytic/arc/integrations/event-gateway` | Unified SSE + WebSocket gateway |
365
547
  | `@classytic/arc/integrations/streamline` | Workflow orchestration |
366
548
  | `@classytic/arc/docs` | OpenAPI generation |
367
- | `@classytic/arc/cli` | CLI commands |
368
-
369
- ## Documentation
370
-
371
- - [Setup](docs/getting-started/setup.md) — Project setup
372
- - [Core Concepts](docs/getting-started/core.md) — Resources, controllers, adapters
373
- - [Authentication](docs/getting-started/auth.md) — JWT, Better Auth, custom auth
374
- - [Permissions](docs/getting-started/permissions.md) — RBAC, ABAC, field-level
375
- - [Presets](docs/getting-started/presets.md) — softDelete, multiTenant, tree, etc.
376
- - [Organizations](docs/getting-started/org.md) — Multi-tenant SaaS
377
- - [Factory](docs/production-ops/factory.md) — createApp() and environment presets
378
- - [Events](docs/production-ops/events.md) — Domain events and transports
379
- - [Plugins](docs/production-ops/plugins.md) — Health, caching, SSE, tracing
380
- - [Hooks](docs/framework-extension/hooks.md) — Lifecycle hooks
549
+ | `@classytic/arc/cli` | CLI commands (programmatic) |
381
550
 
382
551
  ## License
383
552
 
package/bin/arc.js CHANGED
@@ -98,9 +98,10 @@ async function main() {
98
98
  process.exit(1);
99
99
  }
100
100
  } catch (err) {
101
- console.error(`Error: ${err.message}`);
101
+ const message = err instanceof Error ? err.message : String(err);
102
+ console.error(`Error: ${message}`);
102
103
  if (process.env.DEBUG) {
103
- console.error(err.stack);
104
+ console.error(err instanceof Error ? err.stack : err);
104
105
  }
105
106
  process.exit(1);
106
107
  }
@@ -119,9 +120,11 @@ async function handleInit(args) {
119
120
  async function handleGenerate(type, args) {
120
121
  if (!type) {
121
122
  console.error('Missing type argument');
122
- console.log('\nUsage: arc generate <resource|controller|model|repository|schemas> <name>');
123
+ console.log('\nUsage: arc generate <resource|controller|model|repository|schemas|mcp> <name>');
123
124
  console.log('\nExamples:');
124
125
  console.log(' arc generate resource product');
126
+ console.log(' arc generate resource product --mcp');
127
+ console.log(' arc generate mcp product');
125
128
  console.log(' arc g r invoice');
126
129
  process.exit(1);
127
130
  }
@@ -133,6 +136,7 @@ async function handleGenerate(type, args) {
133
136
  m: 'model',
134
137
  repo: 'repository',
135
138
  s: 'schemas',
139
+ mcp: 'mcp',
136
140
  resource: 'resource',
137
141
  controller: 'controller',
138
142
  model: 'model',
@@ -185,6 +189,7 @@ async function handleDoctor(rawArgs) {
185
189
  await doctor(rawArgs);
186
190
  }
187
191
 
192
+
188
193
  // ============================================================================
189
194
  // Option Parsing
190
195
  // ============================================================================
@@ -346,11 +351,14 @@ GENERATE SUBCOMMANDS
346
351
  model, m Generate model only
347
352
  repository, repo Generate repository only
348
353
  schemas, s Generate schemas only
354
+ mcp Generate MCP tools file only
349
355
 
350
356
  GENERATE NOTES
351
357
  - Auto-detects TypeScript/JavaScript from tsconfig.json
352
358
  - Files are created in src/resources/<name>/ directory
353
359
  - Uses prefixed filenames: <name>.model.ts, <name>.repository.ts, etc.
360
+ - Use --mcp flag with resource to include MCP tools file: arc g r product --mcp
361
+ - Set "mcp": true in .arcrc to always generate MCP tools
354
362
 
355
363
  EXAMPLES
356
364
  # Initialize a new project (interactive prompts)