@classytic/mongokit 3.41.0 → 3.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +28 -0
  2. package/dist/{Repository-DlhPbXlG.mjs → Repository-Dj6xqPxJ.mjs} +148 -1
  3. package/dist/{Repository-pnLw1rG5.d.mts → Repository-DnL4xk5w.d.mts} +10 -2
  4. package/dist/ai/index.d.mts +1 -1
  5. package/dist/better-auth/index.mjs +1 -1
  6. package/dist/{create-repository-BUx9vfG9.d.mts → create-repository-CJtVdwig.d.mts} +4 -4
  7. package/dist/{create-repository-Bg0H5rte.mjs → create-repository-UjwPWR1z.mjs} +2 -2
  8. package/dist/derive-DRBiegRH.mjs +375 -0
  9. package/dist/index-C0AjVqX3.d.mts +169 -0
  10. package/dist/index.d.mts +37 -8
  11. package/dist/index.mjs +53 -5
  12. package/dist/{multi-tenant.plugin-a3UhQARj.d.mts → multi-tenant.plugin-B_q9NUHc.d.mts} +1 -1
  13. package/dist/outbox/index.d.mts +1 -1
  14. package/dist/outbox/index.mjs +1 -1
  15. package/dist/{plugin-options-CRhIvSzx.d.mts → plugin-options-Cirmb4Kp.d.mts} +1 -1
  16. package/dist/plugins/index.d.mts +4 -4
  17. package/dist/plugins/index.mjs +50 -18
  18. package/dist/read-model/index.d.mts +1 -1
  19. package/dist/{repository-DNHfjAZX.d.mts → repository-MqAP409e.d.mts} +14 -3
  20. package/dist/search/index.d.mts +2 -0
  21. package/dist/search/index.mjs +2 -0
  22. package/dist/sync/index.d.mts +22 -1
  23. package/dist/sync/index.mjs +236 -61
  24. package/dist/testkit/index.d.mts +2 -2
  25. package/dist/testkit/index.mjs +1 -1
  26. package/dist/{timestamp.plugin-DvZlnKEc.mjs → timestamp.plugin-DpOWavVI.mjs} +1 -1
  27. package/dist/{to-plain-CjgEInWV.d.mts → to-plain-o5tUY2nV.d.mts} +2 -2
  28. package/dist/utils/index.d.mts +1 -1
  29. package/dist/{validation-chain.plugin-CTcNJCmV.d.mts → validation-chain.plugin-BAeC5eVT.d.mts} +12 -3
  30. package/dist/{validation-chain.plugin-D1WsGCYr.mjs → validation-chain.plugin-DRApTmkN.mjs} +3 -2
  31. package/package.json +6 -2
@@ -1,77 +1,133 @@
1
1
  import { o as withTransaction } from "../transaction-DmiB4k2P.mjs";
2
- import { Schema } from "mongoose";
2
+ import { Schema, Types } from "mongoose";
3
3
  //#region src/sync/index.ts
4
4
  function createChangeLogModels(connection, options = {}) {
5
- const { collection = "sync_changes", modelName = "SyncChange" } = options;
5
+ const { collection = "sync_changes", modelName = "SyncChange", sequencing = "in-transaction" } = options;
6
6
  const entriesName = modelName;
7
7
  const counterName = `${modelName}Counter`;
8
- return {
9
- entries: connection.models[entriesName] ?? connection.model(entriesName, (() => {
10
- const schema = new Schema({
11
- seq: {
12
- type: Number,
13
- required: true
14
- },
15
- scope: {
16
- type: String,
17
- required: true
18
- },
19
- docId: {
20
- type: String,
21
- required: true
22
- },
23
- op: {
24
- type: String,
25
- enum: ["upsert", "delete"],
26
- required: true
27
- },
28
- version: {
29
- type: Number,
30
- required: true
31
- },
32
- doc: { type: Schema.Types.Mixed },
33
- tenantId: { type: String },
34
- at: {
35
- type: Date,
36
- required: true
37
- }
38
- }, {
39
- collection,
40
- versionKey: false,
41
- minimize: false
42
- });
43
- schema.index({ seq: 1 }, {
44
- unique: true,
45
- name: "feed_order"
46
- });
47
- schema.index({
48
- tenantId: 1,
49
- scope: 1,
50
- seq: 1
51
- }, { name: "tenant_scope_feed" });
52
- schema.index({
53
- scope: 1,
54
- docId: 1,
55
- seq: 1
56
- }, { name: "doc_versions" });
57
- return schema;
58
- })()),
59
- counter: connection.models[counterName] ?? connection.model(counterName, new Schema({
60
- _id: {
8
+ const entries = connection.models[entriesName] ?? connection.model(entriesName, (() => {
9
+ const schema = new Schema({
10
+ seq: {
11
+ type: Number,
12
+ required: true
13
+ },
14
+ scope: {
61
15
  type: String,
62
16
  required: true
63
17
  },
64
- seq: {
18
+ docId: {
19
+ type: String,
20
+ required: true
21
+ },
22
+ op: {
23
+ type: String,
24
+ enum: ["upsert", "delete"],
25
+ required: true
26
+ },
27
+ version: {
65
28
  type: Number,
66
29
  required: true
67
- }
30
+ },
31
+ doc: { type: Schema.Types.Mixed },
32
+ tenantId: { type: String },
33
+ at: {
34
+ type: Date,
35
+ required: true
36
+ },
37
+ /** The staged row this entry was numbered from (deferred sequencing) — the sequencer's idempotency key. */
38
+ pendingId: { type: String }
68
39
  }, {
69
- collection: `${collection}_counter`,
70
- versionKey: false
71
- })),
40
+ collection,
41
+ versionKey: false,
42
+ minimize: false
43
+ });
44
+ schema.index({ seq: 1 }, {
45
+ unique: true,
46
+ name: "feed_order"
47
+ });
48
+ schema.index({ pendingId: 1 }, {
49
+ unique: true,
50
+ name: "sequenced_from",
51
+ partialFilterExpression: { pendingId: { $gte: "" } }
52
+ });
53
+ schema.index({
54
+ tenantId: 1,
55
+ scope: 1,
56
+ seq: 1
57
+ }, { name: "tenant_scope_feed" });
58
+ schema.index({
59
+ scope: 1,
60
+ docId: 1,
61
+ seq: 1
62
+ }, { name: "doc_versions" });
63
+ return schema;
64
+ })());
65
+ const counter = connection.models[counterName] ?? connection.model(counterName, new Schema({
66
+ _id: {
67
+ type: String,
68
+ required: true
69
+ },
70
+ seq: {
71
+ type: Number,
72
+ required: true
73
+ },
74
+ /** The deferred sequencer's lease. */
75
+ lockUntil: { type: Date },
76
+ lockBy: { type: String }
77
+ }, {
78
+ collection: `${collection}_counter`,
79
+ versionKey: false
80
+ }));
81
+ const pendingName = `${modelName}Pending`;
82
+ const pending = sequencing === "deferred" ? connection.models[pendingName] ?? connection.model(pendingName, new Schema({
83
+ scope: {
84
+ type: String,
85
+ required: true
86
+ },
87
+ docId: {
88
+ type: String,
89
+ required: true
90
+ },
91
+ op: {
92
+ type: String,
93
+ enum: ["upsert", "delete"],
94
+ required: true
95
+ },
96
+ version: {
97
+ type: Number,
98
+ required: true
99
+ },
100
+ doc: { type: Schema.Types.Mixed },
101
+ tenantId: { type: String },
102
+ at: {
103
+ type: Date,
104
+ required: true
105
+ }
106
+ }, {
107
+ collection: `${collection}_pending`,
108
+ versionKey: false,
109
+ minimize: false
110
+ })) : void 0;
111
+ return {
112
+ entries,
113
+ counter,
114
+ ...pending ? { pending } : {},
115
+ sequencing,
72
116
  feed: collection
73
117
  };
74
118
  }
119
+ /** A change as staged under deferred sequencing — the feed row without its `seq`. */
120
+ function stagedRow(entry, at) {
121
+ return {
122
+ scope: entry.scope,
123
+ docId: entry.docId,
124
+ op: entry.op,
125
+ version: entry.version,
126
+ ...entry.op === "upsert" && entry.doc !== void 0 ? { doc: entry.doc } : {},
127
+ ...entry.tenantId !== void 0 ? { tenantId: entry.tenantId } : {},
128
+ at
129
+ };
130
+ }
75
131
  /** Fixed-width, so the opaque cursor also sorts as text. */
76
132
  const toCursor = (seq) => String(seq).padStart(16, "0");
77
133
  const fromCursor = (cursor) => {
@@ -81,8 +137,96 @@ const fromCursor = (cursor) => {
81
137
  return seq;
82
138
  };
83
139
  const DEFAULT_LIMIT = 500;
140
+ /** How long one sequencer holds the lease — far above one batch, so a live holder never loses it mid-batch. */
141
+ const SEQUENCER_LEASE_MS = 3e4;
142
+ const SEQUENCER_BATCH = 500;
84
143
  function createChangeLogStore(models) {
85
- const { entries, counter, feed } = models;
144
+ const { entries, counter, feed, pending } = models;
145
+ const deferred = models.sequencing === "deferred" && pending !== void 0;
146
+ /**
147
+ * One pass under the lease: committed staged rows → the feed, in staging order, numbered above everything.
148
+ * Nothing staged costs ONE read. A crash mid-pass is converged by the next one: an entry already in the feed is
149
+ * recognised by its unique `pendingId`, and a stale counter is caught by the unique `seq` index.
150
+ */
151
+ const sequencePending = async () => {
152
+ if (!deferred || !pending) return 0;
153
+ if (!await pending.exists({}).exec()) return 0;
154
+ const token = new Types.ObjectId().toString();
155
+ const unlocked = {
156
+ _id: feed,
157
+ $or: [
158
+ { lockUntil: { $exists: false } },
159
+ { lockUntil: null },
160
+ { lockUntil: { $lt: /* @__PURE__ */ new Date() } }
161
+ ]
162
+ };
163
+ const takeLease = () => counter.findOneAndUpdate(unlocked, { $set: {
164
+ lockUntil: new Date(Date.now() + SEQUENCER_LEASE_MS),
165
+ lockBy: token
166
+ } }, {
167
+ returnDocument: "after",
168
+ lean: true
169
+ }).exec();
170
+ let lease = await takeLease();
171
+ if (!lease) {
172
+ if (await counter.exists({ _id: feed }).exec()) return null;
173
+ await counter.create([{
174
+ _id: feed,
175
+ seq: 0
176
+ }]).catch((err) => {
177
+ if (err.code !== 11e3) throw err;
178
+ });
179
+ lease = await takeLease();
180
+ if (!lease) return null;
181
+ }
182
+ let seq = lease.seq ?? 0;
183
+ let numbered = 0;
184
+ try {
185
+ for (;;) {
186
+ const rows = await pending.find({}).sort({ _id: 1 }).limit(SEQUENCER_BATCH).lean().exec();
187
+ if (rows.length === 0) break;
188
+ const build = (from, list) => list.map((r, i) => {
189
+ const { _id, ...rest } = r;
190
+ return {
191
+ ...rest,
192
+ seq: from + i + 1,
193
+ pendingId: String(_id)
194
+ };
195
+ });
196
+ try {
197
+ await entries.insertMany(build(seq, rows), { ordered: true });
198
+ seq += rows.length;
199
+ numbered += rows.length;
200
+ } catch (err) {
201
+ if (err.code !== 11e3) throw err;
202
+ const placed = new Set((await entries.find({ pendingId: { $in: rows.map((r) => String(r._id)) } }).select({ pendingId: 1 }).lean().exec()).map((e) => e.pendingId));
203
+ const fresh = rows.filter((r) => !placed.has(String(r._id)));
204
+ const top = await entries.findOne({}).sort({ seq: -1 }).select({ seq: 1 }).lean().exec();
205
+ seq = Math.max(seq, top?.seq ?? 0);
206
+ if (fresh.length > 0) {
207
+ await entries.insertMany(build(seq, fresh), { ordered: true });
208
+ seq += fresh.length;
209
+ numbered += fresh.length;
210
+ }
211
+ }
212
+ await counter.updateOne({
213
+ _id: feed,
214
+ lockBy: token
215
+ }, { $max: { seq } }).exec();
216
+ await pending.deleteMany({ _id: { $in: rows.map((r) => r._id) } }).exec();
217
+ if (rows.length < SEQUENCER_BATCH) break;
218
+ }
219
+ } finally {
220
+ await counter.updateOne({
221
+ _id: feed,
222
+ lockBy: token
223
+ }, { $set: {
224
+ lockUntil: null,
225
+ lockBy: null
226
+ } }).exec();
227
+ }
228
+ return numbered;
229
+ };
86
230
  const sessionOf = (options) => options?.session ? { session: options.session } : {};
87
231
  const filterOf = (after, options = {}) => {
88
232
  const { tenantId, scopes, sharedScopes } = options;
@@ -104,8 +248,37 @@ function createChangeLogStore(models) {
104
248
  cursor: toCursor(row.seq)
105
249
  });
106
250
  return {
251
+ sequencePending,
252
+ async stage(entry, options) {
253
+ if (!deferred || !pending) {
254
+ await this.append(entry, options);
255
+ return;
256
+ }
257
+ await pending.create([stagedRow(entry, /* @__PURE__ */ new Date())], sessionOf(options));
258
+ },
107
259
  async append(entry, options) {
108
260
  const session = sessionOf(options);
261
+ if (deferred && pending) {
262
+ const at = /* @__PURE__ */ new Date();
263
+ const [staged] = await pending.create([stagedRow(entry, at)], session);
264
+ if (options?.session) return {
265
+ ...entry,
266
+ at,
267
+ cursor: ""
268
+ };
269
+ const stagedId = String(staged?._id);
270
+ for (let attempt = 0; attempt < 50; attempt++) {
271
+ await sequencePending();
272
+ const at2 = await entries.findOne({ pendingId: stagedId }).select({ seq: 1 }).lean().exec();
273
+ if (typeof at2?.seq === "number") return {
274
+ ...entry,
275
+ at,
276
+ cursor: toCursor(at2.seq)
277
+ };
278
+ await new Promise((r) => setTimeout(r, 20));
279
+ }
280
+ throw new Error(`[mongokit:sync] staged entry ${stagedId} was not sequenced — a sequencer lease is stuck`);
281
+ }
109
282
  const seq = (await counter.findOneAndUpdate({ _id: feed }, { $inc: { seq: 1 } }, {
110
283
  upsert: true,
111
284
  returnDocument: "after",
@@ -130,6 +303,7 @@ function createChangeLogStore(models) {
130
303
  };
131
304
  },
132
305
  async since(cursor, options = {}) {
306
+ if (deferred) await sequencePending();
133
307
  const limit = options.limit ?? DEFAULT_LIMIT;
134
308
  const rows = await entries.find(filterOf(fromCursor(cursor), options)).sort({ seq: 1 }).limit(limit + 1).lean().exec();
135
309
  const page = rows.slice(0, limit).map(toEntry);
@@ -141,6 +315,7 @@ function createChangeLogStore(models) {
141
315
  };
142
316
  },
143
317
  async latestCursor(options = {}) {
318
+ if (deferred) await sequencePending();
144
319
  const row = await entries.findOne(filterOf(0, options)).sort({ seq: -1 }).select({ seq: 1 }).lean().exec();
145
320
  return row?.seq ? toCursor(row.seq) : "";
146
321
  },
@@ -1,5 +1,5 @@
1
- import { r as Repository } from "../Repository-pnLw1rG5.mjs";
2
- import { n as CreateRepositoryConfig } from "../create-repository-BUx9vfG9.mjs";
1
+ import { r as Repository } from "../Repository-DnL4xk5w.mjs";
2
+ import { n as CreateRepositoryConfig } from "../create-repository-CJtVdwig.mjs";
3
3
  import { Connection, Model, Schema } from "mongoose";
4
4
  import { MongoMemoryReplSet, MongoMemoryServer } from "mongodb-memory-server";
5
5
  //#region src/testkit/query-plan.d.ts
@@ -1,4 +1,4 @@
1
- import { t as createRepository } from "../create-repository-Bg0H5rte.mjs";
1
+ import { t as createRepository } from "../create-repository-UjwPWR1z.mjs";
2
2
  import { createConnection } from "mongoose";
3
3
  //#region src/testkit/query-plan.ts
4
4
  const PLANNABLE = /* @__PURE__ */ new Set([
@@ -1,4 +1,4 @@
1
- import { i as ALL_OPERATIONS, o as OP_REGISTRY, t as HOOK_PRIORITY } from "./Repository-DlhPbXlG.mjs";
1
+ import { a as ALL_OPERATIONS, s as OP_REGISTRY, t as HOOK_PRIORITY } from "./Repository-Dj6xqPxJ.mjs";
2
2
  import { n as warn } from "./logger-K7dPmu2U.mjs";
3
3
  import { n as declareCollectionScope } from "./join-scope-DSAznE8E.mjs";
4
4
  import { throwIfAborted, withRetry } from "@classytic/repo-core/repository";
@@ -1,5 +1,5 @@
1
- import { v as UserContext } from "./repository-DNHfjAZX.mjs";
2
- import { r as FieldPreset } from "./plugin-options-CRhIvSzx.mjs";
1
+ import { y as UserContext } from "./repository-MqAP409e.mjs";
2
+ import { r as FieldPreset } from "./plugin-options-Cirmb4Kp.mjs";
3
3
  import mongoose, { Schema } from "mongoose";
4
4
  import { CacheAdapter } from "@classytic/repo-core/cache";
5
5
  import { HttpError } from "@classytic/repo-core/errors";
@@ -1,4 +1,4 @@
1
- import { _ as getMongooseProjection, a as createMemoryCache, b as createError, c as IdType, d as isObjectIdInstance, f as isValidIdForType, g as getFieldsForUser, h as filterResponseData, i as buildCrudSchemasFromMongooseSchema, l as getSchemaIdType, m as createFieldPreset, n as CrudSchemasFramework, o as LoggerConfig, p as toObjectId, r as buildCrudSchemasFromModel, s as configureLogger, t as toPlain, v as CreateErrorOptions, y as ParseDuplicateKeyOptions } from "../to-plain-CjgEInWV.mjs";
1
+ import { _ as getMongooseProjection, a as createMemoryCache, b as createError, c as IdType, d as isObjectIdInstance, f as isValidIdForType, g as getFieldsForUser, h as filterResponseData, i as buildCrudSchemasFromMongooseSchema, l as getSchemaIdType, m as createFieldPreset, n as CrudSchemasFramework, o as LoggerConfig, p as toObjectId, r as buildCrudSchemasFromModel, s as configureLogger, t as toPlain, v as CreateErrorOptions, y as ParseDuplicateKeyOptions } from "../to-plain-o5tUY2nV.mjs";
2
2
  import { ClientSession, Model } from "mongoose";
3
3
  //#region src/utils/id-chunks.d.ts
4
4
  /**
@@ -1,6 +1,6 @@
1
1
  import { A as ObjectId, F as SortSpec, N as SelectSpec, j as PopulateSpec } from "./pagination-D6psses0.mjs";
2
- import { h as RepositoryInstance, l as Plugin, p as RepositoryContext } from "./repository-DNHfjAZX.mjs";
3
- import { c as ValidationChainOptions, i as Logger, l as ValidatorDefinition, o as SoftDeleteOptions, r as FieldPreset, t as CascadeOptions } from "./plugin-options-CRhIvSzx.mjs";
2
+ import { h as RepositoryInstance, l as Plugin, p as RepositoryContext } from "./repository-MqAP409e.mjs";
3
+ import { c as ValidationChainOptions, i as Logger, l as ValidatorDefinition, o as SoftDeleteOptions, r as FieldPreset, t as CascadeOptions } from "./plugin-options-Cirmb4Kp.mjs";
4
4
  import { BulkWriteResult } from "@classytic/repo-core/repository";
5
5
  import { OffsetPaginationResult } from "@classytic/repo-core/pagination";
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -719,6 +719,15 @@ interface ObservabilityOptions {
719
719
  }
720
720
  declare function observabilityPlugin(options: ObservabilityOptions): Plugin;
721
721
  //#endregion
722
+ //#region src/plugins/search-keys.plugin.d.ts
723
+ interface SearchKeysPluginOptions {
724
+ /** Source fields (dotted paths allowed; string, number, or arrays of them). */
725
+ fields: readonly string[];
726
+ /** Field that stores the keys. Default `'_searchKeys'`. */
727
+ keysField?: string;
728
+ }
729
+ declare function searchKeysPlugin(options: SearchKeysPluginOptions): Plugin;
730
+ //#endregion
722
731
  //#region src/plugins/soft-delete.plugin.d.ts
723
732
  /**
724
733
  * Soft delete plugin
@@ -964,4 +973,4 @@ declare function immutableField(field: string): ValidatorDefinition;
964
973
  */
965
974
  declare function uniqueField(field: string, errorMessage?: string): ValidatorDefinition;
966
975
  //#endregion
967
- export { IdempotencyStoreMethods as A, batchOperationsPlugin as B, LeaseMethods as C, ImmutableStatesPluginOptions as D, ImmutableClaimView as E, elasticSearchPlugin as F, aggregateHelpersPlugin as G, AppendOnlyPluginOptions as H, cascadePlugin as I, BatchOperationsMethods as L, idempotencyStorePlugin as M, fieldFilterPlugin as N, immutableStatesPlugin as O, ElasticSearchOptions as P, BulkWriteOptions as R, methodRegistryPlugin as S, leasePlugin as T, appendOnlyPlugin as U, auditLogPlugin as V, AggregateHelpersMethods as W, OperationMetric as _, requireField as a, mongoOperationsPlugin as b, timestampPlugin as c, createTenantContext as d, SubdocumentMethods as f, ObservabilityOptions as g, softDeletePlugin as h, immutableField as i, IdempotencyStorePluginOptions as j, IdempotencyClaim as k, TenantContext as l, SoftDeleteMethods as m, autoInject as n, uniqueField as o, subdocumentPlugin as p, blockIf as r, validationChainPlugin as s, OperationType as t, TenantStore as u, observabilityPlugin as v, LeasePluginOptions as w, MethodRegistryRepository as x, MongoOperationsMethods as y, BulkWriteResult as z };
976
+ export { immutableStatesPlugin as A, BulkWriteOptions as B, MethodRegistryRepository as C, leasePlugin as D, LeasePluginOptions as E, fieldFilterPlugin as F, appendOnlyPlugin as G, batchOperationsPlugin as H, ElasticSearchOptions as I, AggregateHelpersMethods as K, elasticSearchPlugin as L, IdempotencyStoreMethods as M, IdempotencyStorePluginOptions as N, ImmutableClaimView as O, idempotencyStorePlugin as P, cascadePlugin as R, mongoOperationsPlugin as S, LeaseMethods as T, auditLogPlugin as U, BulkWriteResult as V, AppendOnlyPluginOptions as W, searchKeysPlugin as _, requireField as a, observabilityPlugin as b, timestampPlugin as c, createTenantContext as d, SubdocumentMethods as f, SearchKeysPluginOptions as g, softDeletePlugin as h, immutableField as i, IdempotencyClaim as j, ImmutableStatesPluginOptions as k, TenantContext as l, SoftDeleteMethods as m, autoInject as n, uniqueField as o, subdocumentPlugin as p, aggregateHelpersPlugin as q, blockIf as r, validationChainPlugin as s, OperationType as t, TenantStore as u, ObservabilityOptions as v, methodRegistryPlugin as w, MongoOperationsMethods as x, OperationMetric as y, BatchOperationsMethods as z };
@@ -1,9 +1,9 @@
1
1
  import { n as collectIds, r as idChunks, t as DEFAULT_ID_CHUNK } from "./id-chunks-BmAwsqNk.mjs";
2
- import { i as ALL_OPERATIONS, o as OP_REGISTRY } from "./Repository-DlhPbXlG.mjs";
2
+ import { a as ALL_OPERATIONS, s as OP_REGISTRY } from "./Repository-Dj6xqPxJ.mjs";
3
3
  import { w as upsert } from "./update-WgeAUJ_I.mjs";
4
4
  import { n as warn } from "./logger-K7dPmu2U.mjs";
5
5
  import { t as createError } from "./error-BuByZwKU.mjs";
6
- import { c as tenantContextKeysOf, o as forwardScope } from "./timestamp.plugin-DvZlnKEc.mjs";
6
+ import { c as tenantContextKeysOf, o as forwardScope } from "./timestamp.plugin-DpOWavVI.mjs";
7
7
  import { r as getFieldsForUser } from "./field-selection-FSQFLpsU.mjs";
8
8
  import mongoose from "mongoose";
9
9
  import { HOOK_PRIORITY } from "@classytic/repo-core/hooks";
@@ -1223,6 +1223,7 @@ const MONGOKIT_PLUGIN_NAMES = {
1223
1223
  mongoOperations: "mongo-operations",
1224
1224
  multiTenant: "multi-tenant",
1225
1225
  observability: "observability",
1226
+ searchKeys: "search-keys",
1226
1227
  softDelete: "softDelete",
1227
1228
  subdocument: "subdocument",
1228
1229
  timestamp: "timestamp",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/mongokit",
3
- "version": "3.41.0",
3
+ "version": "3.43.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,
@@ -95,6 +95,10 @@
95
95
  "./kernel-conformance": {
96
96
  "types": "./dist/kernel-conformance/index.d.mts",
97
97
  "default": "./dist/kernel-conformance/index.mjs"
98
+ },
99
+ "./search": {
100
+ "types": "./dist/search/index.d.mts",
101
+ "default": "./dist/search/index.mjs"
98
102
  }
99
103
  },
100
104
  "files": [
@@ -199,7 +203,7 @@
199
203
  "@better-auth/mongo-adapter": "^1.6.29",
200
204
  "@biomejs/biome": "^2.5.8",
201
205
  "@classytic/dev-tools": "^0.2.0",
202
- "@classytic/repo-core": "^0.31.0",
206
+ "@classytic/repo-core": "^0.31.1",
203
207
  "@types/node": "^26.2.0",
204
208
  "@vitest/coverage-v8": "^4.1.10",
205
209
  "ajv": "^8.20.0",