@classytic/mongokit 3.10.2 → 3.11.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/dist/actions/index.d.mts +1 -1
- package/dist/ai/index.d.mts +1 -1
- package/dist/{index-CNNavGpM.d.mts → index-C1MzVFsT.d.mts} +1 -1
- package/dist/index.d.mts +60 -6
- package/dist/index.mjs +1 -1
- package/dist/{mongooseToJsonSchema-Bi8hNbj8.d.mts → mongooseToJsonSchema-BH6OFec5.d.mts} +1 -1
- package/dist/pagination/PaginationEngine.d.mts +1 -1
- package/dist/plugins/index.d.mts +1 -1
- package/dist/plugins/index.mjs +1 -1
- package/dist/{types-CYFJa36g.d.mts → types-xOyTec4n.d.mts} +21 -17
- package/dist/utils/index.d.mts +2 -2
- package/dist/{validation-chain.plugin-BNG3coI0.d.mts → validation-chain.plugin-BGdr5KRw.d.mts} +27 -38
- package/dist/{validation-chain.plugin-Bt8HepWW.mjs → validation-chain.plugin-CLjEFr0c.mjs} +204 -304
- package/package.json +2 -2
package/dist/actions/index.d.mts
CHANGED
|
@@ -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-
|
|
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-C1MzVFsT.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 };
|
package/dist/ai/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $ as ReadPreferenceType, B as ObjectId, D as GroupResult, E as FindOneAndUpdateOptions, R as MinMaxResult, U as OperationOptions, X as PopulateSpec, a as AnyDocument, b as DeleteResult, ft as SortSpec, g as CreateOptions, gt as UpdateOptions, ht as UpdateManyResult, kt as LookupOptions, ot as SelectSpec, vt as UpdateWithValidationResult } from "./types-xOyTec4n.mjs";
|
|
2
2
|
import { Model, PipelineStage } from "mongoose";
|
|
3
3
|
|
|
4
4
|
//#region src/actions/aggregate.d.ts
|
package/dist/index.d.mts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import { t as index_d_exports } from "./index-
|
|
1
|
+
import { $ as ReadPreferenceType, A as InferDocument, B as ObjectId, C as EventPayload, Ct as WithTransactionOptions, D as GroupResult, Dt as IResponseFormatter, E as FindOneAndUpdateOptions, Et as IRequestContext, F as Logger, G as PaginationResult, H as OffsetPaginationResult, I as LookupPopulateOptions, J as PluginFunction, K as PartialBy, L as LookupPopulateResult, M as KeysOfType, N as KeysetPaginationOptions, O as HookMode, Ot as LookupBuilder, P as KeysetPaginationResult, Q as ReadOptions, R as MinMaxResult, S as EventHandlers, St as WithPlugins, T as FieldPreset, Tt as IControllerResponse, U as OperationOptions, V as OffsetPaginationOptions, W as PaginationConfig, X as PopulateSpec, Y as PluginType, Z as PrioritizedHook, _ as DecodedCursor, _t as UpdatePatch, a as AnyDocument, at as RequiredBy, b as DeleteResult, bt as ValidationChainOptions, c as CacheOperationOptions, ct as SoftDeleteFilterMode, d as CacheableOptions, dt as SortDirection, et as RepositoryContext, f as CascadeOptions, ft as SortSpec, g as CreateOptions, gt as UpdateOptions, h as CreateInput, ht as UpdateManyResult, i as AllPluginMethods, it as RepositoryOptions, j as InferRawDoc, k as HttpError, kt as LookupOptions, l as CacheOptions, lt as SoftDeleteOptions, m as CollationOptions, mt as UpdateInput, n as AggregatePaginationOptions, nt as RepositoryInstance, o as AnyModel, ot as SelectSpec, p as CascadeRelation, pt as Strict, q as Plugin, r as AggregatePaginationResult, rt as RepositoryOperation, s as CacheAdapter, st as SessionOptions, t as AggregateOptions, tt as RepositoryEvent, u as CacheStats, ut as SoftDeleteRepository, v as DeepPartial, vt as UpdateWithValidationResult, w as EventPhase, wt as IController, x as DocField, xt as ValidatorDefinition, y as DeleteManyResult, yt as UserContext, z as NonNullableFields } from "./types-xOyTec4n.mjs";
|
|
2
|
+
import { t as index_d_exports } from "./index-C1MzVFsT.mjs";
|
|
3
3
|
import { PaginationEngine } from "./pagination/PaginationEngine.mjs";
|
|
4
|
-
import { $ as aggregateHelpersPlugin, A as PrefixedIdOptions, B as BatchOperationsMethods, C as methodRegistryPlugin, D as CustomIdOptions, E as elasticSearchPlugin, F as prefixedId, G as AuditQueryOptions, H as batchOperationsPlugin, I as sequentialId, J as AuditTrailOptions, K as AuditQueryResult, L as cascadePlugin, M as customIdPlugin, N as dateSequentialId, O as DateSequentialIdOptions, P as getNextSequence, Q as AggregateHelpersMethods, R as CacheMethods, T as ElasticSearchOptions, U as AuditEntry, V as BulkWriteResult, W as AuditOperation, X as auditTrailPlugin, Y as AuditTrailQuery, Z as auditLogPlugin, _ as observabilityPlugin, a as uniqueField, b as MongoOperationsMethods, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as SequentialIdOptions, k as IdGenerator, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, q as AuditTrailMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MultiTenantOptions, w as fieldFilterPlugin, x as mongoOperationsPlugin, y as multiTenantPlugin, z as cachePlugin } from "./validation-chain.plugin-
|
|
4
|
+
import { $ as aggregateHelpersPlugin, A as PrefixedIdOptions, B as BatchOperationsMethods, C as methodRegistryPlugin, D as CustomIdOptions, E as elasticSearchPlugin, F as prefixedId, G as AuditQueryOptions, H as batchOperationsPlugin, I as sequentialId, J as AuditTrailOptions, K as AuditQueryResult, L as cascadePlugin, M as customIdPlugin, N as dateSequentialId, O as DateSequentialIdOptions, P as getNextSequence, Q as AggregateHelpersMethods, R as CacheMethods, T as ElasticSearchOptions, U as AuditEntry, V as BulkWriteResult, W as AuditOperation, X as auditTrailPlugin, Y as AuditTrailQuery, Z as auditLogPlugin, _ as observabilityPlugin, a as uniqueField, b as MongoOperationsMethods, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as SequentialIdOptions, k as IdGenerator, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, q as AuditTrailMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MultiTenantOptions, w as fieldFilterPlugin, x as mongoOperationsPlugin, y as multiTenantPlugin, z as cachePlugin } from "./validation-chain.plugin-BGdr5KRw.mjs";
|
|
5
5
|
import { n as SchemaPathsLike, t as FieldType$1 } from "./coercion-oM8G4b6m.mjs";
|
|
6
6
|
import { n as SchemaIndexes } from "./indexes-C26GNZRU.mjs";
|
|
7
|
-
import { a as createFieldPreset, c as getMongooseProjection, d as parseDuplicateKeyError, i as configureLogger, l as createError, n as buildCrudSchemasFromMongooseSchema, o as filterResponseData, r as createMemoryCache, s as getFieldsForUser, t as buildCrudSchemasFromModel, u as isDuplicateKeyError } from "./mongooseToJsonSchema-
|
|
7
|
+
import { a as createFieldPreset, c as getMongooseProjection, d as parseDuplicateKeyError, i as configureLogger, l as createError, n as buildCrudSchemasFromMongooseSchema, o as filterResponseData, r as createMemoryCache, s as getFieldsForUser, t as buildCrudSchemasFromModel, u as isDuplicateKeyError } from "./mongooseToJsonSchema-BH6OFec5.mjs";
|
|
8
8
|
import * as _$mongoose from "mongoose";
|
|
9
9
|
import { ClientSession, Expression, Model, PipelineStage, PopulateOptions } from "mongoose";
|
|
10
10
|
import { AggPaginationRequest, AggRequest, AggResult, RepositoryBase } from "@classytic/repo-core/repository";
|
|
11
|
+
import { UpdateInput as UpdateInput$1 } from "@classytic/repo-core/update";
|
|
11
12
|
import { OffsetPaginationResultCore } from "@classytic/repo-core/pagination";
|
|
12
13
|
|
|
13
14
|
//#region src/operations.d.ts
|
|
@@ -929,6 +930,17 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
|
|
|
929
930
|
*/
|
|
930
931
|
private _emitHook;
|
|
931
932
|
private _emitErrorHook;
|
|
933
|
+
/**
|
|
934
|
+
* Run a Repository operation under the standard envelope:
|
|
935
|
+
* - invoke `fn`, emit `after:<op>` with `{ context, result }` on success
|
|
936
|
+
* - emit `error:<op>` and rethrow via `_handleError` on failure
|
|
937
|
+
*
|
|
938
|
+
* Methods with branched in-try logic that emit `after:*` from multiple
|
|
939
|
+
* paths (`getById`, `update`, `delete`, `deleteMany`, `aggregatePaginate`,
|
|
940
|
+
* `lookupPopulate`) intentionally keep their inline try/catch rather than
|
|
941
|
+
* being restructured to return a single result through this helper.
|
|
942
|
+
*/
|
|
943
|
+
private _runOp;
|
|
932
944
|
/**
|
|
933
945
|
* Create single document
|
|
934
946
|
*/
|
|
@@ -1060,7 +1072,7 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
|
|
|
1060
1072
|
* );
|
|
1061
1073
|
* ```
|
|
1062
1074
|
*/
|
|
1063
|
-
findOneAndUpdate<TResult = TDoc>(filter: Record<string, unknown>, update:
|
|
1075
|
+
findOneAndUpdate<TResult = TDoc>(filter: Record<string, unknown>, update: UpdateInput$1, options?: FindOneAndUpdateOptions): Promise<TResult | null>;
|
|
1064
1076
|
/**
|
|
1065
1077
|
* Delete a document by ID.
|
|
1066
1078
|
*
|
|
@@ -1087,6 +1099,48 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
|
|
|
1087
1099
|
mode?: 'hard' | 'soft'; /** Legacy opt-in: throw a 404 error on miss instead of returning `{ success: false }`. */
|
|
1088
1100
|
throwOnNotFound?: boolean;
|
|
1089
1101
|
}): Promise<DeleteResult>;
|
|
1102
|
+
/**
|
|
1103
|
+
* Update every document matching the filter.
|
|
1104
|
+
*
|
|
1105
|
+
* Promoted from `batchOperationsPlugin` to a class primitive in 3.11.0 so
|
|
1106
|
+
* the surface matches sqlitekit's always-available `updateMany`. Consumers
|
|
1107
|
+
* who forget to wire `batchOperationsPlugin` no longer get a runtime
|
|
1108
|
+
* `TypeError: repo.updateMany is not a function` — the method is always
|
|
1109
|
+
* here.
|
|
1110
|
+
*
|
|
1111
|
+
* Accepts three `update` forms via `UpdateInput`:
|
|
1112
|
+
* - Portable `UpdateSpec` from `@classytic/repo-core/update` (compiled to
|
|
1113
|
+
* `$set`/`$unset`/`$inc`/`$setOnInsert` before hooks run).
|
|
1114
|
+
* - Raw Mongo operator record (`{ $set, $inc, ... }`) — passed through.
|
|
1115
|
+
* - Mongo aggregation pipeline (`[...]`) — requires `updatePipeline: true`
|
|
1116
|
+
* to guard against accidental pipeline updates.
|
|
1117
|
+
*
|
|
1118
|
+
* Refuses empty filters (defense-in-depth against mass-update accidents)
|
|
1119
|
+
* both before and after policy hooks inject tenant scope.
|
|
1120
|
+
*/
|
|
1121
|
+
updateMany(filter: Record<string, unknown>, update: UpdateInput$1, options?: SessionOptions & {
|
|
1122
|
+
updatePipeline?: boolean;
|
|
1123
|
+
[key: string]: unknown;
|
|
1124
|
+
}): Promise<UpdateManyResult>;
|
|
1125
|
+
/**
|
|
1126
|
+
* Delete every document matching the filter.
|
|
1127
|
+
*
|
|
1128
|
+
* Promoted from `batchOperationsPlugin` to a class primitive in 3.11.0 for
|
|
1129
|
+
* the same reason as `updateMany` — sqlitekit parity and no silent "method
|
|
1130
|
+
* is undefined" footgun when the batch plugin isn't wired.
|
|
1131
|
+
*
|
|
1132
|
+
* Behavior mirrors `delete()`: defaults to soft delete when
|
|
1133
|
+
* `softDeletePlugin` is wired, physical delete otherwise. Pass
|
|
1134
|
+
* `{ mode: 'hard' }` to bypass soft-delete — multi-tenant scoping, audit,
|
|
1135
|
+
* and cache hooks still fire.
|
|
1136
|
+
*
|
|
1137
|
+
* Rejects empty filters up front AND after policy hooks, so a policy-plugin
|
|
1138
|
+
* bug that zeroes out the query can't be exploited to wipe a collection.
|
|
1139
|
+
*/
|
|
1140
|
+
deleteMany(filter: Record<string, unknown>, options?: SessionOptions & {
|
|
1141
|
+
mode?: 'hard' | 'soft';
|
|
1142
|
+
[key: string]: unknown;
|
|
1143
|
+
}): Promise<DeleteManyResult>;
|
|
1090
1144
|
/**
|
|
1091
1145
|
* Kit-native aggregation pipeline — takes a MongoDB stage array and
|
|
1092
1146
|
* returns the raw pipeline output. Use this for `$lookup`, `$unwind`,
|
|
@@ -1418,4 +1472,4 @@ declare function isTransactionUnsupported(error: Error): boolean;
|
|
|
1418
1472
|
*/
|
|
1419
1473
|
declare function createRepository<TDoc>(Model: _$mongoose.Model<TDoc, any, any, any>, plugins?: PluginType[], paginationConfig?: PaginationConfig, options?: RepositoryOptions): Repository<TDoc>;
|
|
1420
1474
|
//#endregion
|
|
1421
|
-
export { ALL_OPERATIONS, type AggregateHelpersMethods, type AggregateOptions, type AggregatePaginationOptions, type AggregatePaginationResult, AggregationBuilder, type AllPluginMethods, type AnyDocument, type AnyModel, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailOptions, AuditTrailQuery, type BatchOperationsMethods, type BulkWriteResult, type CacheAdapter, type CacheMethods, type CacheOperationOptions, type CacheOptions, type CacheStats, type CacheableOptions, type CascadeOptions, type CascadeRelation, type CollationOptions, type CreateInput, type CreateOptions, 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 HttpError, type IController, type IControllerResponse, type IRequestContext, type IResponseFormatter, type IdGenerator, type InferDocument, type InferRawDoc, type KeysOfType, type KeysetPaginationOptions, type KeysetPaginationResult, type Logger, LookupBuilder, type LookupOptions, type LookupPopulateOptions, type LookupPopulateResult, MUTATING_OPERATIONS, type MinMaxResult, type MongoOperationsMethods, type MultiTenantOptions, type NonNullableFields, OP_REGISTRY, type ObjectId, type ObservabilityOptions, type OffsetPaginationOptions, type OffsetPaginationResult, type OperationDescriptor, type OperationMetric, type OperationOptions, type PaginationConfig, PaginationEngine, type PaginationResult, type ParsedQuery, type PartialBy, type Plugin, type PluginFunction, type PluginType, type PolicyKey, type PopulateOption, type PopulateSpec, type PrefixedIdOptions, QueryParser, type QueryParserOptions, READ_OPERATIONS, type ReadOptions, type ReadPreferenceType, Repository, Repository as default, 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 UpdateInput, type UpdateManyResult, type UpdateOptions, type UpdateWithValidationResult, type UserContext, type ValidationChainOptions, type ValidatorDefinition, type WithPlugins, type WithTransactionOptions, index_d_exports as actions, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, configureLogger, createError, createFieldPreset, createMemoryCache, createRepository, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, immutableField, isDuplicateKeyError, isTransactionUnsupported, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin, withTransaction };
|
|
1475
|
+
export { ALL_OPERATIONS, type AggregateHelpersMethods, type AggregateOptions, type AggregatePaginationOptions, type AggregatePaginationResult, AggregationBuilder, type AllPluginMethods, type AnyDocument, type AnyModel, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailOptions, AuditTrailQuery, type BatchOperationsMethods, type BulkWriteResult, type CacheAdapter, type CacheMethods, type CacheOperationOptions, type CacheOptions, type CacheStats, type CacheableOptions, type CascadeOptions, type CascadeRelation, type CollationOptions, type CreateInput, type CreateOptions, 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 HttpError, type IController, type IControllerResponse, type IRequestContext, type IResponseFormatter, type IdGenerator, type InferDocument, type InferRawDoc, type KeysOfType, type KeysetPaginationOptions, type KeysetPaginationResult, type Logger, LookupBuilder, type LookupOptions, type LookupPopulateOptions, type LookupPopulateResult, MUTATING_OPERATIONS, type MinMaxResult, type MongoOperationsMethods, type MultiTenantOptions, type NonNullableFields, OP_REGISTRY, type ObjectId, type ObservabilityOptions, type OffsetPaginationOptions, type OffsetPaginationResult, type OperationDescriptor, type OperationMetric, type OperationOptions, type PaginationConfig, PaginationEngine, type PaginationResult, type ParsedQuery, type PartialBy, type Plugin, type PluginFunction, type PluginType, type PolicyKey, type PopulateOption, type PopulateSpec, type PrefixedIdOptions, QueryParser, type QueryParserOptions, READ_OPERATIONS, type ReadOptions, type ReadPreferenceType, Repository, Repository as default, 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 UpdateInput, type UpdateManyResult, type UpdateOptions, type UpdatePatch, type UpdateWithValidationResult, type UserContext, type ValidationChainOptions, type ValidatorDefinition, type WithPlugins, type WithTransactionOptions, index_d_exports as actions, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, configureLogger, createError, createFieldPreset, createMemoryCache, createRepository, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, immutableField, isDuplicateKeyError, isTransactionUnsupported, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin, withTransaction };
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { r as warn, t as configureLogger } from "./logger-DJ7egMnd.mjs";
|
|
|
2
2
|
import { b as LookupBuilder } from "./update-CWXfFC6O.mjs";
|
|
3
3
|
import { n as isDuplicateKeyError, r as parseDuplicateKeyError, t as createError } from "./error-CzYwrfUP.mjs";
|
|
4
4
|
import { t as actions_exports } from "./actions/index.mjs";
|
|
5
|
-
import { A as AuditTrailQuery, C as cachePlugin, D as withTransaction, E as isTransactionUnsupported, F as MUTATING_OPERATIONS, I as OP_REGISTRY, L as READ_OPERATIONS, M as auditLogPlugin, N as aggregateHelpersPlugin, O as AggregationBuilder, P as ALL_OPERATIONS, R as operationsByPolicyKey, S as cascadePlugin, T as Repository, _ as customIdPlugin, a as uniqueField, b as prefixedId, c as createTenantContext, d as observabilityPlugin, f as multiTenantPlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, j as auditTrailPlugin, k as batchOperationsPlugin, l as subdocumentPlugin, m as methodRegistryPlugin, n as blockIf, o as validationChainPlugin, p as mongoOperationsPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as dateSequentialId, w as HOOK_PRIORITY, x as sequentialId, y as getNextSequence } from "./validation-chain.plugin-
|
|
5
|
+
import { A as AuditTrailQuery, C as cachePlugin, D as withTransaction, E as isTransactionUnsupported, F as MUTATING_OPERATIONS, I as OP_REGISTRY, L as READ_OPERATIONS, M as auditLogPlugin, N as aggregateHelpersPlugin, O as AggregationBuilder, P as ALL_OPERATIONS, R as operationsByPolicyKey, S as cascadePlugin, T as Repository, _ as customIdPlugin, a as uniqueField, b as prefixedId, c as createTenantContext, d as observabilityPlugin, f as multiTenantPlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, j as auditTrailPlugin, k as batchOperationsPlugin, l as subdocumentPlugin, m as methodRegistryPlugin, n as blockIf, o as validationChainPlugin, p as mongoOperationsPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as dateSequentialId, w as HOOK_PRIORITY, x as sequentialId, y as getNextSequence } from "./validation-chain.plugin-CLjEFr0c.mjs";
|
|
6
6
|
import { t as PaginationEngine } from "./PaginationEngine-B93fLCcO.mjs";
|
|
7
7
|
import { isGeoOperator, parseGeoFilter } from "./query/primitives/geo.mjs";
|
|
8
8
|
import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-dI0m4RBs.mjs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { T as FieldPreset, k as HttpError, s as CacheAdapter, yt as UserContext } from "./types-xOyTec4n.mjs";
|
|
2
2
|
import mongoose, { Schema } from "mongoose";
|
|
3
3
|
import { CrudSchemas, SchemaBuilderOptions } from "@classytic/repo-core/schema";
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { H as OffsetPaginationResult, N as KeysetPaginationOptions, P as KeysetPaginationResult, V as OffsetPaginationOptions, W as PaginationConfig, a as AnyDocument, n as AggregatePaginationOptions, r as AggregatePaginationResult } from "../types-xOyTec4n.mjs";
|
|
2
2
|
import { Model } from "mongoose";
|
|
3
3
|
|
|
4
4
|
//#region src/pagination/PaginationEngine.d.ts
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as aggregateHelpersPlugin, A as PrefixedIdOptions, B as BatchOperationsMethods, C as methodRegistryPlugin, D as CustomIdOptions, E as elasticSearchPlugin, F as prefixedId, G as AuditQueryOptions, H as batchOperationsPlugin, I as sequentialId, J as AuditTrailOptions, K as AuditQueryResult, L as cascadePlugin, M as customIdPlugin, N as dateSequentialId, O as DateSequentialIdOptions, P as getNextSequence, Q as AggregateHelpersMethods, R as CacheMethods, S as MethodRegistryRepository, T as ElasticSearchOptions, U as AuditEntry, W as AuditOperation, X as auditTrailPlugin, Y as AuditTrailQuery, Z as auditLogPlugin, _ as observabilityPlugin, a as uniqueField, b as MongoOperationsMethods, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as SequentialIdOptions, k as IdGenerator, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, q as AuditTrailMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MultiTenantOptions, w as fieldFilterPlugin, x as mongoOperationsPlugin, y as multiTenantPlugin, z as cachePlugin } from "../validation-chain.plugin-
|
|
1
|
+
import { $ as aggregateHelpersPlugin, A as PrefixedIdOptions, B as BatchOperationsMethods, C as methodRegistryPlugin, D as CustomIdOptions, E as elasticSearchPlugin, F as prefixedId, G as AuditQueryOptions, H as batchOperationsPlugin, I as sequentialId, J as AuditTrailOptions, K as AuditQueryResult, L as cascadePlugin, M as customIdPlugin, N as dateSequentialId, O as DateSequentialIdOptions, P as getNextSequence, Q as AggregateHelpersMethods, R as CacheMethods, S as MethodRegistryRepository, T as ElasticSearchOptions, U as AuditEntry, W as AuditOperation, X as auditTrailPlugin, Y as AuditTrailQuery, Z as auditLogPlugin, _ as observabilityPlugin, a as uniqueField, b as MongoOperationsMethods, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as SequentialIdOptions, k as IdGenerator, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, q as AuditTrailMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MultiTenantOptions, w as fieldFilterPlugin, x as mongoOperationsPlugin, y as multiTenantPlugin, z as cachePlugin } from "../validation-chain.plugin-BGdr5KRw.mjs";
|
|
2
2
|
export { type AggregateHelpersMethods, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailOptions, AuditTrailQuery, type BatchOperationsMethods, type CacheMethods, type CustomIdOptions, type DateSequentialIdOptions, type ElasticSearchOptions, type IdGenerator, type MethodRegistryRepository, type MongoOperationsMethods, type MultiTenantOptions, type ObservabilityOptions, type OperationMetric, type PrefixedIdOptions, type SequentialIdOptions, type SoftDeleteMethods, type SubdocumentMethods, type TenantContext, type TenantStore, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, getNextSequence, immutableField, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
|
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as AuditTrailQuery, C as cachePlugin, M as auditLogPlugin, N as aggregateHelpersPlugin, S as cascadePlugin, _ as customIdPlugin, a as uniqueField, b as prefixedId, c as createTenantContext, d as observabilityPlugin, f as multiTenantPlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, j as auditTrailPlugin, k as batchOperationsPlugin, l as subdocumentPlugin, m as methodRegistryPlugin, n as blockIf, o as validationChainPlugin, p as mongoOperationsPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as dateSequentialId, x as sequentialId, y as getNextSequence } from "../validation-chain.plugin-
|
|
1
|
+
import { A as AuditTrailQuery, C as cachePlugin, M as auditLogPlugin, N as aggregateHelpersPlugin, S as cascadePlugin, _ as customIdPlugin, a as uniqueField, b as prefixedId, c as createTenantContext, d as observabilityPlugin, f as multiTenantPlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, j as auditTrailPlugin, k as batchOperationsPlugin, l as subdocumentPlugin, m as methodRegistryPlugin, n as blockIf, o as validationChainPlugin, p as mongoOperationsPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as dateSequentialId, x as sequentialId, y as getNextSequence } from "../validation-chain.plugin-CLjEFr0c.mjs";
|
|
2
2
|
export { AuditTrailQuery, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, getNextSequence, immutableField, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
|
|
@@ -2,7 +2,7 @@ import * as _$mongoose from "mongoose";
|
|
|
2
2
|
import { Document, Model, PipelineStage, PopulateOptions, Types } from "mongoose";
|
|
3
3
|
import * as _$_classytic_repo_core_filter0 from "@classytic/repo-core/filter";
|
|
4
4
|
import * as _$_classytic_repo_core_repository0 from "@classytic/repo-core/repository";
|
|
5
|
-
import { DeleteResult, LookupPopulateResult, UpdateManyResult } from "@classytic/repo-core/repository";
|
|
5
|
+
import { DeleteManyResult, DeleteResult, LookupPopulateResult, UpdateManyResult } from "@classytic/repo-core/repository";
|
|
6
6
|
import { ValidationResult } from "@classytic/repo-core/schema";
|
|
7
7
|
|
|
8
8
|
//#region src/query/LookupBuilder.d.ts
|
|
@@ -554,7 +554,25 @@ type NonNullableFields<T> = { [K in keyof T]: NonNullable<T[K]> };
|
|
|
554
554
|
* Create/Update input types from document
|
|
555
555
|
*/
|
|
556
556
|
type CreateInput<TDoc> = Omit<TDoc, '_id' | 'createdAt' | 'updatedAt' | '__v'>;
|
|
557
|
-
|
|
557
|
+
/**
|
|
558
|
+
* Partial-document update patch — the shape `repo.update(id, data)` accepts
|
|
559
|
+
* for its `data` parameter. A `Partial<TDoc>` minus the fields a caller must
|
|
560
|
+
* never set directly (`_id`, `createdAt`, `__v`).
|
|
561
|
+
*
|
|
562
|
+
* **Renamed from `UpdateInput<TDoc>` in 3.11.0** to eliminate a name
|
|
563
|
+
* collision with `@classytic/repo-core/update`'s `UpdateInput` (the
|
|
564
|
+
* `UpdateSpec | Record | Record[]` union consumed by `updateMany` /
|
|
565
|
+
* `findOneAndUpdate`). The legacy name remains as a deprecated alias for
|
|
566
|
+
* one release — remove in 3.12.
|
|
567
|
+
*/
|
|
568
|
+
type UpdatePatch<TDoc> = Partial<Omit<TDoc, '_id' | 'createdAt' | '__v'>>;
|
|
569
|
+
/**
|
|
570
|
+
* @deprecated Renamed to {@link UpdatePatch} in 3.11.0. This alias exists
|
|
571
|
+
* for one release only and will be removed in 3.12. If you actually wanted
|
|
572
|
+
* the bulk/find-and-update union (`UpdateSpec | Record | Record[]`), import
|
|
573
|
+
* `UpdateInput` from `@classytic/repo-core/update` instead.
|
|
574
|
+
*/
|
|
575
|
+
type UpdateInput<TDoc> = UpdatePatch<TDoc>;
|
|
558
576
|
/** Hook execution mode */
|
|
559
577
|
type HookMode = 'sync' | 'async';
|
|
560
578
|
/** Search strategy used by Repository.getAll when `search` is provided */
|
|
@@ -1400,20 +1418,6 @@ type AllPluginMethods<TDoc> = {
|
|
|
1400
1418
|
setMin(id: string | ObjectId, field: DocField<TDoc>, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
|
|
1401
1419
|
setMax(id: string | ObjectId, field: DocField<TDoc>, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
|
|
1402
1420
|
atomicUpdate(id: string | ObjectId, operators: Record<string, Record<string, unknown>>, options?: Record<string, unknown>): Promise<TDoc>;
|
|
1403
|
-
updateMany(query: Record<string, unknown>, data: Record<string, unknown>, options?: {
|
|
1404
|
-
session?: unknown;
|
|
1405
|
-
updatePipeline?: boolean;
|
|
1406
|
-
}): Promise<{
|
|
1407
|
-
acknowledged: boolean;
|
|
1408
|
-
matchedCount: number;
|
|
1409
|
-
modifiedCount: number;
|
|
1410
|
-
upsertedCount: number;
|
|
1411
|
-
upsertedId: unknown;
|
|
1412
|
-
}>;
|
|
1413
|
-
deleteMany(query: Record<string, unknown>, options?: Record<string, unknown>): Promise<{
|
|
1414
|
-
acknowledged: boolean;
|
|
1415
|
-
deletedCount: number;
|
|
1416
|
-
}>;
|
|
1417
1421
|
bulkWrite(operations: Record<string, unknown>[], options?: {
|
|
1418
1422
|
session?: unknown;
|
|
1419
1423
|
ordered?: boolean;
|
|
@@ -1493,4 +1497,4 @@ type AllPluginMethods<TDoc> = {
|
|
|
1493
1497
|
*/
|
|
1494
1498
|
type WithPlugins<TDoc, TRepo extends RepositoryInstance = RepositoryInstance> = TRepo & AllPluginMethods<TDoc>;
|
|
1495
1499
|
//#endregion
|
|
1496
|
-
export {
|
|
1500
|
+
export { ReadPreferenceType as $, InferDocument as A, ObjectId as B, EventPayload as C, WithTransactionOptions as Ct, GroupResult as D, IResponseFormatter as Dt, FindOneAndUpdateOptions as E, IRequestContext as Et, Logger as F, PaginationResult as G, OffsetPaginationResult as H, LookupPopulateOptions as I, PluginFunction as J, PartialBy as K, LookupPopulateResult as L, KeysOfType as M, KeysetPaginationOptions as N, HookMode as O, LookupBuilder as Ot, KeysetPaginationResult as P, ReadOptions as Q, MinMaxResult as R, EventHandlers as S, WithPlugins as St, FieldPreset as T, IControllerResponse as Tt, OperationOptions as U, OffsetPaginationOptions as V, PaginationConfig as W, PopulateSpec as X, PluginType as Y, PrioritizedHook as Z, DecodedCursor as _, UpdatePatch as _t, AnyDocument as a, RequiredBy as at, DeleteResult as b, ValidationChainOptions as bt, CacheOperationOptions as c, SoftDeleteFilterMode as ct, CacheableOptions as d, SortDirection as dt, RepositoryContext as et, CascadeOptions as f, SortSpec as ft, CreateOptions as g, UpdateOptions as gt, CreateInput as h, UpdateManyResult as ht, AllPluginMethods as i, RepositoryOptions as it, InferRawDoc as j, HttpError as k, LookupOptions as kt, CacheOptions as l, SoftDeleteOptions as lt, CollationOptions as m, UpdateInput as mt, AggregatePaginationOptions as n, RepositoryInstance as nt, AnyModel as o, SelectSpec as ot, CascadeRelation as p, Strict as pt, Plugin as q, AggregatePaginationResult as r, RepositoryOperation as rt, CacheAdapter as s, SessionOptions as st, AggregateOptions as t, RepositoryEvent as tt, CacheStats as u, SoftDeleteRepository as ut, DeepPartial as v, UpdateWithValidationResult as vt, EventPhase as w, IController as wt, DocField as x, ValidatorDefinition as xt, DeleteManyResult as y, UserContext as yt, NonNullableFields as z };
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as createFieldPreset, c as getMongooseProjection, i as configureLogger, l as createError, n as buildCrudSchemasFromMongooseSchema, o as filterResponseData, r as createMemoryCache, s as getFieldsForUser, t as buildCrudSchemasFromModel } from "../mongooseToJsonSchema-
|
|
1
|
+
import { X as PopulateSpec, ft as SortSpec, ot as SelectSpec } from "../types-xOyTec4n.mjs";
|
|
2
|
+
import { a as createFieldPreset, c as getMongooseProjection, i as configureLogger, l as createError, n as buildCrudSchemasFromMongooseSchema, o as filterResponseData, r as createMemoryCache, s as getFieldsForUser, t as buildCrudSchemasFromModel } from "../mongooseToJsonSchema-BH6OFec5.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/cache-keys.d.ts
|
|
5
5
|
/**
|
package/dist/{validation-chain.plugin-BNG3coI0.d.mts → validation-chain.plugin-BGdr5KRw.d.mts}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as ObjectId, F as Logger, H as OffsetPaginationResult, T as FieldPreset, X as PopulateSpec, bt as ValidationChainOptions, et as RepositoryContext, f as CascadeOptions, ft as SortSpec, l as CacheOptions, lt as SoftDeleteOptions, nt as RepositoryInstance, ot as SelectSpec, q as Plugin, u as CacheStats, xt as ValidatorDefinition } from "./types-xOyTec4n.mjs";
|
|
2
2
|
import mongoose from "mongoose";
|
|
3
3
|
import { BulkWriteResult } from "@classytic/repo-core/repository";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -245,7 +245,7 @@ interface AuditTrailMethods {
|
|
|
245
245
|
//#endregion
|
|
246
246
|
//#region src/plugins/batch-operations.plugin.d.ts
|
|
247
247
|
/**
|
|
248
|
-
* Batch operations plugin
|
|
248
|
+
* Batch operations plugin.
|
|
249
249
|
*
|
|
250
250
|
* @example
|
|
251
251
|
* const repo = new Repository(Model, [
|
|
@@ -253,47 +253,15 @@ interface AuditTrailMethods {
|
|
|
253
253
|
* batchOperationsPlugin(),
|
|
254
254
|
* ]);
|
|
255
255
|
*
|
|
256
|
+
* // updateMany + deleteMany are primitives on Repository — no plugin needed.
|
|
256
257
|
* await repo.updateMany({ status: 'pending' }, { status: 'active' });
|
|
257
258
|
* await repo.deleteMany({ status: 'deleted' });
|
|
259
|
+
*
|
|
260
|
+
* // bulkWrite is what this plugin contributes.
|
|
261
|
+
* await repo.bulkWrite([...ops]);
|
|
258
262
|
*/
|
|
259
263
|
declare function batchOperationsPlugin(): Plugin;
|
|
260
264
|
interface BatchOperationsMethods {
|
|
261
|
-
/**
|
|
262
|
-
* Update multiple documents matching the query
|
|
263
|
-
* @param query - Query to match documents
|
|
264
|
-
* @param data - Update data
|
|
265
|
-
* @param options - Operation options
|
|
266
|
-
* @returns Update result with matchedCount, modifiedCount, etc.
|
|
267
|
-
*/
|
|
268
|
-
updateMany(query: Record<string, unknown>, data: Record<string, unknown>, options?: {
|
|
269
|
-
session?: unknown;
|
|
270
|
-
updatePipeline?: boolean;
|
|
271
|
-
}): Promise<{
|
|
272
|
-
acknowledged: boolean;
|
|
273
|
-
matchedCount: number;
|
|
274
|
-
modifiedCount: number;
|
|
275
|
-
upsertedCount: number;
|
|
276
|
-
upsertedId: unknown;
|
|
277
|
-
}>;
|
|
278
|
-
/**
|
|
279
|
-
* Delete multiple documents matching the query.
|
|
280
|
-
*
|
|
281
|
-
* Defaults to soft delete when `softDeletePlugin` is wired, physical delete
|
|
282
|
-
* otherwise. Pass `{ mode: 'hard' }` for GDPR / admin cleanup — multi-tenant
|
|
283
|
-
* scoping and audit hooks still fire.
|
|
284
|
-
*
|
|
285
|
-
* @param query - Query to match documents (must be non-empty)
|
|
286
|
-
* @param options - Operation options (session, mode, tenant keys)
|
|
287
|
-
* @returns Delete result with deletedCount
|
|
288
|
-
*/
|
|
289
|
-
deleteMany(query: Record<string, unknown>, options?: {
|
|
290
|
-
session?: unknown;
|
|
291
|
-
mode?: 'hard' | 'soft';
|
|
292
|
-
[key: string]: unknown;
|
|
293
|
-
}): Promise<{
|
|
294
|
-
acknowledged: boolean;
|
|
295
|
-
deletedCount: number;
|
|
296
|
-
}>;
|
|
297
265
|
/**
|
|
298
266
|
* Execute heterogeneous bulk write operations in a single database call.
|
|
299
267
|
* Supports insertOne, updateOne, updateMany, deleteOne, deleteMany, replaceOne.
|
|
@@ -827,6 +795,27 @@ interface MultiTenantOptions {
|
|
|
827
795
|
* `$lookup` and `.populate()` against the referenced collection.
|
|
828
796
|
*/
|
|
829
797
|
fieldType?: 'string' | 'objectId';
|
|
798
|
+
/**
|
|
799
|
+
* When `true` (default), bypass the `required` throw if the operation's
|
|
800
|
+
* policy target already carries the tenant field. This lets hosts that
|
|
801
|
+
* stamp the tenant into the payload themselves (e.g., `data[tenantField]`
|
|
802
|
+
* on writes, `filters[tenantField]` on paginated reads) use the plugin
|
|
803
|
+
* without having to hand-roll a `skipWhen` that duplicates this check.
|
|
804
|
+
*
|
|
805
|
+
* Set to `false` to restore the pre-data-injection strict behavior: the
|
|
806
|
+
* tenant MUST come from `context` or `resolveContext`, otherwise the
|
|
807
|
+
* plugin throws (when `required: true`).
|
|
808
|
+
*
|
|
809
|
+
* Composition: a user-supplied `skipWhen` still runs first and can always
|
|
810
|
+
* opt out. This option controls the behavior after `skipWhen` and
|
|
811
|
+
* `resolveContext` have had their turn.
|
|
812
|
+
*
|
|
813
|
+
* Safety: when the payload supplies the tenant, the plugin skips its own
|
|
814
|
+
* injection (it does not overwrite). Cross-tenant isolation is therefore
|
|
815
|
+
* only as strong as the caller's own stamping — same guarantee as a
|
|
816
|
+
* hand-rolled `skipWhen`.
|
|
817
|
+
*/
|
|
818
|
+
allowDataInjection?: boolean;
|
|
830
819
|
}
|
|
831
820
|
declare function multiTenantPlugin(options?: MultiTenantOptions): Plugin;
|
|
832
821
|
//#endregion
|
|
@@ -9,6 +9,7 @@ import mongoose from "mongoose";
|
|
|
9
9
|
import { isFilter } from "@classytic/repo-core/filter";
|
|
10
10
|
import { HOOK_PRIORITY } from "@classytic/repo-core/hooks";
|
|
11
11
|
import { RepositoryBase, validatePluginOrder } from "@classytic/repo-core/repository";
|
|
12
|
+
import { compileUpdateSpecToMongo, isUpdateSpec } from "@classytic/repo-core/update";
|
|
12
13
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
13
14
|
//#region src/operations.ts
|
|
14
15
|
/**
|
|
@@ -654,7 +655,7 @@ var AuditTrailQuery = class {
|
|
|
654
655
|
//#endregion
|
|
655
656
|
//#region src/plugins/batch-operations.plugin.ts
|
|
656
657
|
/**
|
|
657
|
-
* Batch operations plugin
|
|
658
|
+
* Batch operations plugin.
|
|
658
659
|
*
|
|
659
660
|
* @example
|
|
660
661
|
* const repo = new Repository(Model, [
|
|
@@ -662,8 +663,12 @@ var AuditTrailQuery = class {
|
|
|
662
663
|
* batchOperationsPlugin(),
|
|
663
664
|
* ]);
|
|
664
665
|
*
|
|
666
|
+
* // updateMany + deleteMany are primitives on Repository — no plugin needed.
|
|
665
667
|
* await repo.updateMany({ status: 'pending' }, { status: 'active' });
|
|
666
668
|
* await repo.deleteMany({ status: 'deleted' });
|
|
669
|
+
*
|
|
670
|
+
* // bulkWrite is what this plugin contributes.
|
|
671
|
+
* await repo.bulkWrite([...ops]);
|
|
667
672
|
*/
|
|
668
673
|
function batchOperationsPlugin() {
|
|
669
674
|
return {
|
|
@@ -671,38 +676,6 @@ function batchOperationsPlugin() {
|
|
|
671
676
|
apply(repo) {
|
|
672
677
|
if (!repo.registerMethod) throw new Error("batchOperationsPlugin requires methodRegistryPlugin");
|
|
673
678
|
/**
|
|
674
|
-
* Update multiple documents
|
|
675
|
-
*/
|
|
676
|
-
repo.registerMethod("updateMany", async function(query, data, options = {}) {
|
|
677
|
-
const context = await this._buildContext("updateMany", {
|
|
678
|
-
query,
|
|
679
|
-
data,
|
|
680
|
-
...options
|
|
681
|
-
});
|
|
682
|
-
try {
|
|
683
|
-
const finalQuery = context.query || query;
|
|
684
|
-
if (!finalQuery || Object.keys(finalQuery).length === 0) throw createError(400, "updateMany requires a non-empty query filter. Pass an explicit filter to prevent accidental mass updates.");
|
|
685
|
-
if (Array.isArray(data) && options.updatePipeline !== true) throw createError(400, "Update pipelines (array updates) are disabled by default; pass `{ updatePipeline: true }` to explicitly allow pipeline-style updates.");
|
|
686
|
-
const finalData = context.data || data;
|
|
687
|
-
const result = await this.Model.updateMany(finalQuery, finalData, {
|
|
688
|
-
runValidators: true,
|
|
689
|
-
session: options.session,
|
|
690
|
-
...options.updatePipeline !== void 0 ? { updatePipeline: options.updatePipeline } : {}
|
|
691
|
-
}).exec();
|
|
692
|
-
await this.emitAsync("after:updateMany", {
|
|
693
|
-
context,
|
|
694
|
-
result
|
|
695
|
-
});
|
|
696
|
-
return result;
|
|
697
|
-
} catch (error) {
|
|
698
|
-
this.emit("error:updateMany", {
|
|
699
|
-
context,
|
|
700
|
-
error
|
|
701
|
-
});
|
|
702
|
-
throw this._handleError(error);
|
|
703
|
-
}
|
|
704
|
-
});
|
|
705
|
-
/**
|
|
706
679
|
* Execute heterogeneous bulk write operations in a single database call.
|
|
707
680
|
*
|
|
708
681
|
* Supports insertOne, updateOne, updateMany, deleteOne, deleteMany, and replaceOne
|
|
@@ -751,53 +724,6 @@ function batchOperationsPlugin() {
|
|
|
751
724
|
throw this._handleError(error);
|
|
752
725
|
}
|
|
753
726
|
});
|
|
754
|
-
/**
|
|
755
|
-
* Delete multiple documents matching a query.
|
|
756
|
-
*
|
|
757
|
-
* Behavior mirrors `Repository.delete`: defaults to soft-delete when
|
|
758
|
-
* `softDeletePlugin` is wired, physical delete otherwise. Pass
|
|
759
|
-
* `{ mode: 'hard' }` to bypass soft-delete for GDPR / cleanup paths —
|
|
760
|
-
* multi-tenant scoping and audit hooks still fire.
|
|
761
|
-
*
|
|
762
|
-
* Rejects empty filters even after policy hooks run, to prevent
|
|
763
|
-
* accidental collection wipes.
|
|
764
|
-
*/
|
|
765
|
-
repo.registerMethod("deleteMany", async function(query, options = {}) {
|
|
766
|
-
if (!query || Object.keys(query).length === 0) throw createError(400, "deleteMany requires a non-empty query filter. Pass an explicit filter to prevent accidental mass deletes.");
|
|
767
|
-
const mode = options.mode;
|
|
768
|
-
const context = await this._buildContext("deleteMany", {
|
|
769
|
-
query,
|
|
770
|
-
...options,
|
|
771
|
-
...mode ? { deleteMode: mode } : {}
|
|
772
|
-
});
|
|
773
|
-
try {
|
|
774
|
-
if (context.softDeleted) {
|
|
775
|
-
const result = {
|
|
776
|
-
acknowledged: true,
|
|
777
|
-
deletedCount: 0
|
|
778
|
-
};
|
|
779
|
-
await this.emitAsync("after:deleteMany", {
|
|
780
|
-
context,
|
|
781
|
-
result
|
|
782
|
-
});
|
|
783
|
-
return result;
|
|
784
|
-
}
|
|
785
|
-
const finalQuery = context.query || query;
|
|
786
|
-
if (!finalQuery || Object.keys(finalQuery).length === 0) throw createError(400, "deleteMany requires a non-empty query filter after policy hooks.");
|
|
787
|
-
const result = await this.Model.deleteMany(finalQuery, { session: options.session }).exec();
|
|
788
|
-
await this.emitAsync("after:deleteMany", {
|
|
789
|
-
context,
|
|
790
|
-
result
|
|
791
|
-
});
|
|
792
|
-
return result;
|
|
793
|
-
} catch (error) {
|
|
794
|
-
this.emit("error:deleteMany", {
|
|
795
|
-
context,
|
|
796
|
-
error
|
|
797
|
-
});
|
|
798
|
-
throw this._handleError(error);
|
|
799
|
-
}
|
|
800
|
-
});
|
|
801
727
|
}
|
|
802
728
|
};
|
|
803
729
|
}
|
|
@@ -1817,22 +1743,25 @@ var Repository = class extends RepositoryBase {
|
|
|
1817
1743
|
}
|
|
1818
1744
|
}
|
|
1819
1745
|
/**
|
|
1820
|
-
*
|
|
1746
|
+
* Run a Repository operation under the standard envelope:
|
|
1747
|
+
* - invoke `fn`, emit `after:<op>` with `{ context, result }` on success
|
|
1748
|
+
* - emit `error:<op>` and rethrow via `_handleError` on failure
|
|
1749
|
+
*
|
|
1750
|
+
* Methods with branched in-try logic that emit `after:*` from multiple
|
|
1751
|
+
* paths (`getById`, `update`, `delete`, `deleteMany`, `aggregatePaginate`,
|
|
1752
|
+
* `lookupPopulate`) intentionally keep their inline try/catch rather than
|
|
1753
|
+
* being restructured to return a single result through this helper.
|
|
1821
1754
|
*/
|
|
1822
|
-
async
|
|
1823
|
-
const context = await this._buildContext("create", {
|
|
1824
|
-
data,
|
|
1825
|
-
...options
|
|
1826
|
-
});
|
|
1755
|
+
async _runOp(op, context, fn) {
|
|
1827
1756
|
try {
|
|
1828
|
-
const result = await
|
|
1829
|
-
await this._emitHook(
|
|
1757
|
+
const result = await fn();
|
|
1758
|
+
await this._emitHook(`after:${op}`, {
|
|
1830
1759
|
context,
|
|
1831
1760
|
result
|
|
1832
1761
|
});
|
|
1833
1762
|
return result;
|
|
1834
1763
|
} catch (error) {
|
|
1835
|
-
await this._emitErrorHook(
|
|
1764
|
+
await this._emitErrorHook(`error:${op}`, {
|
|
1836
1765
|
context,
|
|
1837
1766
|
error
|
|
1838
1767
|
});
|
|
@@ -1840,6 +1769,16 @@ var Repository = class extends RepositoryBase {
|
|
|
1840
1769
|
}
|
|
1841
1770
|
}
|
|
1842
1771
|
/**
|
|
1772
|
+
* Create single document
|
|
1773
|
+
*/
|
|
1774
|
+
async create(data, options = {}) {
|
|
1775
|
+
const context = await this._buildContext("create", {
|
|
1776
|
+
data,
|
|
1777
|
+
...options
|
|
1778
|
+
});
|
|
1779
|
+
return this._runOp("create", context, () => create(this.Model, context.data || data, options));
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1843
1782
|
* Create multiple documents
|
|
1844
1783
|
*/
|
|
1845
1784
|
async createMany(dataArray, options = {}) {
|
|
@@ -1847,20 +1786,7 @@ var Repository = class extends RepositoryBase {
|
|
|
1847
1786
|
dataArray,
|
|
1848
1787
|
...options
|
|
1849
1788
|
});
|
|
1850
|
-
|
|
1851
|
-
const result = await createMany(this.Model, context.dataArray || dataArray, options);
|
|
1852
|
-
await this._emitHook("after:createMany", {
|
|
1853
|
-
context,
|
|
1854
|
-
result
|
|
1855
|
-
});
|
|
1856
|
-
return result;
|
|
1857
|
-
} catch (error) {
|
|
1858
|
-
await this._emitErrorHook("error:createMany", {
|
|
1859
|
-
context,
|
|
1860
|
-
error
|
|
1861
|
-
});
|
|
1862
|
-
throw this._handleError(error);
|
|
1863
|
-
}
|
|
1789
|
+
return this._runOp("createMany", context, () => createMany(this.Model, context.dataArray || dataArray, options));
|
|
1864
1790
|
}
|
|
1865
1791
|
/**
|
|
1866
1792
|
* Get document by ID
|
|
@@ -1934,20 +1860,7 @@ var Repository = class extends RepositoryBase {
|
|
|
1934
1860
|
return cachedResult;
|
|
1935
1861
|
}
|
|
1936
1862
|
const finalQuery = context.query || query;
|
|
1937
|
-
|
|
1938
|
-
const result = await getByQuery(this.Model, finalQuery, context);
|
|
1939
|
-
await this._emitHook("after:getByQuery", {
|
|
1940
|
-
context,
|
|
1941
|
-
result
|
|
1942
|
-
});
|
|
1943
|
-
return result;
|
|
1944
|
-
} catch (error) {
|
|
1945
|
-
await this._emitErrorHook("error:getByQuery", {
|
|
1946
|
-
context,
|
|
1947
|
-
error
|
|
1948
|
-
});
|
|
1949
|
-
throw this._handleError(error);
|
|
1950
|
-
}
|
|
1863
|
+
return this._runOp("getByQuery", context, () => getByQuery(this.Model, finalQuery, context));
|
|
1951
1864
|
}
|
|
1952
1865
|
/**
|
|
1953
1866
|
* Get single document by arbitrary filter.
|
|
@@ -1974,20 +1887,7 @@ var Repository = class extends RepositoryBase {
|
|
|
1974
1887
|
return cachedResult;
|
|
1975
1888
|
}
|
|
1976
1889
|
const finalQuery = context.query || query;
|
|
1977
|
-
|
|
1978
|
-
const result = await getByQuery(this.Model, finalQuery, context);
|
|
1979
|
-
await this._emitHook("after:getOne", {
|
|
1980
|
-
context,
|
|
1981
|
-
result
|
|
1982
|
-
});
|
|
1983
|
-
return result;
|
|
1984
|
-
} catch (error) {
|
|
1985
|
-
await this._emitErrorHook("error:getOne", {
|
|
1986
|
-
context,
|
|
1987
|
-
error
|
|
1988
|
-
});
|
|
1989
|
-
throw this._handleError(error);
|
|
1990
|
-
}
|
|
1890
|
+
return this._runOp("getOne", context, () => getByQuery(this.Model, finalQuery, context));
|
|
1991
1891
|
}
|
|
1992
1892
|
/**
|
|
1993
1893
|
* Fetch ALL documents matching filters without pagination.
|
|
@@ -2003,7 +1903,7 @@ var Repository = class extends RepositoryBase {
|
|
|
2003
1903
|
...options
|
|
2004
1904
|
});
|
|
2005
1905
|
const resolvedFilters = context.query ?? filters;
|
|
2006
|
-
|
|
1906
|
+
return this._runOp("findAll", context, async () => {
|
|
2007
1907
|
const query = this.Model.find(resolvedFilters);
|
|
2008
1908
|
const sortSpec = context.sort || options.sort;
|
|
2009
1909
|
if (sortSpec) query.sort(this._parseSort(sortSpec));
|
|
@@ -2013,19 +1913,8 @@ var Repository = class extends RepositoryBase {
|
|
|
2013
1913
|
if (context.lean ?? options.lean ?? true) query.lean();
|
|
2014
1914
|
if (options.session) query.session(options.session);
|
|
2015
1915
|
if (options.readPreference) query.read(options.readPreference);
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
context,
|
|
2019
|
-
result
|
|
2020
|
-
});
|
|
2021
|
-
return result;
|
|
2022
|
-
} catch (error) {
|
|
2023
|
-
await this._emitErrorHook("error:findAll", {
|
|
2024
|
-
context,
|
|
2025
|
-
error
|
|
2026
|
-
});
|
|
2027
|
-
throw this._handleError(error);
|
|
2028
|
-
}
|
|
1916
|
+
return await query.exec();
|
|
1917
|
+
});
|
|
2029
1918
|
}
|
|
2030
1919
|
/**
|
|
2031
1920
|
* Unified pagination - auto-detects offset vs keyset based on params
|
|
@@ -2098,8 +1987,8 @@ var Repository = class extends RepositoryBase {
|
|
|
2098
1987
|
collation: context.collation ?? params.collation
|
|
2099
1988
|
};
|
|
2100
1989
|
const lookups = context.lookups ?? params.lookups;
|
|
2101
|
-
if (lookups && lookups.length > 0)
|
|
2102
|
-
|
|
1990
|
+
if (lookups && lookups.length > 0) return this._runOp("getAll", context, async () => {
|
|
1991
|
+
return await this.lookupPopulate({
|
|
2103
1992
|
filters: query,
|
|
2104
1993
|
lookups,
|
|
2105
1994
|
sort: paginationOptions.sort,
|
|
@@ -2112,19 +2001,8 @@ var Repository = class extends RepositoryBase {
|
|
|
2112
2001
|
collation: paginationOptions.collation,
|
|
2113
2002
|
countStrategy: context.countStrategy ?? params.countStrategy
|
|
2114
2003
|
});
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
result
|
|
2118
|
-
});
|
|
2119
|
-
return result;
|
|
2120
|
-
} catch (error) {
|
|
2121
|
-
await this._emitErrorHook("error:getAll", {
|
|
2122
|
-
context,
|
|
2123
|
-
error
|
|
2124
|
-
});
|
|
2125
|
-
throw this._handleError(error);
|
|
2126
|
-
}
|
|
2127
|
-
try {
|
|
2004
|
+
});
|
|
2005
|
+
return this._runOp("getAll", context, async () => {
|
|
2128
2006
|
let result;
|
|
2129
2007
|
if (useKeyset) result = await this._pagination.stream({
|
|
2130
2008
|
...paginationOptions,
|
|
@@ -2146,18 +2024,8 @@ var Repository = class extends RepositoryBase {
|
|
|
2146
2024
|
countStrategy: context.countStrategy ?? params.countStrategy ?? forcedCountStrategy
|
|
2147
2025
|
});
|
|
2148
2026
|
}
|
|
2149
|
-
await this._emitHook("after:getAll", {
|
|
2150
|
-
context,
|
|
2151
|
-
result
|
|
2152
|
-
});
|
|
2153
2027
|
return result;
|
|
2154
|
-
}
|
|
2155
|
-
await this._emitErrorHook("error:getAll", {
|
|
2156
|
-
context,
|
|
2157
|
-
error
|
|
2158
|
-
});
|
|
2159
|
-
throw this._handleError(error);
|
|
2160
|
-
}
|
|
2028
|
+
});
|
|
2161
2029
|
}
|
|
2162
2030
|
/**
|
|
2163
2031
|
* Get or create document
|
|
@@ -2169,22 +2037,11 @@ var Repository = class extends RepositoryBase {
|
|
|
2169
2037
|
data: createData,
|
|
2170
2038
|
...options
|
|
2171
2039
|
});
|
|
2172
|
-
|
|
2040
|
+
return this._runOp("getOrCreate", context, () => {
|
|
2173
2041
|
const finalQuery = context.query || query;
|
|
2174
2042
|
const finalData = context.data || createData;
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
context,
|
|
2178
|
-
result
|
|
2179
|
-
});
|
|
2180
|
-
return result;
|
|
2181
|
-
} catch (error) {
|
|
2182
|
-
await this._emitErrorHook("error:getOrCreate", {
|
|
2183
|
-
context,
|
|
2184
|
-
error
|
|
2185
|
-
});
|
|
2186
|
-
throw this._handleError(error);
|
|
2187
|
-
}
|
|
2043
|
+
return getOrCreate(this.Model, finalQuery, finalData, options);
|
|
2044
|
+
});
|
|
2188
2045
|
}
|
|
2189
2046
|
/**
|
|
2190
2047
|
* Count documents
|
|
@@ -2195,21 +2052,7 @@ var Repository = class extends RepositoryBase {
|
|
|
2195
2052
|
query,
|
|
2196
2053
|
...options
|
|
2197
2054
|
});
|
|
2198
|
-
|
|
2199
|
-
const finalQuery = context.query || query;
|
|
2200
|
-
const result = await count(this.Model, finalQuery, options);
|
|
2201
|
-
await this._emitHook("after:count", {
|
|
2202
|
-
context,
|
|
2203
|
-
result
|
|
2204
|
-
});
|
|
2205
|
-
return result;
|
|
2206
|
-
} catch (error) {
|
|
2207
|
-
await this._emitErrorHook("error:count", {
|
|
2208
|
-
context,
|
|
2209
|
-
error
|
|
2210
|
-
});
|
|
2211
|
-
throw this._handleError(error);
|
|
2212
|
-
}
|
|
2055
|
+
return this._runOp("count", context, () => count(this.Model, context.query || query, options));
|
|
2213
2056
|
}
|
|
2214
2057
|
/**
|
|
2215
2058
|
* Check if document exists
|
|
@@ -2220,21 +2063,7 @@ var Repository = class extends RepositoryBase {
|
|
|
2220
2063
|
query,
|
|
2221
2064
|
...options
|
|
2222
2065
|
});
|
|
2223
|
-
|
|
2224
|
-
const finalQuery = context.query || query;
|
|
2225
|
-
const result = await exists(this.Model, finalQuery, options);
|
|
2226
|
-
await this._emitHook("after:exists", {
|
|
2227
|
-
context,
|
|
2228
|
-
result
|
|
2229
|
-
});
|
|
2230
|
-
return result;
|
|
2231
|
-
} catch (error) {
|
|
2232
|
-
await this._emitErrorHook("error:exists", {
|
|
2233
|
-
context,
|
|
2234
|
-
error
|
|
2235
|
-
});
|
|
2236
|
-
throw this._handleError(error);
|
|
2237
|
-
}
|
|
2066
|
+
return this._runOp("exists", context, () => exists(this.Model, context.query || query, options));
|
|
2238
2067
|
}
|
|
2239
2068
|
/**
|
|
2240
2069
|
* Update document by ID
|
|
@@ -2308,33 +2137,23 @@ var Repository = class extends RepositoryBase {
|
|
|
2308
2137
|
* ```
|
|
2309
2138
|
*/
|
|
2310
2139
|
async findOneAndUpdate(filter, update, options = {}) {
|
|
2140
|
+
const normalizedUpdate = isUpdateSpec(update) ? compileUpdateSpecToMongo(update) : update;
|
|
2311
2141
|
const context = await this._buildContext("findOneAndUpdate", {
|
|
2312
2142
|
query: filter,
|
|
2313
|
-
data:
|
|
2143
|
+
data: normalizedUpdate,
|
|
2314
2144
|
...options
|
|
2315
2145
|
});
|
|
2316
|
-
|
|
2146
|
+
return this._runOp("findOneAndUpdate", context, async () => {
|
|
2317
2147
|
const finalQuery = context.query || filter;
|
|
2318
|
-
const finalUpdate = context.data ||
|
|
2319
|
-
|
|
2148
|
+
const finalUpdate = context.data || normalizedUpdate;
|
|
2149
|
+
return await findOneAndUpdate(this.Model, finalQuery, finalUpdate, {
|
|
2320
2150
|
...options,
|
|
2321
2151
|
sort: options.sort,
|
|
2322
2152
|
returnDocument: options.returnDocument ?? "after",
|
|
2323
2153
|
upsert: options.upsert ?? false,
|
|
2324
2154
|
session: options.session
|
|
2325
2155
|
});
|
|
2326
|
-
|
|
2327
|
-
context,
|
|
2328
|
-
result
|
|
2329
|
-
});
|
|
2330
|
-
return result;
|
|
2331
|
-
} catch (error) {
|
|
2332
|
-
await this._emitErrorHook("error:findOneAndUpdate", {
|
|
2333
|
-
context,
|
|
2334
|
-
error
|
|
2335
|
-
});
|
|
2336
|
-
throw this._handleError(error);
|
|
2337
|
-
}
|
|
2156
|
+
});
|
|
2338
2157
|
}
|
|
2339
2158
|
/**
|
|
2340
2159
|
* Delete a document by ID.
|
|
@@ -2417,6 +2236,96 @@ var Repository = class extends RepositoryBase {
|
|
|
2417
2236
|
}
|
|
2418
2237
|
}
|
|
2419
2238
|
/**
|
|
2239
|
+
* Update every document matching the filter.
|
|
2240
|
+
*
|
|
2241
|
+
* Promoted from `batchOperationsPlugin` to a class primitive in 3.11.0 so
|
|
2242
|
+
* the surface matches sqlitekit's always-available `updateMany`. Consumers
|
|
2243
|
+
* who forget to wire `batchOperationsPlugin` no longer get a runtime
|
|
2244
|
+
* `TypeError: repo.updateMany is not a function` — the method is always
|
|
2245
|
+
* here.
|
|
2246
|
+
*
|
|
2247
|
+
* Accepts three `update` forms via `UpdateInput`:
|
|
2248
|
+
* - Portable `UpdateSpec` from `@classytic/repo-core/update` (compiled to
|
|
2249
|
+
* `$set`/`$unset`/`$inc`/`$setOnInsert` before hooks run).
|
|
2250
|
+
* - Raw Mongo operator record (`{ $set, $inc, ... }`) — passed through.
|
|
2251
|
+
* - Mongo aggregation pipeline (`[...]`) — requires `updatePipeline: true`
|
|
2252
|
+
* to guard against accidental pipeline updates.
|
|
2253
|
+
*
|
|
2254
|
+
* Refuses empty filters (defense-in-depth against mass-update accidents)
|
|
2255
|
+
* both before and after policy hooks inject tenant scope.
|
|
2256
|
+
*/
|
|
2257
|
+
async updateMany(filter, update, options = {}) {
|
|
2258
|
+
const normalizedData = isUpdateSpec(update) ? compileUpdateSpecToMongo(update) : update;
|
|
2259
|
+
const context = await this._buildContext("updateMany", {
|
|
2260
|
+
query: filter,
|
|
2261
|
+
data: normalizedData,
|
|
2262
|
+
...options
|
|
2263
|
+
});
|
|
2264
|
+
return this._runOp("updateMany", context, async () => {
|
|
2265
|
+
const finalQuery = context.query || filter;
|
|
2266
|
+
if (!finalQuery || Object.keys(finalQuery).length === 0) throw createError(400, "updateMany requires a non-empty query filter. Pass an explicit filter to prevent accidental mass updates.");
|
|
2267
|
+
if (Array.isArray(normalizedData) && options.updatePipeline !== true) throw createError(400, "Update pipelines (array updates) are disabled by default; pass `{ updatePipeline: true }` to explicitly allow pipeline-style updates.");
|
|
2268
|
+
const finalData = context.data || normalizedData;
|
|
2269
|
+
return await this.Model.updateMany(finalQuery, finalData, {
|
|
2270
|
+
runValidators: true,
|
|
2271
|
+
session: options.session,
|
|
2272
|
+
...options.updatePipeline !== void 0 ? { updatePipeline: options.updatePipeline } : {}
|
|
2273
|
+
}).exec();
|
|
2274
|
+
});
|
|
2275
|
+
}
|
|
2276
|
+
/**
|
|
2277
|
+
* Delete every document matching the filter.
|
|
2278
|
+
*
|
|
2279
|
+
* Promoted from `batchOperationsPlugin` to a class primitive in 3.11.0 for
|
|
2280
|
+
* the same reason as `updateMany` — sqlitekit parity and no silent "method
|
|
2281
|
+
* is undefined" footgun when the batch plugin isn't wired.
|
|
2282
|
+
*
|
|
2283
|
+
* Behavior mirrors `delete()`: defaults to soft delete when
|
|
2284
|
+
* `softDeletePlugin` is wired, physical delete otherwise. Pass
|
|
2285
|
+
* `{ mode: 'hard' }` to bypass soft-delete — multi-tenant scoping, audit,
|
|
2286
|
+
* and cache hooks still fire.
|
|
2287
|
+
*
|
|
2288
|
+
* Rejects empty filters up front AND after policy hooks, so a policy-plugin
|
|
2289
|
+
* bug that zeroes out the query can't be exploited to wipe a collection.
|
|
2290
|
+
*/
|
|
2291
|
+
async deleteMany(filter, options = {}) {
|
|
2292
|
+
if (!filter || Object.keys(filter).length === 0) throw createError(400, "deleteMany requires a non-empty query filter. Pass an explicit filter to prevent accidental mass deletes.");
|
|
2293
|
+
const mode = options.mode;
|
|
2294
|
+
const context = await this._buildContext("deleteMany", {
|
|
2295
|
+
query: filter,
|
|
2296
|
+
...options,
|
|
2297
|
+
...mode ? { deleteMode: mode } : {}
|
|
2298
|
+
});
|
|
2299
|
+
try {
|
|
2300
|
+
if (context.softDeleted) {
|
|
2301
|
+
const result = {
|
|
2302
|
+
acknowledged: true,
|
|
2303
|
+
deletedCount: 0,
|
|
2304
|
+
soft: true
|
|
2305
|
+
};
|
|
2306
|
+
await this._emitHook("after:deleteMany", {
|
|
2307
|
+
context,
|
|
2308
|
+
result
|
|
2309
|
+
});
|
|
2310
|
+
return result;
|
|
2311
|
+
}
|
|
2312
|
+
const finalQuery = context.query || filter;
|
|
2313
|
+
if (!finalQuery || Object.keys(finalQuery).length === 0) throw createError(400, "deleteMany requires a non-empty query filter after policy hooks.");
|
|
2314
|
+
const result = await this.Model.deleteMany(finalQuery, { session: options.session }).exec();
|
|
2315
|
+
await this._emitHook("after:deleteMany", {
|
|
2316
|
+
context,
|
|
2317
|
+
result
|
|
2318
|
+
});
|
|
2319
|
+
return result;
|
|
2320
|
+
} catch (error) {
|
|
2321
|
+
await this._emitErrorHook("error:deleteMany", {
|
|
2322
|
+
context,
|
|
2323
|
+
error
|
|
2324
|
+
});
|
|
2325
|
+
throw this._handleError(error);
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
/**
|
|
2420
2329
|
* Kit-native aggregation pipeline — takes a MongoDB stage array and
|
|
2421
2330
|
* returns the raw pipeline output. Use this for `$lookup`, `$unwind`,
|
|
2422
2331
|
* `$facet`, `$graphLookup`, and any other mongo-specific power
|
|
@@ -2440,7 +2349,7 @@ var Repository = class extends RepositoryBase {
|
|
|
2440
2349
|
});
|
|
2441
2350
|
const maxStages = options.maxPipelineStages;
|
|
2442
2351
|
if (maxStages && pipeline.length > maxStages) throw createError(400, `Aggregation pipeline exceeds maximum allowed stages (${pipeline.length} > ${maxStages})`);
|
|
2443
|
-
|
|
2352
|
+
return this._runOp("aggregatePipeline", context, async () => {
|
|
2444
2353
|
const finalPipeline = [...pipeline];
|
|
2445
2354
|
if (context.query && Object.keys(context.query).length > 0) finalPipeline.unshift({ $match: context.query });
|
|
2446
2355
|
const aggregation = this.Model.aggregate(finalPipeline);
|
|
@@ -2451,19 +2360,8 @@ var Repository = class extends RepositoryBase {
|
|
|
2451
2360
|
if (options.comment) aggregation.option({ comment: options.comment });
|
|
2452
2361
|
if (options.readConcern) aggregation.option({ readConcern: options.readConcern });
|
|
2453
2362
|
if (options.collation) aggregation.collation(options.collation);
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
context,
|
|
2457
|
-
result
|
|
2458
|
-
});
|
|
2459
|
-
return result;
|
|
2460
|
-
} catch (error) {
|
|
2461
|
-
await this._emitErrorHook("error:aggregatePipeline", {
|
|
2462
|
-
context,
|
|
2463
|
-
error
|
|
2464
|
-
});
|
|
2465
|
-
throw this._handleError(error);
|
|
2466
|
-
}
|
|
2363
|
+
return await aggregation.exec();
|
|
2364
|
+
});
|
|
2467
2365
|
}
|
|
2468
2366
|
/**
|
|
2469
2367
|
* Paginated kit-native aggregation pipeline. Same mongo-specific
|
|
@@ -2485,20 +2383,7 @@ var Repository = class extends RepositoryBase {
|
|
|
2485
2383
|
...context,
|
|
2486
2384
|
pipeline: finalPipeline
|
|
2487
2385
|
};
|
|
2488
|
-
|
|
2489
|
-
const result = await this._pagination.aggregatePaginate(aggOptions);
|
|
2490
|
-
await this._emitHook("after:aggregatePipelinePaginate", {
|
|
2491
|
-
context,
|
|
2492
|
-
result
|
|
2493
|
-
});
|
|
2494
|
-
return result;
|
|
2495
|
-
} catch (error) {
|
|
2496
|
-
await this._emitErrorHook("error:aggregatePipelinePaginate", {
|
|
2497
|
-
context,
|
|
2498
|
-
error
|
|
2499
|
-
});
|
|
2500
|
-
throw this._handleError(error);
|
|
2501
|
-
}
|
|
2386
|
+
return this._runOp("aggregatePipelinePaginate", context, () => this._pagination.aggregatePaginate(aggOptions));
|
|
2502
2387
|
}
|
|
2503
2388
|
/**
|
|
2504
2389
|
* Portable aggregation. Compiles the repo-core `AggRequest` IR to
|
|
@@ -2524,21 +2409,10 @@ var Repository = class extends RepositoryBase {
|
|
|
2524
2409
|
*/
|
|
2525
2410
|
async aggregate(req) {
|
|
2526
2411
|
const context = await this._buildContext("aggregate", { aggRequest: req });
|
|
2527
|
-
|
|
2412
|
+
return this._runOp("aggregate", context, async () => {
|
|
2528
2413
|
const finalReq = this._injectPolicyScopeIntoAgg(req, context);
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
context,
|
|
2532
|
-
result
|
|
2533
|
-
});
|
|
2534
|
-
return result;
|
|
2535
|
-
} catch (error) {
|
|
2536
|
-
await this._emitErrorHook("error:aggregate", {
|
|
2537
|
-
context,
|
|
2538
|
-
error
|
|
2539
|
-
});
|
|
2540
|
-
throw this._handleError(error);
|
|
2541
|
-
}
|
|
2414
|
+
return { rows: await executeAgg(this.Model, finalReq, { session: context.session }) };
|
|
2415
|
+
});
|
|
2542
2416
|
}
|
|
2543
2417
|
/**
|
|
2544
2418
|
* Offset-paginated portable aggregation. Same IR as `aggregate`,
|
|
@@ -2642,25 +2516,14 @@ var Repository = class extends RepositoryBase {
|
|
|
2642
2516
|
query,
|
|
2643
2517
|
...options
|
|
2644
2518
|
});
|
|
2645
|
-
|
|
2519
|
+
return this._runOp("distinct", context, () => {
|
|
2646
2520
|
const finalQuery = context.query || query;
|
|
2647
2521
|
const readPreference = context.readPreference ?? options.readPreference;
|
|
2648
|
-
|
|
2522
|
+
return distinct(this.Model, field, finalQuery, {
|
|
2649
2523
|
session: options.session,
|
|
2650
2524
|
readPreference
|
|
2651
2525
|
});
|
|
2652
|
-
|
|
2653
|
-
context,
|
|
2654
|
-
result
|
|
2655
|
-
});
|
|
2656
|
-
return result;
|
|
2657
|
-
} catch (error) {
|
|
2658
|
-
await this._emitErrorHook("error:distinct", {
|
|
2659
|
-
context,
|
|
2660
|
-
error
|
|
2661
|
-
});
|
|
2662
|
-
throw this._handleError(error);
|
|
2663
|
-
}
|
|
2526
|
+
});
|
|
2664
2527
|
}
|
|
2665
2528
|
/**
|
|
2666
2529
|
* Query with custom field lookups ($lookup)
|
|
@@ -2941,20 +2804,7 @@ var Repository = class extends RepositoryBase {
|
|
|
2941
2804
|
async _executeQuery(buildQuery) {
|
|
2942
2805
|
const operation = buildQuery.name || "custom";
|
|
2943
2806
|
const context = await this._buildContext(operation, {});
|
|
2944
|
-
|
|
2945
|
-
const result = await buildQuery(this.Model);
|
|
2946
|
-
await this._emitHook(`after:${operation}`, {
|
|
2947
|
-
context,
|
|
2948
|
-
result
|
|
2949
|
-
});
|
|
2950
|
-
return result;
|
|
2951
|
-
} catch (error) {
|
|
2952
|
-
await this._emitErrorHook(`error:${operation}`, {
|
|
2953
|
-
context,
|
|
2954
|
-
error
|
|
2955
|
-
});
|
|
2956
|
-
throw this._handleError(error);
|
|
2957
|
-
}
|
|
2807
|
+
return this._runOp(operation, context, () => buildQuery(this.Model));
|
|
2958
2808
|
}
|
|
2959
2809
|
/**
|
|
2960
2810
|
* Build operation context and run before-hooks (sorted by priority).
|
|
@@ -3787,7 +3637,7 @@ function cascadePlugin(options) {
|
|
|
3787
3637
|
*/
|
|
3788
3638
|
async function cascadeViaRepoBulk(relation, parentId, ctx) {
|
|
3789
3639
|
const targetRepo = relation.repo;
|
|
3790
|
-
if (typeof targetRepo.deleteMany !== "function") throw new Error(`cascadePlugin: target repo for '${ctx.targetModelName}' is missing deleteMany().
|
|
3640
|
+
if (typeof targetRepo.deleteMany !== "function") throw new Error(`cascadePlugin: target repo for '${ctx.targetModelName}' is missing deleteMany(). Ensure the target is a mongokit Repository or implements the StandardRepo deleteMany contract.`);
|
|
3791
3641
|
await targetRepo.deleteMany({ [relation.foreignKey]: parentId }, {
|
|
3792
3642
|
...ctx.scopeForward,
|
|
3793
3643
|
mode: ctx.shouldSoftDelete ? "soft" : "hard",
|
|
@@ -3799,7 +3649,7 @@ async function cascadeViaRepoBulk(relation, parentId, ctx) {
|
|
|
3799
3649
|
*/
|
|
3800
3650
|
async function cascadeViaRepoBulkMany(relation, parentIds, ctx) {
|
|
3801
3651
|
const targetRepo = relation.repo;
|
|
3802
|
-
if (typeof targetRepo.deleteMany !== "function") throw new Error(`cascadePlugin: target repo for '${ctx.targetModelName}' is missing deleteMany().
|
|
3652
|
+
if (typeof targetRepo.deleteMany !== "function") throw new Error(`cascadePlugin: target repo for '${ctx.targetModelName}' is missing deleteMany(). Ensure the target is a mongokit Repository or implements the StandardRepo deleteMany contract.`);
|
|
3803
3653
|
await targetRepo.deleteMany({ [relation.foreignKey]: { $in: parentIds } }, {
|
|
3804
3654
|
...ctx.scopeForward,
|
|
3805
3655
|
mode: ctx.shouldSoftDelete ? "soft" : "hard",
|
|
@@ -4507,8 +4357,57 @@ function mongoOperationsPlugin() {
|
|
|
4507
4357
|
* ]);
|
|
4508
4358
|
* ```
|
|
4509
4359
|
*/
|
|
4360
|
+
/**
|
|
4361
|
+
* True when the op's policy target already has `tenantField` set by the
|
|
4362
|
+
* caller. Used to decide whether the plugin can safely skip injecting a
|
|
4363
|
+
* tenant scope rather than throwing on a missing context.
|
|
4364
|
+
*
|
|
4365
|
+
* - `data` — `context.data[tenantField]` is present
|
|
4366
|
+
* - `dataArray` — every row in `context.dataArray` has `tenantField`
|
|
4367
|
+
* - `query` — `context.query[tenantField]` is present
|
|
4368
|
+
* - `filters` — `context.filters[tenantField]` is present
|
|
4369
|
+
* - `operations` — every bulkWrite sub-op's filter/document has `tenantField`
|
|
4370
|
+
* - `none` — unreachable (the hook isn't registered for these ops)
|
|
4371
|
+
*
|
|
4372
|
+
* For multi-row targets (`dataArray`, `operations`) we require EVERY row to
|
|
4373
|
+
* be stamped. Partial stamping is ambiguous (we have no resolver value to
|
|
4374
|
+
* fill in the gaps) and is safer to treat as "not stamped" so the caller
|
|
4375
|
+
* either stamps all rows or supplies a context/resolver.
|
|
4376
|
+
*/
|
|
4377
|
+
function payloadHasTenantField(context, policyKey, tenantField) {
|
|
4378
|
+
switch (policyKey) {
|
|
4379
|
+
case "data": return context.data?.[tenantField] != null;
|
|
4380
|
+
case "dataArray": {
|
|
4381
|
+
const arr = context.dataArray;
|
|
4382
|
+
if (!Array.isArray(arr) || arr.length === 0) return false;
|
|
4383
|
+
return arr.every((row) => row && row[tenantField] != null);
|
|
4384
|
+
}
|
|
4385
|
+
case "query": return context.query?.[tenantField] != null;
|
|
4386
|
+
case "filters": return context.filters?.[tenantField] != null;
|
|
4387
|
+
case "operations": {
|
|
4388
|
+
const ops = context.operations;
|
|
4389
|
+
if (!Array.isArray(ops) || ops.length === 0) return false;
|
|
4390
|
+
return ops.every((subOp) => {
|
|
4391
|
+
for (const key of [
|
|
4392
|
+
"updateOne",
|
|
4393
|
+
"updateMany",
|
|
4394
|
+
"deleteOne",
|
|
4395
|
+
"deleteMany",
|
|
4396
|
+
"replaceOne"
|
|
4397
|
+
]) {
|
|
4398
|
+
const body = subOp[key];
|
|
4399
|
+
if (body) return body.filter?.[tenantField] != null;
|
|
4400
|
+
}
|
|
4401
|
+
const ins = subOp.insertOne;
|
|
4402
|
+
if (ins) return ins.document?.[tenantField] != null;
|
|
4403
|
+
return false;
|
|
4404
|
+
});
|
|
4405
|
+
}
|
|
4406
|
+
default: return false;
|
|
4407
|
+
}
|
|
4408
|
+
}
|
|
4510
4409
|
function multiTenantPlugin(options = {}) {
|
|
4511
|
-
const { tenantField = "organizationId", contextKey = "organizationId", required = true, skipOperations = [], skipWhen, resolveContext, fieldType = "string" } = options;
|
|
4410
|
+
const { tenantField = "organizationId", contextKey = "organizationId", required = true, skipOperations = [], skipWhen, resolveContext, fieldType = "string", allowDataInjection = true } = options;
|
|
4512
4411
|
const extraOps = [{
|
|
4513
4412
|
op: "getDeleted",
|
|
4514
4413
|
policyKey: "filters"
|
|
@@ -4530,6 +4429,7 @@ function multiTenantPlugin(options = {}) {
|
|
|
4530
4429
|
tenantId = resolveContext();
|
|
4531
4430
|
if (tenantId) context[contextKey] = tenantId;
|
|
4532
4431
|
}
|
|
4432
|
+
if (!tenantId && allowDataInjection && payloadHasTenantField(context, policyKey, tenantField)) return;
|
|
4533
4433
|
if (!tenantId && required) throw new Error(`[mongokit] Multi-tenant: Missing '${contextKey}' in context for '${op}'. Pass it via options or set required: false.`);
|
|
4534
4434
|
if (!tenantId) return;
|
|
4535
4435
|
const castId = fieldType === "objectId" ? new mongoose.Types.ObjectId(tenantId) : tenantId;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/mongokit",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.0",
|
|
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,
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"homepage": "https://github.com/classytic/mongokit#readme",
|
|
95
95
|
"peerDependencies": {
|
|
96
96
|
"mongoose": ">=9.4.1",
|
|
97
|
-
"@classytic/repo-core": ">=0.
|
|
97
|
+
"@classytic/repo-core": ">=0.2.0"
|
|
98
98
|
},
|
|
99
99
|
"engines": {
|
|
100
100
|
"node": ">=22"
|