@classytic/arc 2.8.0 → 2.8.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 (69) hide show
  1. package/README.md +10 -1
  2. package/dist/{BaseController-CpMfCXdn.mjs → BaseController-DAGGc5Xn.mjs} +76 -25
  3. package/dist/{EventTransport-n1KBxC_N.d.mts → EventTransport-CLXJUzyT.d.mts} +37 -1
  4. package/dist/{ResourceRegistry-BOtJuRCs.mjs → ResourceRegistry-Dtcojmu8.mjs} +14 -2
  5. package/dist/adapters/index.d.mts +2 -2
  6. package/dist/adapters/index.mjs +1 -1
  7. package/dist/{adapters-BxGgSHjj.mjs → adapters-BBqAVvPK.mjs} +11 -0
  8. package/dist/auth/index.d.mts +1 -1
  9. package/dist/auth/index.mjs +3 -3
  10. package/dist/{betterAuthOpenApi-CHCIuA-p.mjs → betterAuthOpenApi-C5lDyRH2.mjs} +1 -1
  11. package/dist/cli/commands/docs.mjs +2 -2
  12. package/dist/cli/commands/introspect.mjs +1 -1
  13. package/dist/core/index.d.mts +2 -2
  14. package/dist/core/index.mjs +4 -4
  15. package/dist/{core-BfrfxNqO.mjs → core-CrLDuqoT.mjs} +1 -1
  16. package/dist/{createActionRouter-CbkIAaGh.mjs → createActionRouter-Df1BuawX.mjs} +87 -21
  17. package/dist/{createApp-Cy8eUNKQ.mjs → createApp-p2OThysU.mjs} +2 -2
  18. package/dist/{defineResource-CovBXvTB.mjs → defineResource-CqeUltrW.mjs} +19 -7
  19. package/dist/docs/index.d.mts +1 -1
  20. package/dist/docs/index.mjs +1 -1
  21. package/dist/dynamic/index.d.mts +1 -1
  22. package/dist/dynamic/index.mjs +1 -1
  23. package/dist/{errorHandler-BW08lEiy.mjs → errorHandler-Cw34h_om.mjs} +1 -1
  24. package/dist/{errorHandler-BeN-ERN7.d.mts → errorHandler-DJ7OAB2V.d.mts} +1 -1
  25. package/dist/{eventPlugin-CAOWMQS8.d.mts → eventPlugin-Cdjwo0Gv.d.mts} +1 -1
  26. package/dist/{eventPlugin-x4jo3sG0.mjs → eventPlugin-XijlQmlL.mjs} +19 -1
  27. package/dist/events/index.d.mts +399 -28
  28. package/dist/events/index.mjs +345 -29
  29. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  30. package/dist/events/transports/redis.d.mts +1 -1
  31. package/dist/factory/index.d.mts +1 -1
  32. package/dist/factory/index.mjs +1 -1
  33. package/dist/hooks/index.d.mts +1 -1
  34. package/dist/{index-BpMhrFgn.d.mts → index-0zj73o2U.d.mts} +1 -1
  35. package/dist/{index-qct60lnl.d.mts → index-DadoLP51.d.mts} +35 -3
  36. package/dist/index.d.mts +4 -4
  37. package/dist/index.mjs +7 -7
  38. package/dist/integrations/event-gateway.d.mts +1 -1
  39. package/dist/integrations/index.d.mts +1 -1
  40. package/dist/integrations/mcp/index.d.mts +2 -2
  41. package/dist/integrations/mcp/index.mjs +1 -1
  42. package/dist/integrations/mcp/testing.d.mts +1 -1
  43. package/dist/integrations/mcp/testing.mjs +1 -1
  44. package/dist/{interface-IJqN3pXK.d.mts → interface-CS6d7HiB.d.mts} +549 -107
  45. package/dist/{openapi-AYLVjqVe.mjs → openapi-q6rNKfZy.mjs} +49 -2
  46. package/dist/org/index.d.mts +1 -1
  47. package/dist/plugins/index.d.mts +2 -2
  48. package/dist/plugins/index.mjs +3 -3
  49. package/dist/plugins/tracing-entry.mjs +1 -1
  50. package/dist/presets/index.d.mts +3 -3
  51. package/dist/presets/multiTenant.d.mts +1 -1
  52. package/dist/{redis-stream-CF1lrKVk.d.mts → redis-stream-BgrYzpeq.d.mts} +1 -1
  53. package/dist/registry/index.d.mts +1 -1
  54. package/dist/registry/index.mjs +1 -1
  55. package/dist/{resourceToTools-C_1SMiCz.mjs → resourceToTools-DNNWnZtx.mjs} +193 -63
  56. package/dist/rpc/index.mjs +1 -1
  57. package/dist/testing/index.d.mts +2 -2
  58. package/dist/testing/index.mjs +1 -1
  59. package/dist/types/index.d.mts +2 -2
  60. package/dist/{types-gUxAIZHp.d.mts → types-BlOuKTPw.d.mts} +4 -4
  61. package/dist/{types-Ct0PUUSp.d.mts → types-D3b7hA00.d.mts} +1 -1
  62. package/dist/utils/index.d.mts +2 -14
  63. package/dist/utils/index.mjs +5 -5
  64. package/dist/{utils-B-l6410F.mjs → utils-7sJ8X83I.mjs} +1 -13
  65. package/package.json +4 -3
  66. /package/dist/{circuitBreaker-l18oRgL5.mjs → circuitBreaker-cmi5XDv5.mjs} +0 -0
  67. /package/dist/{errors-Cg58SLNi.mjs → errors-BF2bIOIS.mjs} +0 -0
  68. /package/dist/{requestContext-xHIKedG6.mjs → requestContext-DYvHl113.mjs} +0 -0
  69. /package/dist/{schemaConverter-Y5EejTnJ.mjs → schemaConverter-OxfCshus.mjs} +0 -0
@@ -5,7 +5,7 @@ import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, Rout
5
5
 
6
6
  //#region src/hooks/HookSystem.d.ts
7
7
  type HookPhase = "before" | "around" | "after";
8
- type HookOperation = "create" | "update" | "delete" | "read" | "list";
8
+ type HookOperation = "create" | "update" | "delete" | "restore" | "read" | "list";
9
9
  interface HookContext<T = AnyRecord> {
10
10
  resource: string;
11
11
  operation: HookOperation;
@@ -318,116 +318,479 @@ type PipelineConfig = PipelineStep[] | {
318
318
  //#endregion
319
319
  //#region src/types/repository.d.ts
320
320
  /**
321
- * Repository Interface - Database-Agnostic CRUD Operations
321
+ * Repository Interface Database-Agnostic CRUD Contract
322
322
  *
323
- * This is the standard interface that all repositories must implement.
324
- * MongoKit Repository already implements this interface.
323
+ * This is the canonical contract every arc-compatible repository follows.
324
+ * It is intentionally structural: any object matching the shape works,
325
+ * including the reference implementation at `@classytic/mongokit` and any
326
+ * future `prismakit` / `pgkit` / `sqlitekit` that mirrors it.
325
327
  *
326
- * @example
327
- * ```typescript
328
- * import type { CrudRepository } from '@classytic/arc';
328
+ * ## Design
329
+ *
330
+ * The interface is tiered so a minimal adapter can ship with five methods
331
+ * while a mature one (mongokit 3.6+) can opt into the full surface without
332
+ * type assertions:
333
+ *
334
+ * 1. **Required** — `getAll`, `getById`, `create`, `update`, `delete`.
335
+ * Every resource needs these; arc's BaseController assumes they exist.
336
+ *
337
+ * 2. **Recommended** — `getOne` / `getByQuery`. Used by AccessControl to
338
+ * enforce compound filters (idField + org scope + policy). Without them,
339
+ * arc falls back to `getById` + post-fetch checks, which is slower and
340
+ * produces wrong 404s on custom idFields.
341
+ *
342
+ * 3. **Optional capabilities** — batch ops, soft delete, aggregation,
343
+ * transactions, etc. Declared as optional so kits implement only what
344
+ * their underlying DB supports. arc feature-detects at runtime.
345
+ *
346
+ * All options/results are named types so custom kits can import and
347
+ * implement them directly:
329
348
  *
330
- * // Your repository automatically satisfies this interface
349
+ * ```ts
350
+ * import type {
351
+ * CrudRepository,
352
+ * DeleteOptions,
353
+ * DeleteResult,
354
+ * PaginationResult,
355
+ * UpdateManyResult,
356
+ * BulkWriteOperation,
357
+ * BulkWriteResult,
358
+ * } from '@classytic/arc';
359
+ *
360
+ * class PgRepository<TDoc> implements CrudRepository<TDoc> { … }
361
+ * ```
362
+ *
363
+ * @example Reference implementation
364
+ * ```ts
365
+ * import type { CrudRepository } from '@classytic/arc';
331
366
  * const userRepo: CrudRepository<UserDocument> = new Repository(UserModel);
332
367
  * ```
368
+ *
369
+ * ## Contract gotchas (learned from mongokit 3.6 integration)
370
+ *
371
+ * If you build a custom kit that implements this contract, these are the
372
+ * behaviors arc's tests specifically verify. Align your kit here and
373
+ * arc's `BaseController` + presets will work out of the box:
374
+ *
375
+ * 1. **`getById` / `getOne` miss semantics** — MAY return `null` or throw a
376
+ * 404-style error whose message contains "not found". Arc handles both.
377
+ * Pick one and document it in your kit.
378
+ *
379
+ * 2. **`deleteMany` with soft-delete** — if your kit intercepts
380
+ * `deleteMany` and rewrites it to `updateMany`, the returned
381
+ * `deletedCount` may be `0` even when N docs were soft-deleted. The
382
+ * authoritative count comes from a follow-up query. Consumers shouldn't
383
+ * rely on `deletedCount` reflecting soft-delete work unless your kit
384
+ * promises it.
385
+ *
386
+ * 3. **Lifecycle hooks are shared with plugins** — never use
387
+ * `removeAllListeners(event)` to clean up test hooks. That silently
388
+ * removes soft-delete, cascade, multi-tenant, and audit plugin
389
+ * listeners too, which then makes subsequent operations misbehave
390
+ * (e.g. a soft-delete becomes a hard delete). Always use
391
+ * `.off(event, fn)` with the specific handler reference you registered.
392
+ *
393
+ * 4. **Hard-delete mode** — `delete(id, { mode: 'hard' })` and
394
+ * `deleteMany(q, { mode: 'hard' })` MUST bypass soft-delete
395
+ * interception while still running policy / multi-tenant / cascade /
396
+ * audit hooks. Kits without soft-delete should accept and ignore the
397
+ * flag.
398
+ *
399
+ * 5. **Keyset pagination auto-detection** — `getAll({ sort, limit })`
400
+ * without `page` SHOULD return a `KeysetPaginatedResult` with
401
+ * `method: "keyset"`. Kits that only offer offset pagination can return
402
+ * the legacy offset shape; arc's types still satisfy.
403
+ *
404
+ * 6. **`idField` identity** — kits that key on anything other than `"_id"`
405
+ * MUST set `readonly idField` on the repository so arc's BaseController
406
+ * passes route params straight through to `update`/`delete`/`restore`
407
+ * without translating them.
408
+ *
409
+ * 7. **`before:restore` / `after:restore` hooks** — if you implement
410
+ * `restore`, fire these hooks symmetrically with `before:delete` /
411
+ * `after:delete` so hosts can wire cascade-restore flows.
412
+ *
413
+ * See `tests/core/repository-contract-mongokit.test.ts` for a runnable
414
+ * reference against mongokit 3.6. Copy it, swap in your kit's repository,
415
+ * and make it pass — if everything's green, arc will work against your
416
+ * kit.
333
417
  */
334
418
  /**
335
- * Query options for read operations
419
+ * Opaque transaction session. Adapters bind this to their own type
420
+ * (Mongoose `ClientSession`, Prisma transaction client, `pg.Client`, …).
421
+ */
422
+ type RepositorySession = unknown;
423
+ /**
424
+ * Query options for read operations. Extended ad-hoc by adapters via the
425
+ * index signature — kit authors should namespace custom flags (e.g.
426
+ * `__pgHint`) to avoid collisions.
336
427
  */
337
428
  interface QueryOptions {
338
- /** Transaction session — adapters handle the actual type (e.g., Mongoose ClientSession) */
339
- session?: unknown;
340
- /** Field selection - include or exclude fields */
341
- select?: string | string[] | Record<string, 0 | 1>;
342
- /** Relations to populate - string, array, or Mongoose populate options */
343
- populate?: string | string[] | Record<string, unknown>;
344
- /** Return plain JS objects instead of Mongoose documents */
429
+ /** Transaction session — adapter-specific concrete type */
430
+ session?: RepositorySession;
431
+ /** Return plain objects instead of driver documents */
345
432
  lean?: boolean;
346
- /** Allow additional adapter-specific options */
433
+ /** Include soft-deleted docs in reads (honored by soft-delete plugin) */
434
+ includeDeleted?: boolean;
435
+ /** Forwarded to policy/tenant hooks */
436
+ user?: Record<string, unknown>;
437
+ /** Arc request-scoped metadata (orgId, roles, requestId, …) */
438
+ context?: Record<string, unknown>;
439
+ /**
440
+ * Adapter-specific escape hatch — `select`, `populate`, `populateOptions`,
441
+ * `readPreference`, `maxTimeMS`, and every kit's driver-specific flags
442
+ * flow through here. Arc intentionally does NOT type these concretely
443
+ * because each kit's DB shapes them differently: mongoose uses
444
+ * `PopulateOptions[]`, prisma uses `{ include: {...} }`, pgkit uses SQL
445
+ * JOIN hints, etc. Typing them as (say) `string | Record<string, unknown>`
446
+ * would REJECT the narrower shapes real kits actually expose, breaking
447
+ * structural assignability of `Repository<T> → CrudRepository<T>`.
448
+ */
347
449
  [key: string]: unknown;
348
450
  }
349
451
  /**
350
- * Pagination parameters for list operations
452
+ * Options for write operations (create/update). Superset of QueryOptions
453
+ * so callers can pass a single options object.
454
+ */
455
+ interface WriteOptions extends QueryOptions {
456
+ /** Upsert on update/replace operations */
457
+ upsert?: boolean;
458
+ }
459
+ /**
460
+ * Options for delete operations.
461
+ *
462
+ * `mode: 'hard'` opts out of the soft-delete interception when the adapter
463
+ * has a soft-delete plugin wired. Policy, cascade, audit, and cache hooks
464
+ * still fire — only the soft-delete rewrite is bypassed. Use for GDPR
465
+ * erasure or admin purge paths.
466
+ */
467
+ interface DeleteOptions extends QueryOptions {
468
+ /**
469
+ * Force physical deletion even when soft-delete is active, or force soft
470
+ * when the default would be hard. Adapters without soft-delete support
471
+ * MUST ignore this flag (it is a hint, not a contract).
472
+ */
473
+ mode?: "hard" | "soft";
474
+ }
475
+ /**
476
+ * Result of a single delete operation.
477
+ *
478
+ * Matches mongokit's shape. Adapters without soft-delete awareness can omit
479
+ * `soft` and `count`. Arc's BaseController uses the `success` flag to decide
480
+ * whether to return 200 or 404.
481
+ */
482
+ interface DeleteResult {
483
+ success: boolean;
484
+ message: string;
485
+ /** Primary key of the removed doc (string form) */
486
+ id?: string;
487
+ /** True when a soft-delete plugin intercepted the operation */
488
+ soft?: boolean;
489
+ /** For batch-variant implementations that return the delete count inline */
490
+ count?: number;
491
+ }
492
+ /**
493
+ * Result of a batch delete (`deleteMany`) — distinct from single `delete`
494
+ * because MongoDB's driver returns a different shape for batch operations.
495
+ *
496
+ * **Soft-delete gotcha** — when a soft-delete plugin intercepts
497
+ * `deleteMany` by rewriting it to `updateMany` internally (mongokit 3.6
498
+ * does this in `before:deleteMany`), the `deletedCount` returned here may
499
+ * be `0` because the underlying `Model.deleteMany` was never called. The
500
+ * affected-row count lives inside the hook's `updateMany` result and is
501
+ * not surfaced to the caller. Consumers that need the exact soft-deleted
502
+ * count should run a follow-up query (`repo.count({ deletedAt: { $ne:
503
+ * null }, ...filter })`). 3rd-party kits with soft-delete should document
504
+ * which convention they follow.
505
+ */
506
+ interface DeleteManyResult {
507
+ /** Driver-reported acknowledgement */
508
+ acknowledged?: boolean;
509
+ /**
510
+ * Number of documents removed. May be 0 when soft-delete intercepts;
511
+ * see the "Soft-delete gotcha" note above.
512
+ */
513
+ deletedCount: number;
514
+ /** True when a soft-delete plugin intercepted and did `updateMany` instead */
515
+ soft?: boolean;
516
+ }
517
+ /** Result of a bulk update operation. Matches MongoDB driver shape. */
518
+ interface UpdateManyResult {
519
+ acknowledged?: boolean;
520
+ matchedCount: number;
521
+ modifiedCount: number;
522
+ upsertedCount?: number;
523
+ upsertedId?: unknown;
524
+ }
525
+ /** Shape of a single operation passed to `bulkWrite`. */
526
+ type BulkWriteOperation<TDoc = unknown> = {
527
+ insertOne: {
528
+ document: Partial<TDoc>;
529
+ };
530
+ } | {
531
+ updateOne: {
532
+ filter: Record<string, unknown>;
533
+ update: Record<string, unknown>;
534
+ upsert?: boolean;
535
+ };
536
+ } | {
537
+ updateMany: {
538
+ filter: Record<string, unknown>;
539
+ update: Record<string, unknown>;
540
+ upsert?: boolean;
541
+ };
542
+ } | {
543
+ deleteOne: {
544
+ filter: Record<string, unknown>;
545
+ };
546
+ } | {
547
+ deleteMany: {
548
+ filter: Record<string, unknown>;
549
+ };
550
+ } | {
551
+ replaceOne: {
552
+ filter: Record<string, unknown>;
553
+ replacement: Partial<TDoc>;
554
+ upsert?: boolean;
555
+ };
556
+ };
557
+ /** Result of a heterogeneous bulk write. */
558
+ interface BulkWriteResult {
559
+ ok?: number;
560
+ insertedCount?: number;
561
+ matchedCount?: number;
562
+ modifiedCount?: number;
563
+ deletedCount?: number;
564
+ upsertedCount?: number;
565
+ insertedIds?: Record<number, unknown>;
566
+ upsertedIds?: Record<number, unknown>;
567
+ }
568
+ /**
569
+ * Pagination parameters for list operations.
570
+ *
571
+ * Supports three modes, auto-detected by the adapter:
572
+ * - **Offset** — pass `page` + `limit`.
573
+ * - **Keyset** — pass `sort` + `limit` (+ optional `after` cursor). Required
574
+ * for infinite scroll on large collections; O(1) per page.
575
+ * - **Raw** — pass neither; adapter returns all matching docs.
351
576
  */
352
577
  interface PaginationParams<TDoc = unknown> {
353
578
  /** Filter criteria */
354
579
  filters?: Partial<TDoc> & Record<string, unknown>;
355
- /** Sort specification - string ("-createdAt") or object ({ createdAt: -1 }) */
580
+ /** Sort spec string (`"-createdAt"`) or object (`{ createdAt: -1 }`) */
356
581
  sort?: string | Record<string, 1 | -1>;
357
- /** Page number (1-indexed) */
582
+ /** Page number (1-indexed) — triggers offset pagination */
358
583
  page?: number;
359
584
  /** Items per page */
360
585
  limit?: number;
361
- /** Allow additional options (select, populate, etc.) */
586
+ /** Opaque cursor from a prior `next` field — triggers keyset pagination */
587
+ after?: string;
588
+ /** Allow additional options (select, populate, search, …) */
362
589
  [key: string]: unknown;
363
590
  }
364
591
  /**
365
- * Paginated result from list operations
592
+ * Offset-based paginated result (the default shape when `page` is provided).
593
+ *
594
+ * `method` is optional so legacy adapters returning the bare `{ docs, page,
595
+ * limit, total, pages, hasNext, hasPrev }` shape still satisfy the type.
366
596
  */
367
- interface PaginatedResult<TDoc> {
368
- /** Documents for current page */
597
+ interface OffsetPaginatedResult<TDoc> {
598
+ /** Discriminator omitted or `"offset"` */
599
+ method?: "offset";
369
600
  docs: TDoc[];
370
- /** Current page number */
371
601
  page: number;
372
- /** Items per page */
373
602
  limit: number;
374
- /** Total document count */
375
603
  total: number;
376
- /** Total page count */
377
604
  pages: number;
378
- /** Has next page */
379
605
  hasNext: boolean;
380
- /** Has previous page */
381
606
  hasPrev: boolean;
382
607
  }
608
+ /**
609
+ * Keyset-based paginated result (returned when `sort` is provided without
610
+ * `page`). Ideal for infinite scroll — no `count()` query, O(1) per page.
611
+ */
612
+ interface KeysetPaginatedResult<TDoc> {
613
+ /** Discriminator — always `"keyset"` */
614
+ method: "keyset";
615
+ docs: TDoc[];
616
+ limit: number;
617
+ hasMore: boolean;
618
+ /** Opaque cursor token for the next page, or `null` at the end */
619
+ next: string | null;
620
+ }
621
+ /**
622
+ * Discriminated union of all pagination result shapes.
623
+ * Consumers narrow on the `method` discriminator.
624
+ *
625
+ * @example
626
+ * ```ts
627
+ * const result = await repo.getAll(params);
628
+ * if (result.method === "keyset") {
629
+ * // result.next, result.hasMore
630
+ * } else {
631
+ * // result.page, result.total, result.pages
632
+ * }
633
+ * ```
634
+ */
635
+ type PaginationResult<TDoc> = OffsetPaginatedResult<TDoc> | KeysetPaginatedResult<TDoc>;
636
+ /**
637
+ * Legacy alias. Existing code typed as `PaginatedResult<TDoc>` continues
638
+ * to work unchanged — it resolves to the offset shape, which is the most
639
+ * common. New code should prefer `PaginationResult<TDoc>` for the full
640
+ * discriminated union.
641
+ */
642
+ type PaginatedResult<TDoc> = OffsetPaginatedResult<TDoc>;
383
643
  /**
384
644
  * Standard CRUD Repository Interface
385
645
  *
386
- * Defines the contract for data access operations.
387
- * All database adapters (MongoKit, Prisma, etc.) implement this interface.
646
+ * The canonical contract arc consumes. Tiered so minimal adapters only
647
+ * implement the required five methods; richer kits declare the optional
648
+ * capabilities they support.
649
+ *
650
+ * Every optional method is feature-detected at runtime by arc's
651
+ * BaseController and presets — implement only what your DB can express.
388
652
  *
389
653
  * @typeParam TDoc - The document/entity type
390
654
  */
391
655
  interface CrudRepository<TDoc> {
392
656
  /**
393
- * Get paginated list of documents
657
+ * Native primary key field. Defaults to `"_id"` (Mongo convention).
658
+ *
659
+ * Set to match `defineResource({ idField })` for kits that key on a
660
+ * custom field (e.g. `"id"`, `"uuid"`, `"slug"`). Arc's BaseController
661
+ * reads this to decide whether to pass route params straight through
662
+ * to `update`/`delete`/`restore` or to translate them via a fetched
663
+ * doc's `_id` first.
394
664
  */
395
- getAll(params?: PaginationParams<TDoc>, options?: QueryOptions): Promise<PaginatedResult<TDoc>>;
665
+ readonly idField?: string;
396
666
  /**
397
- * Get single document by ID
667
+ * List documents with pagination. Adapter auto-selects offset vs keyset
668
+ * mode based on the presence of `page` or `after` in `params`.
669
+ *
670
+ * Return shapes (all valid under the contract):
671
+ * - `OffsetPaginatedResult<TDoc>` — when `page` is given
672
+ * - `KeysetPaginatedResult<TDoc>` — when `sort` + optional `after` are given
673
+ * - `TDoc[]` — raw array, when neither `page` nor `sort` drives pagination
674
+ *
675
+ * Arc's BaseController narrows the union before returning to clients.
676
+ */
677
+ getAll(params?: PaginationParams<TDoc>, options?: QueryOptions): Promise<PaginationResult<TDoc> | TDoc[]>;
678
+ /**
679
+ * Fetch a single document by its primary key.
680
+ *
681
+ * **Miss semantics — kits may EITHER return `null` OR throw a 404-style
682
+ * error.** Arc's `BaseController` handles both: `AccessControl.fetchWith­
683
+ * AccessControl` catches errors whose message contains "not found" and
684
+ * converts them to null. 3rd-party kit authors: pick one convention and
685
+ * document it. mongokit 3.6 throws by default; pass
686
+ * `{ throwOnNotFound: false }` to get null. A SQL kit that returns null
687
+ * directly is equally valid.
398
688
  */
399
689
  getById(id: string, options?: QueryOptions): Promise<TDoc | null>;
690
+ /** Insert a single document. */
691
+ create(data: Partial<TDoc>, options?: WriteOptions): Promise<TDoc>;
692
+ /** Update a document by primary key. Returns the updated doc or null. */
693
+ update(id: string, data: Partial<TDoc>, options?: WriteOptions): Promise<TDoc | null>;
400
694
  /**
401
- * Create new document
695
+ * Delete a document by primary key. Pass `{ mode: 'hard' }` to bypass
696
+ * soft-delete interception.
402
697
  */
403
- create(data: Partial<TDoc>, options?: {
404
- session?: unknown;
405
- [key: string]: unknown;
406
- }): Promise<TDoc>;
698
+ delete(id: string, options?: DeleteOptions): Promise<DeleteResult>;
407
699
  /**
408
- * Update document by ID
700
+ * Find a single doc by a compound filter. Used by arc's AccessControl to
701
+ * combine `idField + orgId + policy` in one query. Without it, arc falls
702
+ * back to `getById` + post-fetch scope checks (slower; 404s on custom
703
+ * idFields if the doc lives outside the user's scope).
704
+ *
705
+ * Miss semantics match `getById` — kits may return null or throw. Arc
706
+ * handles both. See the note on `getById` above.
409
707
  */
410
- update(id: string, data: Partial<TDoc>, options?: QueryOptions): Promise<TDoc | null>;
708
+ getOne?(filter: Record<string, unknown>, options?: QueryOptions): Promise<TDoc | null>;
709
+ /** Alias many kits expose alongside `getOne`. Arc checks both. */
710
+ getByQuery?(filter: Record<string, unknown>, options?: QueryOptions): Promise<TDoc | null>;
711
+ /** Count matching documents. Respects soft-delete when applicable. */
712
+ count?(filter?: Record<string, unknown>, options?: QueryOptions): Promise<number>;
411
713
  /**
412
- * Delete document by ID
714
+ * Cheap existence check. Kits may return `boolean` or `{ _id }` — arc
715
+ * coerces to boolean at the call site.
413
716
  */
414
- delete(id: string, options?: {
415
- session?: unknown;
416
- [key: string]: unknown;
417
- }): Promise<{
418
- success: boolean;
419
- message: string;
420
- }>;
421
- /** Allow custom methods (getBySlug, getTree, restore, etc.) */
717
+ exists?(filter: Record<string, unknown>, options?: QueryOptions): Promise<boolean | {
718
+ _id: unknown;
719
+ } | null>;
720
+ /** Return the distinct values of a field matching the filter. */
721
+ distinct?<T = unknown>(field: string, filter?: Record<string, unknown>, options?: QueryOptions): Promise<T[]>;
722
+ /** Return all matching docs as a raw array (no pagination metadata). */
723
+ findAll?(filter?: Record<string, unknown>, options?: QueryOptions): Promise<TDoc[]>;
724
+ /**
725
+ * Atomic "find or create" — return the doc matching the filter, or
726
+ * insert `data` and return it if none exists. MAY return `null` when
727
+ * neither path produces a document (e.g. race loss + validation error
728
+ * handling — mongokit returns null in this window).
729
+ */
730
+ getOrCreate?(filter: Record<string, unknown>, data: Partial<TDoc>, options?: WriteOptions): Promise<TDoc | null>;
731
+ /** Insert multiple documents in one call. */
732
+ createMany?(items: Array<Partial<TDoc>>, options?: WriteOptions): Promise<TDoc[]>;
733
+ /**
734
+ * Update all documents matching `filter`. Should reject empty filters
735
+ * to prevent accidental mass updates (mongokit does this).
736
+ */
737
+ updateMany?(filter: Record<string, unknown>, data: Record<string, unknown>, options?: WriteOptions): Promise<UpdateManyResult>;
738
+ /**
739
+ * Delete all documents matching `filter`. Soft-deletes when a soft-delete
740
+ * plugin is wired; pass `{ mode: 'hard' }` to force physical removal.
741
+ */
742
+ deleteMany?(filter: Record<string, unknown>, options?: DeleteOptions): Promise<DeleteManyResult>;
743
+ /**
744
+ * Heterogeneous bulk write (insertOne / updateOne / deleteMany / …).
745
+ *
746
+ * Structurally typed as `unknown` because each kit uses its own operation
747
+ * shape — mongoose uses `AnyBulkWriteOperation[]`, prisma builds these
748
+ * from its client-extension API, pgkit uses SQL primitives. Arc does
749
+ * not call `bulkWrite` internally, so the exact shape is kit-specific.
750
+ * See `BulkWriteOperation<TDoc>` (exported from arc) for a reference
751
+ * shape you can use when implementing your own kit; mongokit-compatible
752
+ * callers should import its own operation types.
753
+ */
754
+ bulkWrite?: unknown;
755
+ /** Restore a soft-deleted document. Should fire `before:restore` hooks. */
756
+ restore?(id: string, options?: QueryOptions): Promise<TDoc | null>;
757
+ /** Paginated list of soft-deleted documents. */
758
+ getDeleted?(params?: PaginationParams<TDoc>, options?: QueryOptions): Promise<PaginationResult<TDoc> | TDoc[]>;
759
+ /**
760
+ * Run an aggregation pipeline.
761
+ *
762
+ * Structurally typed as `unknown` because each kit uses a different
763
+ * stage type (mongoose's `PipelineStage`, prisma's client-extension
764
+ * builders, pgkit's query-builder primitives, …). Arc does not call
765
+ * `aggregate` internally — it's a capability consumers use directly on
766
+ * the repo. Cast or re-declare at the call site using your kit's types.
767
+ */
768
+ aggregate?: unknown;
769
+ /**
770
+ * Paginated aggregation. Same kit-specificity reasoning as `aggregate`
771
+ * — structurally `unknown`, type-safe at the call site.
772
+ */
773
+ aggregatePaginate?: unknown;
774
+ /**
775
+ * Run `callback` inside a transaction. Adapters should auto-retry on
776
+ * transient transaction errors and expose a `session` the callback can
777
+ * forward to subsequent repo calls.
778
+ */
779
+ withTransaction?<T>(callback: (session: RepositorySession) => Promise<T>, options?: Record<string, unknown>): Promise<T>;
780
+ /** slugLookup preset — fetch by a business slug. */
781
+ getBySlug?(slug: string, options?: QueryOptions): Promise<TDoc | null>;
782
+ /** tree preset — return the full hierarchy. */
783
+ getTree?(options?: QueryOptions): Promise<TDoc[]>;
784
+ /** tree preset — return direct children of a node. */
785
+ getChildren?(parentId: string, options?: QueryOptions): Promise<TDoc[]>;
422
786
  [key: string]: unknown;
423
787
  }
424
788
  /**
425
- * Extract document type from a repository
789
+ * Extract document type from a repository.
426
790
  *
427
791
  * @example
428
- * ```typescript
792
+ * ```ts
429
793
  * type UserDoc = InferDoc<typeof userRepository>;
430
- * // UserDoc is now the document type of userRepository
431
794
  * ```
432
795
  */
433
796
  type InferDoc<R> = R extends CrudRepository<infer T> ? T : never;
@@ -465,6 +828,16 @@ declare class ResourceDefinition<TDoc = AnyRecord> {
465
828
  readonly customSchemas: CrudSchemas;
466
829
  readonly permissions: ResourcePermissions;
467
830
  readonly additionalRoutes: AdditionalRoute[];
831
+ /**
832
+ * Original v2.8 `routes` declaration — retained for downstream consumers
833
+ * (OpenAPI, MCP, registry, CLI introspect). Preserves fields dropped during
834
+ * normalization to `additionalRoutes` (notably `mcp`, `description`,
835
+ * `annotations`). Undefined when the resource was defined with the legacy
836
+ * `additionalRoutes` shape.
837
+ *
838
+ * Added in 2.8.1 — the source-of-truth fix for "canonical resource manifest".
839
+ */
840
+ readonly routes?: readonly RouteDefinition[];
468
841
  readonly middlewares: MiddlewareConfig;
469
842
  readonly disableDefaultRoutes: boolean;
470
843
  readonly disabledRoutes: CrudRouteKey[];
@@ -845,8 +1218,8 @@ interface AccessControlConfig {
845
1218
  }
846
1219
  /** Minimal repository interface for access-controlled fetch operations */
847
1220
  interface AccessControlRepository {
848
- getById(id: string, options?: unknown): Promise<unknown>;
849
- getOne?: (filter: AnyRecord, options?: unknown) => Promise<unknown>;
1221
+ getById(id: string, options?: QueryOptions): Promise<unknown>;
1222
+ getOne?: (filter: AnyRecord, options?: QueryOptions) => Promise<unknown>;
850
1223
  }
851
1224
  declare class AccessControl {
852
1225
  private readonly tenantField;
@@ -890,7 +1263,7 @@ declare class AccessControl {
890
1263
  * Replaces the duplicated pattern in get/update/delete:
891
1264
  * buildIdFilter -> getOne (or getById + checkOrgScope + checkPolicyFilters)
892
1265
  */
893
- fetchWithAccessControl<TDoc>(id: string, req: IRequestContext, repository: AccessControlRepository, queryOptions?: unknown): Promise<TDoc | null>;
1266
+ fetchWithAccessControl<TDoc>(id: string, req: IRequestContext, repository: AccessControlRepository, queryOptions?: QueryOptions): Promise<TDoc | null>;
894
1267
  /**
895
1268
  * Post-fetch access control validation for items fetched by non-ID queries
896
1269
  * (e.g., getBySlug, restore). Applies org scope, policy filters, and
@@ -1123,7 +1496,7 @@ declare class BaseController<TDoc = AnyRecord, TRepository extends RepositoryLik
1123
1496
  soft?: boolean;
1124
1497
  }>>;
1125
1498
  getBySlug(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
1126
- getDeleted(req: IRequestContext): Promise<IControllerResponse<PaginatedResult<TDoc>>>;
1499
+ getDeleted(req: IRequestContext): Promise<IControllerResponse<PaginationResult<TDoc>>>;
1127
1500
  restore(req: IRequestContext): Promise<IControllerResponse<TDoc>>;
1128
1501
  getTree(req: IRequestContext): Promise<IControllerResponse<TDoc[]>>;
1129
1502
  getChildren(req: IRequestContext): Promise<IControllerResponse<TDoc[]>>;
@@ -1958,6 +2331,24 @@ interface AdditionalRoute {
1958
2331
  }>;
1959
2332
  isError?: boolean;
1960
2333
  }>;
2334
+ /**
2335
+ * MCP tool generation config preserved from v2.8 `routes`.
2336
+ * - `false`: skip MCP tool generation for this route
2337
+ * - `true` / omitted: auto-generate when the route goes through Arc's pipeline
2338
+ * - object: explicit description/annotations overrides
2339
+ *
2340
+ * Added in 2.8.1 — previously dropped during `routes → additionalRoutes`
2341
+ * normalization, breaking MCP opt-out and per-route annotations.
2342
+ */
2343
+ mcp?: boolean | {
2344
+ readonly description?: string;
2345
+ readonly annotations?: {
2346
+ readonly readOnlyHint?: boolean;
2347
+ readonly destructiveHint?: boolean;
2348
+ readonly idempotentHint?: boolean;
2349
+ readonly openWorldHint?: boolean;
2350
+ };
2351
+ };
1961
2352
  }
1962
2353
  /** HTTP methods for custom routes */
1963
2354
  type RouteMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
@@ -2075,9 +2466,17 @@ interface RouteSchemaOptions {
2075
2466
  filterableFields?: string[];
2076
2467
  fieldRules?: Record<string, {
2077
2468
  systemManaged?: boolean;
2469
+ hidden?: boolean;
2078
2470
  immutable?: boolean;
2079
2471
  immutableAfterCreate?: boolean;
2080
- optional?: boolean;
2472
+ optional?: boolean; /** String minimum length — auto-maps to OpenAPI `minLength` and MCP tool schema */
2473
+ minLength?: number; /** String maximum length — auto-maps to OpenAPI `maxLength` and MCP tool schema */
2474
+ maxLength?: number; /** Number minimum — auto-maps to OpenAPI `minimum` and MCP tool schema */
2475
+ min?: number; /** Number maximum — auto-maps to OpenAPI `maximum` and MCP tool schema */
2476
+ max?: number; /** Regex pattern — auto-maps to OpenAPI `pattern` and MCP tool schema */
2477
+ pattern?: string; /** Allowed values — auto-maps to OpenAPI `enum` and MCP tool schema */
2478
+ enum?: ReadonlyArray<string | number>; /** Human-readable description — auto-maps to OpenAPI `description` */
2479
+ description?: string;
2081
2480
  [key: string]: unknown;
2082
2481
  }>;
2083
2482
  query?: Record<string, unknown>;
@@ -2544,6 +2943,33 @@ interface RegistryEntry extends ResourceMetadata {
2544
2943
  audit?: boolean | {
2545
2944
  operations?: ("create" | "update" | "delete")[];
2546
2945
  };
2946
+ /**
2947
+ * v2.8 declarative actions metadata — populated from `ResourceConfig.actions`.
2948
+ *
2949
+ * Consumed by OpenAPI generation (renders `POST /:id/action` with a
2950
+ * discriminated body schema) and MCP tool generation.
2951
+ *
2952
+ * Added in 2.8.1.
2953
+ */
2954
+ actions?: Array<{
2955
+ readonly name: string;
2956
+ readonly description?: string; /** Raw per-action schema (JSON Schema, Zod v4, or legacy field map) */
2957
+ readonly schema?: Record<string, unknown>; /** Per-action permission check (if different from resource-level `actionPermissions`) */
2958
+ readonly permissions?: PermissionCheck; /** MCP tool generation flag — `false` to skip, object for overrides */
2959
+ readonly mcp?: boolean | {
2960
+ readonly description?: string;
2961
+ readonly annotations?: Record<string, unknown>;
2962
+ };
2963
+ }>;
2964
+ /**
2965
+ * Resource-level fallback permission for actions without per-action
2966
+ * permissions. Used by OpenAPI to determine auth requirements and by MCP
2967
+ * as the fallback in `createActionToolHandler`.
2968
+ *
2969
+ * Added in 2.8.1 — previously not surfaced to downstream consumers,
2970
+ * causing OpenAPI to mark action endpoints as public when runtime required auth.
2971
+ */
2972
+ actionPermissions?: PermissionCheck;
2547
2973
  }
2548
2974
  interface RegistryStats {
2549
2975
  total?: number;
@@ -2601,68 +3027,84 @@ type TypedRepository<TDoc> = CrudRepository<TDoc>;
2601
3027
  //#endregion
2602
3028
  //#region src/adapters/interface.d.ts
2603
3029
  /**
2604
- * Minimal repository interface for flexible adapter compatibility.
2605
- * Any repository with these method signatures is accepted — no `as any` needed.
3030
+ * Minimal structural repository shape for flexible adapter compatibility.
2606
3031
  *
2607
- * CrudRepository<TDoc> and MongoKit Repository both satisfy this interface.
2608
- */
2609
- /**
2610
- * Minimal repository interface for flexible adapter compatibility.
2611
- * Any repository with these method signatures is accepted.
3032
+ * `RepositoryLike` is the **loose** variant of `CrudRepository<TDoc>` — it
3033
+ * uses `unknown` for document payloads so any object with the right method
3034
+ * names satisfies it without type assertions. Prefer `CrudRepository<TDoc>`
3035
+ * for kits you own; use `RepositoryLike` when wrapping third-party repos.
2612
3036
  *
2613
- * **Required** core CRUD (every resource needs these):
2614
- * getAll, getById, create, update, delete
3037
+ * Both interfaces declare the same tiered capabilities:
2615
3038
  *
2616
- * **Recommended** — used by AccessControl for compound queries:
2617
- * getOne
3039
+ * - **Required** — `getAll`, `getById`, `create`, `update`, `delete`
3040
+ * - **Recommended** — `getOne` / `getByQuery` (used by AccessControl for
3041
+ * compound filters like `idField + orgId + policy`)
3042
+ * - **Optional** — feature-detected at runtime by presets and the
3043
+ * BaseController. Declare only what your DB supports.
2618
3044
  *
2619
- * **Optional** enabled by presets, checked at runtime:
2620
- * getBySlug — slugLookup preset
2621
- * getDeleted — softDelete preset (list soft-deleted)
2622
- * restore — softDelete preset (restore soft-deleted)
2623
- * getTree — tree preset (hierarchical queries)
2624
- * getChildren — tree preset (child nodes)
2625
- * createMany — bulk preset (batch create)
2626
- * updateMany — bulk preset (batch update by filter)
2627
- * deleteMany — bulk preset (batch delete by filter)
3045
+ * See [CrudRepository](../types/repository.ts) for full prose-level docs
3046
+ * on each method and the design rationale behind the tiering.
2628
3047
  */
2629
3048
  interface RepositoryLike {
2630
- getAll(params?: unknown): Promise<unknown>;
2631
- getById(id: string, options?: unknown): Promise<unknown>;
2632
- create(data: unknown, options?: unknown): Promise<unknown>;
2633
- update(id: string, data: unknown, options?: unknown): Promise<unknown>;
2634
- delete(id: string, options?: unknown): Promise<unknown>;
2635
- /**
2636
- * The repository's native primary key field. When set, Arc's BaseController
2637
- * will pass route params through to `update()`/`delete()`/`restore()` calls
3049
+ /**
3050
+ * The repository's native primary key field. When set, arc's BaseController
3051
+ * passes route params through to `update()`/`delete()`/`restore()` calls
2638
3052
  * unchanged instead of translating them to `_id`.
2639
3053
  *
2640
- * Set this to match your `defineResource({ idField })` for repositories that
2641
- * natively look up by a custom field (e.g. MongoKit's
2642
- * `new Repository(Model, [], {}, { idField: 'id' })`). Without it, Arc will
2643
- * try to translate route ids → fetched doc's `_id` which 404s on repos that
2644
- * don't key on `_id`.
3054
+ * Match this to your `defineResource({ idField })` for repositories that
3055
+ * natively look up by a custom field (e.g. mongokit's
3056
+ * `new Repository(Model, [], {}, { idField: 'id' })`). Without it, arc
3057
+ * will try to translate route ids → fetched doc's `_id`, which 404s on
3058
+ * repos that don't key on `_id`.
2645
3059
  *
2646
- * Defaults to `'_id'` (Mongo). Repositories that always use `_id` may omit it.
3060
+ * Defaults to `'_id'` (Mongo). Kits that always use `_id` may omit it.
2647
3061
  */
2648
3062
  readonly idField?: string;
2649
- /** Find single doc by compound filter — used by AccessControl for idField + org/policy scoping.
2650
- * Without this, Arc falls back to getById + post-fetch security checks. */
2651
- getOne?(filter: Record<string, unknown>, options?: unknown): Promise<unknown>;
2652
- getBySlug?(slug: string, options?: unknown): Promise<unknown>;
2653
- getDeleted?(options?: unknown): Promise<unknown>;
2654
- restore?(id: string): Promise<unknown>;
2655
- getTree?(options?: unknown): Promise<unknown>;
2656
- getChildren?(parentId: string, options?: unknown): Promise<unknown>;
2657
- createMany?(items: unknown[], options?: unknown): Promise<unknown>;
2658
- updateMany?(filter: Record<string, unknown>, data: unknown): Promise<unknown>;
2659
- deleteMany?(filter: Record<string, unknown>): Promise<unknown>;
3063
+ getAll(params?: PaginationParams, options?: QueryOptions): Promise<unknown>;
3064
+ getById(id: string, options?: QueryOptions): Promise<unknown>;
3065
+ create(data: unknown, options?: WriteOptions): Promise<unknown>;
3066
+ update(id: string, data: unknown, options?: WriteOptions): Promise<unknown>;
3067
+ /**
3068
+ * Delete by primary key. Pass `{ mode: 'hard' }` to bypass soft-delete
3069
+ * interception (required by arc's hard-delete flow — `?hard=true` on
3070
+ * the DELETE route forwards this option).
3071
+ */
3072
+ delete(id: string, options?: DeleteOptions): Promise<unknown>;
3073
+ /**
3074
+ * Find a single doc by compound filter. Used by AccessControl for
3075
+ * `idField + org + policy` scoping. Without this, arc falls back to
3076
+ * `getById` + post-fetch security checks (slower, and 404s on custom
3077
+ * idFields that live outside the user's scope).
3078
+ */
3079
+ getOne?(filter: Record<string, unknown>, options?: QueryOptions): Promise<unknown>;
3080
+ /** Alias many kits expose alongside `getOne`. Arc checks both. */
3081
+ getByQuery?(filter: Record<string, unknown>, options?: QueryOptions): Promise<unknown>;
3082
+ count?(filter?: Record<string, unknown>, options?: QueryOptions): Promise<number>;
3083
+ exists?(filter: Record<string, unknown>, options?: QueryOptions): Promise<boolean | {
3084
+ _id: unknown;
3085
+ } | null>;
3086
+ distinct?<T = unknown>(field: string, filter?: Record<string, unknown>, options?: QueryOptions): Promise<T[]>;
3087
+ findAll?(filter?: Record<string, unknown>, options?: QueryOptions): Promise<unknown[]>;
3088
+ getOrCreate?(filter: Record<string, unknown>, data: unknown, options?: WriteOptions): Promise<unknown>;
3089
+ createMany?(items: unknown[], options?: WriteOptions): Promise<unknown[]>;
3090
+ updateMany?(filter: Record<string, unknown>, data: Record<string, unknown>, options?: WriteOptions): Promise<UpdateManyResult>;
3091
+ deleteMany?(filter: Record<string, unknown>, options?: DeleteOptions): Promise<DeleteManyResult>;
3092
+ bulkWrite?: unknown;
3093
+ restore?(id: string, options?: QueryOptions): Promise<unknown>;
3094
+ getDeleted?(params?: PaginationParams, options?: QueryOptions): Promise<PaginationResult<unknown> | unknown[]>;
3095
+ aggregate?: unknown;
3096
+ aggregatePaginate?: unknown;
3097
+ withTransaction?<T>(callback: (session: RepositorySession) => Promise<T>, options?: Record<string, unknown>): Promise<T>;
3098
+ getBySlug?(slug: string, options?: QueryOptions): Promise<unknown>;
3099
+ getTree?(options?: QueryOptions): Promise<unknown>;
3100
+ getChildren?(parentId: string, options?: QueryOptions): Promise<unknown>;
2660
3101
  [key: string]: unknown;
2661
3102
  }
2662
3103
  interface DataAdapter<TDoc = unknown> {
2663
3104
  /**
2664
- * Repository implementing CRUD operations
2665
- * Accepts CrudRepository, MongoKit Repository, or any compatible object
3105
+ * Repository implementing CRUD operations. Accepts the typed
3106
+ * `CrudRepository<TDoc>` or the loose `RepositoryLike` arc checks
3107
+ * capabilities at runtime via feature detection.
2666
3108
  */
2667
3109
  repository: CrudRepository<TDoc> | RepositoryLike;
2668
3110
  /** Adapter identifier for introspection */
@@ -2753,4 +3195,4 @@ interface ValidationResult {
2753
3195
  }
2754
3196
  type AdapterFactory<TDoc> = (config: unknown) => DataAdapter<TDoc>;
2755
3197
  //#endregion
2756
- export { PresetFunction as $, QueryOptions as $t, EventsDecorator as A, BaseController as At, InferResourceDoc as B, FastifyHandler as Bt, ConfigError as C, defineHook as Cn, TypedResourceConfig as Ct, CrudRouterOptions as D, ValidationResult$1 as Dt, CrudRouteKey as E, ValidateOptions as Et, GracefulShutdownOptions as F, BodySanitizerConfig as Ft, LookupOption as G, RegisterOptions as Gt, IntrospectionPluginOptions as H, IControllerResponse as Ht, HealthCheck as I, AccessControl as It, ObjectId as J, defineResource as Jt, MiddlewareConfig as K, ResourceRegistry as Kt, HealthOptions as L, AccessControlConfig as Lt, FastifyWithAuth as M, QueryResolver as Mt, FastifyWithDecorators as N, QueryResolverConfig as Nt, CrudSchemas as O, envelope as Ot, FieldRule as P, BodySanitizer as Pt, PopulateOption as Q, PaginationParams as Qt, InferAdapterDoc as R, ControllerHandler as Rt, AuthenticatorContext as S, createHookSystem as Sn, TypedRepository as St, CrudController as T, UserOrganization as Tt, JWTPayload as U, IRequestContext as Ut, IntrospectionData as V, IController as Vt, JwtContext as W, RouteHandler as Wt, OwnershipCheck as X, InferDoc as Xt, OpenApiSchemas as Y, CrudRepository as Yt, ParsedQuery as Z, PaginatedResult as Zt, ArcInternalMetadata as _, afterDelete as _n, RouteMcpConfig as _t, RelationMetadata as a, PipelineContext as an, RegistryStats as at, AuthPluginOptions as b, beforeDelete as bn, TokenPair as bt, ValidationResult as c, DefineHookOptions as cn, RequestWithExtras as ct, ActionHandlerFn as d, HookOperation as dn, ResourceHookContext as dt, Guard as en, PresetHook as et, ActionsMap as f, HookPhase as fn, ResourceHooks as ft, ArcDecorator as g, afterCreate as gn, RouteHandlerMethod$1 as gt, ApiResponse as h, HookSystemOptions as hn, RouteDefinition as ht, FieldMetadata as i, PipelineConfig as in, RegistryEntry as it, FastifyRequestExtras as j, BaseControllerOptions as jt, EventDefinition as k, getUserId as kt, ActionDefinition as l, HookContext as ln, ResourceCacheConfig as lt, AnyRecord as m, HookSystem as mn, ResourcePermissions as mt, AdapterSchemaContext as n, NextFunction as nn, QueryParserInterface as nt, RepositoryLike as o, PipelineStep as on, RequestContext as ot, AdditionalRoute as p, HookRegistration as pn, ResourceMetadata as pt, MiddlewareHandler as q, ResourceDefinition as qt, DataAdapter as r, OperationFilter as rn, RateLimitConfig as rt, SchemaMetadata as s, Transform as sn, RequestIdOptions as st, AdapterFactory as t, Interceptor as tn, PresetResult as tt, ActionEntry as u, HookHandler as un, ResourceConfig as ut, ArcRequest as v, afterUpdate as vn, RouteSchemaOptions as vt, ControllerQueryOptions as w, UserLike as wt, Authenticator as x, beforeUpdate as xn, TypedController as xt, AuthHelpers as y, beforeCreate as yn, ServiceContext as yt, InferDocType as z, ControllerLike as zt };
3198
+ export { PresetFunction as $, DeleteOptions as $t, EventsDecorator as A, beforeCreate as An, BaseController as At, InferResourceDoc as B, FastifyHandler as Bt, ConfigError as C, HookPhase as Cn, TypedResourceConfig as Ct, CrudRouterOptions as D, afterCreate as Dn, ValidationResult$1 as Dt, CrudRouteKey as E, HookSystemOptions as En, ValidateOptions as Et, GracefulShutdownOptions as F, BodySanitizerConfig as Ft, LookupOption as G, RegisterOptions as Gt, IntrospectionPluginOptions as H, IControllerResponse as Ht, HealthCheck as I, AccessControl as It, ObjectId as J, defineResource as Jt, MiddlewareConfig as K, ResourceRegistry as Kt, HealthOptions as L, AccessControlConfig as Lt, FastifyWithAuth as M, beforeUpdate as Mn, QueryResolver as Mt, FastifyWithDecorators as N, createHookSystem as Nn, QueryResolverConfig as Nt, CrudSchemas as O, afterDelete as On, envelope as Ot, FieldRule as P, defineHook as Pn, BodySanitizer as Pt, PopulateOption as Q, DeleteManyResult as Qt, InferAdapterDoc as R, ControllerHandler as Rt, AuthenticatorContext as S, HookOperation as Sn, TypedRepository as St, CrudController as T, HookSystem as Tn, UserOrganization as Tt, JWTPayload as U, IRequestContext as Ut, IntrospectionData as V, IController as Vt, JwtContext as W, RouteHandler as Wt, OwnershipCheck as X, BulkWriteResult as Xt, OpenApiSchemas as Y, BulkWriteOperation as Yt, ParsedQuery as Z, CrudRepository as Zt, ArcInternalMetadata as _, PipelineStep as _n, RouteMcpConfig as _t, RelationMetadata as a, PaginationParams as an, RegistryStats as at, AuthPluginOptions as b, HookContext as bn, TokenPair as bt, ValidationResult as c, RepositorySession as cn, RequestWithExtras as ct, ActionHandlerFn as d, Guard as dn, ResourceHookContext as dt, DeleteResult as en, PresetHook as et, ActionsMap as f, Interceptor as fn, ResourceHooks as ft, ArcDecorator as g, PipelineContext as gn, RouteHandlerMethod$1 as gt, ApiResponse as h, PipelineConfig as hn, RouteDefinition as ht, FieldMetadata as i, PaginatedResult as in, RegistryEntry as it, FastifyRequestExtras as j, beforeDelete as jn, BaseControllerOptions as jt, EventDefinition as k, afterUpdate as kn, getUserId as kt, ActionDefinition as l, UpdateManyResult as ln, ResourceCacheConfig as lt, AnyRecord as m, OperationFilter as mn, ResourcePermissions as mt, AdapterSchemaContext as n, KeysetPaginatedResult as nn, QueryParserInterface as nt, RepositoryLike as o, PaginationResult as on, RequestContext as ot, AdditionalRoute as p, NextFunction as pn, ResourceMetadata as pt, MiddlewareHandler as q, ResourceDefinition as qt, DataAdapter as r, OffsetPaginatedResult as rn, RateLimitConfig as rt, SchemaMetadata as s, QueryOptions as sn, RequestIdOptions as st, AdapterFactory as t, InferDoc as tn, PresetResult as tt, ActionEntry as u, WriteOptions as un, ResourceConfig as ut, ArcRequest as v, Transform as vn, RouteSchemaOptions as vt, ControllerQueryOptions as w, HookRegistration as wn, UserLike as wt, Authenticator as x, HookHandler as xn, TypedController as xt, AuthHelpers as y, DefineHookOptions as yn, ServiceContext as yt, InferDocType as z, ControllerLike as zt };