@classytic/mongokit 3.13.0 → 3.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -0
- package/dist/{Repository-BY40uhcv.mjs → Repository-DwJFh3XA.mjs} +11 -5
- package/dist/actions/index.mjs +1 -1
- package/dist/better-auth/index.mjs +1 -1
- package/dist/index.d.mts +27 -4
- package/dist/index.mjs +4 -4
- package/dist/lock/index.d.mts +28 -0
- package/dist/lock/index.mjs +92 -0
- package/dist/plugins/index.mjs +1 -1
- package/dist/{update-DLcSSa5b.mjs → update-C-3viiw1.mjs} +71 -1
- package/dist/{validation-chain.plugin-_-WuNml2.mjs → validation-chain.plugin-Pj2aD17U.mjs} +2 -2
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -284,6 +284,58 @@ Each plugin is tree-shakeable. Import only what you use.
|
|
|
284
284
|
|
|
285
285
|
---
|
|
286
286
|
|
|
287
|
+
## Composing with mongoose-level plugins
|
|
288
|
+
|
|
289
|
+
mongokit deliberately doesn't reabsorb mongoose plugins that already work via `schema.plugin(...)`. They compose cleanly: the mongoose plugin attaches to the schema before the model lands at mongokit, both layers fire on every write.
|
|
290
|
+
|
|
291
|
+
Canonical examples — install + wire as documented by the upstream package, then hand the model to mongokit:
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
import mongoose from 'mongoose';
|
|
295
|
+
import { Repository, multiTenantPlugin } from '@classytic/mongokit';
|
|
296
|
+
import timelineAuditPlugin from 'mongoose-timeline-audit';
|
|
297
|
+
import slugPlugin from 'mongoose-slug-plugin';
|
|
298
|
+
|
|
299
|
+
const orderSchema = new mongoose.Schema({
|
|
300
|
+
customerId: { type: mongoose.Schema.Types.ObjectId, ref: 'Customer' },
|
|
301
|
+
status: String,
|
|
302
|
+
title: String,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// 1. Wire mongoose-level plugins on the schema
|
|
306
|
+
orderSchema.plugin(timelineAuditPlugin, {
|
|
307
|
+
ownerField: 'customerId',
|
|
308
|
+
eventLimits: { 'order.updated': 20 },
|
|
309
|
+
});
|
|
310
|
+
orderSchema.plugin(slugPlugin, { tmpl: '<%=title%>' });
|
|
311
|
+
|
|
312
|
+
// 2. Build the model
|
|
313
|
+
const Order = mongoose.model('Order', orderSchema);
|
|
314
|
+
|
|
315
|
+
// 3. Hand the model to mongokit — kit plugins layer on top
|
|
316
|
+
const orderRepo = new Repository(Order, [
|
|
317
|
+
multiTenantPlugin({ tenantField: 'organizationId' }),
|
|
318
|
+
]);
|
|
319
|
+
|
|
320
|
+
// Now every write fires BOTH layers:
|
|
321
|
+
// - mongoose pre('save') / post('save') (timeline-audit, slug)
|
|
322
|
+
// - mongokit before:create / after:create (multi-tenant, audit, cache)
|
|
323
|
+
await orderRepo.create({ customerId, status: 'pending', title: 'New order' });
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Why this composition over absorbing into mongokit:** mongoose plugins hook `schema.add()` + `pre('save')`; mongokit plugins hook `before:create` / `after:update`. Different event models, different concerns. Wrapping a mongoose plugin as a mongokit plugin would be a fork, not a wrapper — and the mongoose plugin already works without mongokit being involved at all. Keeping the layers separate also lets each package version independently.
|
|
327
|
+
|
|
328
|
+
**When to use which audit surface:**
|
|
329
|
+
|
|
330
|
+
| Need | Reach for |
|
|
331
|
+
|---|---|
|
|
332
|
+
| Per-doc UX timeline visible to end users (`order.timeline = [{event, actor, at}, ...]`) | `mongoose-timeline-audit` (mongoose plugin) — embedded array on the doc |
|
|
333
|
+
| Compliance audit ledger across models, immutable, queryable | `auditTrailPlugin` (mongokit, ships in this package) — separate collection |
|
|
334
|
+
|
|
335
|
+
The two are complementary, not alternatives. A typical app wires both — timeline for the customer-facing order page, auditTrail for the compliance officer's quarterly export.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
287
339
|
## QueryParser (URL → filter)
|
|
288
340
|
|
|
289
341
|
Turn Express / Fastify query strings into sanitized Mongo filters + sort + pagination.
|
|
@@ -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-
|
|
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";
|
|
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";
|
|
@@ -2934,8 +2934,11 @@ var Repository = class extends RepositoryBase {
|
|
|
2934
2934
|
* `context.filter`, which is merged into the pre-aggregate `$match`
|
|
2935
2935
|
* before compilation.
|
|
2936
2936
|
*/
|
|
2937
|
-
async aggregate(req) {
|
|
2938
|
-
const context = await this._buildContext("aggregate", {
|
|
2937
|
+
async aggregate(req, options = {}) {
|
|
2938
|
+
const context = await this._buildContext("aggregate", {
|
|
2939
|
+
aggRequest: req,
|
|
2940
|
+
...options
|
|
2941
|
+
});
|
|
2939
2942
|
const cached = this._cachedValue(context);
|
|
2940
2943
|
if (cached !== void 0) {
|
|
2941
2944
|
await this._emitHook("after:aggregate", {
|
|
@@ -2988,8 +2991,11 @@ var Repository = class extends RepositoryBase {
|
|
|
2988
2991
|
* `after`. Scales to arbitrary group counts because the planner
|
|
2989
2992
|
* uses `(sort_keys) > (cursor)` instead of `OFFSET N`.
|
|
2990
2993
|
*/
|
|
2991
|
-
async aggregatePaginate(req) {
|
|
2992
|
-
const context = await this._buildContext("aggregatePaginate", {
|
|
2994
|
+
async aggregatePaginate(req, options = {}) {
|
|
2995
|
+
const context = await this._buildContext("aggregatePaginate", {
|
|
2996
|
+
aggRequest: req,
|
|
2997
|
+
...options
|
|
2998
|
+
});
|
|
2993
2999
|
const limit = Math.max(1, Math.min(req.limit ?? 20, 1e3));
|
|
2994
3000
|
const session = context.session;
|
|
2995
3001
|
const useKeyset = isKeysetMode(req);
|
package/dist/actions/index.mjs
CHANGED
|
@@ -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-
|
|
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";
|
|
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-
|
|
1
|
+
import { n as Repository } from "../Repository-DwJFh3XA.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
|
package/dist/index.d.mts
CHANGED
|
@@ -7,11 +7,34 @@ import { n as SchemaIndexes } from "./indexes-CgOF7Wjd.mjs";
|
|
|
7
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-BgRAaXX-.mjs";
|
|
8
8
|
import * as _$mongoose from "mongoose";
|
|
9
9
|
import { ClientSession, Expression, Model, PipelineStage, PopulateOptions } from "mongoose";
|
|
10
|
-
import { AggPaginationRequest, AggRequest, AggResult, KeysetAggPaginationResult, RepositoryBase } from "@classytic/repo-core/repository";
|
|
10
|
+
import { AggPaginationRequest, AggRequest, AggResult, KeysetAggPaginationResult, QueryOptions, RepositoryBase } from "@classytic/repo-core/repository";
|
|
11
11
|
import { UpdateInput } from "@classytic/repo-core/update";
|
|
12
12
|
import { AggregatePaginationResult, KeysetPaginationResult, OffsetPaginationResult, OffsetPaginationResultCore } from "@classytic/repo-core/pagination";
|
|
13
13
|
import { HttpError } from "@classytic/repo-core/errors";
|
|
14
14
|
|
|
15
|
+
//#region src/filter/compile.d.ts
|
|
16
|
+
/**
|
|
17
|
+
* Filter IR → MongoDB query compiler.
|
|
18
|
+
*
|
|
19
|
+
* Mirrors sqlitekit's `compileFilterToDrizzle`: takes a repo-core
|
|
20
|
+
* `Filter` tree and emits a MongoDB query object. Used by `groupBy` /
|
|
21
|
+
* `groupByPaginate` to translate the portable AggRequest's `filter`
|
|
22
|
+
* + `having` into `$match` stages.
|
|
23
|
+
*
|
|
24
|
+
* Scope: the same operator set sqlitekit supports (`eq`, `ne`, `gt`,
|
|
25
|
+
* `gte`, `lt`, `lte`, `in`, `nin`, `exists`, `like`, `regex`, `and`,
|
|
26
|
+
* `or`, `not`, `raw`). Anything else throws so we fail loud instead
|
|
27
|
+
* of silently dropping predicates.
|
|
28
|
+
*
|
|
29
|
+
* Why only this op set here and not everywhere in mongokit? Historic
|
|
30
|
+
* reason: mongokit pre-dates repo-core's Filter IR, so its existing
|
|
31
|
+
* policy plugins (multi-tenant, soft-delete) speak MongoDB query
|
|
32
|
+
* language directly. Converting mongokit's entire filter pipeline
|
|
33
|
+
* to Filter IR is out-of-scope for this change — we introduce the
|
|
34
|
+
* compiler only where the new portable `groupBy` IR needs it.
|
|
35
|
+
*/
|
|
36
|
+
declare function compileFilterToMongo(input: unknown): Record<string, unknown>;
|
|
37
|
+
//#endregion
|
|
15
38
|
//#region src/operations.d.ts
|
|
16
39
|
/**
|
|
17
40
|
* Where a plugin should inject its scoping filter (multi-tenant scope,
|
|
@@ -1616,7 +1639,7 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
|
|
|
1616
1639
|
* `context.filter`, which is merged into the pre-aggregate `$match`
|
|
1617
1640
|
* before compilation.
|
|
1618
1641
|
*/
|
|
1619
|
-
aggregate<TRow extends Record<string, unknown> = Record<string, unknown>>(req: AggRequest): Promise<AggResult<TRow>>;
|
|
1642
|
+
aggregate<TRow extends Record<string, unknown> = Record<string, unknown>>(req: AggRequest, options?: QueryOptions): Promise<AggResult<TRow>>;
|
|
1620
1643
|
/**
|
|
1621
1644
|
* Invalidate every aggregate-cache entry tagged with ANY of the
|
|
1622
1645
|
* given tags. Delegates to the unified cache plugin's handle
|
|
@@ -1647,7 +1670,7 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
|
|
|
1647
1670
|
* `after`. Scales to arbitrary group counts because the planner
|
|
1648
1671
|
* uses `(sort_keys) > (cursor)` instead of `OFFSET N`.
|
|
1649
1672
|
*/
|
|
1650
|
-
aggregatePaginate<TRow extends Record<string, unknown> = Record<string, unknown>>(req: AggPaginationRequest): Promise<OffsetPaginationResultCore<TRow> | KeysetAggPaginationResult<TRow>>;
|
|
1673
|
+
aggregatePaginate<TRow extends Record<string, unknown> = Record<string, unknown>>(req: AggPaginationRequest, options?: QueryOptions): Promise<OffsetPaginationResultCore<TRow> | KeysetAggPaginationResult<TRow>>;
|
|
1651
1674
|
/**
|
|
1652
1675
|
* Internal: the actual aggregate-paginate execution body. Extracted
|
|
1653
1676
|
* so `aggregatePaginate` can wrap it in the cache layer without
|
|
@@ -2114,4 +2137,4 @@ declare function createOptionsExtractor<TCtx extends Record<string, unknown>>(fi
|
|
|
2114
2137
|
*/
|
|
2115
2138
|
declare function createRepository<TDoc>(Model: _$mongoose.Model<TDoc, any, any, any>, plugins?: PluginType[], paginationConfig?: PaginationConfig, options?: RepositoryOptions): Repository<TDoc>;
|
|
2116
2139
|
//#endregion
|
|
2117
|
-
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, 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 };
|
|
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 };
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { b as LookupBuilder } from "./update-
|
|
1
|
+
import { b as LookupBuilder, x as compileFilterToMongo } from "./update-C-3viiw1.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-
|
|
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";
|
|
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-
|
|
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";
|
|
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";
|
|
@@ -1224,4 +1224,4 @@ function createRepository(Model, plugins = [], paginationConfig = {}, options =
|
|
|
1224
1224
|
}
|
|
1225
1225
|
var src_default = Repository;
|
|
1226
1226
|
//#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, 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 };
|
|
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 };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Connection } from "mongoose";
|
|
2
|
+
import { BaseLockAdapterOptions, LockAdapter } from "@classytic/repo-core/lock";
|
|
3
|
+
|
|
4
|
+
//#region src/lock/index.d.ts
|
|
5
|
+
/** Adapter-construction options. */
|
|
6
|
+
interface MongoLockAdapterOptions extends BaseLockAdapterOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Mongoose connection. Defaults to `mongoose.connection` (the
|
|
9
|
+
* default global connection) — most apps connect once via
|
|
10
|
+
* `mongoose.connect()` and want every primitive to share that
|
|
11
|
+
* handle. Apps using a named connection
|
|
12
|
+
* (`mongoose.createConnection(...)`) pass it explicitly.
|
|
13
|
+
*/
|
|
14
|
+
connection?: Connection;
|
|
15
|
+
/** Collection name. Default `mongo_locks`. */
|
|
16
|
+
collectionName?: string;
|
|
17
|
+
/** Override the Mongoose model name. Default `MongoLock`. */
|
|
18
|
+
modelName?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build a Mongo-backed lock adapter. Idempotent — the underlying
|
|
22
|
+
* model is registered once per `(connection, modelName)` pair, so
|
|
23
|
+
* calling this multiple times with the same options shares the same
|
|
24
|
+
* collection (no duplicate-model errors).
|
|
25
|
+
*/
|
|
26
|
+
declare function createMongoLockAdapter(options?: MongoLockAdapterOptions): LockAdapter;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { MongoLockAdapterOptions, createMongoLockAdapter };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import mongoose, { Schema } from "mongoose";
|
|
2
|
+
//#region src/lock/index.ts
|
|
3
|
+
/**
|
|
4
|
+
* Build a Mongo-backed lock adapter. Idempotent — the underlying
|
|
5
|
+
* model is registered once per `(connection, modelName)` pair, so
|
|
6
|
+
* calling this multiple times with the same options shares the same
|
|
7
|
+
* collection (no duplicate-model errors).
|
|
8
|
+
*/
|
|
9
|
+
function createMongoLockAdapter(options = {}) {
|
|
10
|
+
const { connection, collectionName = "mongo_locks", modelName = "MongoLock", defaultLeaseMs = 3e4 } = options;
|
|
11
|
+
const model = getOrCreateModel(connection ?? mongoose, modelName, collectionName);
|
|
12
|
+
return {
|
|
13
|
+
async tryAcquire(name, holderId, leaseMs) {
|
|
14
|
+
const ms = leaseMs > 0 ? leaseMs : defaultLeaseMs;
|
|
15
|
+
const now = /* @__PURE__ */ new Date();
|
|
16
|
+
const expiresAt = new Date(now.getTime() + ms);
|
|
17
|
+
try {
|
|
18
|
+
const result = await model.findOneAndUpdate({
|
|
19
|
+
_id: name,
|
|
20
|
+
$or: [{ expiresAt: { $lt: now } }, { holder: holderId }]
|
|
21
|
+
}, {
|
|
22
|
+
$set: {
|
|
23
|
+
holder: holderId,
|
|
24
|
+
expiresAt
|
|
25
|
+
},
|
|
26
|
+
$setOnInsert: { acquiredAt: now }
|
|
27
|
+
}, {
|
|
28
|
+
upsert: true,
|
|
29
|
+
returnDocument: "after",
|
|
30
|
+
setDefaultsOnInsert: true
|
|
31
|
+
}).lean();
|
|
32
|
+
return !!result && result.holder === holderId;
|
|
33
|
+
} catch (err) {
|
|
34
|
+
if (err?.code === 11e3) return false;
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
async release(name, holderId) {
|
|
39
|
+
return !!await model.findOneAndDelete({
|
|
40
|
+
_id: name,
|
|
41
|
+
holder: holderId
|
|
42
|
+
}).lean();
|
|
43
|
+
},
|
|
44
|
+
async inspect(name) {
|
|
45
|
+
const doc = await model.findById(name).lean();
|
|
46
|
+
if (!doc) return null;
|
|
47
|
+
if (doc.expiresAt.getTime() <= Date.now()) return null;
|
|
48
|
+
return {
|
|
49
|
+
name,
|
|
50
|
+
holder: doc.holder,
|
|
51
|
+
expiresAt: doc.expiresAt,
|
|
52
|
+
acquiredAt: doc.acquiredAt
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Idempotent model registration. Mongoose throws OverwriteModelError
|
|
59
|
+
* if `model('Name', schema)` is called twice with the same name on
|
|
60
|
+
* the same connection — this guard makes the adapter safe to
|
|
61
|
+
* construct multiple times in tests / hot-reload setups.
|
|
62
|
+
*/
|
|
63
|
+
function getOrCreateModel(conn, modelName, collectionName) {
|
|
64
|
+
const existing = "models" in conn ? conn.models[modelName] : void 0;
|
|
65
|
+
if (existing) return existing;
|
|
66
|
+
const schema = new Schema({
|
|
67
|
+
_id: {
|
|
68
|
+
type: String,
|
|
69
|
+
required: true
|
|
70
|
+
},
|
|
71
|
+
holder: {
|
|
72
|
+
type: String,
|
|
73
|
+
required: true
|
|
74
|
+
},
|
|
75
|
+
expiresAt: {
|
|
76
|
+
type: Date,
|
|
77
|
+
required: true
|
|
78
|
+
},
|
|
79
|
+
acquiredAt: {
|
|
80
|
+
type: Date,
|
|
81
|
+
required: true
|
|
82
|
+
}
|
|
83
|
+
}, {
|
|
84
|
+
versionKey: false,
|
|
85
|
+
timestamps: false,
|
|
86
|
+
_id: false
|
|
87
|
+
});
|
|
88
|
+
schema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
|
|
89
|
+
return conn.model(modelName, schema, collectionName);
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { createMongoLockAdapter };
|
package/dist/plugins/index.mjs
CHANGED
|
@@ -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-
|
|
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";
|
|
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 };
|
|
@@ -13,9 +13,79 @@ import { isFilter } from "@classytic/repo-core/filter";
|
|
|
13
13
|
* appropriately. This keeps the portable API ergonomic for apps that
|
|
14
14
|
* write Mongo queries directly today.
|
|
15
15
|
*/
|
|
16
|
+
/**
|
|
17
|
+
* Operator shorthands arc propagates from bracket-syntax URL params.
|
|
18
|
+
* Fastify parses `?createdAt[gte]=...&createdAt[lte]=...` into
|
|
19
|
+
* `{ createdAt: { gte: '...', lte: '...' } }` — without `$` prefixes.
|
|
20
|
+
* Arc intentionally forwards these as-is ("the kit's filter compiler
|
|
21
|
+
* handles them"). This set covers every operator the arc aggregation
|
|
22
|
+
* guard accepts in `parseDateRange` and `hasFilterOnField`.
|
|
23
|
+
*/
|
|
24
|
+
const SHORTHAND_OPS = new Set([
|
|
25
|
+
"eq",
|
|
26
|
+
"ne",
|
|
27
|
+
"gt",
|
|
28
|
+
"gte",
|
|
29
|
+
"lt",
|
|
30
|
+
"lte",
|
|
31
|
+
"in",
|
|
32
|
+
"nin",
|
|
33
|
+
"exists",
|
|
34
|
+
"regex",
|
|
35
|
+
"mod"
|
|
36
|
+
]);
|
|
37
|
+
/** Range operators whose string values should be coerced to Date. */
|
|
38
|
+
const RANGE_OPS = new Set([
|
|
39
|
+
"gt",
|
|
40
|
+
"gte",
|
|
41
|
+
"lt",
|
|
42
|
+
"lte"
|
|
43
|
+
]);
|
|
44
|
+
/** ISO-8601 date-only or datetime string pattern. */
|
|
45
|
+
const ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}/;
|
|
46
|
+
/**
|
|
47
|
+
* Coerce a string value to a Date when it looks like an ISO-8601 date.
|
|
48
|
+
* Needed because MongoDB won't match a BSON Date field against a string
|
|
49
|
+
* value even when using range operators — BSON type comparison treats
|
|
50
|
+
* Date (type 9) and String (type 2) as distinct, so `{ createdAt: {
|
|
51
|
+
* $gte: "2026-05-01" } }` never matches a Date-typed field.
|
|
52
|
+
*/
|
|
53
|
+
function tryCoerceDate(value) {
|
|
54
|
+
if (typeof value === "string" && ISO_DATE_RE.test(value)) {
|
|
55
|
+
const d = new Date(value);
|
|
56
|
+
return Number.isNaN(d.getTime()) ? value : d;
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Expand operator shorthand values in a plain Mongo-style query object.
|
|
62
|
+
* Only expands nested objects whose keys are ALL in `SHORTHAND_OPS` — so
|
|
63
|
+
* real nested documents (e.g. `address: { city: 'Dhaka' }`) pass through
|
|
64
|
+
* unchanged, while `createdAt: { gte: '2026-04-01', lte: '2026-05-01' }`
|
|
65
|
+
* becomes `createdAt: { $gte: Date(2026-04-01), $lte: Date(2026-05-01) }`.
|
|
66
|
+
* String values on range operators are coerced to Date when they match the
|
|
67
|
+
* ISO-8601 pattern, so BSON Date fields in MongoDB are compared correctly.
|
|
68
|
+
*/
|
|
69
|
+
function expandShorthands(obj) {
|
|
70
|
+
const out = {};
|
|
71
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
72
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
73
|
+
const nested = value;
|
|
74
|
+
const keys = Object.keys(nested);
|
|
75
|
+
if (keys.length > 0 && keys.every((k) => !k.startsWith("$") && SHORTHAND_OPS.has(k))) {
|
|
76
|
+
const expanded = {};
|
|
77
|
+
for (const [op, opVal] of Object.entries(nested)) expanded[`$${op}`] = RANGE_OPS.has(op) ? tryCoerceDate(opVal) : opVal;
|
|
78
|
+
out[key] = expanded;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
out[key] = value;
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
16
86
|
function compileFilterToMongo(input) {
|
|
17
87
|
if (!input) return {};
|
|
18
|
-
if (!isFilter(input)) return input;
|
|
88
|
+
if (!isFilter(input)) return expandShorthands(input);
|
|
19
89
|
return compile(input);
|
|
20
90
|
}
|
|
21
91
|
function compile(filter) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { _ as upsert } from "./update-
|
|
1
|
+
import { _ as upsert } from "./update-C-3viiw1.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-
|
|
4
|
+
import { l as OP_REGISTRY, s as ALL_OPERATIONS, t as HOOK_PRIORITY } from "./Repository-DwJFh3XA.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.
|
|
3
|
+
"version": "3.13.2",
|
|
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,
|
|
@@ -40,6 +40,10 @@
|
|
|
40
40
|
"types": "./dist/better-auth/index.d.mts",
|
|
41
41
|
"default": "./dist/better-auth/index.mjs"
|
|
42
42
|
},
|
|
43
|
+
"./lock": {
|
|
44
|
+
"types": "./dist/lock/index.d.mts",
|
|
45
|
+
"default": "./dist/lock/index.mjs"
|
|
46
|
+
},
|
|
43
47
|
"./query/primitives/geo": {
|
|
44
48
|
"types": "./dist/query/primitives/geo.d.mts",
|
|
45
49
|
"default": "./dist/query/primitives/geo.mjs"
|