@classytic/mongokit 3.43.0 → 3.44.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 (92) hide show
  1. package/README.md +14 -0
  2. package/dist/{PaginationEngine-DZnkbSrr.mjs → PaginationEngine-C9iS0U5l.mjs} +13 -27
  3. package/dist/{PaginationEngine-CRHyuUtw.d.mts → PaginationEngine-D17AthKw.d.mts} +1 -3
  4. package/dist/{Repository-DnL4xk5w.d.mts → Repository-B6oL6OMv.d.mts} +33 -11
  5. package/dist/{Repository-Dj6xqPxJ.mjs → Repository-eMRYZ_2Y.mjs} +204 -92
  6. package/dist/actions/index.d.mts +1 -1
  7. package/dist/actions/index.mjs +2 -12
  8. package/dist/actions-DExyXMTy.mjs +11 -0
  9. package/dist/adapter/index.d.mts +6 -10
  10. package/dist/adapter/index.mjs +1 -2
  11. package/dist/{adapter-CkMoMF83.mjs → adapter-Qzgc3ZD_.mjs} +4 -6
  12. package/dist/ai/index.d.mts +21 -8
  13. package/dist/ai/index.mjs +15 -9
  14. package/dist/better-auth/index.d.mts +18 -32
  15. package/dist/better-auth/index.mjs +3 -4
  16. package/dist/{coercion-BjdPX3YF.d.mts → coercion-COp9DRNw.d.mts} +0 -2
  17. package/dist/{create-repository-CJtVdwig.d.mts → create-repository-BOFUYj1l.d.mts} +5 -7
  18. package/dist/{create-repository-UjwPWR1z.mjs → create-repository-D7MQpIZo.mjs} +4 -5
  19. package/dist/{derive-DRBiegRH.mjs → derive-Dxsc31fV.mjs} +3 -6
  20. package/dist/{error-BuByZwKU.mjs → error-DjdmPLWO.mjs} +1 -2
  21. package/dist/{field-selection-FSQFLpsU.mjs → field-selection-DPFaFw9Y.mjs} +1 -2
  22. package/dist/{id-chunks-BmAwsqNk.mjs → id-chunks-DH-RAZWG.mjs} +13 -8
  23. package/dist/{id-resolution-BqDrk0S2.mjs → id-resolution-Cqh49d9J.mjs} +1 -2
  24. package/dist/idempotency/index.d.mts +4 -7
  25. package/dist/idempotency/index.mjs +1 -2
  26. package/dist/{index-SYF8N28D.d.mts → index-8QUEsOFI.d.mts} +3 -4
  27. package/dist/{index-C0AjVqX3.d.mts → index-ehGZLbrd.d.mts} +2 -8
  28. package/dist/index.d.mts +63 -96
  29. package/dist/index.mjs +36 -52
  30. package/dist/indexes/verify-indexes.d.mts +8 -11
  31. package/dist/indexes/verify-indexes.mjs +1 -2
  32. package/dist/{indexes-DIjMpYES.d.mts → indexes-FfyOuhDK.d.mts} +0 -2
  33. package/dist/{join-scope-DSAznE8E.mjs → join-scope-C1gOtZ59.mjs} +2 -3
  34. package/dist/kernel-conformance/index.d.mts +39 -13
  35. package/dist/kernel-conformance/index.mjs +4 -7
  36. package/dist/lock/index.d.mts +134 -7
  37. package/dist/lock/index.mjs +349 -14
  38. package/dist/{logger-K7dPmu2U.mjs → logger-hbWOlj_X.mjs} +1 -2
  39. package/dist/{method-registry.plugin-C63pGn1d.mjs → method-registry.plugin-CGAepsBU.mjs} +3 -5
  40. package/dist/{model-registry-ClKtWiHa.mjs → model-registry-DrVfJ3Nt.mjs} +1 -2
  41. package/dist/{mongooseToJsonSchema-C3k4ide0.mjs → mongooseToJsonSchema-DMDotz4_.mjs} +2 -3
  42. package/dist/{multi-tenant.plugin-B_q9NUHc.d.mts → multi-tenant.plugin-DfeDMiI8.d.mts} +143 -11
  43. package/dist/{operations-XiFgG5US.d.mts → operations-Dw9OFV0V.d.mts} +14 -5
  44. package/dist/outbox/index.d.mts +24 -9
  45. package/dist/outbox/index.mjs +17 -6
  46. package/dist/pagination/PaginationEngine.d.mts +1 -1
  47. package/dist/pagination/PaginationEngine.mjs +1 -2
  48. package/dist/pagination/utils/limits.d.mts +6 -9
  49. package/dist/pagination/utils/limits.mjs +2 -3
  50. package/dist/{pagination-D6psses0.d.mts → pagination-DAUg5zOY.d.mts} +2 -10
  51. package/dist/{plugin-options-Cirmb4Kp.d.mts → plugin-options-Bpcuz9kS.d.mts} +2 -4
  52. package/dist/plugins/index.d.mts +6 -7
  53. package/dist/plugins/index.mjs +6 -7
  54. package/dist/query/primitives/coercion.d.mts +1 -1
  55. package/dist/query/primitives/coercion.mjs +2 -3
  56. package/dist/query/primitives/geo.d.mts +11 -14
  57. package/dist/query/primitives/geo.mjs +1 -2
  58. package/dist/query/primitives/indexes.d.mts +1 -1
  59. package/dist/query/primitives/indexes.mjs +1 -2
  60. package/dist/{query-defaults-BxUcSOoF.mjs → query-defaults-aUoKlEbh.mjs} +2 -3
  61. package/dist/read-model/index.d.mts +18 -21
  62. package/dist/read-model/index.mjs +3 -4
  63. package/dist/{repository-MqAP409e.d.mts → repository-DzVIwhU2.d.mts} +25 -29
  64. package/dist/retention-purge-DqcYvRnG.d.mts +35 -0
  65. package/dist/{rolldown-runtime-D7D4PA-g.mjs → rolldown-runtime-D8jQpDG7.mjs} +1 -2
  66. package/dist/search/index.d.mts +1 -1
  67. package/dist/search/index.mjs +1 -2
  68. package/dist/sync/index.d.mts +95 -15
  69. package/dist/sync/index.mjs +308 -62
  70. package/dist/tenant/index.d.mts +8 -11
  71. package/dist/tenant/index.mjs +1 -2
  72. package/dist/testkit/index.d.mts +179 -23
  73. package/dist/testkit/index.mjs +421 -23
  74. package/dist/{timestamp.plugin-DpOWavVI.mjs → timestamp.plugin-Bz_zL9PX.mjs} +192 -49
  75. package/dist/{to-plain-vksRrLtc.mjs → to-plain-CEKv_ylg.mjs} +2 -4
  76. package/dist/{to-plain-o5tUY2nV.d.mts → to-plain-PO5r1Qoy.d.mts} +2 -16
  77. package/dist/{transaction-DmiB4k2P.mjs → transaction-C2PFxfIv.mjs} +5 -8
  78. package/dist/{type-utils-CLzRFtIA.d.mts → type-utils-CKvuewAL.d.mts} +0 -2
  79. package/dist/{update-WgeAUJ_I.mjs → update-DcfmNbJe.mjs} +12 -20
  80. package/dist/usage/index.d.mts +2 -5
  81. package/dist/usage/index.mjs +8 -6
  82. package/dist/utils/index.d.mts +8 -10
  83. package/dist/utils/index.mjs +7 -7
  84. package/dist/{validation-chain.plugin-BAeC5eVT.d.mts → validation-chain.plugin-B3gZCZj4.d.mts} +135 -48
  85. package/dist/{validation-chain.plugin-DRApTmkN.mjs → validation-chain.plugin-DNY6MIF6.mjs} +207 -55
  86. package/llms.txt +33 -0
  87. package/package.json +29 -25
  88. package/skills/mongokit/SKILL.md +112 -0
  89. package/skills/mongokit/references/pagination-search.md +87 -0
  90. package/skills/mongokit/references/plugins-tenancy.md +102 -0
  91. package/skills/mongokit/references/repository-api.md +83 -0
  92. package/skills/mongokit/references/transactions-outbox.md +101 -0
@@ -1,11 +1,11 @@
1
- import { a as ALL_OPERATIONS, s as OP_REGISTRY, t as HOOK_PRIORITY } from "./Repository-Dj6xqPxJ.mjs";
2
- import { n as warn } from "./logger-K7dPmu2U.mjs";
3
- import { n as declareCollectionScope } from "./join-scope-DSAznE8E.mjs";
1
+ import { a as ALL_OPERATIONS, s as OP_REGISTRY, t as HOOK_PRIORITY } from "./Repository-eMRYZ_2Y.mjs";
2
+ import { n as warn } from "./logger-hbWOlj_X.mjs";
3
+ import { n as declareCollectionScope } from "./join-scope-C1gOtZ59.mjs";
4
4
  import { throwIfAborted, withRetry } from "@classytic/repo-core/repository";
5
5
  import mongoose from "mongoose";
6
6
  import { cachePlugin } from "@classytic/repo-core/cache";
7
7
  import { adminBypass, payloadHasTenantField } from "@classytic/repo-core/plugins";
8
- //#region src/plugins/audit-log.plugin.ts
8
+ // src/plugins/audit-log.plugin.ts
9
9
  /**
10
10
  * Audit log plugin that logs all repository operations
11
11
  *
@@ -102,8 +102,7 @@ function auditLogPlugin(logger) {
102
102
  }
103
103
  };
104
104
  }
105
- //#endregion
106
- //#region src/plugins/audit-trail.plugin.ts
105
+ // src/plugins/audit-trail.plugin.ts
107
106
  /**
108
107
  * Audit Trail Plugin
109
108
  *
@@ -631,8 +630,7 @@ var AuditTrailQuery = class {
631
630
  });
632
631
  }
633
632
  };
634
- //#endregion
635
- //#region src/plugins/custom-id.plugin.ts
633
+ // src/plugins/custom-id.plugin.ts
636
634
  /**
637
635
  * Custom ID Plugin
638
636
  *
@@ -711,10 +709,54 @@ const counterSchema = new mongoose.Schema({
711
709
  * Falls back to the default mongoose connection if none is provided.
712
710
  * Lazy-init to avoid model registration errors if mongoose isn't connected yet.
713
711
  */
714
- function getCounterModel(connection) {
712
+ function getCounterModel(connection, collection) {
715
713
  const conn = connection ?? mongoose.connection;
716
- if (conn.models._MongoKitCounter) return conn.models._MongoKitCounter;
717
- return conn.model("_MongoKitCounter", counterSchema);
714
+ const modelName = collection === void 0 || collection === "_mongokit_counters" ? "_MongoKitCounter" : `_MongoKitCounter:${collection}`;
715
+ if (conn.models[modelName]) return conn.models[modelName];
716
+ return conn.model(modelName, counterSchema, collection ?? "_mongokit_counters");
717
+ }
718
+ /**
719
+ * Bind the atomic counter API to a connection and, optionally, a
720
+ * collection other than `_mongokit_counters`.
721
+ *
722
+ * @example
723
+ * const counters = createSequenceCounter({ connection, collection: 'app_counters' });
724
+ * await withTransaction(connection, async (session) => {
725
+ * const seq = await counters.next('alert.stream', 1, session);
726
+ * });
727
+ */
728
+ function createSequenceCounter(options = {}) {
729
+ const { connection, collection } = options;
730
+ assertCounterCollection("createSequenceCounter", collection);
731
+ const model = () => getCounterModel(connection, collection);
732
+ return {
733
+ next: (counterKey, increment = 1, session) => incrementCounter(model(), counterKey, increment, session),
734
+ read: (counterKey, session) => readCounter(model(), counterKey, session),
735
+ ensureAtLeast: (counterKey, floor, session) => raiseCounter(model(), counterKey, floor, session),
736
+ nextInPeriod: (baseKey, periodOptions) => nextInPeriod(model(), baseKey, periodOptions)
737
+ };
738
+ }
739
+ function assertCounterCollection(fn, collection) {
740
+ if (collection !== void 0 && (typeof collection !== "string" || collection.length === 0)) throw new Error(`${fn}: collection must be a non-empty string`);
741
+ }
742
+ async function incrementCounter(Counter, counterKey, increment, session) {
743
+ const result = await Counter.findOneAndUpdate({ _id: counterKey }, { $inc: { seq: increment } }, {
744
+ upsert: true,
745
+ returnDocument: "after",
746
+ session
747
+ });
748
+ if (!result) throw new Error(`Failed to increment counter '${counterKey}'`);
749
+ return result.seq;
750
+ }
751
+ async function raiseCounter(Counter, counterKey, floor, session) {
752
+ if (!Number.isSafeInteger(floor) || floor < 0) throw new Error(`ensureSequenceAtLeast: floor must be a non-negative integer, got ${floor}`);
753
+ const result = await Counter.findOneAndUpdate({ _id: counterKey }, { $max: { seq: floor } }, {
754
+ upsert: true,
755
+ returnDocument: "after",
756
+ session
757
+ });
758
+ if (!result) throw new Error(`Failed to seed counter '${counterKey}'`);
759
+ return result.seq;
718
760
  }
719
761
  /**
720
762
  * Atomically increment and return the next sequence value for a given key.
@@ -746,20 +788,17 @@ function getCounterModel(connection) {
746
788
  * });
747
789
  */
748
790
  async function getNextSequence(counterKey, increment = 1, connection, session) {
749
- const result = await getCounterModel(connection).findOneAndUpdate({ _id: counterKey }, { $inc: { seq: increment } }, {
750
- upsert: true,
751
- returnDocument: "after",
752
- session
753
- });
754
- if (!result) throw new Error(`Failed to increment counter '${counterKey}'`);
755
- return result.seq;
791
+ return incrementCounter(getCounterModel(connection), counterKey, increment, session);
756
792
  }
757
793
  /**
758
794
  * Current value of a counter, or `null` when it has never been created.
759
795
  * `null` and `0` differ: only a missing counter needs seeding.
760
796
  */
761
- async function readSequence(counterKey, connection) {
762
- const doc = await getCounterModel(connection).findById(counterKey).lean();
797
+ async function readSequence(counterKey, connection, session) {
798
+ return readCounter(getCounterModel(connection), counterKey, session);
799
+ }
800
+ async function readCounter(Counter, counterKey, session) {
801
+ const doc = await Counter.findById(counterKey, null, session ? { session } : {}).lean();
763
802
  return doc ? doc.seq : null;
764
803
  }
765
804
  /**
@@ -772,14 +811,109 @@ async function readSequence(counterKey, connection) {
772
811
  *
773
812
  * @returns The counter's value after the raise.
774
813
  */
775
- async function ensureSequenceAtLeast(counterKey, floor, connection) {
776
- if (!Number.isSafeInteger(floor) || floor < 0) throw new Error(`ensureSequenceAtLeast: floor must be a non-negative integer, got ${floor}`);
777
- const result = await getCounterModel(connection).findOneAndUpdate({ _id: counterKey }, { $max: { seq: floor } }, {
778
- upsert: true,
779
- returnDocument: "after"
780
- });
781
- if (!result) throw new Error(`Failed to seed counter '${counterKey}'`);
782
- return result.seq;
814
+ async function ensureSequenceAtLeast(counterKey, floor, connection, session) {
815
+ return raiseCounter(getCounterModel(connection), counterKey, floor, session);
816
+ }
817
+ const SEQUENCE_PERIODS = /* @__PURE__ */ new Set([
818
+ "year",
819
+ "month",
820
+ "day"
821
+ ]);
822
+ /** One cached formatter per zone; construction is the expensive part of Intl. */
823
+ const civilFormatters = /* @__PURE__ */ new Map();
824
+ function civilFormatter(fn, timeZone) {
825
+ if (typeof timeZone !== "string" || timeZone.length === 0) throw new Error(`${fn}: timeZone must be a non-empty IANA time zone (e.g. 'Asia/Riyadh')`);
826
+ let formatter = civilFormatters.get(timeZone);
827
+ if (!formatter) {
828
+ try {
829
+ formatter = new Intl.DateTimeFormat("en-US", {
830
+ timeZone,
831
+ calendar: "gregory",
832
+ numberingSystem: "latn",
833
+ year: "numeric",
834
+ month: "2-digit",
835
+ day: "2-digit"
836
+ });
837
+ } catch {
838
+ throw new Error(`${fn}: unknown time zone '${timeZone}'`);
839
+ }
840
+ civilFormatters.set(timeZone, formatter);
841
+ }
842
+ return formatter;
843
+ }
844
+ /**
845
+ * The civil date of an instant in a zone. Read from the zone's own rules
846
+ * (Intl), never by adding a fixed offset, so DST shifts and zone-rule changes
847
+ * (a skipped or repeated local day) land on the date a wall clock there shows.
848
+ */
849
+ function civilParts(fn, at, timeZone) {
850
+ const formatter = civilFormatter(fn, timeZone);
851
+ let year = "";
852
+ let month = "";
853
+ let day = "";
854
+ for (const part of formatter.formatToParts(at)) if (part.type === "year") year = part.value;
855
+ else if (part.type === "month") month = part.value;
856
+ else if (part.type === "day") day = part.value;
857
+ return {
858
+ year: year.padStart(4, "0"),
859
+ month,
860
+ day
861
+ };
862
+ }
863
+ function assertInstant(fn, at) {
864
+ if (!(at instanceof Date) || Number.isNaN(at.getTime())) throw new Error(`${fn}: at must be a valid Date`);
865
+ }
866
+ /**
867
+ * The civil period of an instant in an IANA time zone: `'2026'` (year),
868
+ * `'2026-03'` (month) or `'2026-03-14'` (day). The period suffix every
869
+ * period-scoped counter key ends with.
870
+ *
871
+ * @example
872
+ * civilPeriodKey(new Date('2026-12-31T21:00:00Z'), 'year', 'Asia/Riyadh'); // '2027'
873
+ * civilPeriodKey(new Date('2026-12-31T21:00:00Z'), 'year', 'UTC'); // '2026'
874
+ */
875
+ function civilPeriodKey(at, period, timeZone) {
876
+ assertInstant("civilPeriodKey", at);
877
+ if (!SEQUENCE_PERIODS.has(period)) throw new Error(`civilPeriodKey: period must be 'year', 'month' or 'day', got '${period}'`);
878
+ const { year, month, day } = civilParts("civilPeriodKey", at, timeZone);
879
+ if (period === "year") return year;
880
+ if (period === "month") return `${year}-${month}`;
881
+ return `${year}-${month}-${day}`;
882
+ }
883
+ /**
884
+ * The counter key and civil period for a base key at an instant. Use it to
885
+ * `read` / `ensureAtLeast` the same counter `getNextPeriodSequence` bumps.
886
+ */
887
+ function periodCounterKey(baseKey, options) {
888
+ if (typeof baseKey !== "string" || baseKey.length === 0) throw new Error("periodCounterKey: baseKey must be a non-empty string");
889
+ const period = civilPeriodKey(options.at ?? /* @__PURE__ */ new Date(), options.period, options.timeZone);
890
+ return {
891
+ counterKey: `${baseKey}${options.keySeparator ?? ":"}${period}`,
892
+ period
893
+ };
894
+ }
895
+ /**
896
+ * Atomically issue the next value of a counter that resets every civil
897
+ * year / month / day in an IANA time zone. The period is derived from `at`
898
+ * (default now) in `timeZone`, so every replica agrees on it whatever its
899
+ * `TZ`, and DST never moves a boundary.
900
+ *
901
+ * @example Yearly reference `OC-2026-000123` in Riyadh
902
+ * const { seq, period } = await getNextPeriodSequence('occ.occurrence', {
903
+ * period: 'year', timeZone: 'Asia/Riyadh', at: reportedAt, connection, session,
904
+ * });
905
+ * const ref = `OC-${period}-${String(seq).padStart(6, '0')}`;
906
+ */
907
+ async function getNextPeriodSequence(baseKey, options) {
908
+ return nextInPeriod(getCounterModel(options.connection), baseKey, options);
909
+ }
910
+ async function nextInPeriod(Counter, baseKey, options) {
911
+ const { counterKey, period } = periodCounterKey(baseKey, options);
912
+ return {
913
+ seq: await incrementCounter(Counter, counterKey, options.increment ?? 1, options.session),
914
+ period,
915
+ counterKey
916
+ };
783
917
  }
784
918
  /**
785
919
  * Generator: Simple sequential counter.
@@ -796,10 +930,11 @@ async function ensureSequenceAtLeast(counterKey, floor, connection) {
796
930
  * ```
797
931
  */
798
932
  function sequentialId(options) {
799
- const { prefix, model, padding = 4, separator = "-", counterKey } = options;
933
+ const { prefix, model, padding = 4, separator = "-", counterKey, counterCollection } = options;
800
934
  const key = counterKey || model.modelName;
935
+ assertCounterCollection("sequentialId", counterCollection);
801
936
  return async (context) => {
802
- const seq = await getNextSequence(key, 1, context._counterConnection, context.session);
937
+ const seq = await incrementCounter(getCounterModel(context._counterConnection, counterCollection), key, 1, context.session);
803
938
  return `${prefix}${separator}${String(seq).padStart(padding, "0")}`;
804
939
  };
805
940
  }
@@ -834,21 +969,31 @@ function sequentialId(options) {
834
969
  * ```
835
970
  */
836
971
  function dateSequentialId(options) {
837
- const { prefix, model, partition = "monthly", padding = 4, separator = "-", scope = "global", tenantKey = "organizationId", timezone = "local" } = options;
972
+ const { prefix, model, partition = "monthly", padding = 4, separator = "-", scope = "global", tenantKey = "organizationId", timezone = "local", timeZone, at, counterKey: baseCounterKey, counterCollection } = options;
973
+ assertCounterCollection("dateSequentialId", counterCollection);
974
+ if (timeZone !== void 0) civilFormatter("dateSequentialId", timeZone);
975
+ if (baseCounterKey !== void 0 && (typeof baseCounterKey !== "string" || baseCounterKey.length === 0)) throw new Error("dateSequentialId: counterKey must be a non-empty string");
838
976
  /** Per-generator, not module-level — two generators warn independently. */
839
977
  const overflowWarned = /* @__PURE__ */ new Set();
840
978
  const paddingCeiling = 10 ** padding;
841
979
  return async (context) => {
842
- const now = /* @__PURE__ */ new Date();
843
- const utc = timezone === "utc";
844
- const year = String(utc ? now.getUTCFullYear() : now.getFullYear());
845
- const month = String((utc ? now.getUTCMonth() : now.getMonth()) + 1).padStart(2, "0");
846
- const day = String(utc ? now.getUTCDate() : now.getDate()).padStart(2, "0");
847
- let keyBase = model.modelName;
980
+ const now = at?.(context) ?? /* @__PURE__ */ new Date();
981
+ assertInstant("dateSequentialId", now);
982
+ let year;
983
+ let month;
984
+ let day;
985
+ if (timeZone !== void 0) ({year, month, day} = civilParts("dateSequentialId", now, timeZone));
986
+ else {
987
+ const utc = timezone === "utc";
988
+ year = String(utc ? now.getUTCFullYear() : now.getFullYear());
989
+ month = String((utc ? now.getUTCMonth() : now.getMonth()) + 1).padStart(2, "0");
990
+ day = String(utc ? now.getUTCDate() : now.getDate()).padStart(2, "0");
991
+ }
992
+ let keyBase = baseCounterKey ?? model.modelName;
848
993
  if (scope === "tenant") {
849
994
  const tenantId = context[tenantKey];
850
995
  if (tenantId === void 0 || tenantId === null || tenantId === "") throw new Error(`[mongokit] dateSequentialId: scope 'tenant' requires '${tenantKey}' in context for '${model.modelName}' create, but none was provided. Pass it per call (repo.create(data, { ${tenantKey}: '<id>' })) or resolve it ambiently; the generator never falls back to the global counter.`);
851
- keyBase = `${model.modelName}:${String(tenantId)}`;
996
+ keyBase = `${keyBase}:${String(tenantId)}`;
852
997
  }
853
998
  let datePart;
854
999
  let counterKey;
@@ -865,7 +1010,7 @@ function dateSequentialId(options) {
865
1010
  datePart = `${year}${separator}${month}`;
866
1011
  counterKey = `${keyBase}:${year}-${month}`;
867
1012
  }
868
- const seq = await getNextSequence(counterKey, 1, context._counterConnection, context.session);
1013
+ const seq = await incrementCounter(getCounterModel(context._counterConnection, counterCollection), counterKey, 1, context.session);
869
1014
  if (seq >= paddingCeiling && !overflowWarned.has(counterKey)) {
870
1015
  overflowWarned.add(counterKey);
871
1016
  warn(`[mongokit] dateSequentialId: '${model.modelName}' sequence ${seq} exceeds padding ${padding} (max ${paddingCeiling - 1}) for period '${counterKey}'. Ids remain unique but no longer sort lexically — raise \`padding\` for new periods, or use a finer \`partition\`.`);
@@ -957,8 +1102,7 @@ function customIdPlugin(options) {
957
1102
  }
958
1103
  };
959
1104
  }
960
- //#endregion
961
- //#region src/utils/scope.ts
1105
+ // src/utils/scope.ts
962
1106
  /** Tenant context keys forwarded even when no tenant plugin declared one. */
963
1107
  const CONVENTIONAL_TENANT_KEYS = ["organizationId", "tenantId"];
964
1108
  const ATTRIBUTION_KEYS = [
@@ -1013,8 +1157,7 @@ function forwardScope(source, tenantKeys = CONVENTIONAL_TENANT_KEYS) {
1013
1157
  function isScopeKey(key, tenantKeys = CONVENTIONAL_TENANT_KEYS) {
1014
1158
  return key === "bypassTenant" || tenantKeys.includes(key) || ATTRIBUTION_KEYS.includes(key);
1015
1159
  }
1016
- //#endregion
1017
- //#region src/plugins/multi-tenant.plugin.ts
1160
+ // src/plugins/multi-tenant.plugin.ts
1018
1161
  /**
1019
1162
  * Multi-Tenant Plugin
1020
1163
  *
@@ -1063,6 +1206,9 @@ function multiTenantPlugin(options = {}) {
1063
1206
  const extraOps = [{
1064
1207
  op: "getDeleted",
1065
1208
  policyKey: "filters"
1209
+ }, {
1210
+ op: "searchSimilar",
1211
+ policyKey: "filters"
1066
1212
  }];
1067
1213
  return {
1068
1214
  name: "multi-tenant",
@@ -1256,8 +1402,7 @@ function multiTenantPlugin(options = {}) {
1256
1402
  function adminBypass$1(options) {
1257
1403
  return adminBypass(options);
1258
1404
  }
1259
- //#endregion
1260
- //#region src/plugins/soft-delete.plugin.ts
1405
+ // src/plugins/soft-delete.plugin.ts
1261
1406
  /**
1262
1407
  * Build the deletion-state filter for either side of the soft-delete
1263
1408
  * boundary. One parameterized helper covers both directions so the two
@@ -1563,8 +1708,7 @@ function softDeletePlugin(options = {}) {
1563
1708
  }
1564
1709
  };
1565
1710
  }
1566
- //#endregion
1567
- //#region src/plugins/timestamp.plugin.ts
1711
+ // src/plugins/timestamp.plugin.ts
1568
1712
  /**
1569
1713
  * Timestamp plugin that auto-injects timestamps
1570
1714
  *
@@ -1602,5 +1746,4 @@ function timestampPlugin() {
1602
1746
  }
1603
1747
  };
1604
1748
  }
1605
- //#endregion
1606
- export { AuditTrailQuery as _, declareTenantContextKey as a, auditLogPlugin as b, tenantContextKeysOf as c, ensureSequenceAtLeast as d, getNextSequence as f, cachePlugin as g, sequentialId as h, multiTenantPlugin as i, customIdPlugin as l, readSequence as m, softDeletePlugin as n, forwardScope as o, prefixedId as p, adminBypass$1 as r, isScopeKey as s, timestampPlugin as t, dateSequentialId as u, auditTrailPlugin as v, ensureAuditTrailReady as y };
1749
+ export { ensureAuditTrailReady as C, auditTrailPlugin as S, prefixedId as _, declareTenantContextKey as a, cachePlugin as b, tenantContextKeysOf as c, customIdPlugin as d, dateSequentialId as f, periodCounterKey as g, getNextSequence as h, multiTenantPlugin as i, civilPeriodKey as l, getNextPeriodSequence as m, softDeletePlugin as n, forwardScope as o, ensureSequenceAtLeast as p, adminBypass$1 as r, isScopeKey as s, timestampPlugin as t, createSequenceCounter as u, readSequence as v, auditLogPlugin as w, AuditTrailQuery as x, sequentialId as y };
@@ -1,4 +1,4 @@
1
- //#region src/utils/memory-cache.ts
1
+ // src/utils/memory-cache.ts
2
2
  /**
3
3
  * Creates an in-memory cache adapter
4
4
  *
@@ -63,8 +63,7 @@ function createMemoryCache(maxEntries = 1e3) {
63
63
  }
64
64
  };
65
65
  }
66
- //#endregion
67
- //#region src/utils/to-plain.ts
66
+ // src/utils/to-plain.ts
68
67
  /**
69
68
  * Convert a hydrated Mongoose (sub)document to a plain object — the
70
69
  * spread-safety primitive for domain verbs that rebuild embedded lines.
@@ -94,5 +93,4 @@ function createMemoryCache(maxEntries = 1e3) {
94
93
  function toPlain(value) {
95
94
  return value !== null && value !== void 0 && typeof value.toObject === "function" ? value.toObject() : value;
96
95
  }
97
- //#endregion
98
96
  export { createMemoryCache as n, toPlain as t };
@@ -1,10 +1,9 @@
1
- import { y as UserContext } from "./repository-MqAP409e.mjs";
2
- import { r as FieldPreset } from "./plugin-options-Cirmb4Kp.mjs";
1
+ import { y as UserContext } from "./repository-DzVIwhU2.mjs";
2
+ import { r as FieldPreset } from "./plugin-options-Bpcuz9kS.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";
6
6
  import { CrudSchemas, SchemaBuilderOptions } from "@classytic/repo-core/schema";
7
- //#region src/utils/error.d.ts
8
7
  /** Optional structured fields for createError. */
9
8
  interface CreateErrorOptions {
10
9
  /** Stable machine-readable code (e.g. `SEARCH_NOT_CONFIGURED`). */
@@ -70,8 +69,6 @@ interface ParseDuplicateKeyOptions {
70
69
  * Returns `null` when the error is not a duplicate-key error.
71
70
  */
72
71
  declare function parseDuplicateKeyError(error: unknown, options?: ParseDuplicateKeyOptions): HttpError | null;
73
- //#endregion
74
- //#region src/utils/field-selection.d.ts
75
72
  /**
76
73
  * Get allowed fields for a user based on their context
77
74
  *
@@ -138,8 +135,6 @@ declare function filterResponseData<T extends Record<string, unknown>>(data: T |
138
135
  * });
139
136
  */
140
137
  declare function createFieldPreset(config: Partial<FieldPreset>): FieldPreset;
141
- //#endregion
142
- //#region src/utils/id-resolution.d.ts
143
138
  /** Normalized _id type — covers the common Mongoose _id declarations */
144
139
  type IdType = 'objectid' | 'string' | 'number' | 'uuid' | 'unknown';
145
140
  /**
@@ -212,8 +207,6 @@ declare function isValidIdForType(id: unknown, idType: IdType): boolean;
212
207
  * await col.deleteMany({ customerId: { $in: idVariants(userId) } });
213
208
  */
214
209
  declare function idVariants(id: unknown): unknown[];
215
- //#endregion
216
- //#region src/utils/logger.d.ts
217
210
  /**
218
211
  * Internal Logger
219
212
  *
@@ -244,8 +237,6 @@ interface LoggerConfig {
244
237
  * Pass `false` to silence all output.
245
238
  */
246
239
  declare function configureLogger(config: Partial<LoggerConfig> | false): void;
247
- //#endregion
248
- //#region src/utils/memory-cache.d.ts
249
240
  /**
250
241
  * Creates an in-memory cache adapter
251
242
  *
@@ -257,8 +248,6 @@ declare function configureLogger(config: Partial<LoggerConfig> | false): void;
257
248
  * @param maxEntries - Maximum cache entries before oldest are evicted (default: 1000)
258
249
  */
259
250
  declare function createMemoryCache(maxEntries?: number): CacheAdapter;
260
- //#endregion
261
- //#region src/utils/mongooseToJsonSchema.d.ts
262
251
  /**
263
252
  * Return shape for the schema builders. Structurally a `CrudSchemas` plus
264
253
  * an `[key: string]: unknown` index signature so the function reference
@@ -298,8 +287,6 @@ declare function buildCrudSchemasFromMongooseSchema(mongooseSchema: Schema, opti
298
287
  * `SchemaGenerator<Model<unknown>>`, so the contract claim above is unchanged.
299
288
  */
300
289
  declare function buildCrudSchemasFromModel<TDoc = unknown>(mongooseModel: mongoose.Model<TDoc>, options?: SchemaBuilderOptions): CrudSchemasFramework;
301
- //#endregion
302
- //#region src/utils/to-plain.d.ts
303
290
  /**
304
291
  * Convert a hydrated Mongoose (sub)document to a plain object — the
305
292
  * spread-safety primitive for domain verbs that rebuild embedded lines.
@@ -327,5 +314,4 @@ declare function buildCrudSchemasFromModel<TDoc = unknown>(mongooseModel: mongoo
327
314
  * defensively over values that may or may not be hydrated.
328
315
  */
329
316
  declare function toPlain<T>(value: T): T;
330
- //#endregion
331
317
  export { parseDuplicateKeyError as S, getMongooseProjection as _, createMemoryCache as a, createError as b, IdType as c, isObjectIdInstance as d, isValidIdForType as f, getFieldsForUser as g, filterResponseData as h, buildCrudSchemasFromMongooseSchema as i, getSchemaIdType as l, createFieldPreset as m, CrudSchemasFramework as n, LoggerConfig as o, toObjectId as p, buildCrudSchemasFromModel as r, configureLogger as s, toPlain as t, idVariants as u, CreateErrorOptions as v, isDuplicateKeyError as x, ParseDuplicateKeyOptions as y };
@@ -1,8 +1,8 @@
1
- import { t as createError } from "./error-BuByZwKU.mjs";
2
- import { d as setTransactionDeadline } from "./query-defaults-BxUcSOoF.mjs";
1
+ import { d as setTransactionDeadline } from "./query-defaults-aUoKlEbh.mjs";
2
+ import { t as createError } from "./error-DjdmPLWO.mjs";
3
3
  import mongoose from "mongoose";
4
4
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
5
- //#region src/capabilities.ts
5
+ // src/capabilities.ts
6
6
  /**
7
7
  * Read the provenance of a capability descriptor's `transactions` flag.
8
8
  * A descriptor without the field is a static declaration (`'declared'`) —
@@ -252,8 +252,7 @@ async function probeMongoCapabilities(connection, options = {}) {
252
252
  PROBE_INFLIGHT.set(key, run);
253
253
  return DERIVED[await run];
254
254
  }
255
- //#endregion
256
- //#region src/tx-bound.ts
255
+ // src/tx-bound.ts
257
256
  /**
258
257
  * Map of session-aware method name → positional index of the `{ session }`
259
258
  * options bag. Keep this list in sync with the method signatures in
@@ -391,8 +390,7 @@ function createTxBoundRepo(outer, session) {
391
390
  };
392
391
  } });
393
392
  }
394
- //#endregion
395
- //#region src/transaction.ts
393
+ // src/transaction.ts
396
394
  /**
397
395
  * Cross-repo transaction helper.
398
396
  *
@@ -678,5 +676,4 @@ function isTransactionUnsupported(error) {
678
676
  function supportsTransactions(connection) {
679
677
  return resolveTransactionSupport(connection) !== "no";
680
678
  }
681
- //#endregion
682
679
  export { supportsTransactions as a, MONGOKIT_CAPABILITIES as c, probeMongoCapabilities as d, resetCapabilityProbeCache as f, transactionResolutionOf as h, runTransaction as i, capabilitiesForSupport as l, resolveTransactionSupport as m, batchTransaction as n, withTransaction as o, resolveMongoCapabilities as p, isTransactionUnsupported as r, createTxBoundRepo as s, TRANSACTION_ERROR_CODES as t, classifyHelloReply as u };
@@ -1,6 +1,5 @@
1
1
  import { Document, Model } from "mongoose";
2
2
  import { ValidationResult } from "@classytic/repo-core/schema";
3
- //#region src/types/type-utils.d.ts
4
3
  /**
5
4
  * Infer document type from a Mongoose Model
6
5
  * @example
@@ -159,5 +158,4 @@ type UpdateWithValidationResult<T> = {
159
158
  violations?: ValidationResult['violations'];
160
159
  };
161
160
  };
162
- //#endregion
163
161
  export { InferRawDoc as a, NonNullableFields as c, Strict as d, UpdatePatch as f, InferDocument as i, PartialBy as l, DeepPartial as n, KeysOfType as o, UpdateWithValidationResult as p, DocField as r, MongoOperatorUpdate as s, CreateInput as t, RequiredBy as u };
@@ -1,9 +1,9 @@
1
- import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { n as warn } from "./logger-K7dPmu2U.mjs";
3
- import { t as createError } from "./error-BuByZwKU.mjs";
4
- import { i as applyToQuery } from "./query-defaults-BxUcSOoF.mjs";
1
+ import { t as __exportAll } from "./rolldown-runtime-D8jQpDG7.mjs";
2
+ import { t as createError } from "./error-DjdmPLWO.mjs";
3
+ import { n as warn } from "./logger-hbWOlj_X.mjs";
4
+ import { i as applyToQuery } from "./query-defaults-aUoKlEbh.mjs";
5
5
  import { coerceFilterDates, isFilter } from "@classytic/repo-core/filter";
6
- //#region src/actions/create.ts
6
+ // src/actions/create.ts
7
7
  var create_exports = /* @__PURE__ */ __exportAll({
8
8
  create: () => create,
9
9
  createDefault: () => createDefault,
@@ -109,8 +109,7 @@ async function upsert(Model, query, data, options = {}) {
109
109
  ...options.updatePipeline !== void 0 ? { updatePipeline: options.updatePipeline } : {}
110
110
  });
111
111
  }
112
- //#endregion
113
- //#region src/filter/cast-schema.ts
112
+ // src/filter/cast-schema.ts
114
113
  /** Operators whose operand is a single scalar to cast. */
115
114
  const SCALAR_OPS = /* @__PURE__ */ new Set([
116
115
  "$eq",
@@ -225,8 +224,7 @@ function castFilterToSchema(filter, schema) {
225
224
  }
226
225
  return changed ? out : filter;
227
226
  }
228
- //#endregion
229
- //#region src/filter/compile.ts
227
+ // src/filter/compile.ts
230
228
  /**
231
229
  * Compile a Filter IR node to a MongoDB query object. Returns `{}`
232
230
  * when the node is `TRUE` (or an empty `and`) so callers can merge
@@ -464,8 +462,7 @@ function asOperators(condition) {
464
462
  function escapeRegexChar(ch) {
465
463
  return /[.*+?^${}()|[\]\\]/.test(ch) ? `\\${ch}` : ch;
466
464
  }
467
- //#endregion
468
- //#region src/query/LookupBuilder.ts
465
+ // src/query/LookupBuilder.ts
469
466
  /** Stages that are never valid inside a $lookup pipeline */
470
467
  const BLOCKED_PIPELINE_STAGES = [
471
468
  "$out",
@@ -852,8 +849,7 @@ function compileSelectToProjection(select) {
852
849
  * - For read-heavy workloads: Consider caching or materialized views
853
850
  * - For real-time dashboards: Consider separate aggregation collections
854
851
  */
855
- //#endregion
856
- //#region src/actions/aggregate.ts
852
+ // src/actions/aggregate.ts
857
853
  var aggregate_exports = /* @__PURE__ */ __exportAll({
858
854
  aggregate: () => aggregate,
859
855
  aggregatePaginate: () => aggregatePaginate,
@@ -1023,8 +1019,7 @@ async function minMax(Model, field, query = {}, options = {}) {
1023
1019
  max: null
1024
1020
  };
1025
1021
  }
1026
- //#endregion
1027
- //#region src/actions/delete.ts
1022
+ // src/actions/delete.ts
1028
1023
  var delete_exports = /* @__PURE__ */ __exportAll({
1029
1024
  deleteById: () => deleteById,
1030
1025
  deleteByQuery: () => deleteByQuery,
@@ -1113,8 +1108,7 @@ async function restore(Model, id, options = {}) {
1113
1108
  id: String(id)
1114
1109
  };
1115
1110
  }
1116
- //#endregion
1117
- //#region src/actions/read.ts
1111
+ // src/actions/read.ts
1118
1112
  var read_exports = /* @__PURE__ */ __exportAll({
1119
1113
  count: () => count,
1120
1114
  exists: () => exists,
@@ -1251,8 +1245,7 @@ async function exists(Model, query, options = {}) {
1251
1245
  applyToQuery(q, options.queryOptions ?? {});
1252
1246
  return q;
1253
1247
  }
1254
- //#endregion
1255
- //#region src/actions/update.ts
1248
+ // src/actions/update.ts
1256
1249
  var update_exports = /* @__PURE__ */ __exportAll({
1257
1250
  findOneAndUpdate: () => findOneAndUpdate,
1258
1251
  increment: () => increment,
@@ -1435,5 +1428,4 @@ async function pushToArray(Model, id, field, value, options = {}) {
1435
1428
  async function pullFromArray(Model, id, field, value, options = {}) {
1436
1429
  return update(Model, id, { $pull: { [field]: value } }, options);
1437
1430
  }
1438
- //#endregion
1439
1431
  export { isCreateManyPartialError as C, create_exports as S, SHORTHAND_OPS as _, count as a, create as b, getByQuery as c, deleteById as d, deleteByQuery as f, LookupBuilder as g, distinct as h, update_exports as i, getOrCreate as l, aggregate_exports as m, update as n, exists as o, delete_exports as p, updateByQuery as r, getById as s, findOneAndUpdate as t, read_exports as u, compileFilterToMongo as v, upsert as w, createMany as x, castFilterToSchema as y };
@@ -1,8 +1,7 @@
1
1
  import { Connection } from "mongoose";
2
2
  import { UsageStore } from "@classytic/repo-core/usage";
3
- //#region src/usage/index.d.ts
4
3
  /** Adapter-construction options. */
5
- interface MongoUsageStoreOptions {
4
+ export interface MongoUsageStoreOptions {
6
5
  /**
7
6
  * Mongoose connection. Defaults to `mongoose.connection` (the
8
7
  * default global connection). Apps using a named connection pass
@@ -18,6 +17,4 @@ interface MongoUsageStoreOptions {
18
17
  * Build a Mongo-backed usage store. Idempotent — the underlying model
19
18
  * is registered once per `(connection, modelName)` pair.
20
19
  */
21
- declare function createMongoUsageStore(options?: MongoUsageStoreOptions): UsageStore;
22
- //#endregion
23
- export { MongoUsageStoreOptions, createMongoUsageStore };
20
+ export declare function createMongoUsageStore(options?: MongoUsageStoreOptions): UsageStore;
@@ -1,6 +1,6 @@
1
- import { n as isDuplicateKeyError } from "../error-BuByZwKU.mjs";
1
+ import { n as isDuplicateKeyError } from "../error-DjdmPLWO.mjs";
2
2
  import mongoose, { Schema } from "mongoose";
3
- //#region src/usage/index.ts
3
+ // src/usage/index.ts
4
4
  /**
5
5
  * Build a Mongo-backed usage store. Idempotent — the underlying model
6
6
  * is registered once per `(connection, modelName)` pair.
@@ -8,9 +8,12 @@ import mongoose, { Schema } from "mongoose";
8
8
  function createMongoUsageStore(options = {}) {
9
9
  const { connection, collectionName = "usage_counters", modelName = "MongoUsageCounter" } = options;
10
10
  const model = getOrCreateModel(connection ?? mongoose, modelName, collectionName);
11
+ const ready = model.init();
12
+ ready.catch(() => void 0);
11
13
  return {
12
14
  name: "mongo",
13
15
  async increment(bucket, amount) {
16
+ await ready;
14
17
  const filter = {
15
18
  actor: bucket.actor,
16
19
  period: bucket.period,
@@ -28,9 +31,9 @@ function createMongoUsageStore(options = {}) {
28
31
  actor,
29
32
  period
30
33
  }).select("kind count").lean();
31
- const out = {};
32
- for (const row of rows) out[row.kind] = row.count;
33
- return out;
34
+ const sums = /* @__PURE__ */ new Map();
35
+ for (const row of rows) sums.set(row.kind, (sums.get(row.kind) ?? 0) + row.count);
36
+ return Object.fromEntries(sums);
34
37
  }
35
38
  };
36
39
  }
@@ -63,5 +66,4 @@ function getOrCreateModel(conn, modelName, collectionName) {
63
66
  }, { unique: true });
64
67
  return conn.model(modelName, schema, collectionName);
65
68
  }
66
- //#endregion
67
69
  export { createMongoUsageStore };