@classytic/mongokit 3.21.0 → 3.22.1

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 (27) hide show
  1. package/dist/{PaginationEngine-DYpOD2wY.d.mts → PaginationEngine-CYRTLW6s.d.mts} +1 -1
  2. package/dist/{Repository-dbbgOGjK.mjs → Repository-Dr1MDAen.mjs} +103 -14
  3. package/dist/actions/index.d.mts +1 -1
  4. package/dist/ai/index.d.mts +1 -1
  5. package/dist/better-auth/index.mjs +1 -1
  6. package/dist/{create-repository-0TkQhcv-.d.mts → create-repository-DXa3nMG6.d.mts} +95 -4
  7. package/dist/{create-repository-CJATiAKX.mjs → create-repository-DwODY4rZ.mjs} +2 -2
  8. package/dist/{index-Cg3SWugF.d.mts → index-CENL7LRb.d.mts} +1 -1
  9. package/dist/index.d.mts +8 -8
  10. package/dist/index.mjs +6 -6
  11. package/dist/{multi-tenant.plugin-CTsWz0XK.d.mts → multi-tenant.plugin-CFzMOuZP.d.mts} +1 -1
  12. package/dist/pagination/PaginationEngine.d.mts +1 -1
  13. package/dist/plugins/index.d.mts +4 -4
  14. package/dist/plugins/index.mjs +3 -3
  15. package/dist/testkit/index.d.mts +1 -1
  16. package/dist/testkit/index.mjs +1 -1
  17. package/dist/{timestamp.plugin-nG9Z1JEk.mjs → timestamp.plugin-Cn6LKUIZ.mjs} +1 -1
  18. package/dist/{mongooseToJsonSchema-D2CyAMIC.d.mts → to-plain-qzKeAC6z.d.mts} +31 -2
  19. package/dist/{memory-cache-DI1jb4dz.mjs → to-plain-vksRrLtc.mjs} +32 -1
  20. package/dist/{types-DNFMofHy.d.mts → types-BnRqAJVa.d.mts} +8 -0
  21. package/dist/usage/index.d.mts +23 -0
  22. package/dist/usage/index.mjs +67 -0
  23. package/dist/utils/index.d.mts +2 -2
  24. package/dist/utils/index.mjs +2 -2
  25. package/dist/{validation-chain.plugin-buy3uvyk.d.mts → validation-chain.plugin-D-EjrYpR.d.mts} +63 -2
  26. package/dist/{validation-chain.plugin-Dn_wZYLA.mjs → validation-chain.plugin-D6qWMH5d.mjs} +221 -2
  27. package/package.json +178 -184
@@ -0,0 +1,67 @@
1
+ import { n as isDuplicateKeyError } from "../error-BuByZwKU.mjs";
2
+ import mongoose, { Schema } from "mongoose";
3
+ //#region src/usage/index.ts
4
+ /**
5
+ * Build a Mongo-backed usage store. Idempotent — the underlying model
6
+ * is registered once per `(connection, modelName)` pair.
7
+ */
8
+ function createMongoUsageStore(options = {}) {
9
+ const { connection, collectionName = "usage_counters", modelName = "MongoUsageCounter" } = options;
10
+ const model = getOrCreateModel(connection ?? mongoose, modelName, collectionName);
11
+ return {
12
+ name: "mongo",
13
+ async increment(bucket, amount) {
14
+ const filter = {
15
+ actor: bucket.actor,
16
+ period: bucket.period,
17
+ kind: bucket.kind
18
+ };
19
+ try {
20
+ await model.updateOne(filter, { $inc: { count: amount } }, { upsert: true });
21
+ } catch (err) {
22
+ if (!isDuplicateKeyError(err)) throw err;
23
+ await model.updateOne(filter, { $inc: { count: amount } }, { upsert: true });
24
+ }
25
+ },
26
+ async summary(actor, period) {
27
+ const rows = await model.find({
28
+ actor,
29
+ period
30
+ }).select("kind count").lean();
31
+ const out = {};
32
+ for (const row of rows) out[row.kind] = row.count;
33
+ return out;
34
+ }
35
+ };
36
+ }
37
+ function getOrCreateModel(conn, modelName, collectionName) {
38
+ const existing = "models" in conn ? conn.models[modelName] : void 0;
39
+ if (existing) return existing;
40
+ const schema = new Schema({
41
+ actor: {
42
+ type: String,
43
+ required: true
44
+ },
45
+ period: {
46
+ type: String,
47
+ required: true
48
+ },
49
+ kind: {
50
+ type: String,
51
+ required: true
52
+ },
53
+ count: {
54
+ type: Number,
55
+ required: true,
56
+ default: 0
57
+ }
58
+ }, { versionKey: false });
59
+ schema.index({
60
+ actor: 1,
61
+ period: 1,
62
+ kind: 1
63
+ }, { unique: true });
64
+ return conn.model(modelName, schema, collectionName);
65
+ }
66
+ //#endregion
67
+ export { createMongoUsageStore };
@@ -1,2 +1,2 @@
1
- import { a as IdType, c as isObjectIdInstance, d as filterResponseData, f as getFieldsForUser, i as configureLogger, l as isValidIdForType, m as createError, n as buildCrudSchemasFromMongooseSchema, o as getSchemaIdType, p as getMongooseProjection, r as createMemoryCache, t as buildCrudSchemasFromModel, u as createFieldPreset } from "../mongooseToJsonSchema-D2CyAMIC.mjs";
2
- export { type IdType, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, configureLogger, createError, createFieldPreset, createMemoryCache, filterResponseData, getFieldsForUser, getMongooseProjection, getSchemaIdType, isObjectIdInstance, isValidIdForType };
1
+ import { a as configureLogger, d as createFieldPreset, f as filterResponseData, h as createError, i as createMemoryCache, l as isObjectIdInstance, m as getMongooseProjection, n as buildCrudSchemasFromModel, o as IdType, p as getFieldsForUser, r as buildCrudSchemasFromMongooseSchema, s as getSchemaIdType, t as toPlain, u as isValidIdForType } from "../to-plain-qzKeAC6z.mjs";
2
+ export { type IdType, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, configureLogger, createError, createFieldPreset, createMemoryCache, filterResponseData, getFieldsForUser, getMongooseProjection, getSchemaIdType, isObjectIdInstance, isValidIdForType, toPlain };
@@ -2,6 +2,6 @@ import { i as isValidIdForType, r as isObjectIdInstance, t as getSchemaIdType }
2
2
  import { t as configureLogger } from "../logger-K7dPmu2U.mjs";
3
3
  import { t as createError } from "../error-BuByZwKU.mjs";
4
4
  import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "../field-selection-FSQFLpsU.mjs";
5
- import { t as createMemoryCache } from "../memory-cache-DI1jb4dz.mjs";
5
+ import { n as createMemoryCache, t as toPlain } from "../to-plain-vksRrLtc.mjs";
6
6
  import { n as buildCrudSchemasFromMongooseSchema, t as buildCrudSchemasFromModel } from "../mongooseToJsonSchema-CVnrKFDq.mjs";
7
- export { buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, configureLogger, createError, createFieldPreset, createMemoryCache, filterResponseData, getFieldsForUser, getMongooseProjection, getSchemaIdType, isObjectIdInstance, isValidIdForType };
7
+ export { buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, configureLogger, createError, createFieldPreset, createMemoryCache, filterResponseData, getFieldsForUser, getMongooseProjection, getSchemaIdType, isObjectIdInstance, isValidIdForType, toPlain };
@@ -1,4 +1,4 @@
1
- import { B as ObjectId, C as FieldPreset, G as Plugin, J as PopulateSpec, Q as RepositoryContext, _t as ValidationChainOptions, et as RepositoryInstance, it as SelectSpec, j as Logger, l as CascadeOptions, st as SoftDeleteOptions, ut as SortSpec, vt as ValidatorDefinition } from "./types-DNFMofHy.mjs";
1
+ import { B as ObjectId, C as FieldPreset, G as Plugin, J as PopulateSpec, Q as RepositoryContext, _t as ValidationChainOptions, et as RepositoryInstance, it as SelectSpec, j as Logger, l as CascadeOptions, st as SoftDeleteOptions, ut as SortSpec, vt as ValidatorDefinition } from "./types-BnRqAJVa.mjs";
2
2
  import { BulkWriteResult } from "@classytic/repo-core/repository";
3
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
4
  import { OffsetPaginationResult } from "@classytic/repo-core/pagination";
@@ -87,6 +87,26 @@ interface AggregateHelpersMethods {
87
87
  max(field: string, query?: Record<string, unknown>, options?: Record<string, unknown>): Promise<number>;
88
88
  }
89
89
  //#endregion
90
+ //#region src/plugins/append-only.plugin.d.ts
91
+ interface AppendOnlyPluginOptions {
92
+ /**
93
+ * Mutating ops to permit besides `create` / `createMany`. Use for a
94
+ * domain's ONE legitimate mutation path (e.g. GDPR `deleteMany`) —
95
+ * every allowed op still flows through the rest of the plugin stack
96
+ * (tenant scope, audit) as usual.
97
+ */
98
+ allow?: readonly string[] | undefined;
99
+ /**
100
+ * Hook priority for the fence. Default `HOOK_PRIORITY.POLICY` (100).
101
+ * Ledger repos that want the append-only refusal to win over other
102
+ * policy errors (e.g. a missing-tenant throw) register BELOW policy —
103
+ * flow's StockEvent uses `50` so the violation always reads
104
+ * "append-only", never "missing organizationId".
105
+ */
106
+ priority?: number | undefined;
107
+ }
108
+ declare function appendOnlyPlugin(options?: AppendOnlyPluginOptions): Plugin;
109
+ //#endregion
90
110
  //#region src/plugins/audit-log.plugin.d.ts
91
111
  declare function auditLogPlugin(logger: Logger): Plugin;
92
112
  //#endregion
@@ -163,6 +183,47 @@ declare function elasticSearchPlugin(options: ElasticSearchOptions): Plugin;
163
183
  */
164
184
  declare function fieldFilterPlugin(fieldPreset: FieldPreset): Plugin;
165
185
  //#endregion
186
+ //#region src/plugins/immutable-states.plugin.d.ts
187
+ /** The slice of a `before:claim` context `allowClaim` predicates see. */
188
+ interface ImmutableClaimView {
189
+ id: unknown;
190
+ transition: {
191
+ from?: unknown | readonly unknown[];
192
+ to?: unknown;
193
+ field?: string;
194
+ where?: Record<string, unknown>;
195
+ };
196
+ /** Operator-form patch (`{ $set: {...}, ... }`). */
197
+ data: Record<string, unknown> | undefined;
198
+ }
199
+ interface ImmutableStatesPluginOptions {
200
+ /** States in which the document is frozen (e.g. `['posted']`). */
201
+ states: readonly string[];
202
+ /** State field on the doc — dotted paths supported. Default `'state'`. */
203
+ field?: string | undefined;
204
+ /**
205
+ * Context flag the OWNING ENGINE's verbs set to pass the guard
206
+ * (private handshake — see module doc). Default `'_immutableInternal'`.
207
+ */
208
+ internalFlag?: string | undefined;
209
+ /** Tenant field used to scope state lookups (mirrors the repo's scope). */
210
+ tenantField?: string | undefined;
211
+ /**
212
+ * Exempt specific claim shapes that legitimately touch frozen rows
213
+ * (audit-trail stamps like ledger's reverse-mark). Return `true` to
214
+ * allow. Runs only after the claim was determined to reach a frozen
215
+ * state — keep the predicate a strict fingerprint.
216
+ */
217
+ allowClaim?: ((view: ImmutableClaimView) => boolean) | undefined;
218
+ /** Domain error to throw. Default: 403 `IMMUTABLE_STATE_VIOLATION`. */
219
+ errorFactory?: ((ctx: {
220
+ id: unknown;
221
+ operation: string;
222
+ state?: string | undefined;
223
+ }) => Error) | undefined;
224
+ }
225
+ declare function immutableStatesPlugin(options: ImmutableStatesPluginOptions): Plugin;
226
+ //#endregion
166
227
  //#region src/plugins/lease.plugin.d.ts
167
228
  interface LeasePluginOptions {
168
229
  /** Field carrying the row's status. @default `'status'` */
@@ -759,4 +820,4 @@ declare function immutableField(field: string): ValidatorDefinition;
759
820
  */
760
821
  declare function uniqueField(field: string, errorMessage?: string): ValidatorDefinition;
761
822
  //#endregion
762
- export { BulkWriteResult as A, LeasePluginOptions as C, elasticSearchPlugin as D, ElasticSearchOptions as E, auditLogPlugin as M, AggregateHelpersMethods as N, cascadePlugin as O, aggregateHelpersPlugin as P, LeaseMethods as S, fieldFilterPlugin as T, observabilityPlugin as _, uniqueField as a, MethodRegistryRepository as b, TenantContext as c, SubdocumentMethods as d, subdocumentPlugin as f, OperationMetric as g, ObservabilityOptions as h, requireField as i, batchOperationsPlugin as j, BatchOperationsMethods as k, TenantStore as l, softDeletePlugin as m, blockIf as n, validationChainPlugin as o, SoftDeleteMethods as p, immutableField as r, timestampPlugin as s, autoInject as t, createTenantContext as u, MongoOperationsMethods as v, leasePlugin as w, methodRegistryPlugin as x, mongoOperationsPlugin as y };
823
+ export { elasticSearchPlugin as A, LeasePluginOptions as C, immutableStatesPlugin as D, ImmutableStatesPluginOptions as E, auditLogPlugin as F, AppendOnlyPluginOptions as I, appendOnlyPlugin as L, BatchOperationsMethods as M, BulkWriteResult as N, fieldFilterPlugin as O, batchOperationsPlugin as P, AggregateHelpersMethods as R, LeaseMethods as S, ImmutableClaimView as T, observabilityPlugin as _, uniqueField as a, MethodRegistryRepository as b, TenantContext as c, SubdocumentMethods as d, subdocumentPlugin as f, OperationMetric as g, ObservabilityOptions as h, requireField as i, cascadePlugin as j, ElasticSearchOptions as k, TenantStore as l, softDeletePlugin as m, blockIf as n, validationChainPlugin as o, SoftDeleteMethods as p, immutableField as r, timestampPlugin as s, autoInject as t, createTenantContext as u, MongoOperationsMethods as v, leasePlugin as w, methodRegistryPlugin as x, mongoOperationsPlugin as y, aggregateHelpersPlugin as z };
@@ -1,9 +1,10 @@
1
- import { s as ALL_OPERATIONS } from "./Repository-dbbgOGjK.mjs";
1
+ import { l as OP_REGISTRY, s as ALL_OPERATIONS } from "./Repository-Dr1MDAen.mjs";
2
2
  import { _ as upsert } from "./update-1Lq9OMD2.mjs";
3
3
  import { n as warn } from "./logger-K7dPmu2U.mjs";
4
4
  import { t as createError } from "./error-BuByZwKU.mjs";
5
5
  import { r as getFieldsForUser } from "./field-selection-FSQFLpsU.mjs";
6
6
  import mongoose from "mongoose";
7
+ import { HOOK_PRIORITY } from "@classytic/repo-core/hooks";
7
8
  import { AsyncLocalStorage } from "node:async_hooks";
8
9
  //#region src/plugins/aggregate-helpers.plugin.ts
9
10
  /**
@@ -69,6 +70,90 @@ function aggregateHelpersPlugin() {
69
70
  };
70
71
  }
71
72
  //#endregion
73
+ //#region src/plugins/append-only.plugin.ts
74
+ /**
75
+ * Append-Only Plugin
76
+ *
77
+ * Turns a repository into an immutable-facts ledger: inserts land,
78
+ * everything else is refused at POLICY priority. The enforcement
79
+ * layer for event-sourced planes — flow's `StockEvent`,
80
+ * manufacturing's `ProductionEvent`, audit sinks, outbox archives —
81
+ * where a mutable row would silently corrupt the audit/time-travel
82
+ * story that the whole collection exists to provide.
83
+ *
84
+ * Grep-proof by construction: hand-fenced `update()`/`delete()`
85
+ * overrides only cover the verbs someone remembered to override; this
86
+ * plugin derives coverage from `OP_REGISTRY`, so every mutating op —
87
+ * including `claim`, `claimVersion`, `restore`, `bulkWrite`, and any
88
+ * op added to the registry later — is refused automatically.
89
+ *
90
+ * Allowed by default: `create`, `createMany` (appends). `getOrCreate`
91
+ * is registry-classified as a read and stays available — it can only
92
+ * INSERT or return the existing row, never mutate one, which is
93
+ * append-safe by definition (idempotency stores rely on it).
94
+ * Refused by default: `update`, `findOneAndUpdate`, `updateMany`,
95
+ * `delete`, `deleteMany`, `restore`, `claim`, `claimVersion`,
96
+ * `bulkWrite` (wholesale — mixed insert/update batches are ambiguous;
97
+ * append via `createMany` instead). Composed methods from other
98
+ * plugins (`incrementIfBelow`, lease ops) decompose into these
99
+ * registry ops internally, so the fence catches them too.
100
+ *
101
+ * TTL indexes are unaffected (server-side GC never touches the repo
102
+ * layer) — TTL-expiring an idempotency ledger still works. Raw
103
+ * `Model.*` driver calls bypass every plugin, this one included —
104
+ * the org-wide "never drop to Model.deleteOne" rule is what covers
105
+ * that hole, same as for tenant scope.
106
+ *
107
+ * **Escape hatches** (both audited via `after:append-only-bypass`):
108
+ * - Plugin-level `allow` — ops the DOMAIN legitimately needs, wired
109
+ * once at construction (e.g. `allow: ['deleteMany']` for a
110
+ * GDPR-erasure path the host gates behind admin roles).
111
+ * - Per-call `bypassAppendOnly: true` — deliberate one-off
112
+ * maintenance (backfill repair, migration), grep-auditable at the
113
+ * call site.
114
+ *
115
+ * `skipPlugins` is deliberately IGNORED — same policy as tenant scope
116
+ * and soft delete: integrity fences don't have a convenience bypass.
117
+ *
118
+ * @example
119
+ * const events = new Repository(StockEventModel, [
120
+ * multiTenantPlugin({ ... }),
121
+ * appendOnlyPlugin(),
122
+ * ]);
123
+ * await events.create({ ... }); // OK
124
+ * await events.update(id, { ... }); // throws 405 APPEND_ONLY_VIOLATION
125
+ * await events.deleteMany({ ... }, { bypassAppendOnly: true }); // audited bypass
126
+ */
127
+ const ALWAYS_ALLOWED = /* @__PURE__ */ new Set(["create", "createMany"]);
128
+ function appendOnlyPlugin(options = {}) {
129
+ const allowed = /* @__PURE__ */ new Set([...ALWAYS_ALLOWED, ...options.allow ?? []]);
130
+ return {
131
+ name: "appendOnly",
132
+ apply(repo) {
133
+ const modelName = repo.Model?.modelName ?? "model";
134
+ for (const [op, meta] of Object.entries(OP_REGISTRY)) {
135
+ if (!meta.mutates || allowed.has(op)) continue;
136
+ repo.on(`before:${op}`, (context) => {
137
+ if (context.bypassAppendOnly === true) {
138
+ repo.emit("after:append-only-bypass", {
139
+ operation: op,
140
+ context
141
+ });
142
+ return;
143
+ }
144
+ throw createError(405, `${modelName} is append-only: '${op}' is not permitted. Rows on this collection are immutable facts (event ledger). Append a compensating row instead; for deliberate maintenance pass bypassAppendOnly: true (audited via 'after:append-only-bypass').`, {
145
+ code: "APPEND_ONLY_VIOLATION",
146
+ meta: {
147
+ operation: op,
148
+ model: modelName
149
+ }
150
+ });
151
+ }, { priority: options.priority ?? HOOK_PRIORITY.POLICY });
152
+ }
153
+ }
154
+ };
155
+ }
156
+ //#endregion
72
157
  //#region src/plugins/cascade.plugin.ts
73
158
  /**
74
159
  * Cascade Delete Plugin
@@ -391,6 +476,140 @@ function fieldFilterPlugin(fieldPreset) {
391
476
  };
392
477
  }
393
478
  //#endregion
479
+ //#region src/plugins/immutable-states.plugin.ts
480
+ /** Ops this plugin has an explicit strategy for (see module doc). */
481
+ const STRATEGY_OPS = /* @__PURE__ */ new Set([
482
+ "create",
483
+ "createMany",
484
+ "update",
485
+ "delete",
486
+ "claimVersion",
487
+ "restore",
488
+ "findOneAndUpdate",
489
+ "updateMany",
490
+ "deleteMany",
491
+ "bulkWrite",
492
+ "claim"
493
+ ]);
494
+ function immutableStatesPlugin(options) {
495
+ const field = options.field ?? "state";
496
+ const internalFlag = options.internalFlag ?? "_immutableInternal";
497
+ const frozen = new Set(options.states);
498
+ const throwViolation = (ctx) => {
499
+ if (options.errorFactory) throw options.errorFactory(ctx);
500
+ throw createError(403, `Document ${String(ctx.id)} is in immutable state${ctx.state !== void 0 ? ` '${ctx.state}'` : ""} — '${ctx.operation}' is not permitted. Frozen rows change only through the owning engine's transition verbs.`, {
501
+ code: "IMMUTABLE_STATE_VIOLATION",
502
+ meta: { ...ctx }
503
+ });
504
+ };
505
+ return {
506
+ name: "immutableStates",
507
+ apply(repo) {
508
+ const unhandled = Object.entries(OP_REGISTRY).filter(([op, meta]) => meta.mutates && !STRATEGY_OPS.has(op)).map(([op]) => op);
509
+ if (unhandled.length > 0) throw createError(500, `immutableStatesPlugin has no strategy for mutating op(s): ${unhandled.join(", ")}. Mongokit added write ops this plugin predates — extend the plugin before using it.`, {
510
+ code: "IMMUTABLE_STATES_UNHANDLED_OPS",
511
+ meta: { unhandled }
512
+ });
513
+ const Model = repo.Model;
514
+ const getState = (doc) => field.split(".").reduce((acc, key) => acc?.[key], doc);
515
+ const scoped = (base, ctx) => {
516
+ const q = { ...base };
517
+ if (options.tenantField && ctx.query && options.tenantField in ctx.query) q[options.tenantField] = ctx.query[options.tenantField];
518
+ return q;
519
+ };
520
+ /** Raw-Model read — never re-enters the repo hook pipeline. */
521
+ const readStateById = async (ctx) => {
522
+ if (ctx.id === void 0 || ctx.id === null) return void 0;
523
+ const doc = await Model.findOne(scoped({ _id: ctx.id }, ctx)).select({ [field]: 1 }).lean();
524
+ return doc ? getState(doc) : void 0;
525
+ };
526
+ const guardById = (op) => async (ctx) => {
527
+ if (ctx[internalFlag]) return;
528
+ const state = await readStateById(ctx);
529
+ if (state !== void 0 && frozen.has(state)) throwViolation({
530
+ id: ctx.id,
531
+ operation: op,
532
+ state
533
+ });
534
+ };
535
+ for (const op of [
536
+ "update",
537
+ "delete",
538
+ "claimVersion",
539
+ "restore"
540
+ ]) repo.on(`before:${op}`, guardById(op));
541
+ repo.on("before:findOneAndUpdate", async (ctx) => {
542
+ if (ctx[internalFlag]) return;
543
+ const filter = ctx.query ?? {};
544
+ const doc = await Model.findOne(filter).select({
545
+ [field]: 1,
546
+ _id: 1
547
+ }).lean();
548
+ if (!doc) return;
549
+ const state = getState(doc);
550
+ if (state !== void 0 && frozen.has(state)) throwViolation({
551
+ id: doc._id,
552
+ operation: "findOneAndUpdate",
553
+ state
554
+ });
555
+ });
556
+ const guardMany = (op) => async (ctx) => {
557
+ if (ctx[internalFlag]) return;
558
+ const filter = ctx.query ?? {};
559
+ const hit = await Model.exists({ $and: [filter, { [field]: { $in: [...frozen] } }] });
560
+ if (hit) throwViolation({
561
+ id: hit._id,
562
+ operation: op,
563
+ state: void 0
564
+ });
565
+ };
566
+ repo.on("before:updateMany", guardMany("updateMany"));
567
+ repo.on("before:deleteMany", guardMany("deleteMany"));
568
+ repo.on("before:bulkWrite", (ctx) => {
569
+ if (ctx[internalFlag]) return;
570
+ throwViolation({
571
+ id: void 0,
572
+ operation: "bulkWrite",
573
+ state: void 0
574
+ });
575
+ });
576
+ repo.on("before:claim", async (rawCtx) => {
577
+ const ctx = rawCtx;
578
+ if (ctx[internalFlag]) return;
579
+ const t = ctx.transition;
580
+ if (!t) return;
581
+ if ((t.field ?? "status") === field) {
582
+ const fromSpec = t.from;
583
+ if (!(Array.isArray(fromSpec) ? fromSpec.some((f) => frozen.has(String(f))) : frozen.has(String(fromSpec)))) return;
584
+ if (options.allowClaim?.({
585
+ id: ctx.id,
586
+ transition: t,
587
+ data: ctx.data
588
+ })) return;
589
+ throwViolation({
590
+ id: ctx.id,
591
+ operation: "claim",
592
+ state: void 0
593
+ });
594
+ }
595
+ const state = await readStateById(ctx);
596
+ if (state !== void 0 && frozen.has(state)) {
597
+ if (options.allowClaim?.({
598
+ id: ctx.id,
599
+ transition: t,
600
+ data: ctx.data
601
+ })) return;
602
+ throwViolation({
603
+ id: ctx.id,
604
+ operation: "claim",
605
+ state
606
+ });
607
+ }
608
+ });
609
+ }
610
+ };
611
+ }
612
+ //#endregion
394
613
  //#region src/plugins/lease.plugin.ts
395
614
  function leasePlugin(options = {}) {
396
615
  const statusField = options.statusField ?? "status";
@@ -1053,4 +1272,4 @@ function uniqueField(field, errorMessage) {
1053
1272
  };
1054
1273
  }
1055
1274
  //#endregion
1056
- export { uniqueField as a, subdocumentPlugin as c, leasePlugin as d, fieldFilterPlugin as f, aggregateHelpersPlugin as h, requireField as i, observabilityPlugin as l, cascadePlugin as m, blockIf as n, validationChainPlugin as o, elasticSearchPlugin as p, immutableField as r, createTenantContext as s, autoInject as t, mongoOperationsPlugin as u };
1275
+ export { aggregateHelpersPlugin as _, uniqueField as a, subdocumentPlugin as c, leasePlugin as d, immutableStatesPlugin as f, appendOnlyPlugin as g, cascadePlugin as h, requireField as i, observabilityPlugin as l, elasticSearchPlugin as m, blockIf as n, validationChainPlugin as o, fieldFilterPlugin as p, immutableField as r, createTenantContext as s, autoInject as t, mongoOperationsPlugin as u };