@classytic/mongokit 3.24.0 → 3.28.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 +12 -2
- package/dist/{PaginationEngine-BnZ24ehC.d.mts → PaginationEngine-CBS1XGzp.d.mts} +2 -1
- package/dist/{Repository-ClOraFjT.mjs → Repository-BFBTAMlA.mjs} +177 -54
- package/dist/actions/index.d.mts +1 -1
- package/dist/adapter/index.d.mts +1 -1
- package/dist/adapter/index.mjs +1 -1
- package/dist/{adapter-BWgJ_Jdc.mjs → adapter-CLvUHiTW.mjs} +1 -1
- package/dist/ai/index.d.mts +1 -1
- package/dist/better-auth/index.mjs +2 -2
- package/dist/core-62oo3N1C.d.mts +50 -0
- package/dist/{create-repository-CVupgeRG.mjs → create-repository-2DMwkYVz.mjs} +2 -2
- package/dist/{create-repository-Cn1LkvL7.d.mts → create-repository-D4ZU94b3.d.mts} +33 -5
- package/dist/{id-resolution-P2OzcyRl.mjs → id-resolution-BqDrk0S2.mjs} +12 -1
- package/dist/{index-DLQ6pjal.d.mts → index-DceInCTX.d.mts} +3 -1
- package/dist/index.d.mts +70 -213
- package/dist/index.mjs +1071 -922
- package/dist/{mongooseToJsonSchema-CVnrKFDq.mjs → mongooseToJsonSchema-CByt1VhQ.mjs} +1 -1
- package/dist/{multi-tenant.plugin-BjXXuc_g.d.mts → multi-tenant.plugin-DVyVGREb.d.mts} +117 -18
- package/dist/operations-C8IcCHF9.d.mts +463 -0
- package/dist/pagination/PaginationEngine.d.mts +1 -1
- package/dist/pagination-jSUdrACK.d.mts +158 -0
- package/dist/plugin-options-vFufQZMZ.d.mts +125 -0
- package/dist/plugins/index.d.mts +4 -4
- package/dist/plugins/index.mjs +3 -3
- package/dist/query/primitives/coercion.mjs +1 -1
- package/dist/query/primitives/geo.d.mts +1 -1
- package/dist/repository-DCHRWRXw.d.mts +511 -0
- package/dist/testkit/index.d.mts +1 -1
- package/dist/testkit/index.mjs +1 -1
- package/dist/{timestamp.plugin-BhSX0Lu_.mjs → timestamp.plugin-ennbatTW.mjs} +192 -46
- package/dist/{to-plain-g8-gsVxZ.d.mts → to-plain-DkF5px0R.d.mts} +12 -16
- package/dist/type-utils-CLzRFtIA.d.mts +163 -0
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/index.mjs +3 -3
- package/dist/{validation-chain.plugin-CFJbr964.d.mts → validation-chain.plugin-Cm0Ttnd8.d.mts} +140 -2
- package/dist/{validation-chain.plugin-Dtt4tO7R.mjs → validation-chain.plugin-k9joH2qw.mjs} +149 -2
- package/package.json +7 -5
- package/dist/types-BtMuhoX5.d.mts +0 -1830
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import { a as PopulateSpec, i as ObjectId, l as SortSpec, o as ReadPreferenceType, r as FilterQuery, s as SelectSpec, t as AnyDocument } from "./core-62oo3N1C.mjs";
|
|
2
|
+
import { r as DocField } from "./type-utils-CLzRFtIA.mjs";
|
|
3
|
+
import { RepositoryBase } from "@classytic/repo-core/repository";
|
|
4
|
+
import { Model, PipelineStage } from "mongoose";
|
|
5
|
+
import { HttpError } from "@classytic/repo-core/errors";
|
|
6
|
+
import { OffsetPaginationResult } from "@classytic/repo-core/pagination";
|
|
7
|
+
//#region src/plugins/names.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Canonical identifiers exposed by mongokit's bundled plugins.
|
|
10
|
+
*
|
|
11
|
+
* Use these values with `RepositoryOptions.requirePlugins` and
|
|
12
|
+
* `OperationOptions.skipPlugins` instead of repeating string literals.
|
|
13
|
+
* The values intentionally preserve the plugins' existing public names.
|
|
14
|
+
*/
|
|
15
|
+
declare const MONGOKIT_PLUGIN_NAMES: {
|
|
16
|
+
readonly aggregateHelpers: "aggregate-helpers";
|
|
17
|
+
readonly appendOnly: "appendOnly";
|
|
18
|
+
readonly auditLog: "auditLog";
|
|
19
|
+
readonly auditTrail: "auditTrail";
|
|
20
|
+
readonly batchOperations: "batch-operations";
|
|
21
|
+
readonly cache: "cache";
|
|
22
|
+
readonly cascade: "cascade";
|
|
23
|
+
readonly changeLog: "changeLog";
|
|
24
|
+
readonly customId: "custom-id";
|
|
25
|
+
readonly elasticSearch: "elastic-search";
|
|
26
|
+
readonly fieldFilter: "fieldFilter";
|
|
27
|
+
readonly immutableStates: "immutableStates";
|
|
28
|
+
readonly lease: "lease";
|
|
29
|
+
readonly methodRegistry: "method-registry";
|
|
30
|
+
readonly mongoOperations: "mongo-operations";
|
|
31
|
+
readonly multiTenant: "multi-tenant";
|
|
32
|
+
readonly observability: "observability";
|
|
33
|
+
readonly softDelete: "softDelete";
|
|
34
|
+
readonly subdocument: "subdocument";
|
|
35
|
+
readonly timestamp: "timestamp";
|
|
36
|
+
readonly validationChain: "validation-chain";
|
|
37
|
+
};
|
|
38
|
+
type MongoKitPluginName = (typeof MONGOKIT_PLUGIN_NAMES)[keyof typeof MONGOKIT_PLUGIN_NAMES];
|
|
39
|
+
/** Bundled-name autocomplete while remaining open to third-party plugins. */
|
|
40
|
+
type RepositoryPluginName = MongoKitPluginName | (string & Record<never, never>);
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/types/repository.d.ts
|
|
43
|
+
/** Hook execution mode */
|
|
44
|
+
type HookMode = 'sync' | 'async';
|
|
45
|
+
/**
|
|
46
|
+
* Minimal repo surface exposed to wrap-style middleware via the
|
|
47
|
+
* `repo` field of `MiddlewareContext`. Intentionally narrow — middleware
|
|
48
|
+
* shouldn't reach into the full `Repository` (that would couple every
|
|
49
|
+
* middleware to the kit). The 5 floor methods plus `Model` cover every
|
|
50
|
+
* legitimate middleware need (cross-doc reads, custom queries, type-
|
|
51
|
+
* narrowing on `Model.modelName`).
|
|
52
|
+
*/
|
|
53
|
+
interface MinimalRepoView<TDoc> {
|
|
54
|
+
readonly Model: Model<TDoc>;
|
|
55
|
+
readonly model: string;
|
|
56
|
+
getById(id: string | ObjectId, options?: Record<string, unknown>): Promise<TDoc | null>;
|
|
57
|
+
getAll?(params?: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
|
|
58
|
+
create(data: Record<string, unknown>, options?: Record<string, unknown>): Promise<TDoc>;
|
|
59
|
+
update(id: string | ObjectId, data: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
|
|
60
|
+
delete(id: string | ObjectId, options?: Record<string, unknown>): Promise<unknown>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Context passed to a wrap-style middleware. Middleware mutates
|
|
64
|
+
* `context` before calling `await next()` to alter input; inspects /
|
|
65
|
+
* transforms the resolved value to alter output; uses plain try/catch
|
|
66
|
+
* around `next()` for error handling.
|
|
67
|
+
*
|
|
68
|
+
* The `before:` / `after:` / `error:` hook engine still fires from
|
|
69
|
+
* within `next()` — hooks remain authoritative for policy / cache /
|
|
70
|
+
* audit so security and correctness plugins keep working unchanged.
|
|
71
|
+
* Middleware composes ergonomically on top.
|
|
72
|
+
*/
|
|
73
|
+
interface MiddlewareContext<TDoc> {
|
|
74
|
+
/** Operation name (`'create'`, `'getById'`, ..., or any plugin op). */
|
|
75
|
+
readonly operation: string;
|
|
76
|
+
/** Live repository context — mutate fields before `next()` to alter input. */
|
|
77
|
+
readonly context: RepositoryContext;
|
|
78
|
+
/**
|
|
79
|
+
* Continue the chain — the next middleware (or, for the innermost
|
|
80
|
+
* middleware, the actual op + after/error hooks). The return value
|
|
81
|
+
* is the resolved op result; throwing propagates up the chain.
|
|
82
|
+
*/
|
|
83
|
+
next: () => Promise<unknown>;
|
|
84
|
+
/** Narrow view of the repo for cross-doc reads / custom queries. */
|
|
85
|
+
readonly repo: MinimalRepoView<TDoc>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Wrap-style middleware — Prisma `$extends.query` / Express middleware
|
|
89
|
+
* shape. Registered via `repo.useMiddleware(mw)`. Composes around every
|
|
90
|
+
* `_runOp` invocation; runs in registration order (first-registered
|
|
91
|
+
* runs outermost).
|
|
92
|
+
*
|
|
93
|
+
* @example Time every operation
|
|
94
|
+
* ```ts
|
|
95
|
+
* repo.useMiddleware(async ({ operation, next }) => {
|
|
96
|
+
* const start = performance.now();
|
|
97
|
+
* try { return await next(); }
|
|
98
|
+
* finally { console.log(`${operation} took ${performance.now() - start}ms`); }
|
|
99
|
+
* });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
type Middleware<TDoc = unknown> = (ctx: MiddlewareContext<TDoc>) => Promise<unknown>;
|
|
103
|
+
/** Search strategy used by Repository.getAll when `search` is provided */
|
|
104
|
+
type RepositorySearchMode = 'text' | 'regex' | 'auto';
|
|
105
|
+
/** Repository options */
|
|
106
|
+
interface RepositoryOptions {
|
|
107
|
+
/** Whether repository event hooks are awaited */
|
|
108
|
+
hooks?: HookMode;
|
|
109
|
+
/** Custom ID field used by getById/update/delete (default: '_id').
|
|
110
|
+
* Set to 'slug', 'code', 'chatId', etc. for non-ObjectId lookups. */
|
|
111
|
+
idField?: string;
|
|
112
|
+
/**
|
|
113
|
+
* How Repository.getAll handles the `search` parameter.
|
|
114
|
+
* - 'text' (default): uses MongoDB $text; requires a text index.
|
|
115
|
+
* - 'regex': builds case-insensitive $or of $regex across `searchFields`. No index required.
|
|
116
|
+
* - 'auto': uses 'text' if a text index exists, otherwise falls back to 'regex' when `searchFields` is set.
|
|
117
|
+
*
|
|
118
|
+
* Works standalone (Express/Nest/etc.) or alongside QueryParser — when the parser
|
|
119
|
+
* already consumed `search` into filters, Repository does nothing extra.
|
|
120
|
+
*/
|
|
121
|
+
searchMode?: RepositorySearchMode;
|
|
122
|
+
/**
|
|
123
|
+
* Fields used when searchMode is 'regex' (or 'auto' falling back to regex).
|
|
124
|
+
* Required for regex mode to take effect.
|
|
125
|
+
*/
|
|
126
|
+
searchFields?: string[];
|
|
127
|
+
/**
|
|
128
|
+
* How to react to a plugin composition that is known to be unsafe.
|
|
129
|
+
* - 'warn' (default): log a warning via the configured logger.
|
|
130
|
+
* - 'throw': fail fast at construction time — recommended for production.
|
|
131
|
+
* - 'off': disable the check entirely.
|
|
132
|
+
*
|
|
133
|
+
* Checks currently enforced:
|
|
134
|
+
* - soft-delete must precede batch-operations (otherwise deleteMany /
|
|
135
|
+
* updateMany skip the soft-delete filter injection).
|
|
136
|
+
* - multi-tenant must precede cache (otherwise cache keys are computed
|
|
137
|
+
* before tenant scoping → cross-tenant cache poisoning risk).
|
|
138
|
+
*/
|
|
139
|
+
pluginOrderChecks?: 'warn' | 'throw' | 'off';
|
|
140
|
+
/**
|
|
141
|
+
* When `true` (default `false`), the repository checks every read/
|
|
142
|
+
* write op's filter against the schema's declared paths and warns
|
|
143
|
+
* once per `(modelName, fieldName)` pair if a filter key isn't on
|
|
144
|
+
* the schema AND `strictQuery: true` is in effect.
|
|
145
|
+
*
|
|
146
|
+
* **Why this exists.** With `mongoose.set('strictQuery', true)` (the
|
|
147
|
+
* mongoose 6 default; some projects pin it explicitly), filter keys
|
|
148
|
+
* not declared on the schema are SILENTLY STRIPPED before the query
|
|
149
|
+
* runs. `findOne({ code: 'X' })` on a schema without `code` becomes
|
|
150
|
+
* `findOne({})` — returns the first doc, not null — and the bug
|
|
151
|
+
* masquerades as "wrong row returned" without any error or log.
|
|
152
|
+
*
|
|
153
|
+
* The diagnostic catches the trap at runtime: log once per offending
|
|
154
|
+
* key per model, so a single bad call surfaces during dev/staging
|
|
155
|
+
* instead of in production. Costs O(filter keys) per call against
|
|
156
|
+
* a constructor-cached set; negligible.
|
|
157
|
+
*
|
|
158
|
+
* Hosts who route mongokit's `warn` into their own logger (via
|
|
159
|
+
* `configureLogger({ warn: ... })`) can ship this enabled in
|
|
160
|
+
* production without polluting stdout.
|
|
161
|
+
*
|
|
162
|
+
* @default false
|
|
163
|
+
*/
|
|
164
|
+
warnOnStrictQueryStrip?: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* Names of plugins that MUST be present in the plugin chain at
|
|
167
|
+
* construction time. Throws a `TypeError` from the constructor if
|
|
168
|
+
* any listed plugin is absent — eliminates the silent-misconfig
|
|
169
|
+
* shape where a host forgets to wire `multiTenantPlugin` and ships
|
|
170
|
+
* a tenant-leak to production.
|
|
171
|
+
*
|
|
172
|
+
* Names match each plugin's exported `name` property (the plugin's
|
|
173
|
+
* own canonical identifier, NOT the JavaScript function name).
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* import { MONGOKIT_PLUGIN_NAMES } from '@classytic/mongokit';
|
|
178
|
+
*
|
|
179
|
+
* new Repository(OrderModel, [
|
|
180
|
+
* methodRegistryPlugin(),
|
|
181
|
+
* multiTenantPlugin({ tenantField: 'organizationId' }),
|
|
182
|
+
* softDeletePlugin(),
|
|
183
|
+
* auditLogPlugin({ logger }),
|
|
184
|
+
* ], paginationConfig, {
|
|
185
|
+
* requirePlugins: [
|
|
186
|
+
* MONGOKIT_PLUGIN_NAMES.multiTenant,
|
|
187
|
+
* MONGOKIT_PLUGIN_NAMES.softDelete,
|
|
188
|
+
* MONGOKIT_PLUGIN_NAMES.auditLog,
|
|
189
|
+
* ],
|
|
190
|
+
* });
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
requirePlugins?: readonly RepositoryPluginName[];
|
|
194
|
+
/**
|
|
195
|
+
* Standard Schema validator (Zod 3.24+, Valibot 1+, ArkType 2+, ...)
|
|
196
|
+
* for write payloads. Forwarded to `RepositoryBase`, which validates
|
|
197
|
+
* `create` data and every `createMany` doc at
|
|
198
|
+
* `HOOK_PRIORITY.VALIDATION` (150) — after policy plugins, before
|
|
199
|
+
* cache/observability. Failures throw an `HttpError` 400 with
|
|
200
|
+
* structured `validationErrors`. Validator output replaces the
|
|
201
|
+
* payload, so schema-declared coercions/defaults flow into the write.
|
|
202
|
+
*/
|
|
203
|
+
schema?: import('@classytic/repo-core/schema').StandardSchemaV1;
|
|
204
|
+
/**
|
|
205
|
+
* Standard Schema validator for `update` payloads. Separate slot
|
|
206
|
+
* because updates are partial — derive one explicitly (e.g.
|
|
207
|
+
* `schema.partial()` in Zod).
|
|
208
|
+
*/
|
|
209
|
+
updateSchema?: import('@classytic/repo-core/schema').StandardSchemaV1;
|
|
210
|
+
/**
|
|
211
|
+
* Domain-event emission. Pass any arc / `@classytic/primitives`-
|
|
212
|
+
* compatible transport and every mutating op publishes
|
|
213
|
+
* `<resource>.<verb>` events (`user.created`, `user.updated`, ...).
|
|
214
|
+
* Omit and the wiring is inert. See `@classytic/repo-core/events`.
|
|
215
|
+
*/
|
|
216
|
+
events?: import('@classytic/repo-core/events').RepositoryEventsOptions;
|
|
217
|
+
}
|
|
218
|
+
/** User context for operations */
|
|
219
|
+
interface UserContext {
|
|
220
|
+
_id?: ObjectId | string;
|
|
221
|
+
id?: string;
|
|
222
|
+
roles?: string | string[];
|
|
223
|
+
[key: string]: unknown;
|
|
224
|
+
}
|
|
225
|
+
/** Repository operation context */
|
|
226
|
+
interface RepositoryContext {
|
|
227
|
+
/** Operation name */
|
|
228
|
+
operation: string;
|
|
229
|
+
/** Model name */
|
|
230
|
+
model: string;
|
|
231
|
+
/** Document data (for create/update) */
|
|
232
|
+
data?: Record<string, unknown>;
|
|
233
|
+
/** Array of documents (for createMany) */
|
|
234
|
+
dataArray?: Record<string, unknown>[];
|
|
235
|
+
/** Document ID (for update/delete/getById) */
|
|
236
|
+
id?: string | ObjectId;
|
|
237
|
+
/** Query filters */
|
|
238
|
+
query?: FilterQuery<AnyDocument>;
|
|
239
|
+
/** User making the request */
|
|
240
|
+
user?: UserContext;
|
|
241
|
+
/** Organization ID for multi-tenancy */
|
|
242
|
+
organizationId?: string | ObjectId;
|
|
243
|
+
/** Fields to select */
|
|
244
|
+
select?: SelectSpec;
|
|
245
|
+
/** Fields to populate */
|
|
246
|
+
populate?: PopulateSpec;
|
|
247
|
+
/** Return lean documents */
|
|
248
|
+
lean?: boolean;
|
|
249
|
+
/** MongoDB session */
|
|
250
|
+
session?: unknown;
|
|
251
|
+
/** Read preference for replica sets */
|
|
252
|
+
readPreference?: ReadPreferenceType;
|
|
253
|
+
/**
|
|
254
|
+
* Per-call escape hatch — plugin names listed here that opt into the
|
|
255
|
+
* mechanism (`auditLog`, `auditTrail`, `observability`) skip their
|
|
256
|
+
* work for this call. Threaded from `OperationOptions.skipPlugins`
|
|
257
|
+
* via `_buildContext`. Security/correctness plugins (multi-tenant
|
|
258
|
+
* scope, cache invalidation, soft-delete filter) deliberately ignore
|
|
259
|
+
* this — those must not be skippable per-call.
|
|
260
|
+
*/
|
|
261
|
+
skipPlugins?: readonly string[];
|
|
262
|
+
/** Pagination filters */
|
|
263
|
+
filters?: Record<string, unknown>;
|
|
264
|
+
/** Sort specification */
|
|
265
|
+
sort?: SortSpec;
|
|
266
|
+
/** Page number (offset pagination) */
|
|
267
|
+
page?: number;
|
|
268
|
+
/** Items per page */
|
|
269
|
+
limit?: number;
|
|
270
|
+
/** Cursor for next page (keyset pagination) */
|
|
271
|
+
after?: string;
|
|
272
|
+
/** Search query string */
|
|
273
|
+
search?: string;
|
|
274
|
+
/** Pagination mode */
|
|
275
|
+
mode?: 'offset' | 'keyset';
|
|
276
|
+
/** Query hint */
|
|
277
|
+
hint?: string | Record<string, 1 | -1>;
|
|
278
|
+
/** Maximum execution time in milliseconds */
|
|
279
|
+
maxTimeMS?: number;
|
|
280
|
+
/** Count strategy for offset pagination */
|
|
281
|
+
countStrategy?: 'exact' | 'estimated' | 'none';
|
|
282
|
+
/** Whether this is a soft delete operation (set by softDeletePlugin) */
|
|
283
|
+
softDeleted?: boolean;
|
|
284
|
+
/** Include soft-deleted documents in queries */
|
|
285
|
+
includeDeleted?: boolean;
|
|
286
|
+
/**
|
|
287
|
+
* Caller-requested delete semantics, read by softDeletePlugin.
|
|
288
|
+
*
|
|
289
|
+
* - `undefined` (default) — plugin decides: soft if wired, hard otherwise.
|
|
290
|
+
* - `'hard'` — bypass soft-delete and physically remove the document.
|
|
291
|
+
* All policy hooks (multi-tenant scope, audit trails, cache invalidation)
|
|
292
|
+
* still fire; only the soft-delete interception is skipped.
|
|
293
|
+
* - `'soft'` — reserved. Currently treated as default since softDeletePlugin
|
|
294
|
+
* is the only source of soft semantics.
|
|
295
|
+
*/
|
|
296
|
+
deleteMode?: 'hard' | 'soft';
|
|
297
|
+
/**
|
|
298
|
+
* Per-call cache options forwarded by the unified cache plugin
|
|
299
|
+
* (`@classytic/repo-core/cache`). Kits copy this from the caller's
|
|
300
|
+
* `options.cache` onto the context so the plugin's `before:<op>`
|
|
301
|
+
* hook can read it.
|
|
302
|
+
*/
|
|
303
|
+
cache?: import('@classytic/repo-core/cache').CacheOptions;
|
|
304
|
+
/** Whether result was served from cache (internal — set by cache plugin) */
|
|
305
|
+
_cacheHit?: boolean;
|
|
306
|
+
/** Cached result (internal — set by cache plugin) */
|
|
307
|
+
_cachedResult?: unknown;
|
|
308
|
+
/** Resolved cache options stash (internal — set by cache plugin) */
|
|
309
|
+
_cacheKey?: string;
|
|
310
|
+
/** Resolved cache options stash (internal — set by cache plugin) */
|
|
311
|
+
_cacheResolved?: unknown;
|
|
312
|
+
/** Cache hit status (internal — `'fresh'` or `'stale'`) */
|
|
313
|
+
_cacheStatus?: 'fresh' | 'stale' | 'miss' | 'disabled' | 'bypass';
|
|
314
|
+
/** IDs to cascade delete (internal) */
|
|
315
|
+
_cascadeIds?: unknown[];
|
|
316
|
+
/** Custom context data from plugins */
|
|
317
|
+
[key: string]: unknown;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Plugin interface — generic over the repo type so kit-internal plugins
|
|
321
|
+
* can target the narrower `RepositoryInstance` while cross-package
|
|
322
|
+
* plugins from `@classytic/repo-core/cache` etc. that target the
|
|
323
|
+
* shared `RepositoryBase` flow through the same array.
|
|
324
|
+
*
|
|
325
|
+
* `RepositoryInstance` formally `extends RepositoryBase` (see below),
|
|
326
|
+
* so `Plugin<RepositoryBase>` is structurally assignable to
|
|
327
|
+
* `Plugin<RepositoryInstance>` via TypeScript's method-bivariance
|
|
328
|
+
* rule for interface methods. No host-site casts required.
|
|
329
|
+
*/
|
|
330
|
+
interface Plugin<TRepo extends RepositoryBase = RepositoryInstance> {
|
|
331
|
+
/** Plugin name */
|
|
332
|
+
name: string;
|
|
333
|
+
/** Apply plugin to repository */
|
|
334
|
+
apply(repo: TRepo): void;
|
|
335
|
+
}
|
|
336
|
+
/** Plugin function signature. Same TRepo widening as `Plugin`. */
|
|
337
|
+
type PluginFunction<TRepo extends RepositoryBase = RepositoryInstance> = (repo: TRepo) => void;
|
|
338
|
+
/** Plugin type (object or function). */
|
|
339
|
+
type PluginType<TRepo extends RepositoryBase = RepositoryInstance> = Plugin<TRepo> | PluginFunction<TRepo>;
|
|
340
|
+
/** Hook with priority for deterministic phase ordering */
|
|
341
|
+
interface PrioritizedHook {
|
|
342
|
+
listener: (data: any) => void | Promise<void>;
|
|
343
|
+
priority: number;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Repository instance for plugin type reference.
|
|
347
|
+
*
|
|
348
|
+
* Extends `@classytic/repo-core/repository`'s `RepositoryBase` so the
|
|
349
|
+
* full hook + adapter contract is included. This keeps mongokit's
|
|
350
|
+
* `Plugin<RepositoryInstance>` and repo-core's `Plugin<RepositoryBase>`
|
|
351
|
+
* structurally aligned — cross-package plugins (`cachePlugin` from
|
|
352
|
+
* `@classytic/repo-core/cache` etc.) flow into mongokit's `plugins[]`
|
|
353
|
+
* array without per-call casts at the host site.
|
|
354
|
+
*
|
|
355
|
+
* The `[key: string]: unknown` index signature at the bottom keeps
|
|
356
|
+
* dynamic plugin-method registration (`registerMethod`) compatible.
|
|
357
|
+
*/
|
|
358
|
+
interface RepositoryInstance extends RepositoryBase {
|
|
359
|
+
Model: Model<any>;
|
|
360
|
+
model: string;
|
|
361
|
+
_hooks: Map<string, PrioritizedHook[]>;
|
|
362
|
+
_pagination: unknown;
|
|
363
|
+
use(plugin: PluginType): this;
|
|
364
|
+
on(event: RepositoryEvent | (string & {}), listener: (data: any) => void | Promise<void>, options?: {
|
|
365
|
+
priority?: number;
|
|
366
|
+
}): this;
|
|
367
|
+
off(event: RepositoryEvent | (string & {}), listener: (data: any) => void | Promise<void>): this;
|
|
368
|
+
removeAllListeners(event?: string): this;
|
|
369
|
+
emit(event: string, data: unknown): void;
|
|
370
|
+
emitAsync(event: string, data: unknown): Promise<void>;
|
|
371
|
+
registerMethod?(name: string, fn: (...args: any[]) => any): void;
|
|
372
|
+
hasMethod?(name: string): boolean;
|
|
373
|
+
_buildContext(operation: string, options: Record<string, unknown> | object): Promise<RepositoryContext>;
|
|
374
|
+
_handleError(error: Error): HttpError;
|
|
375
|
+
update(id: string | ObjectId, data: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
|
|
376
|
+
findOneAndUpdate(filter: Record<string, unknown>, update: Record<string, unknown> | Record<string, unknown>[], options?: Record<string, unknown>): Promise<unknown>;
|
|
377
|
+
aggregatePipeline(pipeline: PipelineStage[], options?: Record<string, unknown>): Promise<unknown[]>;
|
|
378
|
+
getByQuery(query: Record<string, unknown>, options?: Record<string, unknown>): Promise<unknown>;
|
|
379
|
+
_executeQuery<T>(buildQuery: (Model: Model<any>) => Promise<T>): Promise<T>;
|
|
380
|
+
[key: string]: unknown;
|
|
381
|
+
}
|
|
382
|
+
/** Repository operation names */
|
|
383
|
+
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' | 'watch';
|
|
384
|
+
/** Event lifecycle phases */
|
|
385
|
+
type EventPhase = 'before' | 'after' | 'error';
|
|
386
|
+
/** Repository event names (generated from template literals) */
|
|
387
|
+
type RepositoryEvent = `${EventPhase}:${RepositoryOperation}` | 'method:registered' | 'error:hook';
|
|
388
|
+
/**
|
|
389
|
+
* Type-safe event handler map
|
|
390
|
+
*
|
|
391
|
+
* Hook signature contract:
|
|
392
|
+
* - `before:*` — receives `context: RepositoryContext` directly (not wrapped).
|
|
393
|
+
* Plugins mutate context in-place to inject filters, data, etc.
|
|
394
|
+
* - `after:*` — receives `{ context, result }` where result is the operation output.
|
|
395
|
+
* - `error:*` — receives `{ context, error }` where error is the caught Error.
|
|
396
|
+
*/
|
|
397
|
+
type EventHandlers<TDoc = unknown> = { [K in RepositoryEvent]?: K extends `before:${string}` ? (context: RepositoryContext) => void | Promise<void> : K extends `after:${string}` ? (payload: {
|
|
398
|
+
context: RepositoryContext;
|
|
399
|
+
result: TDoc | TDoc[];
|
|
400
|
+
}) => void | Promise<void> : K extends `error:${string}` ? (payload: {
|
|
401
|
+
context: RepositoryContext;
|
|
402
|
+
error: Error;
|
|
403
|
+
}) => void | Promise<void> : (payload: {
|
|
404
|
+
context: RepositoryContext;
|
|
405
|
+
}) => void | Promise<void>; };
|
|
406
|
+
/** Event payload */
|
|
407
|
+
interface EventPayload {
|
|
408
|
+
context: RepositoryContext;
|
|
409
|
+
result?: unknown;
|
|
410
|
+
error?: Error;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Combines all plugin method types into a single type.
|
|
414
|
+
* Useful when you're using all plugins and want full type safety.
|
|
415
|
+
*
|
|
416
|
+
* @example
|
|
417
|
+
* ```typescript
|
|
418
|
+
* import { Repository } from '@classytic/mongokit';
|
|
419
|
+
* import type { AllPluginMethods } from '@classytic/mongokit';
|
|
420
|
+
*
|
|
421
|
+
* class UserRepo extends Repository<IUser> {}
|
|
422
|
+
*
|
|
423
|
+
* const repo = new UserRepo(Model, [...allPlugins]) as UserRepo & AllPluginMethods<IUser>;
|
|
424
|
+
*
|
|
425
|
+
* await repo.increment(id, 'views', 1);
|
|
426
|
+
* await repo.restore(id);
|
|
427
|
+
* await repo.cache?.invalidateByTags(['org:abc']);
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
type AllPluginMethods<TDoc> = {
|
|
431
|
+
upsert(query: Record<string, unknown>, data: Record<string, unknown>, options?: Record<string, unknown>): Promise<TDoc>;
|
|
432
|
+
increment(id: string | ObjectId, field: DocField<TDoc>, value?: number, options?: Record<string, unknown>): Promise<TDoc>;
|
|
433
|
+
decrement(id: string | ObjectId, field: DocField<TDoc>, value?: number, options?: Record<string, unknown>): Promise<TDoc>;
|
|
434
|
+
pushToArray(id: string | ObjectId, field: DocField<TDoc>, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
|
|
435
|
+
pullFromArray(id: string | ObjectId, field: DocField<TDoc>, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
|
|
436
|
+
addToSet(id: string | ObjectId, field: DocField<TDoc>, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
|
|
437
|
+
setField(id: string | ObjectId, field: DocField<TDoc>, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
|
|
438
|
+
unsetField(id: string | ObjectId, fields: DocField<TDoc> | DocField<TDoc>[], options?: Record<string, unknown>): Promise<TDoc>;
|
|
439
|
+
renameField(id: string | ObjectId, oldName: DocField<TDoc>, newName: string, options?: Record<string, unknown>): Promise<TDoc>;
|
|
440
|
+
multiplyField(id: string | ObjectId, field: DocField<TDoc>, multiplier: number, options?: Record<string, unknown>): Promise<TDoc>;
|
|
441
|
+
setMin(id: string | ObjectId, field: DocField<TDoc>, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
|
|
442
|
+
setMax(id: string | ObjectId, field: DocField<TDoc>, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
|
|
443
|
+
atomicUpdate(id: string | ObjectId, operators: Record<string, Record<string, unknown>>, options?: Record<string, unknown>): Promise<TDoc>;
|
|
444
|
+
bulkWrite(operations: Record<string, unknown>[], options?: {
|
|
445
|
+
session?: unknown;
|
|
446
|
+
ordered?: boolean;
|
|
447
|
+
}): Promise<{
|
|
448
|
+
ok: number;
|
|
449
|
+
insertedCount: number;
|
|
450
|
+
upsertedCount: number;
|
|
451
|
+
matchedCount: number;
|
|
452
|
+
modifiedCount: number;
|
|
453
|
+
deletedCount: number;
|
|
454
|
+
insertedIds: Record<number, unknown>;
|
|
455
|
+
upsertedIds: Record<number, unknown>;
|
|
456
|
+
}>;
|
|
457
|
+
groupBy(field: DocField<TDoc>, options?: {
|
|
458
|
+
limit?: number;
|
|
459
|
+
session?: unknown;
|
|
460
|
+
}): Promise<Array<{
|
|
461
|
+
_id: unknown;
|
|
462
|
+
count: number;
|
|
463
|
+
}>>;
|
|
464
|
+
sum(field: DocField<TDoc>, query?: Record<string, unknown>, options?: Record<string, unknown>): Promise<number>;
|
|
465
|
+
average(field: DocField<TDoc>, query?: Record<string, unknown>, options?: Record<string, unknown>): Promise<number>;
|
|
466
|
+
min(field: DocField<TDoc>, query?: Record<string, unknown>, options?: Record<string, unknown>): Promise<number>;
|
|
467
|
+
max(field: DocField<TDoc>, query?: Record<string, unknown>, options?: Record<string, unknown>): Promise<number>;
|
|
468
|
+
addSubdocument(parentId: string | ObjectId, arrayPath: DocField<TDoc>, subData: Record<string, unknown>, options?: Record<string, unknown>): Promise<TDoc>;
|
|
469
|
+
getSubdocument(parentId: string | ObjectId, arrayPath: DocField<TDoc>, subId: string | ObjectId, options?: {
|
|
470
|
+
lean?: boolean;
|
|
471
|
+
session?: unknown;
|
|
472
|
+
}): Promise<Record<string, unknown>>;
|
|
473
|
+
updateSubdocument(parentId: string | ObjectId, arrayPath: DocField<TDoc>, subId: string | ObjectId, updateData: Record<string, unknown>, options?: {
|
|
474
|
+
session?: unknown;
|
|
475
|
+
}): Promise<TDoc>;
|
|
476
|
+
deleteSubdocument(parentId: string | ObjectId, arrayPath: DocField<TDoc>, subId: string | ObjectId, options?: Record<string, unknown>): Promise<TDoc>;
|
|
477
|
+
restore(id: string | ObjectId, options?: {
|
|
478
|
+
session?: unknown;
|
|
479
|
+
}): Promise<TDoc>;
|
|
480
|
+
getDeleted(params?: {
|
|
481
|
+
filters?: Record<string, unknown>;
|
|
482
|
+
sort?: SortSpec | string;
|
|
483
|
+
page?: number;
|
|
484
|
+
limit?: number;
|
|
485
|
+
}, options?: {
|
|
486
|
+
select?: SelectSpec;
|
|
487
|
+
populate?: PopulateSpec;
|
|
488
|
+
lean?: boolean;
|
|
489
|
+
session?: unknown;
|
|
490
|
+
}): Promise<OffsetPaginationResult<TDoc>>;
|
|
491
|
+
/**
|
|
492
|
+
* Cache plugin handle (set by `cachePlugin({ adapter })`). Exposes the
|
|
493
|
+
* `CacheEngine` plus convenience invalidation helpers. `undefined` when
|
|
494
|
+
* no cache plugin is wired.
|
|
495
|
+
*/
|
|
496
|
+
cache?: import('@classytic/repo-core/cache').RepositoryCacheHandle;
|
|
497
|
+
};
|
|
498
|
+
/**
|
|
499
|
+
* Helper type to add all plugin methods to a repository class.
|
|
500
|
+
* Cleaner than manually typing the intersection.
|
|
501
|
+
*
|
|
502
|
+
* @example
|
|
503
|
+
* ```typescript
|
|
504
|
+
* const orderRepo = new OrderRepo(Model, [...allPlugins]) as WithPlugins<IOrder, OrderRepo>;
|
|
505
|
+
* await orderRepo.getCustomerOrders('123'); // custom methods
|
|
506
|
+
* await orderRepo.increment(orderId, 'total', 100); // plugin methods
|
|
507
|
+
* ```
|
|
508
|
+
*/
|
|
509
|
+
type WithPlugins<TDoc, TRepo extends RepositoryInstance = RepositoryInstance> = TRepo & AllPluginMethods<TDoc>;
|
|
510
|
+
//#endregion
|
|
511
|
+
export { RepositoryPluginName as S, RepositoryOptions as _, HookMode as a, MONGOKIT_PLUGIN_NAMES as b, MinimalRepoView as c, PluginType as d, PrioritizedHook as f, RepositoryOperation as g, RepositoryInstance as h, EventPhase as i, Plugin as l, RepositoryEvent as m, EventHandlers as n, Middleware as o, RepositoryContext as p, EventPayload as r, MiddlewareContext as s, AllPluginMethods as t, PluginFunction as u, UserContext as v, MongoKitPluginName as x, WithPlugins as y };
|
package/dist/testkit/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as Repository, n as CreateRepositoryConfig } from "../create-repository-
|
|
1
|
+
import { a as Repository, n as CreateRepositoryConfig } from "../create-repository-D4ZU94b3.mjs";
|
|
2
2
|
import { Connection, Model, Schema } from "mongoose";
|
|
3
3
|
import { MongoMemoryReplSet, MongoMemoryServer } from "mongodb-memory-server";
|
|
4
4
|
//#region src/testkit/index.d.ts
|
package/dist/testkit/index.mjs
CHANGED