@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
package/dist/index.mjs CHANGED
@@ -1,22 +1,24 @@
1
1
  import { n as idVariants } from "./id-resolution-BqDrk0S2.mjs";
2
- import { a as MUTATING_OPERATIONS, c as operationsByPolicyKey, i as ALL_OPERATIONS, l as createMongoPurgePort, n as Repository, o as OP_REGISTRY, r as AggregationBuilder, s as READ_OPERATIONS, t as HOOK_PRIORITY } from "./Repository-DlhPbXlG.mjs";
2
+ import { a as ALL_OPERATIONS, c as READ_OPERATIONS, i as searchKeysPlugin, l as operationsByPolicyKey, n as Repository, o as MUTATING_OPERATIONS, r as AggregationBuilder, s as OP_REGISTRY, t as HOOK_PRIORITY, u as createMongoPurgePort } from "./Repository-Dj6xqPxJ.mjs";
3
3
  import { C as isCreateManyPartialError, g as LookupBuilder, v as compileFilterToMongo } from "./update-WgeAUJ_I.mjs";
4
4
  import { n as warn, t as configureLogger } from "./logger-K7dPmu2U.mjs";
5
5
  import { n as isDuplicateKeyError, r as parseDuplicateKeyError, t as createError } from "./error-BuByZwKU.mjs";
6
6
  import { a as assertQueryDefaultsConfigured, c as getQueryDefaults, l as resetQueryDefaults, o as configureAggregateDefaults, s as configureQueryDefaults, t as QUERY_DEFAULTS_ERROR_CODES, u as resolveQueryOptions } from "./query-defaults-BxUcSOoF.mjs";
7
7
  import { t as actions_exports } from "./actions/index.mjs";
8
8
  import { a as supportsTransactions, c as MONGOKIT_CAPABILITIES, d as probeMongoCapabilities, f as resetCapabilityProbeCache, h as transactionResolutionOf, i as runTransaction, l as capabilitiesForSupport, m as resolveTransactionSupport, n as batchTransaction, o as withTransaction, p as resolveMongoCapabilities, r as isTransactionUnsupported, t as TRANSACTION_ERROR_CODES, u as classifyHelloReply } from "./transaction-DmiB4k2P.mjs";
9
- import { _ as AuditTrailQuery, a as declareTenantContextKey, b as auditLogPlugin, c as tenantContextKeysOf, d as ensureSequenceAtLeast, f as getNextSequence, g as cachePlugin, h as sequentialId, i as multiTenantPlugin, l as customIdPlugin, m as readSequence, n as softDeletePlugin, o as forwardScope, p as prefixedId, r as adminBypass, s as isScopeKey, t as timestampPlugin, u as dateSequentialId, v as auditTrailPlugin, y as ensureAuditTrailReady } from "./timestamp.plugin-DvZlnKEc.mjs";
9
+ import { _ as AuditTrailQuery, a as declareTenantContextKey, b as auditLogPlugin, c as tenantContextKeysOf, d as ensureSequenceAtLeast, f as getNextSequence, g as cachePlugin, h as sequentialId, i as multiTenantPlugin, l as customIdPlugin, m as readSequence, n as softDeletePlugin, o as forwardScope, p as prefixedId, r as adminBypass, s as isScopeKey, t as timestampPlugin, u as dateSequentialId, v as auditTrailPlugin, y as ensureAuditTrailReady } from "./timestamp.plugin-DpOWavVI.mjs";
10
10
  import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "./method-registry.plugin-C63pGn1d.mjs";
11
11
  import { c as PAGINATION_ERROR_CODES, h as resetPaginationDefaults, m as getPaginationDefaults, p as configurePaginationDefaults, t as PaginationEngine } from "./PaginationEngine-DZnkbSrr.mjs";
12
+ import { _ as tokenizeSearchText, d as buildSearchKeys, f as prefixSearchFilter, g as normalizeSearchText, h as findSearchMatches, l as DEFAULT_SEARCH_KEYS_FIELD, m as searchKeysPath, n as SearchKeysWriteError, p as searchKeysIndex, t as SEARCH_KEYS_ERROR_CODES, u as MAX_PREFIX_SEARCH_TERMS } from "./derive-DRBiegRH.mjs";
12
13
  import { GEO_OPERATORS, parseGeoFilter } from "./query/primitives/geo.mjs";
13
14
  import { n as declareCollectionScope, t as JOIN_ERROR_CODES } from "./join-scope-DSAznE8E.mjs";
14
- import { t as createRepository } from "./create-repository-Bg0H5rte.mjs";
15
+ import { t as createRepository } from "./create-repository-UjwPWR1z.mjs";
15
16
  import { n as defineModel, r as isModelRegistered, t as ModelCollisionError } from "./model-registry-ClKtWiHa.mjs";
16
- import { _ as cascadePlugin, a as uniqueField, c as subdocumentPlugin, d as mongoOperationsPlugin, f as leasePlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, l as observabilityPlugin, m as idempotencyStorePlugin, n as blockIf, o as validationChainPlugin, p as immutableStatesPlugin, r as immutableField, s as createTenantContext, t as autoInject, u as MONGOKIT_PLUGIN_NAMES, v as appendOnlyPlugin, y as aggregateHelpersPlugin } from "./validation-chain.plugin-D1WsGCYr.mjs";
17
+ import { _ as cascadePlugin, a as uniqueField, c as subdocumentPlugin, d as mongoOperationsPlugin, f as leasePlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, l as observabilityPlugin, m as idempotencyStorePlugin, n as blockIf, o as validationChainPlugin, p as immutableStatesPlugin, r as immutableField, s as createTenantContext, t as autoInject, u as MONGOKIT_PLUGIN_NAMES, v as appendOnlyPlugin, y as aggregateHelpersPlugin } from "./validation-chain.plugin-DRApTmkN.mjs";
17
18
  import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-FSQFLpsU.mjs";
18
19
  import { buildFieldTypeMap, coerceHeuristic } from "./query/primitives/coercion.mjs";
19
20
  import { extractSchemaIndexes } from "./query/primitives/indexes.mjs";
21
+ import "./search/index.mjs";
20
22
  import { n as createMemoryCache, t as toPlain } from "./to-plain-vksRrLtc.mjs";
21
23
  import { n as buildCrudSchemasFromMongooseSchema, t as buildCrudSchemasFromModel } from "./mongooseToJsonSchema-C3k4ide0.mjs";
22
24
  import { runChunkedPurge } from "@classytic/repo-core/repository";
@@ -1183,6 +1185,52 @@ function attachCondition(filters, condition) {
1183
1185
  };
1184
1186
  }
1185
1187
  //#endregion
1188
+ //#region src/search/rebuild.ts
1189
+ const sameKeys = (a, b) => Array.isArray(a) && a.length === b.length && a.every((v, i) => v === b[i]);
1190
+ async function rebuildSearchKeys(source, options = {}) {
1191
+ const repo = typeof source === "function" ? void 0 : source;
1192
+ const Model = repo ? repo.Model : source;
1193
+ const fields = options.fields ?? repo?.searchFields ?? [];
1194
+ const keysField = options.keysField ?? repo?.searchKeysField ?? "_searchKeys";
1195
+ if (fields.length === 0) throw new TypeError("[mongokit] rebuildSearchKeys: no source fields (pass `fields` or a prefix-mode repository)");
1196
+ const batchSize = Math.max(1, options.batchSize ?? 500);
1197
+ const session = options.session ?? null;
1198
+ const base = {
1199
+ ...options.filter ?? {},
1200
+ ...options.onlyMissing ? { [keysField]: { $exists: false } } : {}
1201
+ };
1202
+ const projection = Object.fromEntries([...fields, keysField].map((f) => [f, 1]));
1203
+ let scanned = 0;
1204
+ let updated = 0;
1205
+ let after;
1206
+ for (;;) {
1207
+ const page = await Model.find(after === void 0 ? base : { $and: [base, { _id: { $gt: after } }] }, projection).sort({ _id: 1 }).limit(batchSize).session(session).lean();
1208
+ if (page.length === 0) break;
1209
+ scanned += page.length;
1210
+ after = page[page.length - 1]?._id;
1211
+ const writes = page.flatMap((doc) => {
1212
+ const keys = buildSearchKeys(doc, fields);
1213
+ if (sameKeys(doc[keysField], keys)) return [];
1214
+ return [{ updateOne: {
1215
+ filter: { _id: doc._id },
1216
+ update: { $set: { [keysField]: keys } }
1217
+ } }];
1218
+ });
1219
+ if (writes.length > 0) {
1220
+ const res = await Model.bulkWrite(writes, {
1221
+ ordered: false,
1222
+ session: session ?? void 0
1223
+ });
1224
+ updated += res.modifiedCount;
1225
+ }
1226
+ if (page.length < batchSize) break;
1227
+ }
1228
+ return {
1229
+ scanned,
1230
+ updated
1231
+ };
1232
+ }
1233
+ //#endregion
1186
1234
  //#region src/utils/repo-options.ts
1187
1235
  /**
1188
1236
  * Forward request-scoped context fields into a mongokit options bag.
@@ -1437,4 +1485,4 @@ function systemContext(extra) {
1437
1485
  */
1438
1486
  var src_default = Repository;
1439
1487
  //#endregion
1440
- export { ALL_OPERATIONS, AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, JOIN_ERROR_CODES, LookupBuilder, MONGOKIT_CAPABILITIES, MONGOKIT_PLUGIN_NAMES, MUTATING_OPERATIONS, ModelCollisionError, OP_REGISTRY, PAGINATION_ERROR_CODES, PaginationEngine, QUERY_DEFAULTS_ERROR_CODES, QueryParser, READ_OPERATIONS, Repository, TRANSACTION_ERROR_CODES, actions_exports as actions, adminBypass, aggregateHelpersPlugin, appendOnlyPlugin, assertQueryDefaultsConfigured, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, capabilitiesForSupport, cascadePlugin, cascadePurgeReferences, classifyHelloReply, compileFilterToMongo, configureAggregateDefaults, configureLogger, configurePaginationDefaults, configureQueryDefaults, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, declareCollectionScope, declareTenantContextKey, src_default as default, defineModel, defineModels, elasticSearchPlugin, ensureAuditTrailReady, ensureSequenceAtLeast, fieldFilterPlugin, filterResponseData, forwardScope, getFieldsForUser, getMongooseProjection, getNextSequence, getPaginationDefaults, getQueryDefaults, idVariants, idempotencyStorePlugin, immutableField, immutableStatesPlugin, isCreateManyPartialError, isDuplicateKeyError, isModelRegistered, isTransactionUnsupported, keysetSource, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, probeMongoCapabilities, readSequence, repoOptionsFromCtx, requireField, resetCapabilityProbeCache, resetPaginationDefaults, resetQueryDefaults, resolveMongoCapabilities, resolveQueryOptions, resolveTransactionSupport, runTransaction, sequentialId, softDeletePlugin, subdocumentPlugin, supportsTransactions, systemContext, tenantContextKeysOf, timestampPlugin, toPlain, transactionResolutionOf, uniqueField, validationChainPlugin, withTransaction };
1488
+ export { ALL_OPERATIONS, AggregationBuilder, AuditTrailQuery, DEFAULT_SEARCH_KEYS_FIELD, HOOK_PRIORITY, JOIN_ERROR_CODES, LookupBuilder, MAX_PREFIX_SEARCH_TERMS, MONGOKIT_CAPABILITIES, MONGOKIT_PLUGIN_NAMES, MUTATING_OPERATIONS, ModelCollisionError, OP_REGISTRY, PAGINATION_ERROR_CODES, PaginationEngine, QUERY_DEFAULTS_ERROR_CODES, QueryParser, READ_OPERATIONS, Repository, SEARCH_KEYS_ERROR_CODES, SearchKeysWriteError, TRANSACTION_ERROR_CODES, actions_exports as actions, adminBypass, aggregateHelpersPlugin, appendOnlyPlugin, assertQueryDefaultsConfigured, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, buildSearchKeys, cachePlugin, capabilitiesForSupport, cascadePlugin, cascadePurgeReferences, classifyHelloReply, compileFilterToMongo, configureAggregateDefaults, configureLogger, configurePaginationDefaults, configureQueryDefaults, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, declareCollectionScope, declareTenantContextKey, src_default as default, defineModel, defineModels, elasticSearchPlugin, ensureAuditTrailReady, ensureSequenceAtLeast, fieldFilterPlugin, filterResponseData, findSearchMatches, forwardScope, getFieldsForUser, getMongooseProjection, getNextSequence, getPaginationDefaults, getQueryDefaults, idVariants, idempotencyStorePlugin, immutableField, immutableStatesPlugin, isCreateManyPartialError, isDuplicateKeyError, isModelRegistered, isTransactionUnsupported, keysetSource, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, normalizeSearchText, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixSearchFilter, prefixedId, probeMongoCapabilities, readSequence, rebuildSearchKeys, repoOptionsFromCtx, requireField, resetCapabilityProbeCache, resetPaginationDefaults, resetQueryDefaults, resolveMongoCapabilities, resolveQueryOptions, resolveTransactionSupport, runTransaction, searchKeysIndex, searchKeysPath, searchKeysPlugin, sequentialId, softDeletePlugin, subdocumentPlugin, supportsTransactions, systemContext, tenantContextKeysOf, timestampPlugin, toPlain, tokenizeSearchText, transactionResolutionOf, uniqueField, validationChainPlugin, withTransaction };
@@ -1,5 +1,5 @@
1
1
  import { A as ObjectId } from "./pagination-D6psses0.mjs";
2
- import { l as Plugin, p as RepositoryContext } from "./repository-DNHfjAZX.mjs";
2
+ import { l as Plugin, p as RepositoryContext } from "./repository-MqAP409e.mjs";
3
3
  import mongoose, { Connection } from "mongoose";
4
4
  import { CacheAdapter as CacheAdapter$1, CacheOptions, RepositoryCacheHandle as RepositoryCacheHandle$1, RepositoryCachePluginOptions, cachePlugin } from "@classytic/repo-core/cache";
5
5
  import { TenantConfig } from "@classytic/repo-core/tenant";
@@ -1,4 +1,4 @@
1
- import { r as Repository } from "../Repository-pnLw1rG5.mjs";
1
+ import { r as Repository } from "../Repository-DnL4xk5w.mjs";
2
2
  import { Connection, Model } from "mongoose";
3
3
  //#region src/outbox/index.d.ts
4
4
  /** Delivery states. `dead_letter` is terminal and operator-visible. */
@@ -1,4 +1,4 @@
1
- import { n as Repository } from "../Repository-DlhPbXlG.mjs";
1
+ import { n as Repository } from "../Repository-Dj6xqPxJ.mjs";
2
2
  import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "../method-registry.plugin-C63pGn1d.mjs";
3
3
  import { Schema } from "mongoose";
4
4
  //#region src/outbox/index.ts
@@ -1,5 +1,5 @@
1
1
  import { A as ObjectId, F as SortSpec, N as SelectSpec, j as PopulateSpec } from "./pagination-D6psses0.mjs";
2
- import { h as RepositoryInstance, p as RepositoryContext } from "./repository-DNHfjAZX.mjs";
2
+ import { h as RepositoryInstance, p as RepositoryContext } from "./repository-MqAP409e.mjs";
3
3
  import { OffsetPaginationResult } from "@classytic/repo-core/pagination";
4
4
  //#region src/types/plugin-options.d.ts
5
5
  /** Field preset configuration (field-filter plugin) */
@@ -1,6 +1,6 @@
1
- import { S as RepositoryPluginName, b as MONGOKIT_PLUGIN_NAMES, l as Plugin, x as MongoKitPluginName } from "../repository-DNHfjAZX.mjs";
2
- import { A as EnsureAuditTrailReadyOptions, C as AuditQueryOptions, D as AuditTrailOptions, E as AuditTrailMode, M as ensureAuditTrailReady, O as AuditTrailQuery, S as AuditOperation, T as AuditTrailMethods, _ as CacheOptions, a as DateSequentialIdOptions, b as cachePlugin, c as SequentialIdOptions, f as getNextSequence, g as CacheAdapter, h as sequentialId, i as CustomIdOptions, j as auditTrailPlugin, k as AuditTrailQueryOptions, l as customIdPlugin, o as IdGenerator, p as prefixedId, r as multiTenantPlugin, s as PrefixedIdOptions, t as MultiTenantOptions, u as dateSequentialId, v as RepositoryCacheHandle, w as AuditQueryResult, x as AuditEntry, y as RepositoryCachePluginOptions } from "../multi-tenant.plugin-a3UhQARj.mjs";
3
- import { A as IdempotencyStoreMethods, B as batchOperationsPlugin, C as LeaseMethods, D as ImmutableStatesPluginOptions, E as ImmutableClaimView, F as elasticSearchPlugin, G as aggregateHelpersPlugin, H as AppendOnlyPluginOptions, I as cascadePlugin, L as BatchOperationsMethods, M as idempotencyStorePlugin, N as fieldFilterPlugin, O as immutableStatesPlugin, P as ElasticSearchOptions, S as methodRegistryPlugin, T as leasePlugin, U as appendOnlyPlugin, V as auditLogPlugin, W as AggregateHelpersMethods, _ as OperationMetric, a as requireField, b as mongoOperationsPlugin, c as timestampPlugin, d as createTenantContext, f as SubdocumentMethods, g as ObservabilityOptions, h as softDeletePlugin, i as immutableField, j as IdempotencyStorePluginOptions, k as IdempotencyClaim, l as TenantContext, m as SoftDeleteMethods, n as autoInject, o as uniqueField, p as subdocumentPlugin, r as blockIf, s as validationChainPlugin, t as OperationType, u as TenantStore, v as observabilityPlugin, w as LeasePluginOptions, x as MethodRegistryRepository, y as MongoOperationsMethods } from "../validation-chain.plugin-CTcNJCmV.mjs";
1
+ import { C as RepositoryPluginName, S as MongoKitPluginName, l as Plugin, x as MONGOKIT_PLUGIN_NAMES } from "../repository-MqAP409e.mjs";
2
+ import { A as EnsureAuditTrailReadyOptions, C as AuditQueryOptions, D as AuditTrailOptions, E as AuditTrailMode, M as ensureAuditTrailReady, O as AuditTrailQuery, S as AuditOperation, T as AuditTrailMethods, _ as CacheOptions, a as DateSequentialIdOptions, b as cachePlugin, c as SequentialIdOptions, f as getNextSequence, g as CacheAdapter, h as sequentialId, i as CustomIdOptions, j as auditTrailPlugin, k as AuditTrailQueryOptions, l as customIdPlugin, o as IdGenerator, p as prefixedId, r as multiTenantPlugin, s as PrefixedIdOptions, t as MultiTenantOptions, u as dateSequentialId, v as RepositoryCacheHandle, w as AuditQueryResult, x as AuditEntry, y as RepositoryCachePluginOptions } from "../multi-tenant.plugin-B_q9NUHc.mjs";
3
+ import { A as immutableStatesPlugin, C as MethodRegistryRepository, D as leasePlugin, E as LeasePluginOptions, F as fieldFilterPlugin, G as appendOnlyPlugin, H as batchOperationsPlugin, I as ElasticSearchOptions, K as AggregateHelpersMethods, L as elasticSearchPlugin, M as IdempotencyStoreMethods, N as IdempotencyStorePluginOptions, O as ImmutableClaimView, P as idempotencyStorePlugin, R as cascadePlugin, S as mongoOperationsPlugin, T as LeaseMethods, U as auditLogPlugin, W as AppendOnlyPluginOptions, _ as searchKeysPlugin, a as requireField, b as observabilityPlugin, c as timestampPlugin, d as createTenantContext, f as SubdocumentMethods, g as SearchKeysPluginOptions, h as softDeletePlugin, i as immutableField, j as IdempotencyClaim, k as ImmutableStatesPluginOptions, l as TenantContext, m as SoftDeleteMethods, n as autoInject, o as uniqueField, p as subdocumentPlugin, q as aggregateHelpersPlugin, r as blockIf, s as validationChainPlugin, t as OperationType, u as TenantStore, v as ObservabilityOptions, w as methodRegistryPlugin, x as MongoOperationsMethods, y as OperationMetric, z as BatchOperationsMethods } from "../validation-chain.plugin-BAeC5eVT.mjs";
4
4
  import { ChangeLogStore } from "@classytic/repo-core/sync";
5
5
  //#region src/plugins/change-log.plugin.d.ts
6
6
  interface ChangeLogPluginOptions {
@@ -20,4 +20,4 @@ interface ChangeLogPluginOptions {
20
20
  }
21
21
  declare function changeLogPlugin(options: ChangeLogPluginOptions): Plugin;
22
22
  //#endregion
23
- export { type AggregateHelpersMethods, type AppendOnlyPluginOptions, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailMode, type AuditTrailOptions, AuditTrailQuery, type AuditTrailQueryOptions, type BatchOperationsMethods, type CacheAdapter, type CacheOptions, type ChangeLogPluginOptions, type CustomIdOptions, type DateSequentialIdOptions, type ElasticSearchOptions, type EnsureAuditTrailReadyOptions, type IdGenerator, type IdempotencyClaim, type IdempotencyStoreMethods, type IdempotencyStorePluginOptions, type ImmutableClaimView, type ImmutableStatesPluginOptions, type LeaseMethods, type LeasePluginOptions, MONGOKIT_PLUGIN_NAMES, type MethodRegistryRepository, type MongoKitPluginName, type MongoOperationsMethods, type MultiTenantOptions, type ObservabilityOptions, type OperationMetric, type OperationType, type PrefixedIdOptions, type RepositoryCacheHandle, type RepositoryCachePluginOptions, type RepositoryPluginName, type SequentialIdOptions, type SoftDeleteMethods, type SubdocumentMethods, type TenantContext, type TenantStore, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, changeLogPlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, getNextSequence, idempotencyStorePlugin, immutableField, immutableStatesPlugin, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
23
+ export { type AggregateHelpersMethods, type AppendOnlyPluginOptions, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailMode, type AuditTrailOptions, AuditTrailQuery, type AuditTrailQueryOptions, type BatchOperationsMethods, type CacheAdapter, type CacheOptions, type ChangeLogPluginOptions, type CustomIdOptions, type DateSequentialIdOptions, type ElasticSearchOptions, type EnsureAuditTrailReadyOptions, type IdGenerator, type IdempotencyClaim, type IdempotencyStoreMethods, type IdempotencyStorePluginOptions, type ImmutableClaimView, type ImmutableStatesPluginOptions, type LeaseMethods, type LeasePluginOptions, MONGOKIT_PLUGIN_NAMES, type MethodRegistryRepository, type MongoKitPluginName, type MongoOperationsMethods, type MultiTenantOptions, type ObservabilityOptions, type OperationMetric, type OperationType, type PrefixedIdOptions, type RepositoryCacheHandle, type RepositoryCachePluginOptions, type RepositoryPluginName, type SearchKeysPluginOptions, type SequentialIdOptions, type SoftDeleteMethods, type SubdocumentMethods, type TenantContext, type TenantStore, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, changeLogPlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, getNextSequence, idempotencyStorePlugin, immutableField, immutableStatesPlugin, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, searchKeysPlugin, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
@@ -1,12 +1,15 @@
1
- import { _ as AuditTrailQuery, b as auditLogPlugin, f as getNextSequence, g as cachePlugin, h as sequentialId, i as multiTenantPlugin, l as customIdPlugin, n as softDeletePlugin, p as prefixedId, t as timestampPlugin, u as dateSequentialId, v as auditTrailPlugin, y as ensureAuditTrailReady } from "../timestamp.plugin-DvZlnKEc.mjs";
1
+ import { i as searchKeysPlugin } from "../Repository-Dj6xqPxJ.mjs";
2
+ import { _ as AuditTrailQuery, b as auditLogPlugin, f as getNextSequence, g as cachePlugin, h as sequentialId, i as multiTenantPlugin, l as customIdPlugin, n as softDeletePlugin, p as prefixedId, t as timestampPlugin, u as dateSequentialId, v as auditTrailPlugin, y as ensureAuditTrailReady } from "../timestamp.plugin-DpOWavVI.mjs";
2
3
  import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "../method-registry.plugin-C63pGn1d.mjs";
3
- import { _ as cascadePlugin, a as uniqueField, c as subdocumentPlugin, d as mongoOperationsPlugin, f as leasePlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, l as observabilityPlugin, m as idempotencyStorePlugin, n as blockIf, o as validationChainPlugin, p as immutableStatesPlugin, r as immutableField, s as createTenantContext, t as autoInject, u as MONGOKIT_PLUGIN_NAMES, v as appendOnlyPlugin, y as aggregateHelpersPlugin } from "../validation-chain.plugin-D1WsGCYr.mjs";
4
+ import { _ as cascadePlugin, a as uniqueField, c as subdocumentPlugin, d as mongoOperationsPlugin, f as leasePlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, l as observabilityPlugin, m as idempotencyStorePlugin, n as blockIf, o as validationChainPlugin, p as immutableStatesPlugin, r as immutableField, s as createTenantContext, t as autoInject, u as MONGOKIT_PLUGIN_NAMES, v as appendOnlyPlugin, y as aggregateHelpersPlugin } from "../validation-chain.plugin-DRApTmkN.mjs";
4
5
  //#region src/plugins/change-log.plugin.ts
5
6
  const PLUGIN_NAME = "changeLog";
6
7
  /** Marks a repository already captured: the store and scope it writes to. */
7
8
  const CAPTURED = Symbol.for("mongokit.changeLog.capture");
8
9
  /** Where `before:updateMany` / `before:deleteMany` leave the ids they matched for the after hook. */
9
10
  const MATCHED_IDS = Symbol("changeLog.matchedIds");
11
+ /** Ids per `$in` when a many-document write is read back. */
12
+ const READ_BACK_CHUNK = 5e3;
10
13
  function isSkipped(context) {
11
14
  const list = context.skipPlugins;
12
15
  return Array.isArray(list) && list.includes(PLUGIN_NAME);
@@ -29,29 +32,34 @@ function changeLogPlugin(options) {
29
32
  return raw === void 0 || raw === null ? void 0 : String(raw);
30
33
  };
31
34
  const appendOptions = (context) => context.session !== void 0 ? { session: context.session } : void 0;
35
+ /** `stage` when the store offers it (no cursor wait under deferred sequencing); the contract's `append` otherwise. */
36
+ const recordChange = (entry, context) => {
37
+ const staging = store.stage;
38
+ return staging ? staging.call(store, entry, appendOptions(context)) : store.append(entry, appendOptions(context));
39
+ };
32
40
  const upsert = async (doc, context) => {
33
41
  const record = toRecord(doc);
34
42
  if (!record || record._id === void 0) return;
35
43
  const tenantId = tenantOf(record, context);
36
- await store.append({
44
+ await recordChange({
37
45
  scope,
38
46
  docId: String(record._id),
39
47
  op: "upsert",
40
48
  version: versionOf(record),
41
49
  doc: project ? project(record) : record,
42
50
  ...tenantId !== void 0 ? { tenantId } : {}
43
- }, appendOptions(context));
51
+ }, context);
44
52
  };
45
53
  const tombstone = async (docId, context) => {
46
54
  if (docId === void 0 || docId === null) return;
47
55
  const tenantId = tenantOf(null, context);
48
- await store.append({
56
+ await recordChange({
49
57
  scope,
50
58
  docId: String(docId),
51
59
  op: "delete",
52
60
  version: 0,
53
61
  ...tenantId !== void 0 ? { tenantId } : {}
54
- }, appendOptions(context));
62
+ }, context);
55
63
  };
56
64
  return {
57
65
  name: PLUGIN_NAME,
@@ -72,6 +80,22 @@ function changeLogPlugin(options) {
72
80
  if (context.session !== void 0) query.session(context.session);
73
81
  return query.exec();
74
82
  };
83
+ /** A write's result is the feed row only when it is the whole post-image, as a plain object. */
84
+ const isWholePostImage = (result, context) => {
85
+ if (!result || typeof result !== "object") return false;
86
+ if (typeof result.toObject === "function") return false;
87
+ const c = context;
88
+ return !c.select && !c.projection && !c.populate && c.returnDocument !== "before";
89
+ };
90
+ /** `upsert` from the result the write returned; a narrowed result is re-read by id (one read, that verb only). */
91
+ const captureResult = async (result, context) => {
92
+ const id = toRecord(result)?._id;
93
+ if (id === void 0) return;
94
+ if (isWholePostImage(result, context)) return upsert(result, context);
95
+ const doc = await reread(id, context);
96
+ if (doc) await upsert(doc, context);
97
+ else await tombstone(id, context);
98
+ };
75
99
  const matchingIds = async (context, filter) => {
76
100
  const query = repo.Model.find(filter ?? {}).select({ _id: 1 }).lean();
77
101
  if (context.session !== void 0) query.session(context.session);
@@ -85,29 +109,37 @@ function changeLogPlugin(options) {
85
109
  for (const doc of result) await upsert(doc, context);
86
110
  });
87
111
  repo.on("after:update", async ({ context, result }) => {
88
- if (!isSkipped(context) && result) await upsert(result, context);
112
+ if (isSkipped(context) || !result) return;
113
+ const c = context;
114
+ if (c.select || c.projection || c.populate) await captureResult(result, context);
115
+ else await upsert(result, context);
116
+ });
117
+ repo.on("after:getOrCreate", async ({ context, result }) => {
118
+ const doc = result?.doc;
119
+ if (!isSkipped(context) && doc) await upsert(doc, context);
89
120
  });
90
121
  for (const op of [
91
122
  "claim",
92
123
  "claimVersion",
93
124
  "findOneAndUpdate",
94
- "getOrCreate",
95
125
  "restore"
96
126
  ]) repo.on(`after:${op}`, async ({ context, result }) => {
97
- if (isSkipped(context)) return;
98
- const id = toRecord(result)?._id;
99
- if (id === void 0) return;
100
- const doc = await reread(id, context);
101
- if (doc) await upsert(doc, context);
102
- else await tombstone(id, context);
127
+ if (!isSkipped(context)) await captureResult(result, context);
103
128
  });
104
129
  for (const op of ["updateMany", "deleteMany"]) repo.on(`before:${op}`, async (context) => {
105
130
  if (!isSkipped(context)) context[MATCHED_IDS] = await matchingIds(context, context.query);
106
131
  });
107
- /** Each re-read document as an upsert, or a tombstone where it is gone. */
132
+ /** Each target as an upsert, or a tombstone where it is gone — read back in batches, not one query per id. */
108
133
  const capture = async (ids, context) => {
109
- for (const id of ids) {
110
- const doc = await reread(id, context);
134
+ const wanted = ids.filter((id) => id !== void 0 && id !== null);
135
+ const found = /* @__PURE__ */ new Map();
136
+ for (let i = 0; i < wanted.length; i += READ_BACK_CHUNK) {
137
+ const query = repo.Model.find({ _id: { $in: wanted.slice(i, i + READ_BACK_CHUNK) } }).lean();
138
+ if (context.session !== void 0) query.session(context.session);
139
+ for (const doc of await query.exec()) found.set(String(doc._id), doc);
140
+ }
141
+ for (const id of wanted) {
142
+ const doc = found.get(String(id));
111
143
  if (doc) await upsert(doc, context);
112
144
  else await tombstone(id, context);
113
145
  }
@@ -144,4 +176,4 @@ function changeLogPlugin(options) {
144
176
  };
145
177
  }
146
178
  //#endregion
147
- export { AuditTrailQuery, MONGOKIT_PLUGIN_NAMES, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, changeLogPlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, getNextSequence, idempotencyStorePlugin, immutableField, immutableStatesPlugin, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
179
+ export { AuditTrailQuery, MONGOKIT_PLUGIN_NAMES, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, changeLogPlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, getNextSequence, idempotencyStorePlugin, immutableField, immutableStatesPlugin, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, searchKeysPlugin, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
@@ -1,4 +1,4 @@
1
- import { r as Repository } from "../Repository-pnLw1rG5.mjs";
1
+ import { r as Repository } from "../Repository-DnL4xk5w.mjs";
2
2
  import { BulkUpsertResult } from "@classytic/repo-core/repository";
3
3
  //#region src/read-model/index.d.ts
4
4
  declare const READ_MODEL_ERROR_CODES: {
@@ -30,6 +30,7 @@ declare const MONGOKIT_PLUGIN_NAMES: {
30
30
  readonly mongoOperations: "mongo-operations";
31
31
  readonly multiTenant: "multi-tenant";
32
32
  readonly observability: "observability";
33
+ readonly searchKeys: "search-keys";
33
34
  readonly softDelete: "softDelete";
34
35
  readonly subdocument: "subdocument";
35
36
  readonly timestamp: "timestamp";
@@ -101,7 +102,7 @@ interface MiddlewareContext<TDoc> {
101
102
  */
102
103
  type Middleware<TDoc = unknown> = (ctx: MiddlewareContext<TDoc>) => Promise<unknown>;
103
104
  /** Search strategy used by Repository.getAll when `search` is provided */
104
- type RepositorySearchMode = 'text' | 'regex' | 'auto';
105
+ type RepositorySearchMode = 'text' | 'regex' | 'auto' | 'prefix';
105
106
  /** Repository options */
106
107
  interface RepositoryOptions {
107
108
  /** Whether repository event hooks are awaited */
@@ -122,6 +123,11 @@ interface RepositoryOptions {
122
123
  * - 'text' (default): uses MongoDB $text; requires a text index.
123
124
  * - 'regex': builds case-insensitive $or of $regex across `searchFields`. No index required.
124
125
  * - 'auto': uses 'text' if a text index exists, otherwise falls back to 'regex' when `searchFields` is set.
126
+ * - 'prefix': indexed search-as-you-type. Every write stores the normalised word tokens of
127
+ * `searchFields` in `searchKeysField`; a search matches documents where EVERY query word
128
+ * starts some token (`moh ali` finds "Mohammed Al-Ali"), via an anchored regex per word on
129
+ * a multikey index. Needs the keys path on the schema (`searchKeysPath()`) and an index
130
+ * (`searchKeysIndex()`); backfill existing rows with `rebuildSearchKeys`.
125
131
  *
126
132
  * Works standalone (Express/Nest/etc.) or alongside QueryParser — when the parser
127
133
  * already consumed `search` into filters, Repository does nothing extra.
@@ -129,9 +135,14 @@ interface RepositoryOptions {
129
135
  searchMode?: RepositorySearchMode;
130
136
  /**
131
137
  * Fields used when searchMode is 'regex' (or 'auto' falling back to regex).
132
- * Required for regex mode to take effect.
138
+ * Required for regex mode to take effect. In 'prefix' mode: the source fields of the
139
+ * search keys (required; dotted paths, strings/numbers or arrays of them).
133
140
  */
134
141
  searchFields?: string[];
142
+ /**
143
+ * 'prefix' mode only: field storing the search keys. Default `'_searchKeys'`.
144
+ */
145
+ searchKeysField?: string;
135
146
  /**
136
147
  * How to react to a plugin composition that is known to be unsafe.
137
148
  * - 'warn' (default): log a warning via the configured logger.
@@ -592,4 +603,4 @@ type AllPluginMethods<TDoc> = {
592
603
  */
593
604
  type WithPlugins<TDoc, TRepo extends RepositoryInstance = RepositoryInstance> = TRepo & AllPluginMethods<TDoc>;
594
605
  //#endregion
595
- export { RepositoryPluginName as S, RepositoryOptions as _, HookMode as a, MONGOKIT_PLUGIN_NAMES as b, MinimalRepoView as c, PluginType as d, PrioritizedHook as f, RepositoryOperation as g, RepositoryInstance as h, EventPhase as i, Plugin as l, RepositoryEvent as m, EventHandlers as n, Middleware as o, RepositoryContext as p, EventPayload as r, MiddlewareContext as s, AllPluginMethods as t, PluginFunction as u, UserContext as v, MongoKitPluginName as x, WithPlugins as y };
606
+ export { RepositoryPluginName as C, MongoKitPluginName as S, RepositoryOptions as _, HookMode as a, WithPlugins as b, MinimalRepoView as c, PluginType as d, PrioritizedHook as f, RepositoryOperation as g, RepositoryInstance as h, EventPhase as i, Plugin as l, RepositoryEvent as m, EventHandlers as n, Middleware as o, RepositoryContext as p, EventPayload as r, MiddlewareContext as s, AllPluginMethods as t, PluginFunction as u, RepositorySearchMode as v, MONGOKIT_PLUGIN_NAMES as x, UserContext as y };
@@ -0,0 +1,2 @@
1
+ import { a as prefixSearchFilter, c as SearchMatchRange, d as tokenizeSearchText, f as SEARCH_KEYS_ERROR_CODES, i as buildSearchKeys, l as findSearchMatches, n as MAX_PREFIX_SEARCH_TERMS, o as searchKeysIndex, p as SearchKeysWriteError, r as PrefixSearchFilterOptions, s as searchKeysPath, t as DEFAULT_SEARCH_KEYS_FIELD, u as normalizeSearchText } from "../index-C0AjVqX3.mjs";
2
+ export { DEFAULT_SEARCH_KEYS_FIELD, MAX_PREFIX_SEARCH_TERMS, type PrefixSearchFilterOptions, SEARCH_KEYS_ERROR_CODES, SearchKeysWriteError, type SearchMatchRange, buildSearchKeys, findSearchMatches, normalizeSearchText, prefixSearchFilter, searchKeysIndex, searchKeysPath, tokenizeSearchText };
@@ -0,0 +1,2 @@
1
+ import { _ as tokenizeSearchText, d as buildSearchKeys, f as prefixSearchFilter, g as normalizeSearchText, h as findSearchMatches, l as DEFAULT_SEARCH_KEYS_FIELD, m as searchKeysPath, n as SearchKeysWriteError, p as searchKeysIndex, t as SEARCH_KEYS_ERROR_CODES, u as MAX_PREFIX_SEARCH_TERMS } from "../derive-DRBiegRH.mjs";
2
+ export { DEFAULT_SEARCH_KEYS_FIELD, MAX_PREFIX_SEARCH_TERMS, SEARCH_KEYS_ERROR_CODES, SearchKeysWriteError, buildSearchKeys, findSearchMatches, normalizeSearchText, prefixSearchFilter, searchKeysIndex, searchKeysPath, tokenizeSearchText };
@@ -1,15 +1,26 @@
1
1
  import { ClientSession, Connection, Model } from "mongoose";
2
- import { ChangeLogStore, CommandStreamStore, CommandStreamTx } from "@classytic/repo-core/sync";
2
+ import { ChangeLogAppendOptions, ChangeLogStore, CommandStreamStore, CommandStreamTx } from "@classytic/repo-core/sync";
3
3
  //#region src/sync/index.d.ts
4
4
  interface ChangeLogModelOptions {
5
5
  /** Entries collection. Default `sync_changes`; its counter lives in `<collection>_counter`. */
6
6
  collection?: string;
7
7
  /** Mongoose model name prefix. Default `SyncChange`. Must be unique per connection. */
8
8
  modelName?: string;
9
+ /**
10
+ * When an entry gets its cursor. `'in-transaction'` (default): from the counter, inside the writer's transaction —
11
+ * every writer serialises on that one document and overlapping transactions abort and retry. `'deferred'`: the
12
+ * writer stages the entry (`<collection>_pending`, no counter); a single leased sequencer numbers COMMITTED
13
+ * entries before every read. A cursor is only ever given to a committed entry and is above every cursor given
14
+ * before, so no reader can checkpoint past an entry that appears later.
15
+ */
16
+ sequencing?: 'in-transaction' | 'deferred';
9
17
  }
10
18
  interface ChangeLogModels {
11
19
  entries: Model<Record<string, unknown>>;
12
20
  counter: Model<Record<string, unknown>>;
21
+ /** Staged, not-yet-numbered entries — present only under `sequencing: 'deferred'`. */
22
+ pending?: Model<Record<string, unknown>>;
23
+ sequencing: 'in-transaction' | 'deferred';
13
24
  /** The counter document's id (one feed per entries collection). */
14
25
  feed: string;
15
26
  }
@@ -26,6 +37,16 @@ interface MongoChangeLogStore extends ChangeLogStore {
26
37
  }): Promise<{
27
38
  removed: number;
28
39
  }>;
40
+ /**
41
+ * Number every staged entry (deferred sequencing; a no-op otherwise). Reads call it first; a scheduler may too.
42
+ * Returns how many it numbered, or `null` when another sequencer holds the lease.
43
+ */
44
+ sequencePending(): Promise<number | null>;
45
+ /**
46
+ * Record a change WITHOUT waiting for its cursor (deferred sequencing): the next read numbers it. For writers that
47
+ * never use the cursor (`changeLogPlugin`). Under in-transaction sequencing it is `append`.
48
+ */
49
+ stage(entry: Parameters<ChangeLogStore['append']>[0], options?: ChangeLogAppendOptions): Promise<void>;
29
50
  }
30
51
  declare function createChangeLogStore(models: ChangeLogModels): MongoChangeLogStore;
31
52
  interface CommandStreamModelOptions {