@classytic/arc 2.6.1 → 2.6.3

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 (48) hide show
  1. package/README.md +48 -2
  2. package/dist/{BaseController-AbbRx3e0.mjs → BaseController-DzRtluEF.mjs} +88 -8
  3. package/dist/{ResourceRegistry-DeCIFlix.mjs → ResourceRegistry-C6ngvOnn.mjs} +1 -0
  4. package/dist/adapters/index.d.mts +2 -2
  5. package/dist/adapters/index.mjs +1 -1
  6. package/dist/{adapters-CTn28N4y.mjs → adapters-gM-WYjNe.mjs} +6 -4
  7. package/dist/audit/index.d.mts +31 -5
  8. package/dist/audit/index.mjs +21 -3
  9. package/dist/auth/index.d.mts +1 -1
  10. package/dist/cli/commands/docs.mjs +1 -1
  11. package/dist/cli/commands/introspect.mjs +1 -1
  12. package/dist/core/index.d.mts +2 -2
  13. package/dist/core/index.mjs +2 -2
  14. package/dist/{createApp-Bol7DLUf.mjs → createApp-D2w0LdYJ.mjs} +27 -11
  15. package/dist/{defineResource-bVKHjQzE.mjs → defineResource-wWMBB4GP.mjs} +48 -30
  16. package/dist/docs/index.d.mts +1 -1
  17. package/dist/dynamic/index.d.mts +1 -1
  18. package/dist/dynamic/index.mjs +1 -1
  19. package/dist/factory/index.d.mts +1 -1
  20. package/dist/factory/index.mjs +31 -15
  21. package/dist/hooks/index.d.mts +1 -1
  22. package/dist/{index-BIsZ_su5.d.mts → index-CHeJa4Zd.d.mts} +3 -3
  23. package/dist/{index-Cb3gtbg7.d.mts → index-gz6iuzCp.d.mts} +1 -1
  24. package/dist/index.d.mts +3 -3
  25. package/dist/index.mjs +4 -4
  26. package/dist/integrations/index.d.mts +1 -1
  27. package/dist/integrations/mcp/index.d.mts +2 -2
  28. package/dist/integrations/mcp/index.mjs +1 -1
  29. package/dist/integrations/mcp/testing.d.mts +1 -1
  30. package/dist/integrations/mcp/testing.mjs +1 -1
  31. package/dist/{interface-DDW43OmS.d.mts → interface-DYH8AXGe.d.mts} +89 -4
  32. package/dist/org/index.d.mts +1 -1
  33. package/dist/plugins/index.d.mts +1 -1
  34. package/dist/plugins/index.mjs +1 -1
  35. package/dist/plugins/tracing-entry.mjs +1 -1
  36. package/dist/presets/index.d.mts +1 -1
  37. package/dist/presets/multiTenant.d.mts +1 -1
  38. package/dist/registry/index.d.mts +1 -1
  39. package/dist/registry/index.mjs +1 -1
  40. package/dist/{resourceToTools-DH3c3e-T.mjs → resourceToTools-nCJWnG1r.mjs} +250 -13
  41. package/dist/testing/index.d.mts +26 -3
  42. package/dist/testing/index.mjs +46 -2
  43. package/dist/types/index.d.mts +1 -1
  44. package/dist/{types-D5rjsS_i.d.mts → types-B4_TDdPe.d.mts} +1 -1
  45. package/dist/{types-D5hJ-k_3.d.mts → types-By-5mIfn.d.mts} +7 -1
  46. package/dist/utils/index.d.mts +1 -1
  47. package/package.json +18 -18
  48. package/skills/arc/SKILL.md +80 -8
@@ -8,11 +8,11 @@ description: |
8
8
  Triggers: arc, fastify resource, defineResource, createApp, BaseController, arc preset,
9
9
  arc auth, arc events, arc jobs, arc websocket, arc mcp, arc plugin, arc testing, arc cli,
10
10
  arc permissions, arc hooks, arc pipeline, arc factory, arc cache, arc QueryCache.
11
- version: 2.6.0
11
+ version: 2.6.2
12
12
  license: MIT
13
13
  metadata:
14
14
  author: Classytic
15
- version: "2.6.0"
15
+ version: "2.6.2"
16
16
  tags:
17
17
  - fastify
18
18
  - rest-api
@@ -220,6 +220,31 @@ When to use `tenantField: false`:
220
220
  - Cross-org reports or analytics
221
221
  - Single-tenant apps where org scoping isn't needed
222
222
 
223
+ ### idField — Custom Primary Key
224
+
225
+ Default is `'_id'`. Override for resources keyed by a business identifier (UUID, slug, `ORD-2026-0001`, `job-5219f346-a4d`, etc.).
226
+
227
+ ```typescript
228
+ defineResource({
229
+ name: 'job',
230
+ adapter: createMongooseAdapter(JobModel, jobRepository),
231
+ idField: 'jobId', // ← one line
232
+ });
233
+
234
+ // GET /jobs/job-5219f346-a4d → controller runs { jobId: 'job-5219f346-a4d' }
235
+ // GET /jobs/<uuid> → accepted (no ObjectId pattern enforcement)
236
+ ```
237
+
238
+ Changes all three layers:
239
+ - **Fastify AJV** — strips any ObjectId pattern from `params.id` so custom formats aren't pre-rejected
240
+ - **BaseController** — `get`/`update`/`delete` query by `{ [idField]: id }` (merged with tenant + policy filters)
241
+ - **OpenAPI docs** — `spec.paths['/jobs/{id}']` emits a plain string `id` with description
242
+ - **MCP tools** — auto-generated CRUD tools use `idField` transparently
243
+
244
+ URL path segment stays `:id` (not `:jobId`) — clients send the ID value, Arc maps it server-side. User-provided `openApiSchemas.params` still overrides everything.
245
+
246
+ For custom adapters, honor the new `AdapterSchemaContext` passed to `generateSchemas(options, context?)` to emit the right `params.id` pattern from the start. Legacy adapters still work — Arc's safety net strips mismatched ObjectId patterns automatically.
247
+
223
248
  ## QueryCache
224
249
 
225
250
  TanStack Query-inspired server cache with stale-while-revalidate and auto-invalidation on mutations.
@@ -527,21 +552,68 @@ src/resources/order/
527
552
 
528
553
  Generate: `arc generate resource order --mcp` | Wire: `extraTools: [fulfillOrderTool]`
529
554
 
530
- **Auto-load resources** (v2.6.0) — no barrel files, no manual `toPlugin()`:
555
+ **Auto-load resources** — no barrel files, no manual `toPlugin()`:
531
556
 
532
557
  ```typescript
533
558
  import { createApp, loadResources } from '@classytic/arc/factory';
534
559
 
535
560
  const app = await createApp({
536
- resources: await loadResources('./src/resources'), // discovers *.resource.ts
561
+ resourcePrefix: '/api/v1', // optional URL prefix
562
+ resources: await loadResources(import.meta.url), // discovers *.resource.ts
537
563
  auth: { type: 'jwt', jwt: { secret: process.env.JWT_SECRET } },
538
564
  });
539
- // loadResources options: exclude, include, suffix, recursive
540
565
  ```
541
566
 
542
- **Import compatibility:** `loadResources()` uses runtime `import()`. Works with relative imports (`./foo.js`) and Node.js `#` subpath imports (`#shared/utils.js` via `package.json` `imports` both `.js` and `.ts` extensions). Does **NOT** work with tsconfig path aliases (`@/*`, `~/`) those are compile-time only, Node.js ignores them. Projects using tsconfig aliases should use explicit `resources: [r1, r2]` instead.
567
+ `loadResources()` discovers files matching `*.resource.{ts,js,mts,mjs}`, recursively. Pass `import.meta.url` for dev/prod parity (resolves to `src/` in dev, `dist/` in prod automatically). Discovers `default` export, `export const resource`, OR any named export with `toPlugin()` (e.g., `export const userResource`).
568
+
569
+ Options: `exclude`, `include`, `suffix`, `recursive`, `silent`.
570
+
571
+ **Per-resource opt-out of `resourcePrefix`** — for webhooks, admin routes:
572
+ ```typescript
573
+ defineResource({ name: 'webhook', prefix: '/hooks', skipGlobalPrefix: true })
574
+ // Registers at /hooks even with createApp({ resourcePrefix: '/api/v1' })
575
+ ```
576
+
577
+ **Boot sequence:**
578
+ ```typescript
579
+ const app = await createApp({
580
+ resourcePrefix: '/api/v1',
581
+ plugins: async (f) => { await connectDB(); }, // 1. infra (DB, docs)
582
+ bootstrap: [inventoryInit, accountingInit], // 2. domain init (engines)
583
+ resources: await loadResources(import.meta.url), // 3. routes
584
+ afterResources: async (f) => { subscribeEvents(f); }, // 4. post-wiring
585
+ onReady: async (f) => { logger.info('ready'); }, // 5. lifecycle
586
+ });
587
+ ```
588
+
589
+ **Audit per-resource opt-in** — no growing exclude lists:
590
+ ```typescript
591
+ // Register audit plugin with perResource mode
592
+ await fastify.register(auditPlugin, { autoAudit: { perResource: true } });
593
+
594
+ // Opt-in at the resource level
595
+ defineResource({ name: 'order', audit: true });
596
+ defineResource({ name: 'payment', audit: { operations: ['delete'] } });
597
+ defineResource({ name: 'product' }); // not audited
598
+
599
+ // Manual custom() for MCP/additionalRoutes/read auditing
600
+ app.post('/orders/:id/refund', async (req) => {
601
+ await app.audit.custom('order', req.params.id, 'refund', { reason }, { user });
602
+ });
603
+ ```
604
+
605
+ **Import compatibility:** `loadResources()` uses runtime `import()`. Works with relative imports (`./foo.js`) and Node.js `#` subpath imports (`#shared/utils.js` via `package.json` `imports`). Does **NOT** work with tsconfig path aliases (`@/*`, `~/`) — those are compile-time only.
606
+
607
+ **Vitest workaround** (rare): if resources need engine bootstrap or transitive `node_modules` imports that don't compose with dynamic import:
608
+ ```typescript
609
+ import { preloadResources } from '@classytic/arc/testing';
610
+
611
+ export const preloadedResources = preloadResources(
612
+ import.meta.glob('../../src/resources/**/*.resource.ts', { eager: true, import: 'default' }),
613
+ );
614
+ ```
543
615
 
544
- **Unified role check** (v2.6.0) — checks both platform AND org roles:
616
+ **Unified role check** — checks both platform AND org roles:
545
617
 
546
618
  ```typescript
547
619
  import { roles } from '@classytic/arc/permissions';
@@ -552,7 +624,7 @@ permissions: {
552
624
  // Also: requireRoles(['admin'], { includeOrgRoles: true }) for backward compat
553
625
  ```
554
626
 
555
- **DX helpers** (v2.4.4):
627
+ **DX helpers:**
556
628
 
557
629
  ```typescript
558
630
  // Typed request for wrapHandler: false routes — no more (req as any).user