@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.
- package/README.md +48 -2
- package/dist/{BaseController-AbbRx3e0.mjs → BaseController-DzRtluEF.mjs} +88 -8
- package/dist/{ResourceRegistry-DeCIFlix.mjs → ResourceRegistry-C6ngvOnn.mjs} +1 -0
- package/dist/adapters/index.d.mts +2 -2
- package/dist/adapters/index.mjs +1 -1
- package/dist/{adapters-CTn28N4y.mjs → adapters-gM-WYjNe.mjs} +6 -4
- package/dist/audit/index.d.mts +31 -5
- package/dist/audit/index.mjs +21 -3
- package/dist/auth/index.d.mts +1 -1
- package/dist/cli/commands/docs.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +2 -2
- package/dist/{createApp-Bol7DLUf.mjs → createApp-D2w0LdYJ.mjs} +27 -11
- package/dist/{defineResource-bVKHjQzE.mjs → defineResource-wWMBB4GP.mjs} +48 -30
- package/dist/docs/index.d.mts +1 -1
- package/dist/dynamic/index.d.mts +1 -1
- package/dist/dynamic/index.mjs +1 -1
- package/dist/factory/index.d.mts +1 -1
- package/dist/factory/index.mjs +31 -15
- package/dist/hooks/index.d.mts +1 -1
- package/dist/{index-BIsZ_su5.d.mts → index-CHeJa4Zd.d.mts} +3 -3
- package/dist/{index-Cb3gtbg7.d.mts → index-gz6iuzCp.d.mts} +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +4 -4
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +2 -2
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/{interface-DDW43OmS.d.mts → interface-DYH8AXGe.d.mts} +89 -4
- package/dist/org/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +1 -1
- package/dist/plugins/index.mjs +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +1 -1
- package/dist/{resourceToTools-DH3c3e-T.mjs → resourceToTools-nCJWnG1r.mjs} +250 -13
- package/dist/testing/index.d.mts +26 -3
- package/dist/testing/index.mjs +46 -2
- package/dist/types/index.d.mts +1 -1
- package/dist/{types-D5rjsS_i.d.mts → types-B4_TDdPe.d.mts} +1 -1
- package/dist/{types-D5hJ-k_3.d.mts → types-By-5mIfn.d.mts} +7 -1
- package/dist/utils/index.d.mts +1 -1
- package/package.json +18 -18
- package/skills/arc/SKILL.md +80 -8
package/skills/arc/SKILL.md
CHANGED
|
@@ -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.
|
|
11
|
+
version: 2.6.2
|
|
12
12
|
license: MIT
|
|
13
13
|
metadata:
|
|
14
14
|
author: Classytic
|
|
15
|
-
version: "2.6.
|
|
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**
|
|
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
|
-
|
|
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
|
-
|
|
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**
|
|
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
|
|
627
|
+
**DX helpers:**
|
|
556
628
|
|
|
557
629
|
```typescript
|
|
558
630
|
// Typed request for wrapHandler: false routes — no more (req as any).user
|