@classytic/arc 2.8.5 → 2.9.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.
- package/README.md +88 -5
- package/dist/{BaseController-DAGGc5Xn.mjs → BaseController-Vu2yc56T.mjs} +188 -102
- package/dist/EventTransport-CqZ8FyM_.d.mts +293 -0
- package/dist/adapters/index.d.mts +2 -2
- package/dist/audit/index.d.mts +100 -11
- package/dist/audit/index.mjs +71 -18
- package/dist/auth/index.d.mts +16 -8
- package/dist/auth/index.mjs +13 -6
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/{betterAuthOpenApi-BuUcUEJq.mjs → betterAuthOpenApi--rdY15Ld.mjs} +1 -1
- package/dist/cache/index.d.mts +2 -2
- package/dist/cache/index.mjs +2 -2
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +4 -5
- package/dist/{core-F0QoWBt2.mjs → core-DNncu0xF.mjs} +1 -1
- package/dist/{createActionRouter-BORM8f17.mjs → createActionRouter-DH1YFL9m.mjs} +3 -3
- package/dist/{createApp-B1EY8zxa.mjs → createApp-CBJUJKGP.mjs} +13 -12
- package/dist/{defineResource-tcgySDo1.mjs → defineResource-C__jkwvs.mjs} +22 -57
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +1 -1
- package/dist/dynamic/index.d.mts +1 -1
- package/dist/dynamic/index.mjs +3 -3
- package/dist/{elevation-DtFxrG0s.mjs → elevation-DxQ6ACbt.mjs} +21 -7
- package/dist/{errorHandler-f869_8PQ.mjs → errorHandler-CZDW4EXS.mjs} +59 -7
- package/dist/{errorHandler-Bah5JhBd.d.mts → errorHandler-DixGcttC.d.mts} +37 -2
- package/dist/{eventPlugin-D9DKB2zM.d.mts → eventPlugin-BxvaCIZF.d.mts} +14 -2
- package/dist/{eventPlugin-CDjVTM82.mjs → eventPlugin-Dl7MoVWH.mjs} +83 -5
- package/dist/events/index.d.mts +147 -36
- package/dist/events/index.mjs +338 -101
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/factory/index.d.mts +1 -1
- package/dist/factory/index.mjs +2 -2
- package/dist/{fields-DpZQa_Q3.d.mts → fields-BC7zcmI9.d.mts} +15 -3
- package/dist/{fields-ipsbIRPK.mjs → fields-CU6FlaDV.mjs} +18 -5
- package/dist/{filesUpload-C7r7HIeA.mjs → filesUpload-q8oHt--L.mjs} +65 -7
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +29 -5
- package/dist/idempotency/index.mjs +111 -2
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-BLXBmWud.d.mts → index-C-xjcA6F.d.mts} +1 -1
- package/dist/{index-DtDzOBn8.d.mts → index-Cibkchnx.d.mts} +3 -134
- package/dist/{index-C1meYuDn.d.mts → index-CtGKT0lf.d.mts} +1 -1
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +9 -9
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +26 -8
- package/dist/integrations/mcp/index.mjs +96 -17
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/webhooks.d.mts +5 -0
- package/dist/integrations/webhooks.mjs +6 -0
- package/dist/{interface-CMRutPfe.d.mts → interface-YrWsmKqE.d.mts} +287 -179
- package/dist/{openapi-CbKUJY_m.mjs → openapi-CXuTG1M9.mjs} +2 -2
- package/dist/org/index.d.mts +1 -1
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +3 -3
- package/dist/{permissions-CH4cNwJi.mjs → permissions-oNZawnkR.mjs} +1 -1
- package/dist/plugins/index.d.mts +7 -7
- package/dist/plugins/index.mjs +11 -11
- package/dist/plugins/response-cache.mjs +1 -1
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/policies/index.d.mts +25 -32
- package/dist/presets/filesUpload.d.mts +26 -4
- package/dist/presets/filesUpload.mjs +1 -1
- package/dist/presets/index.d.mts +3 -2
- package/dist/presets/index.mjs +4 -3
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +1 -1
- package/dist/presets/search.d.mts +91 -0
- package/dist/presets/search.mjs +150 -0
- package/dist/{presets-C2xgzW6x.mjs → presets-hM4WhNWY.mjs} +1 -1
- package/dist/{queryCachePlugin-BJJGBTlu.d.mts → queryCachePlugin-CnTZZTC5.d.mts} +1 -1
- package/dist/{queryCachePlugin-BH-fidlv.mjs → queryCachePlugin-DbUVroUG.mjs} +2 -2
- package/dist/{redis-BM00zaPB.d.mts → redis-MXLp1oOf.d.mts} +1 -1
- package/dist/{redis-stream-CrsfUmPt.d.mts → redis-stream-Bz-4q96t.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +2 -2
- package/dist/{resourceToTools-8s-EsCCe.mjs → resourceToTools-C3cWymnW.mjs} +64 -47
- package/dist/rpc/index.d.mts +1 -1
- package/dist/rpc/index.mjs +1 -1
- package/dist/{schemaConverter-Y7nCYaLJ.mjs → schemaConverter-BxFDdtXu.mjs} +1 -1
- package/dist/scope/index.mjs +1 -1
- package/dist/{sse-Ad7ypl9e.mjs → sse-CJpt7LGI.mjs} +1 -1
- package/dist/store-helpers-DFiZl5TL.mjs +57 -0
- package/dist/testing/index.d.mts +5 -14
- package/dist/testing/index.mjs +21 -75
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/types/index.d.mts +2 -2
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-BsbNMEDR.d.mts → types-CoSzA-s-.d.mts} +1 -1
- package/dist/{types-Ch9pTQbf.d.mts → types-CunEX4UX.d.mts} +10 -8
- package/dist/utils/index.d.mts +4 -4
- package/dist/utils/index.mjs +6 -6
- package/dist/{utils-yYT3HDXt.mjs → utils-B7FuRr9w.mjs} +1 -1
- package/package.json +8 -11
- package/skills/arc/SKILL.md +92 -14
- package/skills/arc/references/auth.md +94 -0
- package/skills/arc/references/events.md +200 -12
- package/skills/arc/references/mcp.md +4 -17
- package/skills/arc/references/multi-tenancy.md +43 -0
- package/skills/arc/references/production.md +34 -19
- package/dist/EventTransport-BXja8NOc.d.mts +0 -135
- package/dist/audit/mongodb.d.mts +0 -2
- package/dist/audit/mongodb.mjs +0 -2
- package/dist/idempotency/mongodb.d.mts +0 -2
- package/dist/idempotency/mongodb.mjs +0 -123
- package/dist/mongodb-BsP-WbhN.d.mts +0 -127
- package/dist/mongodb-CTcp0hQZ.d.mts +0 -80
- package/dist/mongodb-Utc5k_-0.mjs +0 -90
- /package/dist/{HookSystem-HprTmvVY.mjs → HookSystem-BjFu7zf1.mjs} +0 -0
- /package/dist/{ResourceRegistry-C6uXlWe3.mjs → ResourceRegistry-Dq3_zBQP.mjs} +0 -0
- /package/dist/{applyPermissionResult-D6GPMsvh.mjs → applyPermissionResult-bqGpo9ML.mjs} +0 -0
- /package/dist/{caching-IMuYVjTL.mjs → caching-CjybdRwx.mjs} +0 -0
- /package/dist/{circuitBreaker-dTtG-UyS.d.mts → circuitBreaker-CvXkjfrW.d.mts} +0 -0
- /package/dist/{circuitBreaker-cmi5XDv5.mjs → circuitBreaker-l18oRgL5.mjs} +0 -0
- /package/dist/{errors-Ck2h67pm.d.mts → errors-BI8kEKsO.d.mts} +0 -0
- /package/dist/{errors-BF2bIOIS.mjs → errors-CqWnSqM-.mjs} +0 -0
- /package/dist/{externalPaths-BnkYrNzp.d.mts → externalPaths-Bapitwvd.d.mts} +0 -0
- /package/dist/{interface-DfLGcus7.d.mts → interface-B-pe8fhj.d.mts} +0 -0
- /package/dist/{interface-4y979v99.d.mts → interface-DplgQO2e.d.mts} +0 -0
- /package/dist/{loadResources-PWd0OCpV.mjs → loadResources-Bksk8ydA.mjs} +0 -0
- /package/dist/{logger-D1YrIImS.mjs → logger-CDjpjySd.mjs} +0 -0
- /package/dist/{memory-Cp7_cAko.mjs → memory-BFAYkf8H.mjs} +0 -0
- /package/dist/{metrics-B-PU4-Yu.mjs → metrics-TuOmguhi.mjs} +0 -0
- /package/dist/{queryParser-CgCtsjti.mjs → queryParser-Cs-6SHQK.mjs} +0 -0
- /package/dist/{registry-BiTKT1Dg.mjs → registry-B0Wl7uVV.mjs} +0 -0
- /package/dist/{replyHelpers-CxkYGT81.mjs → replyHelpers-BLojtuvR.mjs} +0 -0
- /package/dist/{requestContext-DYvHl113.mjs → requestContext-DYtmNpm5.mjs} +0 -0
- /package/dist/{sessionManager-DDCmiNIo.d.mts → sessionManager-D-oNWHz3.d.mts} +0 -0
- /package/dist/{storage-Dfzt4VTl.d.mts → storage-BwGQXUpd.d.mts} +0 -0
- /package/dist/{tracing-DdN2-wHJ.d.mts → tracing-xqXzWeaf.d.mts} +0 -0
- /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-Cj5Rgvlg.mjs} +0 -0
- /package/dist/{versioning-CDugduqI.mjs → versioning-Cm8qoFDg.mjs} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as RequestScope } from "./types-BD85MlEK.mjs";
|
|
2
|
-
import { n as FieldPermissionMap } from "./fields-
|
|
2
|
+
import { n as FieldPermissionMap } from "./fields-BC7zcmI9.mjs";
|
|
3
3
|
import { i as UserBase, t as PermissionCheck } from "./types-DZi1aYhm.mjs";
|
|
4
4
|
import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, RouteHandlerMethod, RouteHandlerMethod as RouteHandlerMethod$1 } from "fastify";
|
|
5
5
|
|
|
@@ -472,6 +472,23 @@ interface DeleteOptions extends QueryOptions {
|
|
|
472
472
|
*/
|
|
473
473
|
mode?: "hard" | "soft";
|
|
474
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* Options for the atomic `findOneAndUpdate` compare-and-set primitive.
|
|
477
|
+
*
|
|
478
|
+
* Kit-agnostic — every backend implements the core knobs (`sort`,
|
|
479
|
+
* `returnDocument`, `upsert`, `session`). The index signature lets kits
|
|
480
|
+
* thread through backend-specific options (mongoose `arrayFilters`,
|
|
481
|
+
* `collation`, `maxTimeMS`; prisma `select`, `include`; etc.) without arc
|
|
482
|
+
* having to know about them.
|
|
483
|
+
*/
|
|
484
|
+
interface FindOneAndUpdateOptions extends QueryOptions {
|
|
485
|
+
/** Sort spec disambiguating when filter matches multiple docs (FIFO claim). */
|
|
486
|
+
sort?: Record<string, unknown>;
|
|
487
|
+
/** Return doc state before or after the update. Default: 'after'. */
|
|
488
|
+
returnDocument?: "before" | "after";
|
|
489
|
+
/** Insert if no doc matches. Default: false. */
|
|
490
|
+
upsert?: boolean;
|
|
491
|
+
}
|
|
475
492
|
/**
|
|
476
493
|
* Result of a single delete operation.
|
|
477
494
|
*
|
|
@@ -593,10 +610,24 @@ interface PaginationParams<TDoc = unknown> {
|
|
|
593
610
|
*
|
|
594
611
|
* `method` is optional so legacy adapters returning the bare `{ docs, page,
|
|
595
612
|
* limit, total, pages, hasNext, hasPrev }` shape still satisfy the type.
|
|
613
|
+
*
|
|
614
|
+
* ## Extending with kit-specific fields
|
|
615
|
+
*
|
|
616
|
+
* Kits are free to return extra metadata (query timing, region, index hit-rate,
|
|
617
|
+
* cursor version, …). Supply them via the `TExtra` generic and they appear
|
|
618
|
+
* at the top level alongside the standard fields — no wrapper object, no
|
|
619
|
+
* narrowing gymnastics:
|
|
620
|
+
*
|
|
621
|
+
* ```ts
|
|
622
|
+
* type ProductPage = OffsetPaginatedResult<Product, { tookMs: number; region: string }>;
|
|
623
|
+
* // ^? { method?: "offset"; docs: Product[]; page: number; ...; tookMs: number; region: string }
|
|
624
|
+
* ```
|
|
625
|
+
*
|
|
626
|
+
* Default `TExtra = {}` preserves the standard shape for every caller that
|
|
627
|
+
* doesn't care about extras.
|
|
596
628
|
*/
|
|
597
|
-
|
|
598
|
-
/** Discriminator — omitted or `"offset"` */
|
|
599
|
-
method?: "offset";
|
|
629
|
+
type OffsetPaginatedResult<TDoc, TExtra = {}> = {
|
|
630
|
+
/** Discriminator — omitted or `"offset"` */method?: "offset";
|
|
600
631
|
docs: TDoc[];
|
|
601
632
|
page: number;
|
|
602
633
|
limit: number;
|
|
@@ -604,20 +635,29 @@ interface OffsetPaginatedResult<TDoc> {
|
|
|
604
635
|
pages: number;
|
|
605
636
|
hasNext: boolean;
|
|
606
637
|
hasPrev: boolean;
|
|
607
|
-
|
|
638
|
+
/**
|
|
639
|
+
* Optional performance warning surfaced by the underlying kit for deep
|
|
640
|
+
* offset pagination (e.g. mongokit emits one when `page * limit` exceeds
|
|
641
|
+
* its `deepPageThreshold`). Kits that don't produce warnings leave it
|
|
642
|
+
* absent.
|
|
643
|
+
*/
|
|
644
|
+
warning?: string;
|
|
645
|
+
} & TExtra;
|
|
608
646
|
/**
|
|
609
647
|
* Keyset-based paginated result (returned when `sort` is provided without
|
|
610
648
|
* `page`). Ideal for infinite scroll — no `count()` query, O(1) per page.
|
|
649
|
+
*
|
|
650
|
+
* Cursor tokens are opaque by design; kits encode their own versioning
|
|
651
|
+
* (mongokit carries a `ver` field inside the token). Surface kit-specific
|
|
652
|
+
* extras (e.g. `cursor.version`, `queryPlan`) via the `TExtra` generic.
|
|
611
653
|
*/
|
|
612
|
-
|
|
613
|
-
/** Discriminator — always `"keyset"` */
|
|
614
|
-
method: "keyset";
|
|
654
|
+
type KeysetPaginatedResult<TDoc, TExtra = {}> = {
|
|
655
|
+
/** Discriminator — always `"keyset"` */method: "keyset";
|
|
615
656
|
docs: TDoc[];
|
|
616
657
|
limit: number;
|
|
617
|
-
hasMore: boolean;
|
|
618
|
-
/** Opaque cursor token for the next page, or `null` at the end */
|
|
658
|
+
hasMore: boolean; /** Opaque cursor token for the next page, or `null` at the end */
|
|
619
659
|
next: string | null;
|
|
620
|
-
}
|
|
660
|
+
} & TExtra;
|
|
621
661
|
/**
|
|
622
662
|
* Discriminated union of all pagination result shapes.
|
|
623
663
|
* Consumers narrow on the `method` discriminator.
|
|
@@ -632,14 +672,14 @@ interface KeysetPaginatedResult<TDoc> {
|
|
|
632
672
|
* }
|
|
633
673
|
* ```
|
|
634
674
|
*/
|
|
635
|
-
type PaginationResult<TDoc> = OffsetPaginatedResult<TDoc> | KeysetPaginatedResult<TDoc>;
|
|
675
|
+
type PaginationResult<TDoc, TExtra = {}> = OffsetPaginatedResult<TDoc, TExtra> | KeysetPaginatedResult<TDoc, TExtra>;
|
|
636
676
|
/**
|
|
637
677
|
* Legacy alias. Existing code typed as `PaginatedResult<TDoc>` continues
|
|
638
678
|
* to work unchanged — it resolves to the offset shape, which is the most
|
|
639
679
|
* common. New code should prefer `PaginationResult<TDoc>` for the full
|
|
640
680
|
* discriminated union.
|
|
641
681
|
*/
|
|
642
|
-
type PaginatedResult<TDoc> = OffsetPaginatedResult<TDoc>;
|
|
682
|
+
type PaginatedResult<TDoc, TExtra = {}> = OffsetPaginatedResult<TDoc, TExtra>;
|
|
643
683
|
/**
|
|
644
684
|
* Standard CRUD Repository Interface
|
|
645
685
|
*
|
|
@@ -691,11 +731,47 @@ interface CrudRepository<TDoc> {
|
|
|
691
731
|
create(data: Partial<TDoc>, options?: WriteOptions): Promise<TDoc>;
|
|
692
732
|
/** Update a document by primary key. Returns the updated doc or null. */
|
|
693
733
|
update(id: string, data: Partial<TDoc>, options?: WriteOptions): Promise<TDoc | null>;
|
|
734
|
+
/**
|
|
735
|
+
* Atomic compare-and-set — match one document and mutate it in a single
|
|
736
|
+
* round-trip. Returns the resulting document (post-update by default), or
|
|
737
|
+
* `null` when no match and `upsert` is false.
|
|
738
|
+
*
|
|
739
|
+
* **Why this is a first-class contract method (vs an optional capability):**
|
|
740
|
+
* arc's outbox, distributed-lock, and workflow-semaphore patterns all
|
|
741
|
+
* depend on compare-and-set semantics. Without an atomic primitive they
|
|
742
|
+
* either race or require a two-phase pattern that doesn't translate
|
|
743
|
+
* cleanly to every backend. Every kit targeting arc 2.10+ should
|
|
744
|
+
* implement this.
|
|
745
|
+
*
|
|
746
|
+
* Plugins wire into `before:findOneAndUpdate` / `after:findOneAndUpdate`
|
|
747
|
+
* and read the filter from `context.query` — the canonical field name.
|
|
748
|
+
*
|
|
749
|
+
* @example FIFO claim-lease for an outbox relay
|
|
750
|
+
* ```ts
|
|
751
|
+
* const claimed = await outboxRepo.findOneAndUpdate(
|
|
752
|
+
* { status: 'pending', visibleAt: { $lte: new Date() } },
|
|
753
|
+
* { $set: { status: 'processing', leasedBy: workerId, leaseExpiresAt } },
|
|
754
|
+
* { sort: { createdAt: 1 }, returnDocument: 'after' },
|
|
755
|
+
* );
|
|
756
|
+
* ```
|
|
757
|
+
*/
|
|
758
|
+
findOneAndUpdate?(filter: Record<string, unknown>, update: Record<string, unknown> | Record<string, unknown>[], options?: FindOneAndUpdateOptions): Promise<TDoc | null>;
|
|
694
759
|
/**
|
|
695
760
|
* Delete a document by primary key. Pass `{ mode: 'hard' }` to bypass
|
|
696
761
|
* soft-delete interception.
|
|
697
762
|
*/
|
|
698
763
|
delete(id: string, options?: DeleteOptions): Promise<DeleteResult>;
|
|
764
|
+
/**
|
|
765
|
+
* Classify an error thrown by a write as a unique-index / duplicate-key
|
|
766
|
+
* violation. Used by arc's idempotency and outbox adapters to distinguish
|
|
767
|
+
* "this write already landed" from "transient DB error".
|
|
768
|
+
*
|
|
769
|
+
* Each backend signals this differently (MongoDB `code 11000`,
|
|
770
|
+
* Prisma `P2002`, Postgres `23505`, etc.) — classification lives in the
|
|
771
|
+
* kit that knows its driver. Arc falls back to a Mongo default if absent;
|
|
772
|
+
* non-mongo kits must implement to participate in idempotency semantics.
|
|
773
|
+
*/
|
|
774
|
+
isDuplicateKeyError?(err: unknown): boolean;
|
|
699
775
|
/**
|
|
700
776
|
* Find a single doc by a compound filter. Used by arc's AccessControl to
|
|
701
777
|
* combine `idField + orgId + policy` in one query. Without it, arc falls
|
|
@@ -771,6 +847,37 @@ interface CrudRepository<TDoc> {
|
|
|
771
847
|
* — structurally `unknown`, type-safe at the call site.
|
|
772
848
|
*/
|
|
773
849
|
aggregatePaginate?: unknown;
|
|
850
|
+
/**
|
|
851
|
+
* Fluent aggregation pipeline builder.
|
|
852
|
+
*
|
|
853
|
+
* Same `unknown` reasoning as `aggregate` — each kit returns its own
|
|
854
|
+
* builder class (mongokit returns `AggregationBuilder`, others may
|
|
855
|
+
* return SQL builders, Prisma chains, etc.). Cast at the call site:
|
|
856
|
+
*
|
|
857
|
+
* ```ts
|
|
858
|
+
* import type { AggregationBuilder } from '@classytic/mongokit';
|
|
859
|
+
* const pipeline = (repo.buildAggregation?.() as AggregationBuilder)
|
|
860
|
+
* .match({ status: 'active' })
|
|
861
|
+
* .group({ _id: '$category', count: { $sum: 1 } })
|
|
862
|
+
* .build();
|
|
863
|
+
* ```
|
|
864
|
+
*/
|
|
865
|
+
buildAggregation?(): unknown;
|
|
866
|
+
/**
|
|
867
|
+
* Fluent `$lookup` stage builder. Same kit-specific reasoning as
|
|
868
|
+
* `buildAggregation` — cast at the call site.
|
|
869
|
+
*
|
|
870
|
+
* ```ts
|
|
871
|
+
* import type { LookupBuilder } from '@classytic/mongokit';
|
|
872
|
+
* const stages = (repo.buildLookup?.('departments') as LookupBuilder)
|
|
873
|
+
* .localField('deptSlug')
|
|
874
|
+
* .foreignField('slug')
|
|
875
|
+
* .as('department')
|
|
876
|
+
* .single()
|
|
877
|
+
* .build();
|
|
878
|
+
* ```
|
|
879
|
+
*/
|
|
880
|
+
buildLookup?(from?: string): unknown;
|
|
774
881
|
/**
|
|
775
882
|
* Run `callback` inside a transaction. Adapters should auto-retry on
|
|
776
883
|
* transient transaction errors and expose a `session` the callback can
|
|
@@ -827,17 +934,7 @@ declare class ResourceDefinition<TDoc = AnyRecord> {
|
|
|
827
934
|
readonly schemaOptions: RouteSchemaOptions;
|
|
828
935
|
readonly customSchemas: CrudSchemas;
|
|
829
936
|
readonly permissions: ResourcePermissions;
|
|
830
|
-
readonly
|
|
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[];
|
|
937
|
+
readonly routes: readonly RouteDefinition[];
|
|
841
938
|
readonly middlewares: MiddlewareConfig;
|
|
842
939
|
readonly routeGuards?: RouteHandlerMethod$1[];
|
|
843
940
|
readonly disableDefaultRoutes: boolean;
|
|
@@ -1205,6 +1302,15 @@ interface ControllerLike {
|
|
|
1205
1302
|
}
|
|
1206
1303
|
//#endregion
|
|
1207
1304
|
//#region src/core/AccessControl.d.ts
|
|
1305
|
+
/** Denial reason codes returned by `fetchDetailed()`. */
|
|
1306
|
+
type FetchDenialReason = "NOT_FOUND" | "POLICY_FILTERED" | "ORG_SCOPE_DENIED";
|
|
1307
|
+
/** Result of a detailed fetch with access control. */
|
|
1308
|
+
interface FetchResult<TDoc> {
|
|
1309
|
+
/** The document, or null if denied. */
|
|
1310
|
+
doc: TDoc | null;
|
|
1311
|
+
/** Null when the doc was found. A string code when denied. */
|
|
1312
|
+
reason: FetchDenialReason | null;
|
|
1313
|
+
}
|
|
1208
1314
|
interface AccessControlConfig {
|
|
1209
1315
|
/** Field name used for multi-tenant scoping (default: 'organizationId'). Set to `false` to disable org filtering. */
|
|
1210
1316
|
tenantField: string | false;
|
|
@@ -1265,6 +1371,19 @@ declare class AccessControl {
|
|
|
1265
1371
|
* buildIdFilter -> getOne (or getById + checkOrgScope + checkPolicyFilters)
|
|
1266
1372
|
*/
|
|
1267
1373
|
fetchWithAccessControl<TDoc>(id: string, req: IRequestContext, repository: AccessControlRepository, queryOptions?: QueryOptions): Promise<TDoc | null>;
|
|
1374
|
+
/**
|
|
1375
|
+
* Same as `fetchWithAccessControl` but returns a structured result with
|
|
1376
|
+
* a denial reason so callers can distinguish "doc doesn't exist" from
|
|
1377
|
+
* "doc exists but was filtered by policy/org scope" from "repo threw".
|
|
1378
|
+
*
|
|
1379
|
+
* Codes:
|
|
1380
|
+
* - `null` — doc was found, no denial
|
|
1381
|
+
* - `'NOT_FOUND'` — doc genuinely doesn't exist in the DB
|
|
1382
|
+
* - `'POLICY_FILTERED'` — doc exists but the request's policy filters exclude it
|
|
1383
|
+
* - `'ORG_SCOPE_DENIED'` — doc exists but the caller's org context doesn't match
|
|
1384
|
+
* - `'REPO_ERROR'` — the repository threw a "not found" error (mongokit style)
|
|
1385
|
+
*/
|
|
1386
|
+
fetchDetailed<TDoc>(id: string, req: IRequestContext, repository: AccessControlRepository, queryOptions?: QueryOptions): Promise<FetchResult<TDoc>>;
|
|
1268
1387
|
/**
|
|
1269
1388
|
* Post-fetch access control validation for items fetched by non-ID queries
|
|
1270
1389
|
* (e.g., getBySlug, restore). Applies org scope, policy filters, and
|
|
@@ -1300,12 +1419,27 @@ declare class AccessControl {
|
|
|
1300
1419
|
}
|
|
1301
1420
|
//#endregion
|
|
1302
1421
|
//#region src/core/BodySanitizer.d.ts
|
|
1422
|
+
/**
|
|
1423
|
+
* Policy for handling fields the caller lacks write permission for.
|
|
1424
|
+
*
|
|
1425
|
+
* - `'reject'` (default, secure): throw 403 listing the denied fields so
|
|
1426
|
+
* misconfigurations and attacks surface instead of silently disappearing.
|
|
1427
|
+
* - `'strip'` (legacy): silently drop the field and continue. Preserved for
|
|
1428
|
+
* apps that relied on the pre-2.9 behaviour — new code should not use it.
|
|
1429
|
+
*/
|
|
1430
|
+
type FieldWriteDenialPolicy = "reject" | "strip";
|
|
1303
1431
|
interface BodySanitizerConfig {
|
|
1304
1432
|
/** Schema options for field sanitization */
|
|
1305
1433
|
schemaOptions: RouteSchemaOptions;
|
|
1434
|
+
/**
|
|
1435
|
+
* What to do when a request contains fields the caller can't write.
|
|
1436
|
+
* Default: `'reject'` — surface the misconfiguration as a 403.
|
|
1437
|
+
*/
|
|
1438
|
+
onFieldWriteDenied?: FieldWriteDenialPolicy;
|
|
1306
1439
|
}
|
|
1307
1440
|
declare class BodySanitizer {
|
|
1308
1441
|
private schemaOptions;
|
|
1442
|
+
private onFieldWriteDenied;
|
|
1309
1443
|
constructor(config: BodySanitizerConfig);
|
|
1310
1444
|
/**
|
|
1311
1445
|
* Strip readonly and system-managed fields from request body.
|
|
@@ -1415,6 +1549,16 @@ interface BaseControllerOptions {
|
|
|
1415
1549
|
slugField?: string;
|
|
1416
1550
|
parentField?: string;
|
|
1417
1551
|
};
|
|
1552
|
+
/**
|
|
1553
|
+
* Policy for requests that include fields the caller can't write.
|
|
1554
|
+
*
|
|
1555
|
+
* - `'reject'` (default): 403 with the denied field names. Surfaces
|
|
1556
|
+
* misconfigurations and attempts to set protected fields instead of
|
|
1557
|
+
* silently dropping them.
|
|
1558
|
+
* - `'strip'`: legacy silent-drop behaviour. Only opt in when migrating
|
|
1559
|
+
* code that relied on the pre-2.9 permissive default.
|
|
1560
|
+
*/
|
|
1561
|
+
onFieldWriteDenied?: FieldWriteDenialPolicy;
|
|
1418
1562
|
}
|
|
1419
1563
|
/**
|
|
1420
1564
|
* Framework-agnostic base controller implementing IController.
|
|
@@ -1475,6 +1619,16 @@ declare class BaseController<TDoc = AnyRecord, TRepository extends RepositoryLik
|
|
|
1475
1619
|
* aliasing that Arc 2.6.3 introduced for repos keyed on `_id`.
|
|
1476
1620
|
*/
|
|
1477
1621
|
private resolveRepoId;
|
|
1622
|
+
/**
|
|
1623
|
+
* Centralized 404 response builder. Maps the denial reason from
|
|
1624
|
+
* `fetchDetailed()` into a structured `details.code` so consumers can
|
|
1625
|
+
* programmatically distinguish "doc doesn't exist" from "doc filtered
|
|
1626
|
+
* by policy/org scope" without parsing error strings.
|
|
1627
|
+
*
|
|
1628
|
+
* Error messages are intentionally vague in the `error` field (don't
|
|
1629
|
+
* leak whether the doc exists) — the detail is in `details.code` only.
|
|
1630
|
+
*/
|
|
1631
|
+
private notFoundResponse;
|
|
1478
1632
|
/** Resolve cache config for a specific operation, merging per-op overrides */
|
|
1479
1633
|
private resolveCacheConfig;
|
|
1480
1634
|
/**
|
|
@@ -2027,6 +2181,16 @@ interface ResourceConfig<TDoc = AnyRecord> {
|
|
|
2027
2181
|
* ```
|
|
2028
2182
|
*/
|
|
2029
2183
|
fields?: FieldPermissionMap;
|
|
2184
|
+
/**
|
|
2185
|
+
* Policy for requests that include fields the caller can't write.
|
|
2186
|
+
*
|
|
2187
|
+
* - `'reject'` (default, secure): 403 with the denied field names. Surfaces
|
|
2188
|
+
* misconfigurations and write-side permission violations instead of
|
|
2189
|
+
* silently dropping them.
|
|
2190
|
+
* - `'strip'`: legacy silent-drop behaviour — only opt in when migrating
|
|
2191
|
+
* code that relied on the pre-2.9 permissive default.
|
|
2192
|
+
*/
|
|
2193
|
+
onFieldWriteDenied?: 'reject' | 'strip';
|
|
2030
2194
|
middlewares?: MiddlewareConfig;
|
|
2031
2195
|
/**
|
|
2032
2196
|
* PreHandler guards auto-applied to **every** route on this resource
|
|
@@ -2113,22 +2277,6 @@ interface ResourceConfig<TDoc = AnyRecord> {
|
|
|
2113
2277
|
skipValidation?: boolean;
|
|
2114
2278
|
skipRegistry?: boolean;
|
|
2115
2279
|
_appliedPresets?: string[];
|
|
2116
|
-
/**
|
|
2117
|
-
* Called during plugin registration with the scoped Fastify instance.
|
|
2118
|
-
* Use for wiring singletons, reading decorators, or setting up resource-specific
|
|
2119
|
-
* services that need access to the Fastify instance.
|
|
2120
|
-
*
|
|
2121
|
-
* @example
|
|
2122
|
-
* ```typescript
|
|
2123
|
-
* defineResource({
|
|
2124
|
-
* name: 'notification',
|
|
2125
|
-
* onRegister: (fastify) => {
|
|
2126
|
-
* setSseManager(fastify.sseManager);
|
|
2127
|
-
* },
|
|
2128
|
-
* })
|
|
2129
|
-
* ```
|
|
2130
|
-
*/
|
|
2131
|
-
onRegister?: (fastify: FastifyInstance) => void | Promise<void>;
|
|
2132
2280
|
/** HTTP method for update routes. Default: 'PATCH' */
|
|
2133
2281
|
updateMethod?: 'PUT' | 'PATCH' | 'both';
|
|
2134
2282
|
/**
|
|
@@ -2231,141 +2379,6 @@ interface ResourceHooks {
|
|
|
2231
2379
|
/** Runs after delete — ctx.data is the deleted doc, ctx.meta.id has the resource ID */
|
|
2232
2380
|
afterDelete?: (ctx: ResourceHookContext) => Promise<void> | void;
|
|
2233
2381
|
}
|
|
2234
|
-
/**
|
|
2235
|
-
* Additional route definition for custom endpoints
|
|
2236
|
-
*/
|
|
2237
|
-
interface AdditionalRoute {
|
|
2238
|
-
/** HTTP method */
|
|
2239
|
-
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
2240
|
-
/** Route path (relative to resource prefix) */
|
|
2241
|
-
path: string;
|
|
2242
|
-
/**
|
|
2243
|
-
* Handler - string (controller method name) or function.
|
|
2244
|
-
*
|
|
2245
|
-
* When `wrapHandler: true`:
|
|
2246
|
-
* - `string` — calls controller method by name (e.g., `'approve'`)
|
|
2247
|
-
* - `ControllerHandler` — receives `IRequestContext`, returns `IControllerResponse`
|
|
2248
|
-
*
|
|
2249
|
-
* When `wrapHandler: false`:
|
|
2250
|
-
* - Fastify handler `(request, reply) => unknown`
|
|
2251
|
-
*/
|
|
2252
|
-
handler: string | ControllerHandler | RouteHandlerMethod | ((request: FastifyRequest<any>, reply: FastifyReply) => unknown);
|
|
2253
|
-
/** Permission check - REQUIRED */
|
|
2254
|
-
permissions: PermissionCheck;
|
|
2255
|
-
/**
|
|
2256
|
-
* Handler type - REQUIRED, no auto-detection
|
|
2257
|
-
* true = ControllerHandler (receives context object)
|
|
2258
|
-
* false = FastifyHandler (receives request, reply)
|
|
2259
|
-
*/
|
|
2260
|
-
wrapHandler: boolean;
|
|
2261
|
-
/**
|
|
2262
|
-
* Logical operation name for pipeline keys and permission actions.
|
|
2263
|
-
* Defaults to handler name (string handlers) or method+path slug.
|
|
2264
|
-
* Prevents collisions when multiple routes share the same HTTP method.
|
|
2265
|
-
*
|
|
2266
|
-
* @example
|
|
2267
|
-
* operation: 'listDeleted' // Used as pipeline key and permission action
|
|
2268
|
-
* operation: 'restore'
|
|
2269
|
-
*/
|
|
2270
|
-
operation?: string;
|
|
2271
|
-
/** OpenAPI summary */
|
|
2272
|
-
summary?: string;
|
|
2273
|
-
/** OpenAPI description */
|
|
2274
|
-
description?: string;
|
|
2275
|
-
/** OpenAPI tags */
|
|
2276
|
-
tags?: string[];
|
|
2277
|
-
/**
|
|
2278
|
-
* Custom route-level middleware
|
|
2279
|
-
* Can be an array of handlers or a function that receives fastify and returns handlers
|
|
2280
|
-
* @example
|
|
2281
|
-
* // Direct array
|
|
2282
|
-
* preHandler: [myMiddleware]
|
|
2283
|
-
* // Function that receives fastify (for accessing decorators)
|
|
2284
|
-
* preHandler: (fastify) => [fastify.customerContext({ required: true })]
|
|
2285
|
-
*/
|
|
2286
|
-
preHandler?: RouteHandlerMethod[] | ((fastify: FastifyInstance) => RouteHandlerMethod[]);
|
|
2287
|
-
/**
|
|
2288
|
-
* Pre-auth handlers — run BEFORE authentication middleware.
|
|
2289
|
-
* Use for promoting query params to headers (e.g., EventSource ?token= → Authorization).
|
|
2290
|
-
*
|
|
2291
|
-
* @example
|
|
2292
|
-
* ```typescript
|
|
2293
|
-
* preAuth: [(req) => {
|
|
2294
|
-
* const token = (req.query as Record<string, string>)?.token;
|
|
2295
|
-
* if (token) req.headers.authorization = `Bearer ${token}`;
|
|
2296
|
-
* }]
|
|
2297
|
-
* ```
|
|
2298
|
-
*/
|
|
2299
|
-
preAuth?: RouteHandlerMethod[];
|
|
2300
|
-
/**
|
|
2301
|
-
* Streaming response mode — designed for SSE and AI streaming routes.
|
|
2302
|
-
* When `true`:
|
|
2303
|
-
* - Forces `wrapHandler: false` (no `{ success, data }` wrapper)
|
|
2304
|
-
* - Sets SSE headers: `Content-Type: text/event-stream`, `Cache-Control: no-cache`, `Connection: keep-alive`
|
|
2305
|
-
* - `request.signal` (Fastify 5 built-in) is available for abort-on-disconnect
|
|
2306
|
-
*
|
|
2307
|
-
* @example
|
|
2308
|
-
* ```typescript
|
|
2309
|
-
* {
|
|
2310
|
-
* method: 'POST',
|
|
2311
|
-
* path: '/stream',
|
|
2312
|
-
* streamResponse: true,
|
|
2313
|
-
* permissions: requireAuth(),
|
|
2314
|
-
* handler: async (request, reply) => {
|
|
2315
|
-
* const { stream } = await generateStream({ abortSignal: request.signal });
|
|
2316
|
-
* return reply.send(stream);
|
|
2317
|
-
* },
|
|
2318
|
-
* }
|
|
2319
|
-
* ```
|
|
2320
|
-
*/
|
|
2321
|
-
streamResponse?: boolean;
|
|
2322
|
-
/** Fastify route schema */
|
|
2323
|
-
schema?: Record<string, unknown>;
|
|
2324
|
-
/**
|
|
2325
|
-
* MCP handler for routes with `wrapHandler: false`.
|
|
2326
|
-
* When set, this route becomes an MCP tool without needing `wrapHandler: true`.
|
|
2327
|
-
* The HTTP handler stays a plain Fastify handler; MCP gets a parallel entry point.
|
|
2328
|
-
*
|
|
2329
|
-
* @example
|
|
2330
|
-
* ```typescript
|
|
2331
|
-
* additionalRoutes: [{
|
|
2332
|
-
* method: 'GET',
|
|
2333
|
-
* path: '/stats',
|
|
2334
|
-
* handler: (req, reply) => reply.send(getStats()),
|
|
2335
|
-
* wrapHandler: false,
|
|
2336
|
-
* permissions: isAuthenticated,
|
|
2337
|
-
* mcpHandler: async (input) => ({
|
|
2338
|
-
* content: [{ type: 'text', text: JSON.stringify(await getStats()) }],
|
|
2339
|
-
* }),
|
|
2340
|
-
* }]
|
|
2341
|
-
* ```
|
|
2342
|
-
*/
|
|
2343
|
-
mcpHandler?: (input: Record<string, unknown>) => Promise<{
|
|
2344
|
-
content: Array<{
|
|
2345
|
-
type: string;
|
|
2346
|
-
text: string;
|
|
2347
|
-
}>;
|
|
2348
|
-
isError?: boolean;
|
|
2349
|
-
}>;
|
|
2350
|
-
/**
|
|
2351
|
-
* MCP tool generation config preserved from v2.8 `routes`.
|
|
2352
|
-
* - `false`: skip MCP tool generation for this route
|
|
2353
|
-
* - `true` / omitted: auto-generate when the route goes through Arc's pipeline
|
|
2354
|
-
* - object: explicit description/annotations overrides
|
|
2355
|
-
*
|
|
2356
|
-
* Added in 2.8.1 — previously dropped during `routes → additionalRoutes`
|
|
2357
|
-
* normalization, breaking MCP opt-out and per-route annotations.
|
|
2358
|
-
*/
|
|
2359
|
-
mcp?: boolean | {
|
|
2360
|
-
readonly description?: string;
|
|
2361
|
-
readonly annotations?: {
|
|
2362
|
-
readonly readOnlyHint?: boolean;
|
|
2363
|
-
readonly destructiveHint?: boolean;
|
|
2364
|
-
readonly idempotentHint?: boolean;
|
|
2365
|
-
readonly openWorldHint?: boolean;
|
|
2366
|
-
};
|
|
2367
|
-
};
|
|
2368
|
-
}
|
|
2369
2382
|
/** HTTP methods for custom routes */
|
|
2370
2383
|
type RouteMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
2371
2384
|
/** MCP tool configuration for a route or action */
|
|
@@ -2381,7 +2394,7 @@ interface RouteMcpConfig {
|
|
|
2381
2394
|
};
|
|
2382
2395
|
}
|
|
2383
2396
|
/**
|
|
2384
|
-
* Route definition —
|
|
2397
|
+
* Route definition — the single custom-route shape (user-facing + internal).
|
|
2385
2398
|
*
|
|
2386
2399
|
* - `handler: 'string'` → controller method → full Arc pipeline + MCP tool
|
|
2387
2400
|
* - `handler: function` → inline handler → full Arc pipeline + MCP tool
|
|
@@ -2891,6 +2904,23 @@ interface AuthPluginOptions {
|
|
|
2891
2904
|
* ```
|
|
2892
2905
|
*/
|
|
2893
2906
|
isRevoked?: (decoded: Record<string, unknown>) => boolean | Promise<boolean>;
|
|
2907
|
+
/**
|
|
2908
|
+
* Enforce strict JWT `type` claim validation (default: `true`).
|
|
2909
|
+
*
|
|
2910
|
+
* When enabled, `authenticate` requires `decoded.type === "access"`.
|
|
2911
|
+
* Tokens with a missing or unexpected `type` claim are rejected — defence in
|
|
2912
|
+
* depth for apps that reuse the JWT secret to sign other token kinds (invite
|
|
2913
|
+
* links, one-time verification codes, legacy tokens from a third-party
|
|
2914
|
+
* issuer).
|
|
2915
|
+
*
|
|
2916
|
+
* Arc's own token issuance (`issueTokens`) always sets `type: "access"` or
|
|
2917
|
+
* `type: "refresh"`, so this default is safe for arc-generated tokens.
|
|
2918
|
+
*
|
|
2919
|
+
* Set to `false` ONLY when you must accept tokens signed without a `type`
|
|
2920
|
+
* claim (e.g. a legacy issuer you don't control). In that mode arc still
|
|
2921
|
+
* rejects tokens explicitly marked `type: "refresh"`.
|
|
2922
|
+
*/
|
|
2923
|
+
strictTokenType?: boolean;
|
|
2894
2924
|
}
|
|
2895
2925
|
interface IntrospectionPluginOptions {
|
|
2896
2926
|
path?: string;
|
|
@@ -2909,8 +2939,8 @@ interface CrudRouterOptions {
|
|
|
2909
2939
|
schemas?: Partial<CrudSchemas>;
|
|
2910
2940
|
/** Middlewares for each CRUD operation */
|
|
2911
2941
|
middlewares?: MiddlewareConfig;
|
|
2912
|
-
/**
|
|
2913
|
-
|
|
2942
|
+
/** Custom routes (from presets or user-defined) */
|
|
2943
|
+
routes?: readonly RouteDefinition[];
|
|
2914
2944
|
/** Disable all default CRUD routes */
|
|
2915
2945
|
disableDefaultRoutes?: boolean;
|
|
2916
2946
|
/** Disable specific CRUD routes */
|
|
@@ -3095,6 +3125,29 @@ type TypedRepository<TDoc> = CrudRepository<TDoc>;
|
|
|
3095
3125
|
*
|
|
3096
3126
|
* See [CrudRepository](../types/repository.ts) for full prose-level docs
|
|
3097
3127
|
* on each method and the design rationale behind the tiering.
|
|
3128
|
+
*
|
|
3129
|
+
* ## Store-backing contract (audit / outbox / idempotency)
|
|
3130
|
+
*
|
|
3131
|
+
* Arc's pluggable stores (audit log, event outbox, HTTP idempotency) all
|
|
3132
|
+
* consume a `RepositoryLike` directly — no wrapper classes, no adapters to
|
|
3133
|
+
* register. If you want to back one of these subsystems with any database,
|
|
3134
|
+
* pass a repository satisfying the method subset below. `mongokit ≥3.8`
|
|
3135
|
+
* implements every method; other kits must match the relevant subset.
|
|
3136
|
+
*
|
|
3137
|
+
* | Subsystem | Required methods |
|
|
3138
|
+
* |--------------------|---------------------------------------------------------|
|
|
3139
|
+
* | `auditPlugin` | `create`, `findAll` |
|
|
3140
|
+
* | `idempotencyPlugin`| `getOne`, `deleteMany`, `findOneAndUpdate` |
|
|
3141
|
+
* | `EventOutbox` | `create`, `getOne`, `findAll`, `deleteMany`, `findOneAndUpdate` |
|
|
3142
|
+
*
|
|
3143
|
+
* The outbox is the strictest — its atomic FIFO claim-lease loop depends
|
|
3144
|
+
* on `findOneAndUpdate` returning the post-update doc. Kits without atomic
|
|
3145
|
+
* CAS cannot back the outbox; use an in-memory / transport-native store
|
|
3146
|
+
* (`MemoryOutboxStore`, Redis Streams, etc.) instead.
|
|
3147
|
+
*
|
|
3148
|
+
* Every store adapter throws at construction with the list of missing
|
|
3149
|
+
* methods if the repository doesn't satisfy its subset, so misconfigurations
|
|
3150
|
+
* fail fast rather than at first request.
|
|
3098
3151
|
*/
|
|
3099
3152
|
interface RepositoryLike {
|
|
3100
3153
|
/**
|
|
@@ -3115,12 +3168,50 @@ interface RepositoryLike {
|
|
|
3115
3168
|
getById(id: string, options?: QueryOptions): Promise<unknown>;
|
|
3116
3169
|
create(data: unknown, options?: WriteOptions): Promise<unknown>;
|
|
3117
3170
|
update(id: string, data: unknown, options?: WriteOptions): Promise<unknown>;
|
|
3171
|
+
/**
|
|
3172
|
+
* Atomic compare-and-set — match one document and mutate it in a single
|
|
3173
|
+
* round-trip. Returns the post-update document (or pre-update when
|
|
3174
|
+
* `returnDocument: 'before'`), or `null` when no document matches and
|
|
3175
|
+
* `upsert` is false.
|
|
3176
|
+
*
|
|
3177
|
+
* Used by the transactional outbox, distributed locks, and workflow
|
|
3178
|
+
* semaphores. Kits without atomic CAS can omit this method — arc stores
|
|
3179
|
+
* that require it throw a clear capability error at construction.
|
|
3180
|
+
*
|
|
3181
|
+
* Options follow mongokit's {@link https://github.com/classytic/mongokit | FindOneAndUpdateOptions}
|
|
3182
|
+
* shape: `{ sort, returnDocument, upsert, session, ... }`. Plugins reading
|
|
3183
|
+
* the hook context find the filter under `context.query` (the canonical
|
|
3184
|
+
* field name across every method on this contract).
|
|
3185
|
+
*/
|
|
3186
|
+
findOneAndUpdate?(filter: Record<string, unknown>, update: Record<string, unknown> | Record<string, unknown>[], options?: {
|
|
3187
|
+
sort?: Record<string, unknown>;
|
|
3188
|
+
returnDocument?: "before" | "after";
|
|
3189
|
+
upsert?: boolean;
|
|
3190
|
+
session?: RepositorySession;
|
|
3191
|
+
[key: string]: unknown;
|
|
3192
|
+
}): Promise<unknown>;
|
|
3118
3193
|
/**
|
|
3119
3194
|
* Delete by primary key. Pass `{ mode: 'hard' }` to bypass soft-delete
|
|
3120
3195
|
* interception (required by arc's hard-delete flow — `?hard=true` on
|
|
3121
3196
|
* the DELETE route forwards this option).
|
|
3122
3197
|
*/
|
|
3123
3198
|
delete(id: string, options?: DeleteOptions): Promise<unknown>;
|
|
3199
|
+
/**
|
|
3200
|
+
* Classify an error thrown by `create` / `findOneAndUpdate` / `update` as
|
|
3201
|
+
* a unique-index / duplicate-key violation.
|
|
3202
|
+
*
|
|
3203
|
+
* Arc's idempotency and outbox adapters need to distinguish "this write
|
|
3204
|
+
* already landed (idempotent no-op)" from "transient DB error (retry)".
|
|
3205
|
+
* Since every backend signals dup-key differently — MongoDB `code 11000`,
|
|
3206
|
+
* Prisma `P2002`, Postgres `23505`, SQLite `UNIQUE constraint failed` —
|
|
3207
|
+
* we put the classification back in the kit that knows its driver.
|
|
3208
|
+
*
|
|
3209
|
+
* If a kit omits this predicate, arc falls back to a conservative MongoDB
|
|
3210
|
+
* check (`code === 11000 || codeName === "DuplicateKey"`), so mongokit
|
|
3211
|
+
* ≤3.8 keeps working without changes. Non-mongo kits MUST implement it to
|
|
3212
|
+
* participate in idempotency semantics.
|
|
3213
|
+
*/
|
|
3214
|
+
isDuplicateKeyError?(err: unknown): boolean;
|
|
3124
3215
|
/**
|
|
3125
3216
|
* Find a single doc by compound filter. Used by AccessControl for
|
|
3126
3217
|
* `idField + org + policy` scoping. Without this, arc falls back to
|
|
@@ -3145,10 +3236,27 @@ interface RepositoryLike {
|
|
|
3145
3236
|
getDeleted?(params?: PaginationParams, options?: QueryOptions): Promise<PaginationResult<unknown> | unknown[]>;
|
|
3146
3237
|
aggregate?: unknown;
|
|
3147
3238
|
aggregatePaginate?: unknown;
|
|
3239
|
+
/**
|
|
3240
|
+
* Fluent aggregation builder. Mongokit returns `AggregationBuilder`;
|
|
3241
|
+
* other kits may return their own builder class. Cast at the call site
|
|
3242
|
+
* — arc never calls this internally. See [CrudRepository.buildAggregation](../types/repository.ts).
|
|
3243
|
+
*/
|
|
3244
|
+
buildAggregation?(): unknown;
|
|
3245
|
+
/**
|
|
3246
|
+
* Fluent `$lookup` stage builder. Mongokit returns `LookupBuilder`;
|
|
3247
|
+
* other kits may return nothing. Cast at the call site.
|
|
3248
|
+
*/
|
|
3249
|
+
buildLookup?(from?: string): unknown;
|
|
3148
3250
|
withTransaction?<T>(callback: (session: RepositorySession) => Promise<T>, options?: Record<string, unknown>): Promise<T>;
|
|
3149
3251
|
getBySlug?(slug: string, options?: QueryOptions): Promise<unknown>;
|
|
3150
3252
|
getTree?(options?: QueryOptions): Promise<unknown>;
|
|
3151
3253
|
getChildren?(parentId: string, options?: QueryOptions): Promise<unknown>;
|
|
3254
|
+
/** Full-text / engine-backed search. Present when e.g. mongokit's `elasticSearchPlugin` is registered. */
|
|
3255
|
+
search?(query: unknown, options?: unknown): Promise<unknown>;
|
|
3256
|
+
/** Semantic / vector similarity search. Present when e.g. mongokit's `vectorPlugin` is registered. */
|
|
3257
|
+
searchSimilar?(query: unknown, options?: unknown): Promise<unknown>;
|
|
3258
|
+
/** Embed a text/media input into its vector representation. */
|
|
3259
|
+
embed?(input: unknown): Promise<number[]>;
|
|
3152
3260
|
[key: string]: unknown;
|
|
3153
3261
|
}
|
|
3154
3262
|
interface DataAdapter<TDoc = unknown> {
|
|
@@ -3246,4 +3354,4 @@ interface ValidationResult {
|
|
|
3246
3354
|
}
|
|
3247
3355
|
type AdapterFactory<TDoc> = (config: unknown) => DataAdapter<TDoc>;
|
|
3248
3356
|
//#endregion
|
|
3249
|
-
export {
|
|
3357
|
+
export { PresetHook as $, DeleteResult as $t, FastifyRequestExtras as A, beforeCreate as An, BaseControllerOptions as At, IntrospectionData as B, IController as Bt, ControllerQueryOptions as C, HookPhase as Cn, UserLike as Ct, CrudSchemas as D, afterCreate as Dn, envelope as Dt, CrudRouterOptions as E, HookSystemOptions as En, ValidationResult$1 as Et, HealthCheck as F, AccessControl as Ft, MiddlewareConfig as G, ResourceRegistry as Gt, JWTPayload as H, IRequestContext as Ht, HealthOptions as I, AccessControlConfig as It, OpenApiSchemas as J, BulkWriteOperation as Jt, MiddlewareHandler as K, ResourceDefinition as Kt, InferAdapterDoc as L, ControllerHandler as Lt, FastifyWithDecorators as M, beforeUpdate as Mn, QueryResolverConfig as Mt, FieldRule as N, createHookSystem as Nn, BodySanitizer as Nt, EventDefinition as O, afterDelete as On, getUserId as Ot, GracefulShutdownOptions as P, defineHook as Pn, BodySanitizerConfig as Pt, PresetFunction as Q, DeleteOptions as Qt, InferDocType as R, ControllerLike as Rt, ConfigError as S, HookOperation as Sn, TypedResourceConfig as St, CrudRouteKey as T, HookSystem as Tn, ValidateOptions as Tt, JwtContext as U, RouteHandler as Ut, IntrospectionPluginOptions as V, IControllerResponse as Vt, LookupOption as W, RegisterOptions as Wt, ParsedQuery as X, CrudRepository as Xt, OwnershipCheck as Y, BulkWriteResult as Yt, PopulateOption as Z, DeleteManyResult as Zt, ArcRequest as _, PipelineStep as _n, RouteSchemaOptions as _t, RelationMetadata as a, PaginationParams as an, RequestContext as at, Authenticator as b, HookContext as bn, TypedController as bt, ValidationResult as c, RepositorySession as cn, ResourceCacheConfig as ct, ActionHandlerFn as d, Guard as dn, ResourceHooks as dt, FindOneAndUpdateOptions as en, PresetResult as et, ActionsMap as f, Interceptor as fn, ResourceMetadata as ft, ArcInternalMetadata as g, PipelineContext as gn, RouteMcpConfig as gt, ArcDecorator as h, PipelineConfig as hn, RouteHandlerMethod$1 as ht, FieldMetadata as i, PaginatedResult as in, RegistryStats as it, FastifyWithAuth as j, beforeDelete as jn, QueryResolver as jt, EventsDecorator as k, afterUpdate as kn, BaseController as kt, ActionDefinition as l, UpdateManyResult as ln, ResourceConfig as lt, ApiResponse as m, OperationFilter as mn, RouteDefinition as mt, AdapterSchemaContext as n, KeysetPaginatedResult as nn, RateLimitConfig as nt, RepositoryLike as o, PaginationResult as on, RequestIdOptions as ot, AnyRecord as p, NextFunction as pn, ResourcePermissions as pt, ObjectId as q, defineResource as qt, DataAdapter as r, OffsetPaginatedResult as rn, RegistryEntry as rt, SchemaMetadata as s, QueryOptions as sn, RequestWithExtras as st, AdapterFactory as t, InferDoc as tn, QueryParserInterface as tt, ActionEntry as u, WriteOptions as un, ResourceHookContext as ut, AuthHelpers as v, Transform as vn, ServiceContext as vt, CrudController as w, HookRegistration as wn, UserOrganization as wt, AuthenticatorContext as x, HookHandler as xn, TypedRepository as xt, AuthPluginOptions as y, DefineHookOptions as yn, TokenPair as yt, InferResourceDoc as z, FastifyHandler as zt };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as getUserRoles } from "./types-ZUu_h0jp.mjs";
|
|
2
|
-
import { n as convertRouteSchema } from "./schemaConverter-
|
|
3
|
-
import { t as buildActionBodySchema } from "./createActionRouter-
|
|
2
|
+
import { n as convertRouteSchema } from "./schemaConverter-BxFDdtXu.mjs";
|
|
3
|
+
import { t as buildActionBodySchema } from "./createActionRouter-DH1YFL9m.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/docs/openapi.ts
|
|
6
6
|
const openApiPlugin = async (fastify, opts = {}) => {
|
package/dist/org/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ut as RouteHandler } from "../interface-YrWsmKqE.mjs";
|
|
2
2
|
import { i as UserBase } from "../types-DZi1aYhm.mjs";
|
|
3
3
|
import { InvitationAdapter, InvitationDoc, MemberDoc, OrgAdapter, OrgDoc, OrgPermissionStatement, OrgRole, OrganizationPluginOptions } from "./types.mjs";
|
|
4
4
|
import { FastifyPluginAsync, RouteHandlerMethod } from "fastify";
|