@classytic/mongokit 3.11.1 → 3.13.0
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 +75 -5
- package/dist/{PaginationEngine-CvDFfVXT.mjs → PaginationEngine-CfQRM1yz.mjs} +16 -16
- package/dist/Repository-BY40uhcv.mjs +3603 -0
- package/dist/actions/index.d.mts +1 -1
- package/dist/actions/index.mjs +1 -1
- package/dist/adapter/index.d.mts +190 -0
- package/dist/adapter/index.mjs +2 -0
- package/dist/adapter-DTDMnUOH.mjs +158 -0
- package/dist/ai/index.d.mts +1 -1
- package/dist/ai/index.mjs +3 -3
- package/dist/better-auth/index.d.mts +149 -0
- package/dist/better-auth/index.mjs +81 -0
- package/dist/field-selection-Br3WPOEb.mjs +99 -0
- package/dist/{index-dNTmpgK8.d.mts → index-BbHaKwi5.d.mts} +33 -13
- package/dist/index.d.mts +678 -36
- package/dist/index.mjs +240 -17
- package/dist/{logger-DJ7egMnd.mjs → logger-DprijP5h.mjs} +1 -5
- package/dist/{mongooseToJsonSchema-D1sdCOmj.mjs → mongooseToJsonSchema-BYbG894y.mjs} +68 -20
- package/dist/{mongooseToJsonSchema-DXxjFkLB.d.mts → mongooseToJsonSchema-BgRAaXX-.d.mts} +10 -2
- package/dist/pagination/PaginationEngine.d.mts +3 -2
- package/dist/pagination/PaginationEngine.mjs +1 -1
- package/dist/plugins/index.d.mts +2 -2
- package/dist/plugins/index.mjs +2 -2
- package/dist/query/primitives/coercion.d.mts +1 -1
- package/dist/query/primitives/coercion.mjs +1 -1
- package/dist/query/primitives/indexes.d.mts +1 -1
- package/dist/{types-Dc2aDVUi.d.mts → types-BbaPZleN.d.mts} +419 -205
- package/dist/{update-DEJYcgfV.mjs → update-DLcSSa5b.mjs} +184 -45
- package/dist/utils/index.d.mts +2 -91
- package/dist/utils/index.mjs +5 -5
- package/dist/{validation-chain.plugin-B0VSdcwa.d.mts → validation-chain.plugin-CAPYCj_T.d.mts} +228 -98
- package/dist/validation-chain.plugin-_-WuNml2.mjs +2399 -0
- package/package.json +25 -5
- package/dist/field-selection-dI0m4RBs.mjs +0 -221
- package/dist/validation-chain.plugin-Dllv-cml.mjs +0 -5122
- /package/dist/{coercion-oM8G4b6m.d.mts → coercion-TRMmR49h.d.mts} +0 -0
- /package/dist/{id-resolution-e4BT9zAV.mjs → id-resolution-B0Odvvou.mjs} +0 -0
- /package/dist/{indexes-C26GNZRU.d.mts → indexes-CgOF7Wjd.d.mts} +0 -0
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import * as _$_classytic_repo_core_filter0 from "@classytic/repo-core/filter";
|
|
2
|
+
import { Filter } from "@classytic/repo-core/filter";
|
|
1
3
|
import * as _$mongoose from "mongoose";
|
|
2
4
|
import { Document, Model, PipelineStage, PopulateOptions, Types } from "mongoose";
|
|
3
|
-
import * as _$
|
|
5
|
+
import * as _$_classytic_repo_core_cache0 from "@classytic/repo-core/cache";
|
|
4
6
|
import * as _$_classytic_repo_core_repository0 from "@classytic/repo-core/repository";
|
|
5
|
-
import { DeleteManyResult, DeleteResult, LookupPopulateResult, UpdateManyResult } from "@classytic/repo-core/repository";
|
|
7
|
+
import { DeleteManyResult, DeleteResult, LookupPopulateResult, RepositoryBase, UpdateManyResult } from "@classytic/repo-core/repository";
|
|
6
8
|
import { ValidationResult } from "@classytic/repo-core/schema";
|
|
9
|
+
import { AnyPaginationResult, OffsetPaginationResult } from "@classytic/repo-core/pagination";
|
|
10
|
+
import { HttpError } from "@classytic/repo-core/errors";
|
|
7
11
|
|
|
8
12
|
//#region src/query/LookupBuilder.d.ts
|
|
9
13
|
interface LookupOptions {
|
|
@@ -17,8 +21,15 @@ interface LookupOptions {
|
|
|
17
21
|
as?: string;
|
|
18
22
|
/** Whether to unwrap array to single object */
|
|
19
23
|
single?: boolean;
|
|
20
|
-
/**
|
|
21
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Field selection on the joined collection (shorthand for `$project`).
|
|
26
|
+
* Three accepted shapes — all map to a single `$project` stage:
|
|
27
|
+
* - `string` — CSV form (`'name,price'`); leading `-` marks an exclusion.
|
|
28
|
+
* - `readonly string[]` — array form (`['name', 'price']` / `['-status']`);
|
|
29
|
+
* mirrors `repo-core/lookup` `LookupSpec.select` for cross-kit parity.
|
|
30
|
+
* - `Record<string, 0 | 1>` — explicit Mongo projection map.
|
|
31
|
+
*/
|
|
32
|
+
select?: string | readonly string[] | Record<string, 0 | 1>;
|
|
22
33
|
/** Additional pipeline to run on the joined collection */
|
|
23
34
|
pipeline?: PipelineStage[];
|
|
24
35
|
/** Optional let variables for pipeline */
|
|
@@ -31,6 +42,18 @@ interface LookupOptions {
|
|
|
31
42
|
};
|
|
32
43
|
/** Sanitize pipeline stages (default: true). Set false only for trusted server-side pipelines */
|
|
33
44
|
sanitize?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Joined-side filter, compiled into a `$match` stage inside the lookup
|
|
47
|
+
* pipeline AFTER the auto-generated join correlation. Mirrors
|
|
48
|
+
* `repo-core/lookup/types.ts#LookupSpec.where` and sqlitekit's
|
|
49
|
+
* predicate-on-the-JOIN behavior, so cross-kit code that narrows the
|
|
50
|
+
* foreign side (`where: eq('status', 'active')`) gets identical rows
|
|
51
|
+
* from both backends.
|
|
52
|
+
*
|
|
53
|
+
* Accepts Filter IR or a plain mongo query — `compileFilterToMongo`
|
|
54
|
+
* routes each correctly.
|
|
55
|
+
*/
|
|
56
|
+
where?: Filter | Record<string, unknown>;
|
|
34
57
|
}
|
|
35
58
|
/**
|
|
36
59
|
* Fluent builder for MongoDB $lookup aggregation stage
|
|
@@ -298,7 +321,7 @@ interface IController<TDoc> {
|
|
|
298
321
|
* success: true,
|
|
299
322
|
* data: {
|
|
300
323
|
* method: 'offset',
|
|
301
|
-
*
|
|
324
|
+
* data: [...],
|
|
302
325
|
* total: 100,
|
|
303
326
|
* page: 1,
|
|
304
327
|
* pages: 5
|
|
@@ -307,7 +330,7 @@ interface IController<TDoc> {
|
|
|
307
330
|
* }
|
|
308
331
|
* ```
|
|
309
332
|
*/
|
|
310
|
-
list(context: IRequestContext): Promise<IControllerResponse<
|
|
333
|
+
list(context: IRequestContext): Promise<IControllerResponse<AnyPaginationResult<TDoc>>>;
|
|
311
334
|
/**
|
|
312
335
|
* Get single resource by ID
|
|
313
336
|
*
|
|
@@ -457,7 +480,7 @@ interface IResponseFormatter {
|
|
|
457
480
|
* Format paginated response
|
|
458
481
|
* @param result - Pagination result from Repository.getAll()
|
|
459
482
|
*/
|
|
460
|
-
paginated<T>(result:
|
|
483
|
+
paginated<T>(result: AnyPaginationResult<T>): IControllerResponse<AnyPaginationResult<T>>;
|
|
461
484
|
}
|
|
462
485
|
//#endregion
|
|
463
486
|
//#region src/types.d.ts
|
|
@@ -567,14 +590,161 @@ type CreateInput<TDoc> = Omit<TDoc, '_id' | 'createdAt' | 'updatedAt' | '__v'>;
|
|
|
567
590
|
*/
|
|
568
591
|
type UpdatePatch<TDoc> = Partial<Omit<TDoc, '_id' | 'createdAt' | '__v'>>;
|
|
569
592
|
/**
|
|
570
|
-
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
593
|
+
* Typed Mongo update document — the operator-shape that `findOneAndUpdate`
|
|
594
|
+
* / `updateMany` accept on the wire (`{ $set, $inc, $unset, ... }`).
|
|
595
|
+
*
|
|
596
|
+
* The explicit operator keys give IDE autocomplete + per-operator value
|
|
597
|
+
* typing; the trailing `[op: string]: unknown` index signature lets a
|
|
598
|
+
* caller-built `MongoOperatorUpdate` value assign to `Record<string,
|
|
599
|
+
* unknown>` without a cast — matching repo-core's `UpdateInput` slot
|
|
600
|
+
* (`UpdateSpec | Record<string, unknown> | Record<string, unknown>[]`)
|
|
601
|
+
* and unblocking the historic
|
|
602
|
+
*
|
|
603
|
+
* `normalizeUpdate(update) as unknown as Record<string, unknown>`
|
|
604
|
+
*
|
|
605
|
+
* footgun every domain package eventually wrote.
|
|
606
|
+
*
|
|
607
|
+
* Trade-off: with the index signature, an operator typo (`$st` instead
|
|
608
|
+
* of `$set`) won't fail TypeScript — Mongo would silently drop it. The
|
|
609
|
+
* common operators are listed explicitly so `obj.$set` / `obj.$inc`
|
|
610
|
+
* autocomplete stays useful; the escape hatch covers everything else
|
|
611
|
+
* (`$min`, `$max`, `$rename`, `$pop`, etc.).
|
|
612
|
+
*
|
|
613
|
+
* @example
|
|
614
|
+
* ```ts
|
|
615
|
+
* import type { MongoOperatorUpdate } from '@classytic/mongokit';
|
|
616
|
+
*
|
|
617
|
+
* const patch: MongoOperatorUpdate = {
|
|
618
|
+
* $set: { status: 'paid', paidAt: new Date() },
|
|
619
|
+
* $inc: { revision: 1 },
|
|
620
|
+
* };
|
|
621
|
+
* await repo.findOneAndUpdate({ _id: id }, patch); // no cast
|
|
622
|
+
* ```
|
|
574
623
|
*/
|
|
575
|
-
|
|
624
|
+
interface MongoOperatorUpdate {
|
|
625
|
+
/** Assign top-level or dotted-path values. */
|
|
626
|
+
$set?: Record<string, unknown>;
|
|
627
|
+
/** Set only on insert (upsert path). */
|
|
628
|
+
$setOnInsert?: Record<string, unknown>;
|
|
629
|
+
/** Remove fields (`Mongo` accepts `''`, `1`, or `true`). */
|
|
630
|
+
$unset?: Record<string, '' | 1 | true>;
|
|
631
|
+
/** Atomic numeric increment / decrement. */
|
|
632
|
+
$inc?: Record<string, number>;
|
|
633
|
+
/** Atomic numeric multiplication. */
|
|
634
|
+
$mul?: Record<string, number>;
|
|
635
|
+
/** Push to array (or `{ $each, $position, $slice, $sort }`). */
|
|
636
|
+
$push?: Record<string, unknown>;
|
|
637
|
+
/** Pull matching values from array. */
|
|
638
|
+
$pull?: Record<string, unknown>;
|
|
639
|
+
/** Pull a list of literals from array. */
|
|
640
|
+
$pullAll?: Record<string, unknown[]>;
|
|
641
|
+
/** Push only when not already present. */
|
|
642
|
+
$addToSet?: Record<string, unknown>;
|
|
643
|
+
/** Remove first / last element (`-1` / `1`). */
|
|
644
|
+
$pop?: Record<string, -1 | 1>;
|
|
645
|
+
/** Set only when the new value is smaller / larger. */
|
|
646
|
+
$min?: Record<string, unknown>;
|
|
647
|
+
$max?: Record<string, unknown>;
|
|
648
|
+
/** Rename top-level fields. */
|
|
649
|
+
$rename?: Record<string, string>;
|
|
650
|
+
/** Auto-set Date / Timestamp on this update. */
|
|
651
|
+
$currentDate?: Record<string, true | {
|
|
652
|
+
$type: 'date' | 'timestamp';
|
|
653
|
+
}>;
|
|
654
|
+
/** Bitwise ops on integer fields. */
|
|
655
|
+
$bit?: Record<string, {
|
|
656
|
+
and?: number;
|
|
657
|
+
or?: number;
|
|
658
|
+
xor?: number;
|
|
659
|
+
}>;
|
|
660
|
+
/**
|
|
661
|
+
* Escape hatch for any operator not explicitly listed above (e.g.
|
|
662
|
+
* positional-array operators like `$[<identifier>]` inside a path
|
|
663
|
+
* key). Index signature is what makes the type assign to
|
|
664
|
+
* `Record<string, unknown>` without a cast.
|
|
665
|
+
*/
|
|
666
|
+
[op: string]: unknown;
|
|
667
|
+
}
|
|
576
668
|
/** Hook execution mode */
|
|
577
669
|
type HookMode = 'sync' | 'async';
|
|
670
|
+
/**
|
|
671
|
+
* Minimal repo surface exposed to wrap-style middleware via the
|
|
672
|
+
* `repo` field of `MiddlewareContext`. Intentionally narrow — middleware
|
|
673
|
+
* shouldn't reach into the full `Repository` (that would couple every
|
|
674
|
+
* middleware to the kit). The 5 floor methods plus `Model` cover every
|
|
675
|
+
* legitimate middleware need (cross-doc reads, custom queries, type-
|
|
676
|
+
* narrowing on `Model.modelName`).
|
|
677
|
+
*/
|
|
678
|
+
interface MinimalRepoView<TDoc> {
|
|
679
|
+
readonly Model: Model<TDoc>;
|
|
680
|
+
readonly model: string;
|
|
681
|
+
getById(id: string | ObjectId, options?: Record<string, unknown>): Promise<TDoc | null>;
|
|
682
|
+
getAll?(params?: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
|
|
683
|
+
create(data: Record<string, unknown>, options?: Record<string, unknown>): Promise<TDoc>;
|
|
684
|
+
update(id: string | ObjectId, data: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
|
|
685
|
+
delete(id: string | ObjectId, options?: Record<string, unknown>): Promise<unknown>;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Context passed to a wrap-style middleware. Middleware mutates
|
|
689
|
+
* `context` before calling `await next()` to alter input; inspects /
|
|
690
|
+
* transforms the resolved value to alter output; uses plain try/catch
|
|
691
|
+
* around `next()` for error handling.
|
|
692
|
+
*
|
|
693
|
+
* The `before:` / `after:` / `error:` hook engine still fires from
|
|
694
|
+
* within `next()` — hooks remain authoritative for policy / cache /
|
|
695
|
+
* audit so security and correctness plugins keep working unchanged.
|
|
696
|
+
* Middleware composes ergonomically on top.
|
|
697
|
+
*/
|
|
698
|
+
interface MiddlewareContext<TDoc> {
|
|
699
|
+
/** Operation name (`'create'`, `'getById'`, ..., or any plugin op). */
|
|
700
|
+
readonly operation: string;
|
|
701
|
+
/** Live repository context — mutate fields before `next()` to alter input. */
|
|
702
|
+
readonly context: RepositoryContext;
|
|
703
|
+
/**
|
|
704
|
+
* Continue the chain — the next middleware (or, for the innermost
|
|
705
|
+
* middleware, the actual op + after/error hooks). The return value
|
|
706
|
+
* is the resolved op result; throwing propagates up the chain.
|
|
707
|
+
*/
|
|
708
|
+
next: () => Promise<unknown>;
|
|
709
|
+
/** Narrow view of the repo for cross-doc reads / custom queries. */
|
|
710
|
+
readonly repo: MinimalRepoView<TDoc>;
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Wrap-style middleware — Prisma `$extends.query` / Express middleware
|
|
714
|
+
* shape. Registered via `repo.useMiddleware(mw)`. Composes around every
|
|
715
|
+
* `_runOp` invocation; runs in registration order (first-registered
|
|
716
|
+
* runs outermost).
|
|
717
|
+
*
|
|
718
|
+
* @example Time every operation
|
|
719
|
+
* ```ts
|
|
720
|
+
* repo.useMiddleware(async ({ operation, next }) => {
|
|
721
|
+
* const start = performance.now();
|
|
722
|
+
* try { return await next(); }
|
|
723
|
+
* finally { console.log(`${operation} took ${performance.now() - start}ms`); }
|
|
724
|
+
* });
|
|
725
|
+
* ```
|
|
726
|
+
*
|
|
727
|
+
* @example Stamp tenant on every create
|
|
728
|
+
* ```ts
|
|
729
|
+
* repo.useMiddleware(async ({ operation, context, next }) => {
|
|
730
|
+
* if (operation === 'create' && context.data) {
|
|
731
|
+
* context.data.tenantId = currentTenant();
|
|
732
|
+
* }
|
|
733
|
+
* return next();
|
|
734
|
+
* });
|
|
735
|
+
* ```
|
|
736
|
+
*
|
|
737
|
+
* @example Short-circuit (skip the actual op) — return without calling next()
|
|
738
|
+
* ```ts
|
|
739
|
+
* repo.useMiddleware(async ({ operation, context, next }) => {
|
|
740
|
+
* if (operation === 'getById' && readOnlyMode) {
|
|
741
|
+
* return cachedReadOnlyResponse(context.id);
|
|
742
|
+
* }
|
|
743
|
+
* return next();
|
|
744
|
+
* });
|
|
745
|
+
* ```
|
|
746
|
+
*/
|
|
747
|
+
type Middleware<TDoc = unknown> = (ctx: MiddlewareContext<TDoc>) => Promise<unknown>;
|
|
578
748
|
/** Search strategy used by Repository.getAll when `search` is provided */
|
|
579
749
|
type RepositorySearchMode = 'text' | 'regex' | 'auto';
|
|
580
750
|
/** Repository options */
|
|
@@ -612,6 +782,82 @@ interface RepositoryOptions {
|
|
|
612
782
|
* before tenant scoping → cross-tenant cache poisoning risk).
|
|
613
783
|
*/
|
|
614
784
|
pluginOrderChecks?: 'warn' | 'throw' | 'off';
|
|
785
|
+
/**
|
|
786
|
+
* When `true` (default `false`), the repository checks every read/
|
|
787
|
+
* write op's filter against the schema's declared paths and warns
|
|
788
|
+
* once per `(modelName, fieldName)` pair if a filter key isn't on
|
|
789
|
+
* the schema AND `strictQuery: true` is in effect.
|
|
790
|
+
*
|
|
791
|
+
* **Why this exists.** With `mongoose.set('strictQuery', true)` (the
|
|
792
|
+
* mongoose 6 default; some projects pin it explicitly), filter keys
|
|
793
|
+
* not declared on the schema are SILENTLY STRIPPED before the query
|
|
794
|
+
* runs. `findOne({ code: 'X' })` on a schema without `code` becomes
|
|
795
|
+
* `findOne({})` — returns the first doc, not null — and the bug
|
|
796
|
+
* masquerades as "wrong row returned" without any error or log.
|
|
797
|
+
*
|
|
798
|
+
* The diagnostic catches the trap at runtime: log once per offending
|
|
799
|
+
* key per model, so a single bad call surfaces during dev/staging
|
|
800
|
+
* instead of in production. Costs O(filter keys) per call against
|
|
801
|
+
* a constructor-cached set; negligible.
|
|
802
|
+
*
|
|
803
|
+
* Hosts who route mongokit's `warn` into their own logger (via
|
|
804
|
+
* `configureLogger({ warn: ... })`) can ship this enabled in
|
|
805
|
+
* production without polluting stdout.
|
|
806
|
+
*
|
|
807
|
+
* @default false
|
|
808
|
+
*/
|
|
809
|
+
warnOnStrictQueryStrip?: boolean;
|
|
810
|
+
/**
|
|
811
|
+
* Names of plugins that MUST be present in the plugin chain at
|
|
812
|
+
* construction time. Throws a `TypeError` from the constructor if
|
|
813
|
+
* any listed plugin is absent — eliminates the silent-misconfig
|
|
814
|
+
* shape where a host forgets to wire `multiTenantPlugin` and ships
|
|
815
|
+
* a tenant-leak to production.
|
|
816
|
+
*
|
|
817
|
+
* Names match each plugin's exported `name` property (the plugin's
|
|
818
|
+
* own canonical identifier, NOT the JavaScript function name).
|
|
819
|
+
* Bundled plugins as of 3.13:
|
|
820
|
+
* - `'multi-tenant'` (from `multiTenantPlugin`)
|
|
821
|
+
* - `'softDelete'` (from `softDeletePlugin`)
|
|
822
|
+
* - `'auditLog'` (from `auditLogPlugin`)
|
|
823
|
+
* - `'auditTrail'` (from `auditTrailPlugin`)
|
|
824
|
+
* - `'cache'` (from `cachePlugin`)
|
|
825
|
+
* - `'observability'` (from `observabilityPlugin`)
|
|
826
|
+
* - `'method-registry'` (from `methodRegistryPlugin`)
|
|
827
|
+
* - `'batch-operations'`, `'mongo-operations'`,
|
|
828
|
+
* `'aggregate-helpers'`, `'subdocument'`, `'cascade'`,
|
|
829
|
+
* `'custom-id'`, `'elastic-search'`, `'fieldFilter'`,
|
|
830
|
+
* `'validation-chain'`, `'lease'`, `'timestamp'`
|
|
831
|
+
*
|
|
832
|
+
* Naming follows each plugin's `name` field — kebab-case for some,
|
|
833
|
+
* camelCase for others. The error message lists "Installed plugins"
|
|
834
|
+
* verbatim so a typo surfaces as a side-by-side diff.
|
|
835
|
+
*
|
|
836
|
+
* **Why this exists.** "Always wire `multiTenantPlugin`" and "always
|
|
837
|
+
* wire `softDeletePlugin`" are documented as conventions in 7+ and
|
|
838
|
+
* 9+ CLAUDE.md files across the classytic codebase respectively,
|
|
839
|
+
* but documentation drifts and convention-by-comment isn't
|
|
840
|
+
* enforceable. Listing the required plugins at the boot boundary
|
|
841
|
+
* fails closed — the constructor throws with the missing name, the
|
|
842
|
+
* bug surfaces on first run (or first test), not in production.
|
|
843
|
+
*
|
|
844
|
+
* @example
|
|
845
|
+
* ```ts
|
|
846
|
+
* new Repository(OrderModel, [
|
|
847
|
+
* methodRegistryPlugin(),
|
|
848
|
+
* multiTenantPlugin({ tenantField: 'organizationId' }),
|
|
849
|
+
* softDeletePlugin(),
|
|
850
|
+
* auditLogPlugin({ logger }),
|
|
851
|
+
* ], paginationConfig, {
|
|
852
|
+
* requirePlugins: ['multiTenant', 'softDelete', 'auditLog'],
|
|
853
|
+
* });
|
|
854
|
+
* ```
|
|
855
|
+
*
|
|
856
|
+
* If any name in this list isn't matched by a plugin's `name`
|
|
857
|
+
* property in the chain, the constructor throws with the missing
|
|
858
|
+
* names listed.
|
|
859
|
+
*/
|
|
860
|
+
requirePlugins?: readonly string[];
|
|
615
861
|
}
|
|
616
862
|
/** Pagination configuration */
|
|
617
863
|
interface PaginationConfig {
|
|
@@ -732,69 +978,75 @@ interface AggregatePaginationOptions {
|
|
|
732
978
|
/** Read preference for replica sets (e.g. 'secondaryPreferred') */
|
|
733
979
|
readPreference?: ReadPreferenceType;
|
|
734
980
|
}
|
|
735
|
-
/** Offset pagination result */
|
|
736
|
-
interface OffsetPaginationResult<T = unknown> {
|
|
737
|
-
/** Pagination method used */
|
|
738
|
-
method: 'offset';
|
|
739
|
-
/** Array of documents */
|
|
740
|
-
docs: T[];
|
|
741
|
-
/** Current page number */
|
|
742
|
-
page: number;
|
|
743
|
-
/** Documents per page */
|
|
744
|
-
limit: number;
|
|
745
|
-
/** Total document count */
|
|
746
|
-
total: number;
|
|
747
|
-
/** Total page count */
|
|
748
|
-
pages: number;
|
|
749
|
-
/** Whether next page exists */
|
|
750
|
-
hasNext: boolean;
|
|
751
|
-
/** Whether previous page exists */
|
|
752
|
-
hasPrev: boolean;
|
|
753
|
-
/** Performance warning for deep pagination */
|
|
754
|
-
warning?: string;
|
|
755
|
-
}
|
|
756
|
-
/** Keyset pagination result */
|
|
757
|
-
interface KeysetPaginationResult<T = unknown> {
|
|
758
|
-
/** Pagination method used */
|
|
759
|
-
method: 'keyset';
|
|
760
|
-
/** Array of documents */
|
|
761
|
-
docs: T[];
|
|
762
|
-
/** Documents per page */
|
|
763
|
-
limit: number;
|
|
764
|
-
/** Whether more documents exist */
|
|
765
|
-
hasMore: boolean;
|
|
766
|
-
/** Cursor token for next page */
|
|
767
|
-
next: string | null;
|
|
768
|
-
}
|
|
769
|
-
/** Aggregate pagination result */
|
|
770
|
-
interface AggregatePaginationResult<T = unknown> {
|
|
771
|
-
/** Pagination method used */
|
|
772
|
-
method: 'aggregate';
|
|
773
|
-
/** Array of documents */
|
|
774
|
-
docs: T[];
|
|
775
|
-
/** Current page number */
|
|
776
|
-
page: number;
|
|
777
|
-
/** Documents per page */
|
|
778
|
-
limit: number;
|
|
779
|
-
/** Total document count */
|
|
780
|
-
total: number;
|
|
781
|
-
/** Total page count */
|
|
782
|
-
pages: number;
|
|
783
|
-
/** Whether next page exists */
|
|
784
|
-
hasNext: boolean;
|
|
785
|
-
/** Whether previous page exists */
|
|
786
|
-
hasPrev: boolean;
|
|
787
|
-
/** Performance warning for deep pagination */
|
|
788
|
-
warning?: string;
|
|
789
|
-
}
|
|
790
|
-
/** Union type for all pagination results */
|
|
791
|
-
type PaginationResult<T = unknown> = OffsetPaginationResult<T> | KeysetPaginationResult<T> | AggregatePaginationResult<T>;
|
|
792
981
|
/** Session-only options — shared base for lightweight operations */
|
|
793
982
|
interface SessionOptions {
|
|
794
983
|
/** MongoDB session for transactions */
|
|
795
984
|
session?: unknown;
|
|
796
985
|
/** Organization/tenant ID for multi-tenant plugin scoping */
|
|
797
986
|
organizationId?: string | ObjectId;
|
|
987
|
+
/**
|
|
988
|
+
* Per-call escape hatch — plugins listed here that opt into the
|
|
989
|
+
* mechanism (`auditLog`, `auditTrail`, `observability`) read this
|
|
990
|
+
* from `RepositoryContext` and skip their work for this call. Use
|
|
991
|
+
* on hot paths (e.g. heartbeats / counters) where the audit overhead
|
|
992
|
+
* dominates.
|
|
993
|
+
*
|
|
994
|
+
* Plugins that enforce correctness or security (multi-tenant scope,
|
|
995
|
+
* cache invalidation, soft-delete filter) deliberately ignore this
|
|
996
|
+
* flag — security/correctness must not be skippable per-call.
|
|
997
|
+
*
|
|
998
|
+
* **Match exactly by `plugin.name`** (camelCase, NOT kebab-case):
|
|
999
|
+
* - `'auditLog'` — see `auditLogPlugin()` in
|
|
1000
|
+
* `src/plugins/audit-log.plugin.ts` (`name: 'auditLog'`)
|
|
1001
|
+
* - `'auditTrail'` — see `auditTrailPlugin()` in
|
|
1002
|
+
* `src/plugins/audit-trail.plugin.ts` (`name: 'auditTrail'`)
|
|
1003
|
+
* - `'observability'` — see `observabilityPlugin()` in
|
|
1004
|
+
* `src/plugins/observability.plugin.ts` (`name: 'observability'`)
|
|
1005
|
+
*
|
|
1006
|
+
* Pre-3.13.0 docs referenced `'audit-log'` / `'audit-trail'` —
|
|
1007
|
+
* those don't match any plugin's `name` field, so passing them
|
|
1008
|
+
* silently fails to skip anything. The strings above are the
|
|
1009
|
+
* authoritative ones.
|
|
1010
|
+
*/
|
|
1011
|
+
skipPlugins?: readonly string[];
|
|
1012
|
+
/**
|
|
1013
|
+
* Per-call escape hatch for `multiTenantPlugin`. When `true`, the
|
|
1014
|
+
* tenant scope hook returns without injecting `organizationId` (or
|
|
1015
|
+
* the configured `tenantField`) into the operation's policy slot
|
|
1016
|
+
* for THIS call only. The plugin emits `after:tenant-bypass` with
|
|
1017
|
+
* `{ context, operation, reason: 'option' }` so audit / observability
|
|
1018
|
+
* plugins can distinguish bypassed queries from tenant-scoped ones.
|
|
1019
|
+
*
|
|
1020
|
+
* Reach for this when:
|
|
1021
|
+
* - A super-admin / platform-admin user needs cross-tenant read
|
|
1022
|
+
* for support tooling.
|
|
1023
|
+
* - A migration script writes / backfills across tenants
|
|
1024
|
+
* deliberately.
|
|
1025
|
+
* - A scheduled job (TTL cleanup, cross-tenant aggregate, billing
|
|
1026
|
+
* rollup) needs the unscoped view.
|
|
1027
|
+
*
|
|
1028
|
+
* **`bypassTenant` is the most-specific decision** — runs before
|
|
1029
|
+
* `skipWhen` (plugin-level callback) and `resolveContext` (CLS
|
|
1030
|
+
* fallback). Distinct from `skipWhen` which fires for every call
|
|
1031
|
+
* and reads context to decide; `bypassTenant: true` is a deliberate
|
|
1032
|
+
* per-call opt-out with no role check, so the calling code carries
|
|
1033
|
+
* the responsibility.
|
|
1034
|
+
*
|
|
1035
|
+
* **NOT a substitute for proper access control.** This flag bypasses
|
|
1036
|
+
* tenant scoping; it does NOT bypass authentication, RBAC, or any
|
|
1037
|
+
* other host-level access check. Wrap it with your auth boundary at
|
|
1038
|
+
* the controller / service layer.
|
|
1039
|
+
*
|
|
1040
|
+
* @example
|
|
1041
|
+
* ```ts
|
|
1042
|
+
* // Support engineer pulling a customer's order across tenants:
|
|
1043
|
+
* await orderRepo.findAll({}, { bypassTenant: true });
|
|
1044
|
+
*
|
|
1045
|
+
* // Migration backfill across the whole estate:
|
|
1046
|
+
* for await (const doc of repo.cursor({}, { bypassTenant: true })) { ... }
|
|
1047
|
+
* ```
|
|
1048
|
+
*/
|
|
1049
|
+
bypassTenant?: boolean;
|
|
798
1050
|
/** Extensible — plugins can read custom fields from options */
|
|
799
1051
|
[key: string]: unknown;
|
|
800
1052
|
}
|
|
@@ -821,12 +1073,14 @@ interface OperationOptions extends ReadOptions {
|
|
|
821
1073
|
/** Additional query filters (e.g., for soft delete) */
|
|
822
1074
|
query?: Record<string, unknown>;
|
|
823
1075
|
}
|
|
824
|
-
/**
|
|
1076
|
+
/**
|
|
1077
|
+
* Cache-aware operation options — extends OperationOptions with the
|
|
1078
|
+
* unified cache plugin's per-call shape. Pass `cache: { staleTime, swr,
|
|
1079
|
+
* tags, bypass, ... }` to override freshness for this call.
|
|
1080
|
+
*/
|
|
825
1081
|
interface CacheableOptions extends OperationOptions {
|
|
826
|
-
/**
|
|
827
|
-
|
|
828
|
-
/** Custom TTL for this operation in seconds */
|
|
829
|
-
cacheTtl?: number;
|
|
1082
|
+
/** Per-call cache override forwarded to the unified cache plugin. */
|
|
1083
|
+
cache?: _$_classytic_repo_core_cache0.CacheOptions;
|
|
830
1084
|
}
|
|
831
1085
|
/** withTransaction options */
|
|
832
1086
|
interface WithTransactionOptions {
|
|
@@ -1009,6 +1263,15 @@ interface RepositoryContext {
|
|
|
1009
1263
|
session?: unknown;
|
|
1010
1264
|
/** Read preference for replica sets */
|
|
1011
1265
|
readPreference?: ReadPreferenceType;
|
|
1266
|
+
/**
|
|
1267
|
+
* Per-call escape hatch — plugin names listed here that opt into the
|
|
1268
|
+
* mechanism (`auditLog`, `auditTrail`, `observability`) skip their
|
|
1269
|
+
* work for this call. Threaded from `OperationOptions.skipPlugins`
|
|
1270
|
+
* via `_buildContext`. Security/correctness plugins (multi-tenant
|
|
1271
|
+
* scope, cache invalidation, soft-delete filter) deliberately ignore
|
|
1272
|
+
* this — those must not be skippable per-call.
|
|
1273
|
+
*/
|
|
1274
|
+
skipPlugins?: readonly string[];
|
|
1012
1275
|
/** Pagination filters */
|
|
1013
1276
|
filters?: Record<string, unknown>;
|
|
1014
1277
|
/** Sort specification */
|
|
@@ -1044,37 +1307,68 @@ interface RepositoryContext {
|
|
|
1044
1307
|
* is the only source of soft semantics.
|
|
1045
1308
|
*/
|
|
1046
1309
|
deleteMode?: 'hard' | 'soft';
|
|
1047
|
-
/**
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1310
|
+
/**
|
|
1311
|
+
* Per-call cache options forwarded by the unified cache plugin
|
|
1312
|
+
* (`@classytic/repo-core/cache`). Kits copy this from the caller's
|
|
1313
|
+
* `options.cache` onto the context so the plugin's `before:<op>`
|
|
1314
|
+
* hook can read it.
|
|
1315
|
+
*/
|
|
1316
|
+
cache?: _$_classytic_repo_core_cache0.CacheOptions;
|
|
1317
|
+
/** Whether result was served from cache (internal — set by cache plugin) */
|
|
1052
1318
|
_cacheHit?: boolean;
|
|
1053
|
-
/** Cached result (internal) */
|
|
1319
|
+
/** Cached result (internal — set by cache plugin) */
|
|
1054
1320
|
_cachedResult?: unknown;
|
|
1321
|
+
/** Resolved cache options stash (internal — set by cache plugin) */
|
|
1322
|
+
_cacheKey?: string;
|
|
1323
|
+
/** Resolved cache options stash (internal — set by cache plugin) */
|
|
1324
|
+
_cacheResolved?: unknown;
|
|
1325
|
+
/** Cache hit status (internal — `'fresh'` or `'stale'`) */
|
|
1326
|
+
_cacheStatus?: 'fresh' | 'stale' | 'miss' | 'disabled' | 'bypass';
|
|
1055
1327
|
/** IDs to cascade delete (internal) */
|
|
1056
1328
|
_cascadeIds?: unknown[];
|
|
1057
1329
|
/** Custom context data from plugins */
|
|
1058
1330
|
[key: string]: unknown;
|
|
1059
1331
|
}
|
|
1060
|
-
/**
|
|
1061
|
-
|
|
1332
|
+
/**
|
|
1333
|
+
* Plugin interface — generic over the repo type so kit-internal plugins
|
|
1334
|
+
* can target the narrower `RepositoryInstance` while cross-package
|
|
1335
|
+
* plugins from `@classytic/repo-core/cache` etc. that target the
|
|
1336
|
+
* shared `RepositoryBase` flow through the same array.
|
|
1337
|
+
*
|
|
1338
|
+
* `RepositoryInstance` formally `extends RepositoryBase` (see below),
|
|
1339
|
+
* so `Plugin<RepositoryBase>` is structurally assignable to
|
|
1340
|
+
* `Plugin<RepositoryInstance>` via TypeScript's method-bivariance
|
|
1341
|
+
* rule for interface methods. No host-site casts required.
|
|
1342
|
+
*/
|
|
1343
|
+
interface Plugin<TRepo extends RepositoryBase = RepositoryInstance> {
|
|
1062
1344
|
/** Plugin name */
|
|
1063
1345
|
name: string;
|
|
1064
1346
|
/** Apply plugin to repository */
|
|
1065
|
-
apply(repo:
|
|
1347
|
+
apply(repo: TRepo): void;
|
|
1066
1348
|
}
|
|
1067
|
-
/** Plugin function signature */
|
|
1068
|
-
type PluginFunction = (repo:
|
|
1069
|
-
/** Plugin type (object or function) */
|
|
1070
|
-
type PluginType = Plugin | PluginFunction
|
|
1349
|
+
/** Plugin function signature. Same TRepo widening as `Plugin`. */
|
|
1350
|
+
type PluginFunction<TRepo extends RepositoryBase = RepositoryInstance> = (repo: TRepo) => void;
|
|
1351
|
+
/** Plugin type (object or function). */
|
|
1352
|
+
type PluginType<TRepo extends RepositoryBase = RepositoryInstance> = Plugin<TRepo> | PluginFunction<TRepo>;
|
|
1071
1353
|
/** Hook with priority for deterministic phase ordering */
|
|
1072
1354
|
interface PrioritizedHook {
|
|
1073
1355
|
listener: (data: any) => void | Promise<void>;
|
|
1074
1356
|
priority: number;
|
|
1075
1357
|
}
|
|
1076
|
-
/**
|
|
1077
|
-
|
|
1358
|
+
/**
|
|
1359
|
+
* Repository instance for plugin type reference.
|
|
1360
|
+
*
|
|
1361
|
+
* Extends `@classytic/repo-core/repository`'s `RepositoryBase` so the
|
|
1362
|
+
* full hook + adapter contract is included. This keeps mongokit's
|
|
1363
|
+
* `Plugin<RepositoryInstance>` and repo-core's `Plugin<RepositoryBase>`
|
|
1364
|
+
* structurally aligned — cross-package plugins (`cachePlugin` from
|
|
1365
|
+
* `@classytic/repo-core/cache` etc.) flow into mongokit's `plugins[]`
|
|
1366
|
+
* array without per-call casts at the host site.
|
|
1367
|
+
*
|
|
1368
|
+
* The `[key: string]: unknown` index signature at the bottom keeps
|
|
1369
|
+
* dynamic plugin-method registration (`registerMethod`) compatible.
|
|
1370
|
+
*/
|
|
1371
|
+
interface RepositoryInstance extends RepositoryBase {
|
|
1078
1372
|
Model: Model<any>;
|
|
1079
1373
|
model: string;
|
|
1080
1374
|
_hooks: Map<string, PrioritizedHook[]>;
|
|
@@ -1092,13 +1386,14 @@ interface RepositoryInstance {
|
|
|
1092
1386
|
_buildContext(operation: string, options: Record<string, unknown> | object): Promise<RepositoryContext>;
|
|
1093
1387
|
_handleError(error: Error): HttpError;
|
|
1094
1388
|
update(id: string | ObjectId, data: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
|
|
1389
|
+
findOneAndUpdate(filter: Record<string, unknown>, update: Record<string, unknown> | Record<string, unknown>[], options?: Record<string, unknown>): Promise<unknown>;
|
|
1095
1390
|
aggregatePipeline(pipeline: PipelineStage[], options?: Record<string, unknown>): Promise<unknown[]>;
|
|
1096
1391
|
getByQuery(query: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
|
|
1097
1392
|
_executeQuery<T>(buildQuery: (Model: Model<any>) => Promise<T>): Promise<T>;
|
|
1098
1393
|
[key: string]: unknown;
|
|
1099
1394
|
}
|
|
1100
1395
|
/** Repository operation names */
|
|
1101
|
-
type RepositoryOperation = 'create' | 'createMany' | 'update' | 'updateMany' | 'findOneAndUpdate' | 'delete' | 'deleteMany' | 'restore' | 'getById' | 'getByQuery' | 'getOne' | 'getAll' | 'findAll' | 'getOrCreate' | 'count' | 'exists' | 'distinct' | 'aggregate' | 'aggregatePaginate' | 'aggregatePipeline' | 'aggregatePipelinePaginate' | 'lookupPopulate' | 'bulkWrite';
|
|
1396
|
+
type RepositoryOperation = 'create' | 'createMany' | 'update' | 'updateMany' | 'findOneAndUpdate' | 'delete' | 'deleteMany' | 'restore' | 'getById' | 'getByQuery' | 'getOne' | 'getAll' | 'findAll' | 'getOrCreate' | 'count' | 'exists' | 'distinct' | 'aggregate' | 'aggregatePaginate' | 'aggregatePipeline' | 'aggregatePipelinePaginate' | 'lookupPopulate' | 'bulkWrite' | 'claim' | 'claimVersion' | 'cursor';
|
|
1102
1397
|
/** Event lifecycle phases */
|
|
1103
1398
|
type EventPhase = 'before' | 'after' | 'error';
|
|
1104
1399
|
/** Repository event names (generated from template literals) */
|
|
@@ -1136,6 +1431,27 @@ interface FieldPreset {
|
|
|
1136
1431
|
/** Additional fields for admins */
|
|
1137
1432
|
admin?: string[];
|
|
1138
1433
|
}
|
|
1434
|
+
/** Value type identifier for cursor serialization */
|
|
1435
|
+
type ValueType = 'date' | 'objectid' | 'boolean' | 'number' | 'string' | 'null' | 'unknown';
|
|
1436
|
+
/** Cursor payload */
|
|
1437
|
+
interface CursorPayload {
|
|
1438
|
+
/** Primary sort field value (legacy single-field) */
|
|
1439
|
+
v: string | number | boolean | null;
|
|
1440
|
+
/** Value type identifier (legacy single-field) */
|
|
1441
|
+
t: ValueType;
|
|
1442
|
+
/** Document ID */
|
|
1443
|
+
id: string;
|
|
1444
|
+
/** ID type identifier */
|
|
1445
|
+
idType: ValueType;
|
|
1446
|
+
/** Sort specification */
|
|
1447
|
+
sort: SortSpec;
|
|
1448
|
+
/** Cursor version */
|
|
1449
|
+
ver: number;
|
|
1450
|
+
/** Compound sort field values (multi-field keyset) */
|
|
1451
|
+
vals?: Record<string, string | number | boolean | null>;
|
|
1452
|
+
/** Compound sort value types */
|
|
1453
|
+
types?: Record<string, ValueType>;
|
|
1454
|
+
}
|
|
1139
1455
|
/** Decoded cursor */
|
|
1140
1456
|
interface DecodedCursor {
|
|
1141
1457
|
/** Primary sort field value (rehydrated) — legacy compat */
|
|
@@ -1237,84 +1553,13 @@ interface MinMaxResult {
|
|
|
1237
1553
|
max: unknown;
|
|
1238
1554
|
}
|
|
1239
1555
|
/**
|
|
1240
|
-
*
|
|
1241
|
-
*
|
|
1242
|
-
*
|
|
1243
|
-
* Shape is aligned with `@classytic/repo-core/cache#CacheAdapter` and arc's
|
|
1244
|
-
* `CacheStore` so one adapter implementation satisfies every consumer. The
|
|
1245
|
-
* `delete` method is named for consistency with `Map.delete` / `Set.delete` /
|
|
1246
|
-
* `MinimalRepo.delete(id)` — your Redis wrapper translates to `redis.del`.
|
|
1247
|
-
*
|
|
1248
|
-
* @example Redis implementation:
|
|
1249
|
-
* ```typescript
|
|
1250
|
-
* const redisCache: CacheAdapter = {
|
|
1251
|
-
* async get(key) { return JSON.parse(await redis.get(key) || 'null'); },
|
|
1252
|
-
* async set(key, value, ttl) { await redis.setex(key, ttl, JSON.stringify(value)); },
|
|
1253
|
-
* async delete(key) { await redis.del(key); },
|
|
1254
|
-
* async clear(pattern) {
|
|
1255
|
-
* const keys = await redis.keys(pattern || '*');
|
|
1256
|
-
* if (keys.length) await redis.del(...keys);
|
|
1257
|
-
* }
|
|
1258
|
-
* };
|
|
1259
|
-
* ```
|
|
1556
|
+
* Per-call cache options — TanStack-shaped (`staleTime`, `gcTime`, `swr`,
|
|
1557
|
+
* `tags`, `bypass`, `enabled`, `key`). Threaded through read methods via
|
|
1558
|
+
* `options.cache` (CRUD) or `aggRequest.cache` (aggregate).
|
|
1260
1559
|
*/
|
|
1261
|
-
interface CacheAdapter {
|
|
1262
|
-
/** Get value by key, returns null if not found or expired */
|
|
1263
|
-
get<T = unknown>(key: string): Promise<T | null>;
|
|
1264
|
-
/** Set value with TTL in seconds */
|
|
1265
|
-
set<T = unknown>(key: string, value: T, ttl: number): Promise<void>;
|
|
1266
|
-
/** Delete a single key. No-op when the key doesn't exist. */
|
|
1267
|
-
delete(key: string): Promise<void>;
|
|
1268
|
-
/** Clear keys matching pattern (optional, used for bulk invalidation) */
|
|
1269
|
-
clear?(pattern?: string): Promise<void>;
|
|
1270
|
-
}
|
|
1271
|
-
/** Cache plugin options */
|
|
1272
|
-
interface CacheOptions {
|
|
1273
|
-
/** Cache adapter implementation (required) */
|
|
1274
|
-
adapter: CacheAdapter;
|
|
1275
|
-
/** Default TTL in seconds (default: 60) */
|
|
1276
|
-
ttl?: number;
|
|
1277
|
-
/** TTL for byId queries in seconds (default: same as ttl) */
|
|
1278
|
-
byIdTtl?: number;
|
|
1279
|
-
/** TTL for query/list results in seconds (default: same as ttl) */
|
|
1280
|
-
queryTtl?: number;
|
|
1281
|
-
/** Key prefix for namespacing (default: 'mk') */
|
|
1282
|
-
prefix?: string;
|
|
1283
|
-
/** Enable debug logging (default: false) */
|
|
1284
|
-
debug?: boolean;
|
|
1285
|
-
/**
|
|
1286
|
-
* Skip caching for queries with these characteristics:
|
|
1287
|
-
* - largeLimit: Skip if limit > value (default: 100)
|
|
1288
|
-
*/
|
|
1289
|
-
skipIf?: {
|
|
1290
|
-
largeLimit?: number;
|
|
1291
|
-
};
|
|
1292
|
-
/**
|
|
1293
|
-
* TTL jitter to mitigate cache stampedes. When many entries share the same
|
|
1294
|
-
* TTL and expire simultaneously, every concurrent reader misses and hammers
|
|
1295
|
-
* the DB at once. Jitter spreads expirations over a window.
|
|
1296
|
-
*
|
|
1297
|
-
* - `number` in [0, 1]: fractional symmetric jitter. `0.1` multiplies the
|
|
1298
|
-
* TTL by a random factor in [0.9, 1.1]. Default: 0 (no jitter).
|
|
1299
|
-
* - `function(ttl)`: full control — receive the configured TTL (seconds),
|
|
1300
|
-
* return the effective TTL (seconds) to store with.
|
|
1301
|
-
*/
|
|
1302
|
-
jitter?: number | ((ttlSeconds: number) => number);
|
|
1303
|
-
}
|
|
1304
|
-
/** Options for cache-aware operations */
|
|
1305
1560
|
interface CacheOperationOptions {
|
|
1306
|
-
/**
|
|
1307
|
-
|
|
1308
|
-
/** Custom TTL for this operation in seconds */
|
|
1309
|
-
cacheTtl?: number;
|
|
1310
|
-
}
|
|
1311
|
-
/** Cache statistics (for debugging/monitoring) */
|
|
1312
|
-
interface CacheStats {
|
|
1313
|
-
hits: number;
|
|
1314
|
-
misses: number;
|
|
1315
|
-
sets: number;
|
|
1316
|
-
invalidations: number;
|
|
1317
|
-
errors: number;
|
|
1561
|
+
/** Per-call cache override forwarded to the unified cache plugin. */
|
|
1562
|
+
cache?: _$_classytic_repo_core_cache0.CacheOptions;
|
|
1318
1563
|
}
|
|
1319
1564
|
/** Cascade relation definition */
|
|
1320
1565
|
interface CascadeRelation {
|
|
@@ -1349,37 +1594,6 @@ interface CascadeOptions {
|
|
|
1349
1594
|
/** Logger for cascade operations */
|
|
1350
1595
|
logger?: Logger;
|
|
1351
1596
|
}
|
|
1352
|
-
/** HTTP Error with status code */
|
|
1353
|
-
interface HttpError extends Error {
|
|
1354
|
-
status: number;
|
|
1355
|
-
/**
|
|
1356
|
-
* Stable machine-readable error code. Hosts can switch on this in catch
|
|
1357
|
-
* blocks instead of grepping the message string. Currently emitted for:
|
|
1358
|
-
* - `SEARCH_NOT_CONFIGURED` — search requested on a repo with no text
|
|
1359
|
-
* index AND no `searchMode: 'regex' | 'auto'` + `searchFields` config.
|
|
1360
|
-
*/
|
|
1361
|
-
code?: string;
|
|
1362
|
-
/**
|
|
1363
|
-
* Free-form structured metadata for diagnostics. Pairs with `code` so
|
|
1364
|
-
* hosts can render a clearer message in their own UI without parsing
|
|
1365
|
-
* the human-readable `message` string. Safe for logs.
|
|
1366
|
-
*/
|
|
1367
|
-
meta?: Record<string, unknown>;
|
|
1368
|
-
validationErrors?: Array<{
|
|
1369
|
-
validator: string;
|
|
1370
|
-
error: string;
|
|
1371
|
-
}>;
|
|
1372
|
-
/**
|
|
1373
|
-
* Structured metadata for duplicate-key (E11000) errors. Safe to surface in
|
|
1374
|
-
* logs/audit. Includes the offending field names and a boolean-only mirror
|
|
1375
|
-
* of whether each field had a duplicate value — never the value itself
|
|
1376
|
-
* unless `parseDuplicateKeyError` was called with `{ exposeValues: true }`.
|
|
1377
|
-
*/
|
|
1378
|
-
duplicate?: {
|
|
1379
|
-
fields: string[]; /** Only populated when parseDuplicateKeyError was called with exposeValues:true. */
|
|
1380
|
-
values?: Record<string, unknown>;
|
|
1381
|
-
};
|
|
1382
|
-
}
|
|
1383
1597
|
/**
|
|
1384
1598
|
* Combines all plugin method types into a single type
|
|
1385
1599
|
* Useful when you're using all plugins and want full type safety
|
|
@@ -1396,7 +1610,7 @@ interface HttpError extends Error {
|
|
|
1396
1610
|
* // TypeScript knows about all plugin methods!
|
|
1397
1611
|
* await repo.increment(id, 'views', 1);
|
|
1398
1612
|
* await repo.restore(id);
|
|
1399
|
-
* await repo.
|
|
1613
|
+
* await repo.cache?.invalidateByTags(['org:abc']);
|
|
1400
1614
|
* ```
|
|
1401
1615
|
*
|
|
1402
1616
|
* Note: Import the individual plugin method types if you need them:
|
|
@@ -1407,7 +1621,6 @@ interface HttpError extends Error {
|
|
|
1407
1621
|
* AggregateHelpersMethods,
|
|
1408
1622
|
* SubdocumentMethods,
|
|
1409
1623
|
* SoftDeleteMethods,
|
|
1410
|
-
* CacheMethods,
|
|
1411
1624
|
* } from '@classytic/mongokit';
|
|
1412
1625
|
* ```
|
|
1413
1626
|
*/
|
|
@@ -1478,11 +1691,12 @@ type AllPluginMethods<TDoc> = {
|
|
|
1478
1691
|
lean?: boolean;
|
|
1479
1692
|
session?: unknown;
|
|
1480
1693
|
}): Promise<OffsetPaginationResult<TDoc>>;
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1694
|
+
/**
|
|
1695
|
+
* Cache plugin handle (set by `cachePlugin({ adapter })`). Exposes the
|
|
1696
|
+
* `CacheEngine` plus convenience invalidation helpers. `undefined` when
|
|
1697
|
+
* no cache plugin is wired.
|
|
1698
|
+
*/
|
|
1699
|
+
cache?: _$_classytic_repo_core_cache0.RepositoryCacheHandle;
|
|
1486
1700
|
};
|
|
1487
1701
|
/**
|
|
1488
1702
|
* Helper type to add all plugin methods to a repository class
|
|
@@ -1510,4 +1724,4 @@ type AllPluginMethods<TDoc> = {
|
|
|
1510
1724
|
*/
|
|
1511
1725
|
type WithPlugins<TDoc, TRepo extends RepositoryInstance = RepositoryInstance> = TRepo & AllPluginMethods<TDoc>;
|
|
1512
1726
|
//#endregion
|
|
1513
|
-
export {
|
|
1727
|
+
export { RepositoryEvent as $, KeysetPaginationOptions as A, ObjectId as B, FieldPreset as C, IControllerResponse as Ct, InferDocument as D, LookupOptions as Dt, HookMode as E, LookupBuilder as Et, MiddlewareContext as F, Plugin as G, OperationOptions as H, MinMaxResult as I, PopulateSpec as J, PluginFunction as K, MinimalRepoView as L, LookupPopulateOptions as M, LookupPopulateResult as N, InferRawDoc as O, Middleware as P, RepositoryContext as Q, MongoOperatorUpdate as R, EventPhase as S, IController as St, GroupResult as T, IResponseFormatter as Tt, PaginationConfig as U, OffsetPaginationOptions as V, PartialBy as W, ReadOptions as X, PrioritizedHook as Y, ReadPreferenceType as Z, DeleteManyResult as _, ValidationChainOptions as _t, AnyModel as a, SessionOptions as at, EventHandlers as b, WithPlugins as bt, CacheableOptions as c, SoftDeleteRepository as ct, CollationOptions as d, Strict as dt, RepositoryInstance as et, CreateInput as f, UpdateManyResult as ft, DeepPartial as g, UserContext as gt, DecodedCursor as h, UpdateWithValidationResult as ht, AnyDocument as i, SelectSpec as it, Logger as j, KeysOfType as k, CascadeOptions as l, SortDirection as lt, CursorPayload as m, UpdatePatch as mt, AggregatePaginationOptions as n, RepositoryOptions as nt, BasePaginationOptions as o, SoftDeleteFilterMode as ot, CreateOptions as p, UpdateOptions as pt, PluginType as q, AllPluginMethods as r, RequiredBy as rt, CacheOperationOptions as s, SoftDeleteOptions as st, AggregateOptions as t, RepositoryOperation as tt, CascadeRelation as u, SortSpec as ut, DeleteResult as v, ValidatorDefinition as vt, FindOneAndUpdateOptions as w, IRequestContext as wt, EventPayload as x, WithTransactionOptions as xt, DocField as y, ValueType as yt, NonNullableFields as z };
|