@classytic/mongokit 3.38.1 → 3.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{PaginationEngine-CBS1XGzp.d.mts → PaginationEngine-D-P19Y9m.d.mts} +34 -9
- package/dist/PaginationEngine-Dvl8jcRY.mjs +1198 -0
- package/dist/{Repository-Ck5x43wF.mjs → Repository-BMgPSkqZ.mjs} +672 -655
- package/dist/{Repository-BdSkQ8vc.d.mts → Repository-CR8Y9g2E.d.mts} +106 -34
- package/dist/actions/index.d.mts +1 -1
- package/dist/actions/index.mjs +1 -1
- package/dist/adapter/index.d.mts +21 -0
- package/dist/adapter/index.mjs +1 -1
- package/dist/{adapter-Bfqz8yHE.mjs → adapter-BrVfPFSW.mjs} +45 -1
- package/dist/ai/index.d.mts +1 -1
- package/dist/better-auth/index.mjs +2 -2
- package/dist/{create-repository-B5EulQ31.mjs → create-repository-BzBUMvd1.mjs} +3 -3
- package/dist/{create-repository-BU4x9Kb0.d.mts → create-repository-CAle1MSU.d.mts} +5 -5
- package/dist/idempotency/index.d.mts +23 -0
- package/dist/idempotency/index.mjs +134 -0
- package/dist/{index-DKEql4cL.d.mts → index-CmjNyulu.d.mts} +67 -8
- package/dist/index.d.mts +125 -35
- package/dist/index.mjs +286 -676
- package/dist/indexes/verify-indexes.d.mts +14 -1
- package/dist/indexes/verify-indexes.mjs +54 -7
- package/dist/{method-registry.plugin-DblM7V6j.mjs → method-registry.plugin-C63pGn1d.mjs} +54 -4
- package/dist/{multi-tenant.plugin-BwSvTirB.d.mts → multi-tenant.plugin-CBgCo9im.d.mts} +77 -5
- package/dist/{operations-DZodvjC-.d.mts → operations-CrRIoc8Z.d.mts} +22 -7
- package/dist/outbox/index.d.mts +1 -1
- package/dist/outbox/index.mjs +7 -5
- package/dist/pagination/PaginationEngine.d.mts +1 -1
- package/dist/pagination/PaginationEngine.mjs +1 -1
- package/dist/pagination/utils/limits.d.mts +1 -1
- package/dist/pagination/utils/limits.mjs +22 -1
- package/dist/pagination-BpIv2bdS.d.mts +364 -0
- package/dist/{plugin-options-DXnlKM3B.d.mts → plugin-options-Zk46lD6F.d.mts} +16 -2
- package/dist/plugins/index.d.mts +8 -3
- package/dist/plugins/index.mjs +99 -26
- package/dist/{repository-H_ffGgpE.d.mts → repository-odl03582.d.mts} +50 -6
- package/dist/sync/index.d.mts +40 -0
- package/dist/sync/index.mjs +334 -0
- package/dist/tenant/index.d.mts +89 -0
- package/dist/tenant/index.mjs +96 -0
- package/dist/testkit/index.d.mts +2 -2
- package/dist/testkit/index.mjs +1 -1
- package/dist/{timestamp.plugin-CBQukQzV.mjs → timestamp.plugin-DF0gJB6p.mjs} +116 -9
- package/dist/{to-plain-Dyz_qvuo.d.mts → to-plain-k7SjNlSS.d.mts} +2 -2
- package/dist/transaction-B5L9glO0.mjs +533 -0
- package/dist/{update-jSXp3nu3.mjs → update-DG0fCwRJ.mjs} +293 -83
- package/dist/utils/index.d.mts +1 -1
- package/dist/{validation-chain.plugin-DhNyxNCw.mjs → validation-chain.plugin-BLY2gLEk.mjs} +192 -279
- package/dist/{validation-chain.plugin-yQRob3Av.d.mts → validation-chain.plugin-DYp9E7K2.d.mts} +26 -20
- package/package.json +17 -4
- package/dist/PaginationEngine-BX9o_-4R.mjs +0 -735
- package/dist/core-62oo3N1C.d.mts +0 -50
- package/dist/pagination-jSUdrACK.d.mts +0 -158
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as ALL_OPERATIONS, o as OP_REGISTRY, t as HOOK_PRIORITY } from "./Repository-BMgPSkqZ.mjs";
|
|
2
2
|
import { n as warn } from "./logger-K7dPmu2U.mjs";
|
|
3
3
|
import { throwIfAborted, withRetry } from "@classytic/repo-core/repository";
|
|
4
4
|
import mongoose from "mongoose";
|
|
@@ -754,6 +754,33 @@ async function getNextSequence(counterKey, increment = 1, connection, session) {
|
|
|
754
754
|
return result.seq;
|
|
755
755
|
}
|
|
756
756
|
/**
|
|
757
|
+
* Current value of a counter, or `null` when it has never been created.
|
|
758
|
+
* `null` and `0` differ: only a missing counter needs seeding.
|
|
759
|
+
*/
|
|
760
|
+
async function readSequence(counterKey, connection) {
|
|
761
|
+
const doc = await getCounterModel(connection).findById(counterKey).lean();
|
|
762
|
+
return doc ? doc.seq : null;
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Raise a counter to at least `floor` — never lowers it. Atomic (`$max` +
|
|
766
|
+
* upsert), so concurrent seeders converge on the highest floor.
|
|
767
|
+
*
|
|
768
|
+
* Use before a counter's first increment when ids were already issued under
|
|
769
|
+
* another key (e.g. moving from a global to a per-tenant sequence); otherwise
|
|
770
|
+
* the new counter restarts at 1 and re-issues taken ids.
|
|
771
|
+
*
|
|
772
|
+
* @returns The counter's value after the raise.
|
|
773
|
+
*/
|
|
774
|
+
async function ensureSequenceAtLeast(counterKey, floor, connection) {
|
|
775
|
+
if (!Number.isSafeInteger(floor) || floor < 0) throw new Error(`ensureSequenceAtLeast: floor must be a non-negative integer, got ${floor}`);
|
|
776
|
+
const result = await getCounterModel(connection).findOneAndUpdate({ _id: counterKey }, { $max: { seq: floor } }, {
|
|
777
|
+
upsert: true,
|
|
778
|
+
returnDocument: "after"
|
|
779
|
+
});
|
|
780
|
+
if (!result) throw new Error(`Failed to seed counter '${counterKey}'`);
|
|
781
|
+
return result.seq;
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
757
784
|
* Generator: Simple sequential counter.
|
|
758
785
|
* Produces IDs like `INV-0001`, `INV-0002`, etc.
|
|
759
786
|
*
|
|
@@ -795,30 +822,53 @@ function sequentialId(options) {
|
|
|
795
822
|
* }),
|
|
796
823
|
* })
|
|
797
824
|
* ```
|
|
825
|
+
*
|
|
826
|
+
* @example Per-tenant numbering in a shared database
|
|
827
|
+
* ```typescript
|
|
828
|
+
* customIdPlugin({
|
|
829
|
+
* field: 'orderNumber',
|
|
830
|
+
* generator: dateSequentialId({ prefix: 'ORD', model: OrderModel, scope: 'tenant' }),
|
|
831
|
+
* })
|
|
832
|
+
* // org-a: ORD-2026-02-0001, ORD-2026-02-0002 … org-b: ORD-2026-02-0001 …
|
|
833
|
+
* ```
|
|
798
834
|
*/
|
|
799
835
|
function dateSequentialId(options) {
|
|
800
|
-
const { prefix, model, partition = "monthly", padding = 4, separator = "-" } = options;
|
|
836
|
+
const { prefix, model, partition = "monthly", padding = 4, separator = "-", scope = "global", tenantKey = "organizationId", timezone = "local" } = options;
|
|
837
|
+
/** Per-generator, not module-level — two generators warn independently. */
|
|
838
|
+
const overflowWarned = /* @__PURE__ */ new Set();
|
|
839
|
+
const paddingCeiling = 10 ** padding;
|
|
801
840
|
return async (context) => {
|
|
802
841
|
const now = /* @__PURE__ */ new Date();
|
|
803
|
-
const
|
|
804
|
-
const
|
|
805
|
-
const
|
|
842
|
+
const utc = timezone === "utc";
|
|
843
|
+
const year = String(utc ? now.getUTCFullYear() : now.getFullYear());
|
|
844
|
+
const month = String((utc ? now.getUTCMonth() : now.getMonth()) + 1).padStart(2, "0");
|
|
845
|
+
const day = String(utc ? now.getUTCDate() : now.getDate()).padStart(2, "0");
|
|
846
|
+
let keyBase = model.modelName;
|
|
847
|
+
if (scope === "tenant") {
|
|
848
|
+
const tenantId = context[tenantKey];
|
|
849
|
+
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.`);
|
|
850
|
+
keyBase = `${model.modelName}:${String(tenantId)}`;
|
|
851
|
+
}
|
|
806
852
|
let datePart;
|
|
807
853
|
let counterKey;
|
|
808
854
|
switch (partition) {
|
|
809
855
|
case "yearly":
|
|
810
856
|
datePart = year;
|
|
811
|
-
counterKey = `${
|
|
857
|
+
counterKey = `${keyBase}:${year}`;
|
|
812
858
|
break;
|
|
813
859
|
case "daily":
|
|
814
860
|
datePart = `${year}${separator}${month}${separator}${day}`;
|
|
815
|
-
counterKey = `${
|
|
861
|
+
counterKey = `${keyBase}:${year}-${month}-${day}`;
|
|
816
862
|
break;
|
|
817
863
|
default:
|
|
818
864
|
datePart = `${year}${separator}${month}`;
|
|
819
|
-
counterKey = `${
|
|
865
|
+
counterKey = `${keyBase}:${year}-${month}`;
|
|
820
866
|
}
|
|
821
867
|
const seq = await getNextSequence(counterKey, 1, context._counterConnection, context.session);
|
|
868
|
+
if (seq >= paddingCeiling && !overflowWarned.has(counterKey)) {
|
|
869
|
+
overflowWarned.add(counterKey);
|
|
870
|
+
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\`.`);
|
|
871
|
+
}
|
|
822
872
|
return `${prefix}${separator}${datePart}${separator}${String(seq).padStart(padding, "0")}`;
|
|
823
873
|
};
|
|
824
874
|
}
|
|
@@ -907,6 +957,62 @@ function customIdPlugin(options) {
|
|
|
907
957
|
};
|
|
908
958
|
}
|
|
909
959
|
//#endregion
|
|
960
|
+
//#region src/utils/scope.ts
|
|
961
|
+
/** Tenant context keys forwarded even when no tenant plugin declared one. */
|
|
962
|
+
const CONVENTIONAL_TENANT_KEYS = ["organizationId", "tenantId"];
|
|
963
|
+
const ATTRIBUTION_KEYS = [
|
|
964
|
+
"session",
|
|
965
|
+
"user",
|
|
966
|
+
"userId",
|
|
967
|
+
"requestId"
|
|
968
|
+
];
|
|
969
|
+
/** Where a repository records the tenant context keys its plugins read. */
|
|
970
|
+
const TENANT_KEYS = Symbol.for("@classytic/mongokit/tenant-context-keys");
|
|
971
|
+
/**
|
|
972
|
+
* Record that `repo` resolves its tenant from `contextKey`. Called by
|
|
973
|
+
* `multiTenantPlugin` at bind, so forwarding knows a custom key (`branchId`)
|
|
974
|
+
* without the host restating it.
|
|
975
|
+
*/
|
|
976
|
+
function declareTenantContextKey(repo, contextKey) {
|
|
977
|
+
const holder = repo;
|
|
978
|
+
if (!holder[TENANT_KEYS]) Object.defineProperty(holder, TENANT_KEYS, {
|
|
979
|
+
value: /* @__PURE__ */ new Set(),
|
|
980
|
+
enumerable: false
|
|
981
|
+
});
|
|
982
|
+
holder[TENANT_KEYS]?.add(contextKey);
|
|
983
|
+
}
|
|
984
|
+
/** Every tenant context key relevant to a call spanning these repositories. */
|
|
985
|
+
function tenantContextKeysOf(...repos) {
|
|
986
|
+
const keys = new Set(CONVENTIONAL_TENANT_KEYS);
|
|
987
|
+
for (const repo of repos) for (const key of repo?.[TENANT_KEYS] ?? []) keys.add(key);
|
|
988
|
+
return [...keys];
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* The scope to hand a second repository call made on behalf of `source`
|
|
992
|
+
* (a hook's `RepositoryContext`, or a host's request context).
|
|
993
|
+
*
|
|
994
|
+
* Absent values are omitted, never written as `undefined`, so spreading the
|
|
995
|
+
* result into an options bag cannot erase a value set there.
|
|
996
|
+
*/
|
|
997
|
+
function forwardScope(source, tenantKeys = CONVENTIONAL_TENANT_KEYS) {
|
|
998
|
+
if (!source) return {};
|
|
999
|
+
const out = {};
|
|
1000
|
+
let hasTenant = false;
|
|
1001
|
+
for (const key of tenantKeys) {
|
|
1002
|
+
const value = source[key];
|
|
1003
|
+
if (value === void 0) continue;
|
|
1004
|
+
out[key] = value;
|
|
1005
|
+
if (value !== null && value !== "") hasTenant = true;
|
|
1006
|
+
}
|
|
1007
|
+
if (!hasTenant && source.bypassTenant === true) out.bypassTenant = true;
|
|
1008
|
+
for (const key of ATTRIBUTION_KEYS) if (source[key] !== void 0) out[key] = source[key];
|
|
1009
|
+
return out;
|
|
1010
|
+
}
|
|
1011
|
+
/** Every key `forwardScope` governs. Other forwarding helpers must not copy these themselves. */
|
|
1012
|
+
function isScopeKey(key, tenantKeys = CONVENTIONAL_TENANT_KEYS) {
|
|
1013
|
+
return key === "bypassTenant" || tenantKeys.includes(key) || ATTRIBUTION_KEYS.includes(key);
|
|
1014
|
+
}
|
|
1015
|
+
//#endregion
|
|
910
1016
|
//#region src/plugins/multi-tenant.plugin.ts
|
|
911
1017
|
/**
|
|
912
1018
|
* Multi-Tenant Plugin
|
|
@@ -960,6 +1066,7 @@ function multiTenantPlugin(options = {}) {
|
|
|
960
1066
|
return {
|
|
961
1067
|
name: "multi-tenant",
|
|
962
1068
|
apply(repo) {
|
|
1069
|
+
declareTenantContextKey(repo, contextKey);
|
|
963
1070
|
const allOps = [...ALL_OPERATIONS.map((op) => ({
|
|
964
1071
|
op,
|
|
965
1072
|
policyKey: OP_REGISTRY[op].policyKey
|
|
@@ -1477,4 +1584,4 @@ function timestampPlugin() {
|
|
|
1477
1584
|
};
|
|
1478
1585
|
}
|
|
1479
1586
|
//#endregion
|
|
1480
|
-
export {
|
|
1587
|
+
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 };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { v as UserContext } from "./repository-
|
|
2
|
-
import { r as FieldPreset } from "./plugin-options-
|
|
1
|
+
import { v as UserContext } from "./repository-odl03582.mjs";
|
|
2
|
+
import { r as FieldPreset } from "./plugin-options-Zk46lD6F.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";
|