@classytic/mongokit 3.24.0 → 3.25.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.
Files changed (38) hide show
  1. package/README.md +11 -2
  2. package/dist/{PaginationEngine-BnZ24ehC.d.mts → PaginationEngine-CBS1XGzp.d.mts} +2 -1
  3. package/dist/{Repository-ClOraFjT.mjs → Repository-KKR8jxfD.mjs} +70 -41
  4. package/dist/actions/index.d.mts +1 -1
  5. package/dist/adapter/index.d.mts +1 -1
  6. package/dist/adapter/index.mjs +1 -1
  7. package/dist/{adapter-BWgJ_Jdc.mjs → adapter-CLvUHiTW.mjs} +1 -1
  8. package/dist/ai/index.d.mts +1 -1
  9. package/dist/better-auth/index.mjs +2 -2
  10. package/dist/core-62oo3N1C.d.mts +50 -0
  11. package/dist/{create-repository-CVupgeRG.mjs → create-repository-DA5VJh-3.mjs} +2 -2
  12. package/dist/{create-repository-Cn1LkvL7.d.mts → create-repository-HjMD9h9_.d.mts} +18 -5
  13. package/dist/{id-resolution-P2OzcyRl.mjs → id-resolution-BqDrk0S2.mjs} +12 -1
  14. package/dist/{index-DLQ6pjal.d.mts → index-DceInCTX.d.mts} +3 -1
  15. package/dist/index.d.mts +53 -213
  16. package/dist/index.mjs +1071 -922
  17. package/dist/{mongooseToJsonSchema-CVnrKFDq.mjs → mongooseToJsonSchema-CByt1VhQ.mjs} +1 -1
  18. package/dist/{multi-tenant.plugin-BjXXuc_g.d.mts → multi-tenant.plugin-DVyVGREb.d.mts} +117 -18
  19. package/dist/operations-C8IcCHF9.d.mts +463 -0
  20. package/dist/pagination/PaginationEngine.d.mts +1 -1
  21. package/dist/pagination-jSUdrACK.d.mts +158 -0
  22. package/dist/plugin-options-vFufQZMZ.d.mts +125 -0
  23. package/dist/plugins/index.d.mts +4 -4
  24. package/dist/plugins/index.mjs +3 -3
  25. package/dist/query/primitives/coercion.mjs +1 -1
  26. package/dist/query/primitives/geo.d.mts +1 -1
  27. package/dist/repository-DCHRWRXw.d.mts +511 -0
  28. package/dist/testkit/index.d.mts +1 -1
  29. package/dist/testkit/index.mjs +1 -1
  30. package/dist/{timestamp.plugin-BhSX0Lu_.mjs → timestamp.plugin-B8252GuQ.mjs} +192 -46
  31. package/dist/{to-plain-g8-gsVxZ.d.mts → to-plain-DkF5px0R.d.mts} +12 -16
  32. package/dist/type-utils-CLzRFtIA.d.mts +163 -0
  33. package/dist/utils/index.d.mts +2 -2
  34. package/dist/utils/index.mjs +3 -3
  35. package/dist/{validation-chain.plugin-Dtt4tO7R.mjs → validation-chain.plugin-C3Vwp-Rk.mjs} +1 -1
  36. package/dist/{validation-chain.plugin-CFJbr964.d.mts → validation-chain.plugin-DxD15h-2.d.mts} +3 -1
  37. package/package.json +5 -3
  38. package/dist/types-BtMuhoX5.d.mts +0 -1830
@@ -1,4 +1,4 @@
1
- import { r as isObjectIdInstance } from "./id-resolution-P2OzcyRl.mjs";
1
+ import { r as isObjectIdInstance } from "./id-resolution-BqDrk0S2.mjs";
2
2
  import mongoose from "mongoose";
3
3
  import { applyFieldRules, collectFieldsToOmit } from "@classytic/repo-core/schema";
4
4
  //#region src/utils/mongooseToJsonSchema.ts
@@ -1,16 +1,62 @@
1
- import { B as ObjectId, G as Plugin, Q as RepositoryContext } from "./types-BtMuhoX5.mjs";
2
- import mongoose from "mongoose";
1
+ import { i as ObjectId } from "./core-62oo3N1C.mjs";
2
+ import { l as Plugin, p as RepositoryContext } from "./repository-DCHRWRXw.mjs";
3
+ import mongoose, { Connection } from "mongoose";
3
4
  import { CacheAdapter as CacheAdapter$1, CacheOptions, RepositoryCacheHandle, RepositoryCachePluginOptions, cachePlugin } from "@classytic/repo-core/cache";
4
5
  import { TenantConfig } from "@classytic/repo-core/tenant";
5
6
  //#region src/plugins/audit-trail.plugin.d.ts
7
+ /**
8
+ * Audit write delivery mode.
9
+ *
10
+ * - `'best-effort'`: async fire-and-forget; never blocks or fails the
11
+ * operation. Observability-grade.
12
+ * - `'transactional'`: awaited insert sharing the operation's session; a
13
+ * failed audit write fails the operation.
14
+ *
15
+ * PRECISION: `'transactional'` is **atomic only when the operation actually
16
+ * runs inside a MongoDB transaction** (`withTransaction` / an explicit
17
+ * `session`). Without one, the mode still guarantees "the call does not
18
+ * return success unless the audit entry landed" — but the business write has
19
+ * already committed by the time the audit insert runs, so an audit failure
20
+ * rejects the call while leaving the business write in place. For
21
+ * compliance-grade "both or neither", wrap the operation in a transaction.
22
+ * Call {@link ensureAuditTrailReady} at boot so the collection and indexes
23
+ * are not created lazily inside the first transaction.
24
+ */
25
+ type AuditTrailMode = 'best-effort' | 'transactional';
6
26
  interface AuditTrailOptions {
27
+ /**
28
+ * Delivery guarantee for audit writes (default: 'best-effort').
29
+ * See {@link AuditTrailMode}. `'transactional'` requires the repository's
30
+ * default `hooks: 'async'` mode — plugin registration throws on a
31
+ * `hooks: 'sync'` repository because awaited delivery cannot be honored.
32
+ */
33
+ mode?: AuditTrailMode;
34
+ /**
35
+ * Mongoose connection to register the audit model on. Defaults to the
36
+ * audited model's OWN connection (`repo.Model.db`), so
37
+ * `mongoose.createConnection()` apps get audit entries in the same database
38
+ * as their data automatically. Pass this explicitly only to centralize
39
+ * audit storage on a different connection than the model lives on. Mirrors
40
+ * the `connection` option on the lock and usage stores.
41
+ *
42
+ * Note: `AuditTrailQuery` / `ensureAuditTrailReady` have no repository to
43
+ * infer from and default to the global `mongoose.connection` — pass the
44
+ * same `connection` there for multi-DB apps.
45
+ */
46
+ connection?: Connection;
7
47
  /** Operations to track (default: ['create', 'update', 'delete']) */
8
48
  operations?: AuditOperation[];
9
49
  /** Store field-level before/after diff on updates (default: true) */
10
50
  trackChanges?: boolean;
11
51
  /** Store full document snapshot on create (default: false — can be heavy) */
12
52
  trackDocument?: boolean;
13
- /** Auto-purge after N days via MongoDB TTL index (default: undefined — keep forever) */
53
+ /**
54
+ * Auto-purge after N days via MongoDB TTL index (default: undefined — keep
55
+ * forever). Retention is collection-level configuration: every plugin (and
56
+ * `AuditTrailQuery`) touching the same collection on the same connection
57
+ * must agree on `ttlDays`. A conflicting value throws at registration time
58
+ * instead of silently keeping whichever caller ran first.
59
+ */
14
60
  ttlDays?: number;
15
61
  /** MongoDB collection name (default: 'audit_trails') */
16
62
  collectionName?: string;
@@ -25,19 +71,19 @@ interface AuditTrailOptions {
25
71
  */
26
72
  excludeFields?: string[];
27
73
  /**
28
- * Custom callback fired when an audit write fails. Receives the error
29
- * and the entry that failed to land. Default: `console.warn` only.
74
+ * Callback fired when an audit write fails. Receives the error and the
75
+ * entry that failed to land.
30
76
  *
31
- * **Use this for compliance-grade audit trails (SOC 2 / PCI / HIPAA)
32
- * where missing entries must be surfaced upstream** — e.g. forward to
33
- * a dead-letter queue, page on-call, or short-circuit the request.
34
- * The default `warn` log is appropriate for development and
35
- * eventually-consistent observability use cases but is invisible to
36
- * compliance review.
37
- *
38
- * The callback runs on the same fire-and-forget microtask that wrote
39
- * the entry, so throwing from here will surface as an unhandled
40
- * rejection — handle (or rethrow into your error reporter) inside.
77
+ * - In `'best-effort'` mode this is the ONLY failure signal — the
78
+ * operation has already succeeded. Forward to a dead-letter queue or
79
+ * error reporter; the default is a `console.warn`-level log. Note that
80
+ * a callback cannot make fire-and-forget writes atomic — if you need
81
+ * "no business write without an audit entry", use `mode:
82
+ * 'transactional'` inside a transaction.
83
+ * - In `'transactional'` mode the callback observes the failure before
84
+ * the operation itself rejects (and, inside a transaction, before the
85
+ * transaction aborts). Throwing from the callback is safe — the
86
+ * original write error still propagates.
41
87
  */
42
88
  onWriteError?: (error: Error, entry: Omit<AuditEntry, 'timestamp'>) => void;
43
89
  }
@@ -57,6 +103,38 @@ interface AuditEntry {
57
103
  timestamp: Date;
58
104
  }
59
105
  declare function auditTrailPlugin(options?: AuditTrailOptions): Plugin;
106
+ /** Options for {@link ensureAuditTrailReady}. */
107
+ interface EnsureAuditTrailReadyOptions {
108
+ /** Connection the audit model lives on (default: `mongoose.connection`). */
109
+ connection?: Connection;
110
+ /** MongoDB collection name (default: 'audit_trails'). */
111
+ collectionName?: string;
112
+ /**
113
+ * TTL in days. Omit to reuse whatever the plugin already registered;
114
+ * pass explicitly (before the plugin, e.g. in a migration script) to
115
+ * declare retention up front — conflicts with an existing registration
116
+ * throw.
117
+ */
118
+ ttlDays?: number;
119
+ }
120
+ /**
121
+ * Create the audit collection and build its indexes BEFORE traffic begins.
122
+ *
123
+ * Call once at boot (after the plugin is registered, before serving
124
+ * requests) — especially with `mode: 'transactional'`: creating the
125
+ * collection or building indexes lazily inside the first transaction
126
+ * forces MongoDB catalog locks and transient `TransientTransactionError`
127
+ * retries. Idempotent; safe to call on every boot.
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * const repo = new Repository(Model, [
132
+ * auditTrailPlugin({ mode: 'transactional', ttlDays: 90 }),
133
+ * ]);
134
+ * await ensureAuditTrailReady(); // collection + indexes exist before traffic
135
+ * ```
136
+ */
137
+ declare function ensureAuditTrailReady(options?: EnsureAuditTrailReadyOptions): Promise<void>;
60
138
  interface AuditQueryOptions {
61
139
  model?: string;
62
140
  documentId?: string | ObjectId;
@@ -77,6 +155,24 @@ interface AuditQueryResult {
77
155
  hasNext: boolean;
78
156
  hasPrev: boolean;
79
157
  }
158
+ /** Constructor options for {@link AuditTrailQuery}. */
159
+ interface AuditTrailQueryOptions {
160
+ /** MongoDB collection name (default: 'audit_trails') */
161
+ collectionName?: string;
162
+ /**
163
+ * TTL in days. Omit (recommended) to reuse whatever retention the writing
164
+ * plugin configured — readers don't need to know it. When passed
165
+ * explicitly it is conflict-checked against the existing registration and
166
+ * a mismatch throws.
167
+ */
168
+ ttlDays?: number;
169
+ /**
170
+ * Mongoose connection the audit model lives on. Defaults to the global
171
+ * `mongoose.connection`. Pass the same connection you gave
172
+ * `auditTrailPlugin` for multi-DB apps.
173
+ */
174
+ connection?: Connection;
175
+ }
80
176
  /**
81
177
  * Standalone audit trail query utility.
82
178
  * Use this to query audits across all models — e.g., admin dashboards, audit APIs.
@@ -85,7 +181,10 @@ interface AuditQueryResult {
85
181
  * ```typescript
86
182
  * import { AuditTrailQuery } from '@classytic/mongokit';
87
183
  *
88
- * const auditQuery = new AuditTrailQuery(); // defaults to 'audit_trails' collection
184
+ * const auditQuery = new AuditTrailQuery(); // defaults to 'audit_trails' on mongoose.connection
185
+ *
186
+ * // Named connection (multi-DB apps)
187
+ * const tenantAudits = new AuditTrailQuery({ connection: tenantConnection });
89
188
  *
90
189
  * // All audits for an org
91
190
  * const orgAudits = await auditQuery.query({ orgId: '...' });
@@ -117,7 +216,7 @@ interface AuditQueryResult {
117
216
  */
118
217
  declare class AuditTrailQuery {
119
218
  private model;
120
- constructor(collectionName?: string, ttlDays?: number);
219
+ constructor(options?: AuditTrailQueryOptions);
121
220
  /**
122
221
  * Get the underlying Mongoose model for custom queries
123
222
  */
@@ -483,4 +582,4 @@ declare function adminBypass(options: {
483
582
  adminRoles: readonly string[];
484
583
  }): (context: RepositoryContext, operation: string) => boolean;
485
584
  //#endregion
486
- export { AuditTrailMethods as C, auditTrailPlugin as E, AuditQueryResult as S, AuditTrailQuery as T, RepositoryCachePluginOptions as _, DateSequentialIdOptions as a, AuditOperation as b, SequentialIdOptions as c, getNextSequence as d, prefixedId as f, RepositoryCacheHandle as g, CacheOptions as h, CustomIdOptions as i, customIdPlugin as l, CacheAdapter$1 as m, adminBypass as n, IdGenerator as o, sequentialId as p, multiTenantPlugin as r, PrefixedIdOptions as s, MultiTenantOptions as t, dateSequentialId as u, cachePlugin as v, AuditTrailOptions as w, AuditQueryOptions as x, AuditEntry as y };
585
+ export { ensureAuditTrailReady as A, AuditTrailMethods as C, AuditTrailQueryOptions as D, AuditTrailQuery as E, EnsureAuditTrailReadyOptions as O, AuditQueryResult as S, AuditTrailOptions as T, RepositoryCachePluginOptions as _, DateSequentialIdOptions as a, AuditOperation as b, SequentialIdOptions as c, getNextSequence as d, prefixedId as f, RepositoryCacheHandle as g, CacheOptions as h, CustomIdOptions as i, auditTrailPlugin as k, customIdPlugin as l, CacheAdapter$1 as m, adminBypass as n, IdGenerator as o, sequentialId as p, multiTenantPlugin as r, PrefixedIdOptions as s, MultiTenantOptions as t, dateSequentialId as u, cachePlugin as v, AuditTrailMode as w, AuditQueryOptions as x, AuditEntry as y };
@@ -0,0 +1,463 @@
1
+ import { a as PopulateSpec, i as ObjectId, l as SortSpec, o as ReadPreferenceType, s as SelectSpec } from "./core-62oo3N1C.mjs";
2
+ import { r as CollationOptions } from "./pagination-jSUdrACK.mjs";
3
+ import { DeleteManyResult, DeleteResult, LookupPopulateResult, UpdateManyResult } from "@classytic/repo-core/repository";
4
+ import { PipelineStage, PopulateOptions } from "mongoose";
5
+ import { Filter } from "@classytic/repo-core/filter";
6
+ //#region src/query/LookupBuilder.d.ts
7
+ interface LookupOptions {
8
+ /** Collection to join with */
9
+ from: string;
10
+ /** Field from the input documents */
11
+ localField: string;
12
+ /** Field from the documents of the "from" collection */
13
+ foreignField: string;
14
+ /** Name of the new array field to add to the input documents */
15
+ as?: string;
16
+ /** Whether to unwrap array to single object */
17
+ single?: boolean;
18
+ /**
19
+ * Field selection on the joined collection (shorthand for `$project`).
20
+ * Three accepted shapes — all map to a single `$project` stage:
21
+ * - `string` — CSV form (`'name,price'`); leading `-` marks an exclusion.
22
+ * - `readonly string[]` — array form (`['name', 'price']` / `['-status']`);
23
+ * mirrors `repo-core/lookup` `LookupSpec.select` for cross-kit parity.
24
+ * - `Record<string, 0 | 1>` — explicit Mongo projection map.
25
+ */
26
+ select?: string | readonly string[] | Record<string, 0 | 1>;
27
+ /** Additional pipeline to run on the joined collection */
28
+ pipeline?: PipelineStage[];
29
+ /** Optional let variables for pipeline */
30
+ let?: Record<string, string>;
31
+ /** Query filter to apply before join (legacy, for aggregate.ts compatibility) */
32
+ query?: Record<string, unknown>;
33
+ /** Query options (legacy, for aggregate.ts compatibility) */
34
+ options?: {
35
+ session?: unknown;
36
+ };
37
+ /** Sanitize pipeline stages (default: true). Set false only for trusted server-side pipelines */
38
+ sanitize?: boolean;
39
+ /**
40
+ * Joined-side filter, compiled into a `$match` stage inside the lookup
41
+ * pipeline AFTER the auto-generated join correlation. Mirrors
42
+ * `repo-core/lookup/types.ts#LookupSpec.where` and sqlitekit's
43
+ * predicate-on-the-JOIN behavior, so cross-kit code that narrows the
44
+ * foreign side (`where: eq('status', 'active')`) gets identical rows
45
+ * from both backends.
46
+ *
47
+ * Accepts Filter IR or a plain mongo query — `compileFilterToMongo`
48
+ * routes each correctly.
49
+ */
50
+ where?: Filter | Record<string, unknown>;
51
+ }
52
+ /**
53
+ * Fluent builder for MongoDB $lookup aggregation stage
54
+ * Optimized for custom field joins at scale
55
+ */
56
+ declare class LookupBuilder {
57
+ private options;
58
+ constructor(from?: string);
59
+ /**
60
+ * Set the collection to join with
61
+ */
62
+ from(collection: string): this;
63
+ /**
64
+ * Set the local field (source collection)
65
+ * IMPORTANT: This field should be indexed for optimal performance
66
+ */
67
+ localField(field: string): this;
68
+ /**
69
+ * Set the foreign field (target collection)
70
+ * IMPORTANT: This field should be indexed (preferably unique) for optimal performance
71
+ */
72
+ foreignField(field: string): this;
73
+ /**
74
+ * Set the output field name
75
+ * Defaults to the collection name if not specified
76
+ */
77
+ as(fieldName: string): this;
78
+ /**
79
+ * Mark this lookup as returning a single document
80
+ * Automatically unwraps the array result to a single object or null
81
+ */
82
+ single(isSingle?: boolean): this;
83
+ /**
84
+ * Add a pipeline to filter/transform joined documents
85
+ * Useful for filtering, sorting, or limiting joined results
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * lookup.pipeline([
90
+ * { $match: { status: 'active' } },
91
+ * { $sort: { priority: -1 } },
92
+ * { $limit: 5 }
93
+ * ]);
94
+ * ```
95
+ */
96
+ pipeline(stages: PipelineStage[]): this;
97
+ /**
98
+ * Set let variables for use in pipeline
99
+ * Allows referencing local document fields in the pipeline
100
+ */
101
+ let(variables: Record<string, string>): this;
102
+ /**
103
+ * Control pipeline sanitization (default: true)
104
+ * Set to false for auto-generated pipelines that are known safe
105
+ */
106
+ sanitize(enabled: boolean): this;
107
+ /**
108
+ * Build the $lookup aggregation stage(s)
109
+ * Returns an array of pipeline stages including $lookup and optional $unwind
110
+ *
111
+ * IMPORTANT: MongoDB $lookup has two mutually exclusive forms:
112
+ * 1. Simple form: { from, localField, foreignField, as }
113
+ * 2. Pipeline form: { from, let, pipeline, as }
114
+ *
115
+ * When pipeline or let is specified, we use the pipeline form.
116
+ * Otherwise, we use the simpler localField/foreignField form.
117
+ */
118
+ build(): PipelineStage[];
119
+ /**
120
+ * Build and return only the $lookup stage (without $unwind)
121
+ * Useful when you want to handle unwrapping yourself
122
+ */
123
+ buildLookupOnly(): PipelineStage.Lookup;
124
+ /**
125
+ * Static helper: Create a simple lookup in one line
126
+ */
127
+ static simple(from: string, localField: string, foreignField: string, options?: {
128
+ as?: string;
129
+ single?: boolean;
130
+ }): PipelineStage[];
131
+ /**
132
+ * Static helper: Create multiple lookups at once
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const pipeline = LookupBuilder.multiple([
137
+ * { from: 'departments', localField: 'deptSlug', foreignField: 'slug', single: true },
138
+ * { from: 'managers', localField: 'managerId', foreignField: '_id', single: true }
139
+ * ]);
140
+ * ```
141
+ */
142
+ static multiple(lookups: LookupOptions[]): PipelineStage[];
143
+ /**
144
+ * Static helper: Create a nested lookup (lookup within lookup)
145
+ * Useful for multi-level joins like Order -> Product -> Category
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * // Join orders with products, then products with categories
150
+ * const pipeline = LookupBuilder.nested([
151
+ * { from: 'products', localField: 'productSku', foreignField: 'sku', as: 'product', single: true },
152
+ * { from: 'categories', localField: 'product.categorySlug', foreignField: 'slug', as: 'product.category', single: true }
153
+ * ]);
154
+ * ```
155
+ */
156
+ static nested(lookups: LookupOptions[]): PipelineStage[];
157
+ /**
158
+ * Sanitize pipeline stages by blocking dangerous stages and operators.
159
+ * Used internally by build() and available for external use (e.g., aggregate.ts).
160
+ */
161
+ static sanitizePipeline(stages: PipelineStage[]): PipelineStage[];
162
+ /**
163
+ * Recursively remove dangerous operators from an expression object.
164
+ */
165
+ private static _sanitizeDeep;
166
+ }
167
+ /**
168
+ * Performance Guidelines for $lookup at Scale:
169
+ *
170
+ * 1. **Index Requirements** (Critical for millions of records):
171
+ * - localField should be indexed on source collection
172
+ * - foreignField should be indexed on target collection (unique index preferred)
173
+ *
174
+ * Example:
175
+ * ```typescript
176
+ * // Employee collection
177
+ * employeeSchema.index({ departmentSlug: 1 });
178
+ *
179
+ * // Department collection
180
+ * departmentSchema.index({ slug: 1 }, { unique: true });
181
+ * ```
182
+ *
183
+ * 2. **Query Performance**:
184
+ * - With proper indexes: O(log n) per lookup
185
+ * - Without indexes: O(n * m) - AVOID THIS!
186
+ * - Use explain() to verify index usage: IXSCAN (good) vs COLLSCAN (bad)
187
+ *
188
+ * 3. **Pipeline Optimization**:
189
+ * - Place $match stages as early as possible
190
+ * - Use $project to reduce field size before lookups
191
+ * - Limit joined results with pipeline: [{ $match: {...} }, { $limit: n }]
192
+ *
193
+ * 4. **Memory Considerations**:
194
+ * - Each lookup creates a new field in memory
195
+ * - Use $project after lookup to remove unnecessary fields
196
+ * - Consider allowDiskUse: true for very large datasets
197
+ *
198
+ * 5. **Alternative Approaches**:
199
+ * - For 1:1 relationships with high read frequency: Consider storing ObjectId + slug
200
+ * - For read-heavy workloads: Consider caching or materialized views
201
+ * - For real-time dashboards: Consider separate aggregation collections
202
+ */
203
+ //#endregion
204
+ //#region src/types/operations.d.ts
205
+ /** Session-only options — shared base for lightweight operations */
206
+ interface SessionOptions {
207
+ /** MongoDB session for transactions */
208
+ session?: unknown;
209
+ /** Organization/tenant ID for multi-tenant plugin scoping */
210
+ organizationId?: string | ObjectId;
211
+ /**
212
+ * Per-call escape hatch — plugins listed here that opt into the
213
+ * mechanism (`auditLog`, `auditTrail`, `observability`) read this
214
+ * from `RepositoryContext` and skip their work for this call. Use
215
+ * on hot paths (e.g. heartbeats / counters) where the audit overhead
216
+ * dominates.
217
+ *
218
+ * Plugins that enforce correctness or security (multi-tenant scope,
219
+ * cache invalidation, soft-delete filter) deliberately ignore this
220
+ * flag — security/correctness must not be skippable per-call.
221
+ *
222
+ * **Match exactly by `plugin.name`** (camelCase, NOT kebab-case):
223
+ * - `'auditLog'` — see `auditLogPlugin()` in
224
+ * `src/plugins/audit-log.plugin.ts` (`name: 'auditLog'`)
225
+ * - `'auditTrail'` — see `auditTrailPlugin()` in
226
+ * `src/plugins/audit-trail.plugin.ts` (`name: 'auditTrail'`)
227
+ * - `'observability'` — see `observabilityPlugin()` in
228
+ * `src/plugins/observability.plugin.ts` (`name: 'observability'`)
229
+ */
230
+ skipPlugins?: readonly string[];
231
+ /**
232
+ * Per-call escape hatch for `multiTenantPlugin`. When `true`, the
233
+ * tenant scope hook returns without injecting `organizationId` (or
234
+ * the configured `tenantField`) into the operation's policy slot
235
+ * for THIS call only. The plugin emits `after:tenant-bypass` with
236
+ * `{ context, operation, reason: 'option' }` so audit / observability
237
+ * plugins can distinguish bypassed queries from tenant-scoped ones.
238
+ *
239
+ * Reach for this when:
240
+ * - A super-admin / platform-admin user needs cross-tenant read
241
+ * for support tooling.
242
+ * - A migration script writes / backfills across tenants
243
+ * deliberately.
244
+ * - A scheduled job (TTL cleanup, cross-tenant aggregate, billing
245
+ * rollup) needs the unscoped view.
246
+ *
247
+ * **`bypassTenant` is the most-specific decision** — runs before
248
+ * `skipWhen` (plugin-level callback) and `resolveContext` (CLS
249
+ * fallback). Distinct from `skipWhen` which fires for every call
250
+ * and reads context to decide; `bypassTenant: true` is a deliberate
251
+ * per-call opt-out with no role check, so the calling code carries
252
+ * the responsibility.
253
+ *
254
+ * **NOT a substitute for proper access control.** This flag bypasses
255
+ * tenant scoping; it does NOT bypass authentication, RBAC, or any
256
+ * other host-level access check. Wrap it with your auth boundary at
257
+ * the controller / service layer.
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * // Support engineer pulling a customer's order across tenants:
262
+ * await orderRepo.findAll({}, { bypassTenant: true });
263
+ *
264
+ * // Migration backfill across the whole estate:
265
+ * for await (const doc of repo.cursor({}, { bypassTenant: true })) { ... }
266
+ * ```
267
+ */
268
+ bypassTenant?: boolean;
269
+ /**
270
+ * Per-call escape hatch for `appendOnlyPlugin` — deliberate
271
+ * maintenance on an immutable-facts collection (backfill repair,
272
+ * migration). Audited via `after:append-only-bypass`; grep for this
273
+ * flag the same way you grep `bypassTenant` / `mode: 'hard'`. NOT a
274
+ * substitute for access control — gate it at the host layer.
275
+ */
276
+ bypassAppendOnly?: boolean;
277
+ /** Extensible — plugins can read custom fields from options */
278
+ [key: string]: unknown;
279
+ }
280
+ /** Read options — session + readPreference for read-only operations */
281
+ interface ReadOptions extends SessionOptions {
282
+ /** Read preference for replica sets (e.g. 'secondaryPreferred') */
283
+ readPreference?: ReadPreferenceType;
284
+ }
285
+ /** Repository operation options */
286
+ interface OperationOptions extends ReadOptions {
287
+ /** Fields to select */
288
+ select?: SelectSpec;
289
+ /** Fields to populate */
290
+ populate?: PopulateSpec;
291
+ /** Advanced populate options (from QueryParser or Arc's BaseController) */
292
+ populateOptions?: PopulateOptions[];
293
+ /** Return plain JavaScript objects */
294
+ lean?: boolean;
295
+ /** Throw error if document not found (default: true) */
296
+ throwOnNotFound?: boolean;
297
+ /** Override the ID field for this call (default: repo.idField or '_id').
298
+ * Use when the same repo sometimes queries by _id and sometimes by slug/code. */
299
+ idField?: string;
300
+ /** Additional query filters (e.g., for soft delete) */
301
+ query?: Record<string, unknown>;
302
+ }
303
+ /**
304
+ * Cache-aware operation options — extends OperationOptions with the
305
+ * unified cache plugin's per-call shape. Pass `cache: { staleTime, swr,
306
+ * tags, bypass, ... }` to override freshness for this call.
307
+ */
308
+ interface CacheableOptions extends OperationOptions {
309
+ /** Per-call cache override forwarded to the unified cache plugin. */
310
+ cache?: import('@classytic/repo-core/cache').CacheOptions;
311
+ }
312
+ /**
313
+ * Per-call cache options — TanStack-shaped (`staleTime`, `gcTime`, `swr`,
314
+ * `tags`, `bypass`, `enabled`, `key`). Threaded through read methods via
315
+ * `options.cache` (CRUD) or `aggRequest.cache` (aggregate).
316
+ *
317
+ * Mongokit 3.13+ delegates cache wiring to `@classytic/repo-core/cache` —
318
+ * the unified plugin owns the canonical `CacheAdapter` / `CacheOptions` /
319
+ * `RepositoryCacheHandle` types; kits import them directly.
320
+ */
321
+ interface CacheOperationOptions {
322
+ /** Per-call cache override forwarded to the unified cache plugin. */
323
+ cache?: import('@classytic/repo-core/cache').CacheOptions;
324
+ }
325
+ /** withTransaction options */
326
+ interface WithTransactionOptions {
327
+ /** Allow non-transactional fallback when transactions are unsupported */
328
+ allowFallback?: boolean;
329
+ /** Optional hook to observe fallback triggers */
330
+ onFallback?: (error: Error) => void;
331
+ /** MongoDB transaction options (readConcern, writeConcern, readPreference, maxCommitTimeMS) */
332
+ transactionOptions?: import('mongoose').mongo.TransactionOptions;
333
+ }
334
+ /** Create operation options */
335
+ interface CreateOptions extends SessionOptions {
336
+ /** Keep insertion order on error (default: false).
337
+ * When false, all valid documents insert even if some fail (e.g. duplicates).
338
+ * Set to true to abort remaining inserts on first error. */
339
+ ordered?: boolean;
340
+ }
341
+ /** Update operation options */
342
+ interface UpdateOptions extends OperationOptions {
343
+ /** Enable update pipeline syntax */
344
+ updatePipeline?: boolean;
345
+ /** Array filters for positional operator $[<identifier>] updates */
346
+ arrayFilters?: Record<string, unknown>[];
347
+ }
348
+ /**
349
+ * Options for atomic findOneAndUpdate (compare-and-set primitive).
350
+ *
351
+ * Designed for outbox/lock/semaphore patterns that need a single round-trip
352
+ * match-and-mutate. Goes through the full hook pipeline (multi-tenant scope,
353
+ * soft-delete, audit) the same way `update()` does.
354
+ */
355
+ interface FindOneAndUpdateOptions extends OperationOptions {
356
+ /** Sort to disambiguate when filter matches multiple docs (e.g. FIFO claim). */
357
+ sort?: SortSpec;
358
+ /** Return doc state before or after the update. Default: 'after'. */
359
+ returnDocument?: 'before' | 'after';
360
+ /** Insert if no doc matches. Default: false. */
361
+ upsert?: boolean;
362
+ /** Array filters for positional `$[<identifier>]` updates. */
363
+ arrayFilters?: Record<string, unknown>[];
364
+ /** Allow aggregation-pipeline updates (array form). Default: false. */
365
+ updatePipeline?: boolean;
366
+ /** Run mongoose schema validators on the update. Default: true. */
367
+ runValidators?: boolean;
368
+ /** Collation for locale-aware string comparison. */
369
+ collation?: CollationOptions;
370
+ /** Maximum execution time in milliseconds. */
371
+ maxTimeMS?: number;
372
+ }
373
+ /** Aggregate operation options */
374
+ interface AggregateOptions extends ReadOptions {
375
+ /** Allow aggregation to use disk for large sorts/groups */
376
+ allowDiskUse?: boolean;
377
+ /** Comment for profiler/logs */
378
+ comment?: string;
379
+ /** Maximum execution time in milliseconds */
380
+ maxTimeMS?: number;
381
+ /** Read concern level */
382
+ readConcern?: {
383
+ level: string;
384
+ };
385
+ /** Collation for locale-aware string comparison */
386
+ collation?: CollationOptions;
387
+ /** Maximum allowed pipeline stages (governance) */
388
+ maxPipelineStages?: number;
389
+ }
390
+ /**
391
+ * Mongokit's `lookupPopulate` options — kit-native superset of the
392
+ * portable `LookupPopulateOptions<TBase>` from repo-core. Widens three
393
+ * slots so arc / mongoose consumers compose cleanly:
394
+ *
395
+ * - `filters`: accepts Filter IR, a `Partial<TBase> & Record<string,
396
+ * unknown>` literal (same shape repo-core's contract uses), or a
397
+ * plain `Record<string, unknown>` for legacy callers.
398
+ * - `lookups`: accepts either the portable `LookupSpec[]` (works on
399
+ * every kit) or mongokit's `LookupOptions[]` (adds `pipeline` /
400
+ * `let` / `sanitize` for mongo-correlated joins).
401
+ * - `select`: accepts mongoose's space-separated string form
402
+ * (`'name email'`) in addition to the portable array / inclusion
403
+ * map. Kept for arc + existing controller convenience.
404
+ *
405
+ * `TBase` defaults to `unknown` so legacy non-generic usage still
406
+ * compiles; pass the doc type for arc `RepositoryLike<TDoc>` structural
407
+ * assignment — `LookupPopulateOptions<TDoc>` is bit-compatible with
408
+ * `import('@classytic/repo-core/repository').LookupPopulateOptions<TDoc>`.
409
+ */
410
+ interface LookupPopulateOptions<TBase = unknown> {
411
+ /**
412
+ * Base-table filter. Accepts Filter IR from repo-core, a typed
413
+ * `Partial<TBase> & Record<string, unknown>` literal (what arc
414
+ * controllers produce when typed against `StandardRepo<TDoc>`), or
415
+ * a plain `Record<string, unknown>` for ad-hoc callers.
416
+ */
417
+ filters?: import('@classytic/repo-core/filter').Filter | (Partial<TBase> & Record<string, unknown>) | Record<string, unknown>;
418
+ /**
419
+ * Portable `LookupSpec[]` (cross-kit) or mongokit-native
420
+ * `LookupOptions[]` (with `pipeline` / `let` / `sanitize`). The
421
+ * portable shape is a structural subset, so callers can mix.
422
+ */
423
+ lookups: readonly import('@classytic/repo-core/repository').LookupSpec[] | LookupOptions[];
424
+ /** Sort specification */
425
+ sort?: SortSpec | string;
426
+ /** Page number (offset mode, 1-indexed) */
427
+ page?: number;
428
+ /** Cursor token for next page (keyset mode) */
429
+ after?: string;
430
+ /** Number of documents per page */
431
+ limit?: number;
432
+ /** Fields to select */
433
+ select?: SelectSpec;
434
+ /** Collation for locale-aware string comparison */
435
+ collation?: CollationOptions;
436
+ /** Count strategy for offset pagination */
437
+ countStrategy?: 'exact' | 'estimated' | 'none';
438
+ /**
439
+ * Transaction session. Typed `unknown` to match repo-core's
440
+ * `RepositorySession` contract — mongokit narrows to `ClientSession`
441
+ * internally. Not inherited from `ReadOptions` because the repo-core
442
+ * contract type has no `[key: string]: unknown` escape hatch, and
443
+ * arc's `Partial<StandardRepo<TDoc>>` boundary rejects index-signature
444
+ * drift under strict function-type variance.
445
+ */
446
+ session?: unknown;
447
+ /** Read preference for replica sets (e.g. 'secondaryPreferred') */
448
+ readPreference?: ReadPreferenceType;
449
+ /** Multi-tenant plugin scoping (applied via before:lookupPopulate hook). */
450
+ organizationId?: string | ObjectId;
451
+ }
452
+ /** Group result */
453
+ interface GroupResult {
454
+ _id: unknown;
455
+ count: number;
456
+ }
457
+ /** Min/Max result */
458
+ interface MinMaxResult {
459
+ min: unknown;
460
+ max: unknown;
461
+ }
462
+ //#endregion
463
+ export { WithTransactionOptions as _, DeleteManyResult as a, GroupResult as c, MinMaxResult as d, OperationOptions as f, UpdateOptions as g, UpdateManyResult as h, CreateOptions as i, LookupPopulateOptions as l, SessionOptions as m, CacheOperationOptions as n, DeleteResult as o, ReadOptions as p, CacheableOptions as r, FindOneAndUpdateOptions as s, AggregateOptions as t, LookupPopulateResult as u, LookupBuilder as v, LookupOptions as y };
@@ -1,2 +1,2 @@
1
- import { t as PaginationEngine } from "../PaginationEngine-BnZ24ehC.mjs";
1
+ import { t as PaginationEngine } from "../PaginationEngine-CBS1XGzp.mjs";
2
2
  export { PaginationEngine };