@classytic/mongokit 3.13.2 → 3.13.4

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.
@@ -1,4 +1,4 @@
1
- import { a as count, b as LookupBuilder, c as getByQuery, d as deleteById, f as deleteByQuery, h as createMany, l as getOrCreate, m as create, n as update, o as exists, r as updateByQuery, s as getById, t as findOneAndUpdate, x as compileFilterToMongo, y as distinct } from "./update-C-3viiw1.mjs";
1
+ import { a as count, b as LookupBuilder, c as getByQuery, d as deleteById, f as deleteByQuery, h as createMany, l as getOrCreate, m as create, n as update, o as exists, r as updateByQuery, s as getById, t as findOneAndUpdate, x as compileFilterToMongo, y as distinct } from "./update-BXMnCvO6.mjs";
2
2
  import { n as warn } from "./logger-DprijP5h.mjs";
3
3
  import { n as isDuplicateKeyError, r as parseDuplicateKeyError, t as createError } from "./error-B00pI7dV.mjs";
4
4
  import { t as PaginationEngine } from "./PaginationEngine-CfQRM1yz.mjs";
@@ -2018,6 +2018,8 @@ var Repository = class extends RepositoryBase {
2018
2018
  if (context.lean ?? options.lean ?? true) query.lean();
2019
2019
  if (options.session) query.session(options.session);
2020
2020
  if (options.readPreference) query.read(options.readPreference);
2021
+ const limitSpec = context.limit ?? options.limit;
2022
+ if (typeof limitSpec === "number" && limitSpec > 0) query.limit(limitSpec);
2021
2023
  return await query.exec();
2022
2024
  });
2023
2025
  }
@@ -2126,10 +2128,14 @@ var Repository = class extends RepositoryBase {
2126
2128
  });
2127
2129
  return cachedResult;
2128
2130
  });
2129
- if (params.noPagination) return this.findAll(context.filters ?? params.filters ?? {}, {
2130
- ...options,
2131
- sort: context.sort ?? params.sort
2132
- });
2131
+ if (params.noPagination) {
2132
+ const forwardedLimit = context.limit ?? params.limit ?? params.pagination?.limit;
2133
+ return this.findAll(context.filters ?? params.filters ?? {}, {
2134
+ ...options,
2135
+ sort: context.sort ?? params.sort,
2136
+ ...typeof forwardedLimit === "number" ? { limit: forwardedLimit } : {}
2137
+ });
2138
+ }
2133
2139
  const filters = context.filters ?? params.filters ?? {};
2134
2140
  const search = context.search;
2135
2141
  const isNearQuery = hasNearOperator(filters);
@@ -3594,9 +3600,25 @@ var Repository = class extends RepositoryBase {
3594
3600
  return isDuplicateKeyError(err);
3595
3601
  }
3596
3602
  /**
3597
- * Handle errors with proper HTTP status codes
3603
+ * Handle errors with proper HTTP status codes.
3604
+ *
3605
+ * **Transactional retry signal preservation.** MongoDB driver errors
3606
+ * carry `errorLabels` (e.g. `'TransientTransactionError'`,
3607
+ * `'UnknownTransactionCommitResult'`) that `mongoose.Connection.
3608
+ * withTransaction()` reads via `error.hasErrorLabel(label)` to decide
3609
+ * whether to auto-retry the transaction. Wrapping such an error in a
3610
+ * fresh `Error` via `createError(...)` strips both the labels and the
3611
+ * `hasErrorLabel` method — the retry signal vanishes and concurrent
3612
+ * writers see WriteConflicts surface to userland instead of being
3613
+ * retried by the driver. To keep the standard MongoDB transaction-
3614
+ * retry mechanism intact, we re-throw any error that carries
3615
+ * `errorLabels` exactly as we received it. The caller still sees a
3616
+ * thrown error — only the wrapping layer is skipped.
3598
3617
  */
3599
3618
  _handleError(error) {
3619
+ const labels = error.errorLabels;
3620
+ const hasLabel = error.hasErrorLabel;
3621
+ if (Array.isArray(labels) && (labels.includes("TransientTransactionError") || labels.includes("UnknownTransactionCommitResult")) || typeof hasLabel === "function" && (hasLabel.call(error, "TransientTransactionError") || hasLabel.call(error, "UnknownTransactionCommitResult"))) return error;
3600
3622
  if (error instanceof mongoose.Error.ValidationError) return createError(400, `Validation Error: ${Object.values(error.errors).map((err) => err.message).join(", ")}`);
3601
3623
  if (error instanceof mongoose.Error.CastError) return createError(400, `Invalid ${error.path}: ${error.value}`);
3602
3624
  const duplicateErr = parseDuplicateKeyError(error);
@@ -1,2 +1,2 @@
1
- import { a as create_d_exports, i as delete_d_exports, n as update_d_exports, o as aggregate_d_exports, r as read_d_exports } from "../index-BbHaKwi5.mjs";
1
+ import { a as create_d_exports, i as delete_d_exports, n as update_d_exports, o as aggregate_d_exports, r as read_d_exports } from "../index-Dccha9dw.mjs";
2
2
  export { aggregate_d_exports as aggregate, create_d_exports as create, delete_d_exports as deleteActions, read_d_exports as read, update_d_exports as update };
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "../chunk-CfYAbeIz.mjs";
2
- import { g as create_exports, i as update_exports, p as delete_exports, u as read_exports, v as aggregate_exports } from "../update-C-3viiw1.mjs";
2
+ import { g as create_exports, i as update_exports, p as delete_exports, u as read_exports, v as aggregate_exports } from "../update-BXMnCvO6.mjs";
3
3
  //#region src/actions/index.ts
4
4
  var actions_exports = /* @__PURE__ */ __exportAll({
5
5
  aggregate: () => aggregate_exports,
@@ -1,4 +1,4 @@
1
- import { n as Repository } from "../Repository-DwJFh3XA.mjs";
1
+ import { n as Repository } from "../Repository-BZrU_nXs.mjs";
2
2
  import { n as createMongooseAdapter } from "../adapter-DTDMnUOH.mjs";
3
3
  import { pluralizeBetterAuthCollection, resolveBetterAuthCollections } from "@classytic/repo-core/better-auth";
4
4
  //#region src/better-auth/index.ts
@@ -175,7 +175,9 @@ declare function getById<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | Ob
175
175
  * @returns Document or null
176
176
  * @throws Error if document not found and throwOnNotFound is true
177
177
  */
178
- declare function getByQuery<TDoc = AnyDocument>(Model: Model<TDoc>, query: Record<string, unknown>, options?: OperationOptions): Promise<TDoc | null>;
178
+ declare function getByQuery<TDoc = AnyDocument>(Model: Model<TDoc>, query: Record<string, unknown>, options?: OperationOptions & {
179
+ sort?: SortSpec;
180
+ }): Promise<TDoc | null>;
179
181
  /**
180
182
  * Get document by query without throwing (returns null if not found)
181
183
  */
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { $ as RepositoryEvent, A as KeysetPaginationOptions, B as ObjectId, C as FieldPreset, Ct as IControllerResponse, D as InferDocument, Dt as LookupOptions, E as HookMode, Et as LookupBuilder, F as MiddlewareContext, G as Plugin, H as OperationOptions, I as MinMaxResult, J as PopulateSpec, K as PluginFunction, L as MinimalRepoView, M as LookupPopulateOptions, N as LookupPopulateResult, O as InferRawDoc, P as Middleware, Q as RepositoryContext, R as MongoOperatorUpdate, S as EventPhase, St as IController, T as GroupResult, Tt as IResponseFormatter, U as PaginationConfig, V as OffsetPaginationOptions, W as PartialBy, X as ReadOptions, Y as PrioritizedHook, Z as ReadPreferenceType, _ as DeleteManyResult, _t as ValidationChainOptions, a as AnyModel, at as SessionOptions, b as EventHandlers, bt as WithPlugins, c as CacheableOptions, ct as SoftDeleteRepository, d as CollationOptions, dt as Strict, et as RepositoryInstance, f as CreateInput, ft as UpdateManyResult, g as DeepPartial, gt as UserContext, h as DecodedCursor, ht as UpdateWithValidationResult, i as AnyDocument, it as SelectSpec, j as Logger, k as KeysOfType, l as CascadeOptions, lt as SortDirection, m as CursorPayload, mt as UpdatePatch, n as AggregatePaginationOptions, nt as RepositoryOptions, o as BasePaginationOptions, ot as SoftDeleteFilterMode, p as CreateOptions, pt as UpdateOptions, q as PluginType, r as AllPluginMethods, rt as RequiredBy, s as CacheOperationOptions, st as SoftDeleteOptions, t as AggregateOptions, tt as RepositoryOperation, u as CascadeRelation, ut as SortSpec, v as DeleteResult, vt as ValidatorDefinition, w as FindOneAndUpdateOptions, wt as IRequestContext, x as EventPayload, xt as WithTransactionOptions, y as DocField, yt as ValueType, z as NonNullableFields } from "./types-BbaPZleN.mjs";
2
- import { t as index_d_exports } from "./index-BbHaKwi5.mjs";
2
+ import { t as index_d_exports } from "./index-Dccha9dw.mjs";
3
3
  import { PaginationEngine } from "./pagination/PaginationEngine.mjs";
4
4
  import { $ as AuditQueryResult, A as elasticSearchPlugin, B as sequentialId, D as leasePlugin, E as LeasePluginOptions, F as SequentialIdOptions, G as RepositoryCachePluginOptions, H as CacheAdapter, I as customIdPlugin, J as BulkWriteResult, K as cachePlugin, L as dateSequentialId, M as DateSequentialIdOptions, N as IdGenerator, O as fieldFilterPlugin, P as PrefixedIdOptions, Q as AuditQueryOptions, R as getNextSequence, S as mongoOperationsPlugin, T as LeaseMethods, U as CacheOptions, V as cascadePlugin, W as RepositoryCacheHandle, X as AuditEntry, Y as batchOperationsPlugin, Z as AuditOperation, _ as observabilityPlugin, a as uniqueField, at as AggregateHelpersMethods, b as multiTenantPlugin, c as TenantContext, d as SubdocumentMethods, et as AuditTrailMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, it as auditLogPlugin, j as CustomIdOptions, k as ElasticSearchOptions, l as TenantStore, m as softDeletePlugin, n as blockIf, nt as AuditTrailQuery, o as validationChainPlugin, ot as aggregateHelpersPlugin, p as SoftDeleteMethods, q as BatchOperationsMethods, r as immutableField, rt as auditTrailPlugin, s as timestampPlugin, t as autoInject, tt as AuditTrailOptions, u as createTenantContext, v as MultiTenantOptions, w as methodRegistryPlugin, x as MongoOperationsMethods, y as adminBypass, z as prefixedId } from "./validation-chain.plugin-CAPYCj_T.mjs";
5
5
  import { n as SchemaPathsLike, t as FieldType$1 } from "./coercion-TRMmR49h.mjs";
@@ -1095,7 +1095,9 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
1095
1095
  /**
1096
1096
  * Get single document by query
1097
1097
  */
1098
- getByQuery(query: Record<string, unknown>, options?: CacheableOptions): Promise<TDoc | null>;
1098
+ getByQuery(query: Record<string, unknown>, options?: CacheableOptions & {
1099
+ sort?: SortSpec;
1100
+ }): Promise<TDoc | null>;
1099
1101
  /**
1100
1102
  * Get single document by arbitrary filter.
1101
1103
  * Unlike getByQuery, this method is designed for Arc/controller use where
@@ -1104,7 +1106,9 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
1104
1106
  * @example
1105
1107
  * const product = await repo.getOne({ slug: 'laptop', organizationId: 'org_1' });
1106
1108
  */
1107
- getOne(query: Record<string, unknown>, options?: CacheableOptions): Promise<TDoc | null>;
1109
+ getOne(query: Record<string, unknown>, options?: CacheableOptions & {
1110
+ sort?: SortSpec;
1111
+ }): Promise<TDoc | null>;
1108
1112
  /**
1109
1113
  * Fetch ALL documents matching filters without pagination.
1110
1114
  * Use for background jobs, exports, batch processing where you need every doc.
@@ -1115,6 +1119,7 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
1115
1119
  */
1116
1120
  findAll(filters?: Record<string, unknown>, options?: OperationOptions & {
1117
1121
  sort?: SortSpec | string;
1122
+ limit?: number;
1118
1123
  }): Promise<TDoc[]>;
1119
1124
  /**
1120
1125
  * Streaming reads — async iterator over data, suitable for migrations,
@@ -1894,7 +1899,20 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
1894
1899
  */
1895
1900
  isDuplicateKeyError(err: unknown): boolean;
1896
1901
  /**
1897
- * Handle errors with proper HTTP status codes
1902
+ * Handle errors with proper HTTP status codes.
1903
+ *
1904
+ * **Transactional retry signal preservation.** MongoDB driver errors
1905
+ * carry `errorLabels` (e.g. `'TransientTransactionError'`,
1906
+ * `'UnknownTransactionCommitResult'`) that `mongoose.Connection.
1907
+ * withTransaction()` reads via `error.hasErrorLabel(label)` to decide
1908
+ * whether to auto-retry the transaction. Wrapping such an error in a
1909
+ * fresh `Error` via `createError(...)` strips both the labels and the
1910
+ * `hasErrorLabel` method — the retry signal vanishes and concurrent
1911
+ * writers see WriteConflicts surface to userland instead of being
1912
+ * retried by the driver. To keep the standard MongoDB transaction-
1913
+ * retry mechanism intact, we re-throw any error that carries
1914
+ * `errorLabels` exactly as we received it. The caller still sees a
1915
+ * thrown error — only the wrapping layer is skipped.
1898
1916
  */
1899
1917
  _handleError(error: Error): HttpError;
1900
1918
  }
@@ -2123,6 +2141,80 @@ declare function repoOptionsFromCtx<TCtx extends Record<string, unknown>>(ctx: T
2123
2141
  * options bag never erases inherited values).
2124
2142
  */
2125
2143
  declare function createOptionsExtractor<TCtx extends Record<string, unknown>>(fields: readonly (keyof TCtx & string)[]): (ctx: TCtx | undefined | null) => Record<string, unknown>;
2144
+ /**
2145
+ * Canonical options bag for repo calls made OUTSIDE any request scope —
2146
+ * event handlers, cron jobs, queue workers, migration scripts.
2147
+ *
2148
+ * `multiTenantPlugin` requires `organizationId` in the operation
2149
+ * context so reads/writes can't leak across tenants from the request
2150
+ * layer. Background work has no inherited request scope; calling a
2151
+ * tenant-aware repo from an event subscriber without a bypass throws:
2152
+ *
2153
+ * "[mongokit] Multi-tenant: Missing 'organizationId' in context for ..."
2154
+ *
2155
+ * Worse, if the call site is wrapped in `try/catch` (as event handlers
2156
+ * routinely are — they fire-and-forget), the error is swallowed and
2157
+ * the side effect silently doesn't happen. We've shipped that bug.
2158
+ *
2159
+ * `systemContext()` returns the canonical opts bag for that case —
2160
+ * currently `{ bypassTenant: true }`. The multi-tenant plugin still
2161
+ * emits an `after:tenant-bypass` audit event with `reason: 'option'`
2162
+ * so observability sees these calls distinct from request-scoped ones.
2163
+ *
2164
+ * **Greppable on purpose.** A reviewer scanning the codebase can find
2165
+ * every "I'm running outside any tenant scope" callsite by searching
2166
+ * `systemContext(` — no chasing magic option spreads.
2167
+ *
2168
+ * **NOT a substitute for proper access control.** Same caveat as raw
2169
+ * `bypassTenant`: this bypasses tenant scoping; it does NOT bypass
2170
+ * authentication or RBAC. Use only at trusted boundaries — event
2171
+ * handlers wired at startup, cron jobs scheduled in code, scripts
2172
+ * running with admin credentials.
2173
+ *
2174
+ * The `extra` overload composes cleanly with transaction sessions and
2175
+ * audit-attribution fields; the function's `bypassTenant: true` is
2176
+ * applied last so it cannot be accidentally overridden.
2177
+ *
2178
+ * @example Event handler
2179
+ * ```ts
2180
+ * import { systemContext } from '@classytic/mongokit';
2181
+ *
2182
+ * order.events.subscribe('order:completed', async (event) => {
2183
+ * const matches = await enrollmentRepo.aggregatePipeline(
2184
+ * [{ $match: { orderId: event.payload.orderId, status: 'active' } }],
2185
+ * systemContext(),
2186
+ * );
2187
+ * for (const m of matches) {
2188
+ * await enrollmentRepo.update(m._id, { status: 'fulfilled' }, systemContext());
2189
+ * }
2190
+ * });
2191
+ * ```
2192
+ *
2193
+ * @example Composed with a transaction
2194
+ * ```ts
2195
+ * await withTransaction(conn, async (session) => {
2196
+ * await enrollmentRepo.update(id, data, systemContext({ session }));
2197
+ * await auditRepo.create(entry, systemContext({ session }));
2198
+ * });
2199
+ * ```
2200
+ *
2201
+ * @example Cron / scheduled cleanup
2202
+ * ```ts
2203
+ * for await (const doc of repo.cursor(
2204
+ * { expiresAt: { $lt: new Date() } },
2205
+ * systemContext(),
2206
+ * )) {
2207
+ * await repo.delete(doc._id, systemContext());
2208
+ * }
2209
+ * ```
2210
+ *
2211
+ * @param extra - Optional extra options to merge in (e.g. `session`
2212
+ * for transactions, `userId` for audit attribution). Any
2213
+ * `bypassTenant` value here is overridden — the function name is
2214
+ * load-bearing.
2215
+ * @returns A `SessionOptions` object with `bypassTenant: true` set.
2216
+ */
2217
+ declare function systemContext(extra?: SessionOptions): SessionOptions;
2126
2218
  //#endregion
2127
2219
  //#region src/index.d.ts
2128
2220
  /**
@@ -2137,4 +2229,4 @@ declare function createOptionsExtractor<TCtx extends Record<string, unknown>>(fi
2137
2229
  */
2138
2230
  declare function createRepository<TDoc>(Model: _$mongoose.Model<TDoc, any, any, any>, plugins?: PluginType[], paginationConfig?: PaginationConfig, options?: RepositoryOptions): Repository<TDoc>;
2139
2231
  //#endregion
2140
- export { ALL_OPERATIONS, type AggregateHelpersMethods, type AggregateOptions, type AggregatePaginationOptions, AggregationBuilder, type AllPluginMethods, type AnyDocument, type AnyModel, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailOptions, AuditTrailQuery, type BasePaginationOptions, type BatchOperationsMethods, type BulkWriteResult, type CacheAdapter, type CacheOperationOptions, type CacheOptions, type CacheableOptions, type CascadeOptions, type CascadeRelation, type CollationOptions, type CreateInput, type CreateOptions, type CursorPayload, type CustomIdOptions, type DateSequentialIdOptions, type DecodedCursor, type DeepPartial, type DeleteResult, type DocField, type ElasticSearchOptions, type EventHandlers, type EventPayload, type EventPhase, type FieldPreset, type FieldType, type FilterQuery, type FindOneAndUpdateOptions, type GroupResult, HOOK_PRIORITY, type HookMode, type IController, type IControllerResponse, type IRequestContext, type IResponseFormatter, type IdGenerator, type InferDocument, type InferRawDoc, type KeysOfType, type KeysetPaginationOptions, type LeaseMethods, type LeasePluginOptions, type Logger, LookupBuilder, type LookupOptions, type LookupPopulateOptions, type LookupPopulateResult, MUTATING_OPERATIONS, type Middleware, type MiddlewareContext, type MinMaxResult, type MinimalRepoView, type MongoOperationsMethods, type MongoOperatorUpdate, type MultiTenantOptions, type NonNullableFields, OP_REGISTRY, type ObjectId, type ObservabilityOptions, type OffsetPaginationOptions, type OperationDescriptor, type OperationMetric, type OperationOptions, type PaginationConfig, PaginationEngine, type ParsedQuery, type PartialBy, type Plugin, type PluginFunction, type PluginType, type PolicyKey, type PopulateOption, type PopulateSpec, type PrefixedIdOptions, type PrioritizedHook, QueryParser, type QueryParserOptions, READ_OPERATIONS, type ReadOptions, type ReadPreferenceType, Repository, Repository as default, type RepositoryCacheHandle, type RepositoryCachePluginOptions, type RepositoryContext, type RepositoryEvent, type RepositoryInstance, type RepositoryOperation, type RepositoryOptions, type RequiredBy, type SchemaLike, type SearchMode, type SelectSpec, type SequentialIdOptions, type SessionOptions, type SoftDeleteFilterMode, type SoftDeleteMethods, type SoftDeleteOptions, type SoftDeleteRepository, type SortDirection, type SortSpec, type Strict, type SubdocumentMethods, type TenantContext, type TenantStore, type UpdateManyResult, type UpdateOptions, type UpdatePatch, type UpdateWithValidationResult, type UserContext, type ValidationChainOptions, type ValidatorDefinition, type ValueType, type WithPlugins, type WithTransactionOptions, index_d_exports as actions, adminBypass, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, compileFilterToMongo, configureLogger, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, immutableField, isDuplicateKeyError, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, repoOptionsFromCtx, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin, withTransaction };
2232
+ export { ALL_OPERATIONS, type AggregateHelpersMethods, type AggregateOptions, type AggregatePaginationOptions, AggregationBuilder, type AllPluginMethods, type AnyDocument, type AnyModel, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailOptions, AuditTrailQuery, type BasePaginationOptions, type BatchOperationsMethods, type BulkWriteResult, type CacheAdapter, type CacheOperationOptions, type CacheOptions, type CacheableOptions, type CascadeOptions, type CascadeRelation, type CollationOptions, type CreateInput, type CreateOptions, type CursorPayload, type CustomIdOptions, type DateSequentialIdOptions, type DecodedCursor, type DeepPartial, type DeleteResult, type DocField, type ElasticSearchOptions, type EventHandlers, type EventPayload, type EventPhase, type FieldPreset, type FieldType, type FilterQuery, type FindOneAndUpdateOptions, type GroupResult, HOOK_PRIORITY, type HookMode, type IController, type IControllerResponse, type IRequestContext, type IResponseFormatter, type IdGenerator, type InferDocument, type InferRawDoc, type KeysOfType, type KeysetPaginationOptions, type LeaseMethods, type LeasePluginOptions, type Logger, LookupBuilder, type LookupOptions, type LookupPopulateOptions, type LookupPopulateResult, MUTATING_OPERATIONS, type Middleware, type MiddlewareContext, type MinMaxResult, type MinimalRepoView, type MongoOperationsMethods, type MongoOperatorUpdate, type MultiTenantOptions, type NonNullableFields, OP_REGISTRY, type ObjectId, type ObservabilityOptions, type OffsetPaginationOptions, type OperationDescriptor, type OperationMetric, type OperationOptions, type PaginationConfig, PaginationEngine, type ParsedQuery, type PartialBy, type Plugin, type PluginFunction, type PluginType, type PolicyKey, type PopulateOption, type PopulateSpec, type PrefixedIdOptions, type PrioritizedHook, QueryParser, type QueryParserOptions, READ_OPERATIONS, type ReadOptions, type ReadPreferenceType, Repository, Repository as default, type RepositoryCacheHandle, type RepositoryCachePluginOptions, type RepositoryContext, type RepositoryEvent, type RepositoryInstance, type RepositoryOperation, type RepositoryOptions, type RequiredBy, type SchemaLike, type SearchMode, type SelectSpec, type SequentialIdOptions, type SessionOptions, type SoftDeleteFilterMode, type SoftDeleteMethods, type SoftDeleteOptions, type SoftDeleteRepository, type SortDirection, type SortSpec, type Strict, type SubdocumentMethods, type TenantContext, type TenantStore, type UpdateManyResult, type UpdateOptions, type UpdatePatch, type UpdateWithValidationResult, type UserContext, type ValidationChainOptions, type ValidatorDefinition, type ValueType, type WithPlugins, type WithTransactionOptions, index_d_exports as actions, adminBypass, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, compileFilterToMongo, configureLogger, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, immutableField, isDuplicateKeyError, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, repoOptionsFromCtx, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, systemContext, timestampPlugin, uniqueField, validationChainPlugin, withTransaction };
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
- import { b as LookupBuilder, x as compileFilterToMongo } from "./update-C-3viiw1.mjs";
1
+ import { b as LookupBuilder, x as compileFilterToMongo } from "./update-BXMnCvO6.mjs";
2
2
  import { n as warn, t as configureLogger } from "./logger-DprijP5h.mjs";
3
3
  import { n as isDuplicateKeyError, r as parseDuplicateKeyError, t as createError } from "./error-B00pI7dV.mjs";
4
4
  import { t as actions_exports } from "./actions/index.mjs";
5
- import { a as withTransaction, c as MUTATING_OPERATIONS, d as operationsByPolicyKey, i as isTransactionUnsupported, l as OP_REGISTRY, n as Repository, o as AggregationBuilder, r as batchTransaction, s as ALL_OPERATIONS, t as HOOK_PRIORITY, u as READ_OPERATIONS } from "./Repository-DwJFh3XA.mjs";
5
+ import { a as withTransaction, c as MUTATING_OPERATIONS, d as operationsByPolicyKey, i as isTransactionUnsupported, l as OP_REGISTRY, n as Repository, o as AggregationBuilder, r as batchTransaction, s as ALL_OPERATIONS, t as HOOK_PRIORITY, u as READ_OPERATIONS } from "./Repository-BZrU_nXs.mjs";
6
6
  import { t as PaginationEngine } from "./PaginationEngine-CfQRM1yz.mjs";
7
- import { A as aggregateHelpersPlugin, C as sequentialId, D as AuditTrailQuery, E as batchOperationsPlugin, O as auditTrailPlugin, S as prefixedId, T as cachePlugin, _ as fieldFilterPlugin, a as uniqueField, b as dateSequentialId, c as createTenantContext, d as observabilityPlugin, f as adminBypass, g as leasePlugin, h as methodRegistryPlugin, i as requireField, k as auditLogPlugin, l as subdocumentPlugin, m as mongoOperationsPlugin, n as blockIf, o as validationChainPlugin, p as multiTenantPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as elasticSearchPlugin, w as cascadePlugin, x as getNextSequence, y as customIdPlugin } from "./validation-chain.plugin-Pj2aD17U.mjs";
7
+ import { A as aggregateHelpersPlugin, C as sequentialId, D as AuditTrailQuery, E as batchOperationsPlugin, O as auditTrailPlugin, S as prefixedId, T as cachePlugin, _ as fieldFilterPlugin, a as uniqueField, b as dateSequentialId, c as createTenantContext, d as observabilityPlugin, f as adminBypass, g as leasePlugin, h as methodRegistryPlugin, i as requireField, k as auditLogPlugin, l as subdocumentPlugin, m as mongoOperationsPlugin, n as blockIf, o as validationChainPlugin, p as multiTenantPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as elasticSearchPlugin, w as cascadePlugin, x as getNextSequence, y as customIdPlugin } from "./validation-chain.plugin-BJwwV9jQ.mjs";
8
8
  import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-Br3WPOEb.mjs";
9
9
  import { isGeoOperator, parseGeoFilter } from "./query/primitives/geo.mjs";
10
10
  import { buildFieldTypeMap, coerceFieldValue, coerceHeuristic } from "./query/primitives/coercion.mjs";
@@ -1207,6 +1207,85 @@ function createOptionsExtractor(fields) {
1207
1207
  return out;
1208
1208
  };
1209
1209
  }
1210
+ /**
1211
+ * Canonical options bag for repo calls made OUTSIDE any request scope —
1212
+ * event handlers, cron jobs, queue workers, migration scripts.
1213
+ *
1214
+ * `multiTenantPlugin` requires `organizationId` in the operation
1215
+ * context so reads/writes can't leak across tenants from the request
1216
+ * layer. Background work has no inherited request scope; calling a
1217
+ * tenant-aware repo from an event subscriber without a bypass throws:
1218
+ *
1219
+ * "[mongokit] Multi-tenant: Missing 'organizationId' in context for ..."
1220
+ *
1221
+ * Worse, if the call site is wrapped in `try/catch` (as event handlers
1222
+ * routinely are — they fire-and-forget), the error is swallowed and
1223
+ * the side effect silently doesn't happen. We've shipped that bug.
1224
+ *
1225
+ * `systemContext()` returns the canonical opts bag for that case —
1226
+ * currently `{ bypassTenant: true }`. The multi-tenant plugin still
1227
+ * emits an `after:tenant-bypass` audit event with `reason: 'option'`
1228
+ * so observability sees these calls distinct from request-scoped ones.
1229
+ *
1230
+ * **Greppable on purpose.** A reviewer scanning the codebase can find
1231
+ * every "I'm running outside any tenant scope" callsite by searching
1232
+ * `systemContext(` — no chasing magic option spreads.
1233
+ *
1234
+ * **NOT a substitute for proper access control.** Same caveat as raw
1235
+ * `bypassTenant`: this bypasses tenant scoping; it does NOT bypass
1236
+ * authentication or RBAC. Use only at trusted boundaries — event
1237
+ * handlers wired at startup, cron jobs scheduled in code, scripts
1238
+ * running with admin credentials.
1239
+ *
1240
+ * The `extra` overload composes cleanly with transaction sessions and
1241
+ * audit-attribution fields; the function's `bypassTenant: true` is
1242
+ * applied last so it cannot be accidentally overridden.
1243
+ *
1244
+ * @example Event handler
1245
+ * ```ts
1246
+ * import { systemContext } from '@classytic/mongokit';
1247
+ *
1248
+ * order.events.subscribe('order:completed', async (event) => {
1249
+ * const matches = await enrollmentRepo.aggregatePipeline(
1250
+ * [{ $match: { orderId: event.payload.orderId, status: 'active' } }],
1251
+ * systemContext(),
1252
+ * );
1253
+ * for (const m of matches) {
1254
+ * await enrollmentRepo.update(m._id, { status: 'fulfilled' }, systemContext());
1255
+ * }
1256
+ * });
1257
+ * ```
1258
+ *
1259
+ * @example Composed with a transaction
1260
+ * ```ts
1261
+ * await withTransaction(conn, async (session) => {
1262
+ * await enrollmentRepo.update(id, data, systemContext({ session }));
1263
+ * await auditRepo.create(entry, systemContext({ session }));
1264
+ * });
1265
+ * ```
1266
+ *
1267
+ * @example Cron / scheduled cleanup
1268
+ * ```ts
1269
+ * for await (const doc of repo.cursor(
1270
+ * { expiresAt: { $lt: new Date() } },
1271
+ * systemContext(),
1272
+ * )) {
1273
+ * await repo.delete(doc._id, systemContext());
1274
+ * }
1275
+ * ```
1276
+ *
1277
+ * @param extra - Optional extra options to merge in (e.g. `session`
1278
+ * for transactions, `userId` for audit attribution). Any
1279
+ * `bypassTenant` value here is overridden — the function name is
1280
+ * load-bearing.
1281
+ * @returns A `SessionOptions` object with `bypassTenant: true` set.
1282
+ */
1283
+ function systemContext(extra) {
1284
+ return {
1285
+ ...extra,
1286
+ bypassTenant: true
1287
+ };
1288
+ }
1210
1289
  //#endregion
1211
1290
  //#region src/index.ts
1212
1291
  /**
@@ -1224,4 +1303,4 @@ function createRepository(Model, plugins = [], paginationConfig = {}, options =
1224
1303
  }
1225
1304
  var src_default = Repository;
1226
1305
  //#endregion
1227
- export { ALL_OPERATIONS, AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, LookupBuilder, MUTATING_OPERATIONS, OP_REGISTRY, PaginationEngine, QueryParser, READ_OPERATIONS, Repository, actions_exports as actions, adminBypass, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, compileFilterToMongo, configureLogger, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, src_default as default, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, immutableField, isDuplicateKeyError, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, repoOptionsFromCtx, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin, withTransaction };
1306
+ export { ALL_OPERATIONS, AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, LookupBuilder, MUTATING_OPERATIONS, OP_REGISTRY, PaginationEngine, QueryParser, READ_OPERATIONS, Repository, actions_exports as actions, adminBypass, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, compileFilterToMongo, configureLogger, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, src_default as default, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, immutableField, isDuplicateKeyError, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, repoOptionsFromCtx, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, systemContext, timestampPlugin, uniqueField, validationChainPlugin, withTransaction };
@@ -1,2 +1,2 @@
1
- import { A as aggregateHelpersPlugin, C as sequentialId, D as AuditTrailQuery, E as batchOperationsPlugin, O as auditTrailPlugin, S as prefixedId, T as cachePlugin, _ as fieldFilterPlugin, a as uniqueField, b as dateSequentialId, c as createTenantContext, d as observabilityPlugin, g as leasePlugin, h as methodRegistryPlugin, i as requireField, k as auditLogPlugin, l as subdocumentPlugin, m as mongoOperationsPlugin, n as blockIf, o as validationChainPlugin, p as multiTenantPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as elasticSearchPlugin, w as cascadePlugin, x as getNextSequence, y as customIdPlugin } from "../validation-chain.plugin-Pj2aD17U.mjs";
1
+ import { A as aggregateHelpersPlugin, C as sequentialId, D as AuditTrailQuery, E as batchOperationsPlugin, O as auditTrailPlugin, S as prefixedId, T as cachePlugin, _ as fieldFilterPlugin, a as uniqueField, b as dateSequentialId, c as createTenantContext, d as observabilityPlugin, g as leasePlugin, h as methodRegistryPlugin, i as requireField, k as auditLogPlugin, l as subdocumentPlugin, m as mongoOperationsPlugin, n as blockIf, o as validationChainPlugin, p as multiTenantPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as elasticSearchPlugin, w as cascadePlugin, x as getNextSequence, y as customIdPlugin } from "../validation-chain.plugin-BJwwV9jQ.mjs";
2
2
  export { AuditTrailQuery, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, getNextSequence, immutableField, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
@@ -864,6 +864,7 @@ async function getByQuery(Model, query, options = {}) {
864
864
  const mongoQuery = Model.findOne(query);
865
865
  if (options.select) mongoQuery.select(options.select);
866
866
  if (options.populate) mongoQuery.populate(parsePopulate$1(options.populate));
867
+ if (options.sort) mongoQuery.sort(options.sort);
867
868
  if (options.lean) mongoQuery.lean();
868
869
  if (options.session) mongoQuery.session(options.session);
869
870
  if (options.readPreference) mongoQuery.read(options.readPreference);
@@ -1,7 +1,7 @@
1
- import { _ as upsert } from "./update-C-3viiw1.mjs";
1
+ import { _ as upsert } from "./update-BXMnCvO6.mjs";
2
2
  import { n as warn } from "./logger-DprijP5h.mjs";
3
3
  import { t as createError } from "./error-B00pI7dV.mjs";
4
- import { l as OP_REGISTRY, s as ALL_OPERATIONS, t as HOOK_PRIORITY } from "./Repository-DwJFh3XA.mjs";
4
+ import { l as OP_REGISTRY, s as ALL_OPERATIONS, t as HOOK_PRIORITY } from "./Repository-BZrU_nXs.mjs";
5
5
  import { r as getFieldsForUser } from "./field-selection-Br3WPOEb.mjs";
6
6
  import mongoose from "mongoose";
7
7
  import { cachePlugin } from "@classytic/repo-core/cache";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/mongokit",
3
- "version": "3.13.2",
3
+ "version": "3.13.4",
4
4
  "description": "Production-grade MongoDB repositories - smart pagination, events, and plugins. Built on @classytic/repo-core.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -105,7 +105,7 @@
105
105
  },
106
106
  "homepage": "https://github.com/classytic/mongokit#readme",
107
107
  "peerDependencies": {
108
- "@classytic/repo-core": ">=0.4.0",
108
+ "@classytic/repo-core": "^0.4.2",
109
109
  "better-auth": ">=1.6.0",
110
110
  "mongoose": ">=9.4.1"
111
111
  },
@@ -149,7 +149,7 @@
149
149
  "@better-auth/mongo-adapter": "^1.6.9",
150
150
  "@biomejs/biome": "^2.4.10",
151
151
  "@classytic/dev-tools": "^0.2.0",
152
- "@classytic/repo-core": "^0.4.0",
152
+ "@classytic/repo-core": "0.4.2",
153
153
  "@types/node": "^22.0.0",
154
154
  "@vitest/coverage-v8": "^3.2.4",
155
155
  "ajv": "^8.18.0",