@classytic/mongokit 3.29.0 → 3.31.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/README.md +43 -0
- package/dist/{PaginationEngine-CczbvCAq.mjs → PaginationEngine-CuONvwCM.mjs} +21 -1
- package/dist/{create-repository-D4ZU94b3.d.mts → Repository-BAzymuOz.d.mts} +31 -58
- package/dist/{Repository-DThvFCaV.mjs → Repository-BQ3FEnOa.mjs} +352 -56
- package/dist/actions/index.d.mts +1 -1
- package/dist/actions/index.mjs +1 -1
- package/dist/adapter/index.mjs +1 -1
- package/dist/{adapter-CLvUHiTW.mjs → adapter-BitHg-CN.mjs} +1 -1
- package/dist/ai/index.d.mts +1 -1
- package/dist/better-auth/index.d.mts +13 -0
- package/dist/better-auth/index.mjs +76 -6
- package/dist/{create-repository-uhKt_oUT.mjs → create-repository-BOTVMsIg.mjs} +3 -2
- package/dist/create-repository-mEdfH9sz.d.mts +54 -0
- package/dist/{index-DceInCTX.d.mts → index-Beyr1r4d.d.mts} +1 -1
- package/dist/index.d.mts +305 -43
- package/dist/index.mjs +118 -11
- package/dist/kernel-conformance/index.d.mts +217 -0
- package/dist/kernel-conformance/index.mjs +603 -0
- package/dist/method-registry.plugin-DblM7V6j.mjs +112 -0
- package/dist/model-registry-ClKtWiHa.mjs +67 -0
- package/dist/{mongooseToJsonSchema-CByt1VhQ.mjs → mongooseToJsonSchema-DKbnKduN.mjs} +14 -3
- package/dist/{multi-tenant.plugin-DVyVGREb.d.mts → multi-tenant.plugin-2VtSSm1Y.d.mts} +1 -1
- package/dist/{operations-C8IcCHF9.d.mts → operations-CuzX9m2Z.d.mts} +42 -3
- package/dist/outbox/index.d.mts +63 -0
- package/dist/outbox/index.mjs +239 -0
- package/dist/pagination/PaginationEngine.mjs +1 -1
- package/dist/{plugin-options-vFufQZMZ.d.mts → plugin-options-BsFH0sTi.d.mts} +1 -1
- package/dist/plugins/index.d.mts +4 -4
- package/dist/plugins/index.mjs +3 -2
- package/dist/{repository-DCHRWRXw.d.mts → repository-Q16ErTu8.d.mts} +19 -1
- package/dist/testkit/index.d.mts +8 -1
- package/dist/testkit/index.mjs +1 -1
- package/dist/{timestamp.plugin-CFL6LlYZ.mjs → timestamp.plugin-i9Qxm6kn.mjs} +32 -143
- package/dist/{to-plain-DkF5px0R.d.mts → to-plain-BuX1RUZV.d.mts} +12 -4
- package/dist/{update-CI2NdAW6.mjs → update-DAO5STDv.mjs} +62 -7
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/index.mjs +1 -1
- package/dist/{validation-chain.plugin-BLecaKa0.d.mts → validation-chain.plugin-BSshLJTA.d.mts} +3 -3
- package/dist/{validation-chain.plugin-CwOcUJC1.mjs → validation-chain.plugin-CpcB6dOd.mjs} +4 -3
- package/package.json +193 -187
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { t as createError } from "./error-BuByZwKU.mjs";
|
|
2
|
+
//#region src/plugins/batch-operations.plugin.ts
|
|
3
|
+
/**
|
|
4
|
+
* Batch operations plugin.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const repo = new Repository(Model, [
|
|
8
|
+
* methodRegistryPlugin(),
|
|
9
|
+
* batchOperationsPlugin(),
|
|
10
|
+
* ]);
|
|
11
|
+
*
|
|
12
|
+
* // updateMany + deleteMany are primitives on Repository — no plugin needed.
|
|
13
|
+
* await repo.updateMany({ status: 'pending' }, { status: 'active' });
|
|
14
|
+
* await repo.deleteMany({ status: 'deleted' });
|
|
15
|
+
*
|
|
16
|
+
* // bulkWrite is what this plugin contributes.
|
|
17
|
+
* await repo.bulkWrite([...ops]);
|
|
18
|
+
*/
|
|
19
|
+
function batchOperationsPlugin() {
|
|
20
|
+
return {
|
|
21
|
+
name: "batch-operations",
|
|
22
|
+
apply(repo) {
|
|
23
|
+
if (!repo.registerMethod) throw new Error("batchOperationsPlugin requires methodRegistryPlugin");
|
|
24
|
+
/**
|
|
25
|
+
* Execute heterogeneous bulk write operations in a single database call.
|
|
26
|
+
*
|
|
27
|
+
* Supports insertOne, updateOne, updateMany, deleteOne, deleteMany, and replaceOne
|
|
28
|
+
* operations mixed together for maximum efficiency.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* await repo.bulkWrite([
|
|
32
|
+
* { insertOne: { document: { name: 'New Item', price: 10 } } },
|
|
33
|
+
* { updateOne: { filter: { _id: id1 }, update: { $inc: { views: 1 } } } },
|
|
34
|
+
* { updateMany: { filter: { status: 'draft' }, update: { $set: { status: 'published' } } } },
|
|
35
|
+
* { deleteOne: { filter: { _id: id2 } } },
|
|
36
|
+
* ]);
|
|
37
|
+
*/
|
|
38
|
+
repo.registerMethod("bulkWrite", async function(operations, options = {}) {
|
|
39
|
+
const context = await this._buildContext("bulkWrite", {
|
|
40
|
+
operations,
|
|
41
|
+
...options
|
|
42
|
+
});
|
|
43
|
+
try {
|
|
44
|
+
const finalOps = context.operations || operations;
|
|
45
|
+
if (!finalOps || finalOps.length === 0) throw createError(400, "bulkWrite requires at least one operation");
|
|
46
|
+
const result = await this.Model.bulkWrite(finalOps, {
|
|
47
|
+
ordered: options.ordered ?? true,
|
|
48
|
+
session: options.session
|
|
49
|
+
});
|
|
50
|
+
const bulkResult = {
|
|
51
|
+
ok: result.ok,
|
|
52
|
+
insertedCount: result.insertedCount,
|
|
53
|
+
upsertedCount: result.upsertedCount,
|
|
54
|
+
matchedCount: result.matchedCount,
|
|
55
|
+
modifiedCount: result.modifiedCount,
|
|
56
|
+
deletedCount: result.deletedCount,
|
|
57
|
+
insertedIds: result.insertedIds,
|
|
58
|
+
upsertedIds: result.upsertedIds
|
|
59
|
+
};
|
|
60
|
+
await this.emitAsync("after:bulkWrite", {
|
|
61
|
+
context,
|
|
62
|
+
result: bulkResult
|
|
63
|
+
});
|
|
64
|
+
return bulkResult;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
this.emit("error:bulkWrite", {
|
|
67
|
+
context,
|
|
68
|
+
error
|
|
69
|
+
});
|
|
70
|
+
throw this._handleError(error);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/plugins/method-registry.plugin.ts
|
|
78
|
+
/**
|
|
79
|
+
* Method registry plugin that enables dynamic method registration
|
|
80
|
+
*/
|
|
81
|
+
function methodRegistryPlugin() {
|
|
82
|
+
return {
|
|
83
|
+
name: "method-registry",
|
|
84
|
+
apply(repo) {
|
|
85
|
+
const registeredMethods = [];
|
|
86
|
+
/**
|
|
87
|
+
* Register a new method on the repository instance
|
|
88
|
+
*/
|
|
89
|
+
repo.registerMethod = (name, fn) => {
|
|
90
|
+
if (repo[name]) throw new Error(`Cannot register method '${name}': Method already exists on repository. Choose a different name or use a plugin that doesn't conflict.`);
|
|
91
|
+
if (!name || typeof name !== "string") throw new Error("Method name must be a non-empty string");
|
|
92
|
+
if (typeof fn !== "function") throw new Error(`Method '${name}' must be a function`);
|
|
93
|
+
repo[name] = fn.bind(repo);
|
|
94
|
+
registeredMethods.push(name);
|
|
95
|
+
repo.emit("method:registered", {
|
|
96
|
+
name,
|
|
97
|
+
fn
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Check if a method is registered
|
|
102
|
+
*/
|
|
103
|
+
repo.hasMethod = (name) => typeof repo[name] === "function";
|
|
104
|
+
/**
|
|
105
|
+
* Get list of all dynamically registered methods
|
|
106
|
+
*/
|
|
107
|
+
repo.getRegisteredMethods = () => [...registeredMethods];
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
export { batchOperationsPlugin as n, methodRegistryPlugin as t };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
//#region src/model-registry.ts
|
|
3
|
+
/**
|
|
4
|
+
* Model registration — the ONE place `mongoose.models` is touched.
|
|
5
|
+
*
|
|
6
|
+
* ## Why this exists
|
|
7
|
+
*
|
|
8
|
+
* Mongoose locks a schema on the first `model()` call and throws (`OverwriteModelError`)
|
|
9
|
+
* on the second, so every caller that might register twice hand-rolls a guard:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* const Counter = mongoose.models.LoyaltyCardCounter ?? mongoose.model('LoyaltyCardCounter', schema);
|
|
13
|
+
* if (!mongoose.models.organization) mongoose.model('organization', new Schema({}, { strict: false }));
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* That idiom was written at least five times across the hosts and twice inside mongokit
|
|
17
|
+
* itself. Worse, the hand-rolled form silently REUSES whatever is registered — so a second
|
|
18
|
+
* call passing extra fields or indexes has them dropped with no error, which is how a
|
|
19
|
+
* model ends up missing an index nobody can explain.
|
|
20
|
+
*
|
|
21
|
+
* ## The two policies, made explicit
|
|
22
|
+
*
|
|
23
|
+
* - `onExisting: 'reuse'` — idempotent registration. The caller adds nothing new, so an
|
|
24
|
+
* existing model is the right answer (stubs, counters, boot-time guards).
|
|
25
|
+
* - `onExisting: 'throw'` — the caller is contributing schema (extra fields, indexes) that
|
|
26
|
+
* mongoose CANNOT apply to a locked schema. Silently reusing loses it; this fails loud.
|
|
27
|
+
*
|
|
28
|
+
* There is deliberately no default. Picking one for the caller is how the silent-drop bug
|
|
29
|
+
* got in, and the two are not interchangeable.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* A model name was already registered when a `throw` policy required it to be free.
|
|
33
|
+
*
|
|
34
|
+
* Typed (not a bare `Error`) so consumers — kernels wrapping their own domain collision error,
|
|
35
|
+
* tests asserting the failure — can `instanceof`-check it instead of matching a message string.
|
|
36
|
+
*/
|
|
37
|
+
var ModelCollisionError = class extends Error {
|
|
38
|
+
modelName;
|
|
39
|
+
constructor(modelName, detail) {
|
|
40
|
+
super(`[mongokit] model '${modelName}' is already registered. Mongoose locks a schema on the first model() call, so anything this call adds (fields, indexes) would be SILENTLY DROPPED.${detail ? ` ${detail}` : ""}`);
|
|
41
|
+
this.name = "ModelCollisionError";
|
|
42
|
+
this.modelName = modelName;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/** Is this model name already registered? Prefer {@link defineModel} over asking. */
|
|
46
|
+
function isModelRegistered(name, connection) {
|
|
47
|
+
const registry = connection ? connection.models : mongoose.models;
|
|
48
|
+
return Boolean(registry[name]);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Register a model, or return the existing one, according to `onExisting`.
|
|
52
|
+
*
|
|
53
|
+
* `schema` is a FACTORY so it is never built when an existing model wins — building a
|
|
54
|
+
* schema has side effects (plugin application, index declaration) that should not run for
|
|
55
|
+
* a registration that is about to be discarded.
|
|
56
|
+
*/
|
|
57
|
+
function defineModel(name, schema, options) {
|
|
58
|
+
const target = options.connection;
|
|
59
|
+
const existing = (target ? target.models : mongoose.models)[name];
|
|
60
|
+
if (existing) {
|
|
61
|
+
if (options.onExisting === "throw") throw new ModelCollisionError(name, options.throwHint);
|
|
62
|
+
return existing;
|
|
63
|
+
}
|
|
64
|
+
return target ? target.model(name, schema(), options.collection) : mongoose.model(name, schema(), options.collection);
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
export { defineModel as n, isModelRegistered as r, ModelCollisionError as t };
|
|
@@ -69,6 +69,14 @@ function buildCrudSchemasFromMongooseSchema(mongooseSchema, options = {}) {
|
|
|
69
69
|
* field accepts this function directly with no glue. Future Mongo-shaped
|
|
70
70
|
* kits implementing the same contract are interchangeable at the adapter
|
|
71
71
|
* boundary.
|
|
72
|
+
*
|
|
73
|
+
* GENERIC over the document type, not fixed to `Model<unknown>`. Only
|
|
74
|
+
* `.schema` is read, so the document type is irrelevant here — but Mongoose's
|
|
75
|
+
* `Model<T>` is INVARIANT through `schema.static`, so a hard `Model<unknown>`
|
|
76
|
+
* parameter rejects every concretely-typed model a host actually has. That is
|
|
77
|
+
* not theoretical: it made `arc init`'s generated `example.schemas.ts` fail
|
|
78
|
+
* `tsc` on a brand-new project. Instantiating `TDoc = unknown` still satisfies
|
|
79
|
+
* `SchemaGenerator<Model<unknown>>`, so the contract claim above is unchanged.
|
|
72
80
|
*/
|
|
73
81
|
function buildCrudSchemasFromModel(mongooseModel, options = {}) {
|
|
74
82
|
if (!mongooseModel?.schema) throw new Error("Invalid mongoose model");
|
|
@@ -118,7 +126,8 @@ function buildJsonSchemaFromPaths(mongooseSchema, options) {
|
|
|
118
126
|
properties
|
|
119
127
|
};
|
|
120
128
|
if (required.length) schema.required = required;
|
|
121
|
-
|
|
129
|
+
const fieldsToOmit = collectFieldsToOmit(options, "create");
|
|
130
|
+
applyFieldRules(schema, fieldsToOmit, options);
|
|
122
131
|
const reqOv = options?.create?.requiredOverrides || {};
|
|
123
132
|
const optOv = options?.create?.optionalOverrides || {};
|
|
124
133
|
schema.required = schema.required || [];
|
|
@@ -468,7 +477,8 @@ function stripDefaultsDeep(node) {
|
|
|
468
477
|
function buildJsonSchemaForUpdate(createJson, options) {
|
|
469
478
|
const clone = JSON.parse(JSON.stringify(createJson));
|
|
470
479
|
delete clone.required;
|
|
471
|
-
|
|
480
|
+
const fieldsToOmit = collectFieldsToOmit(options, "update");
|
|
481
|
+
applyFieldRules(clone, fieldsToOmit, options);
|
|
472
482
|
stripDefaultsDeep(clone);
|
|
473
483
|
if (options?.strictAdditionalProperties === true) clone.additionalProperties = false;
|
|
474
484
|
if (options?.update?.requireAtLeastOne === true) clone.minProperties = 1;
|
|
@@ -499,7 +509,8 @@ function buildJsonSchemaForResponse(mongooseSchema, options) {
|
|
|
499
509
|
fieldRules: stripCreateOnlyFieldRulesForResponse(options.fieldRules),
|
|
500
510
|
create: void 0
|
|
501
511
|
});
|
|
502
|
-
|
|
512
|
+
const fieldsToOmit = collectFieldsToOmit(options, "response");
|
|
513
|
+
applyFieldRules(responseSchema, fieldsToOmit, options);
|
|
503
514
|
responseSchema.required = void 0;
|
|
504
515
|
if (options?.strictAdditionalProperties !== true) responseSchema.additionalProperties = true;
|
|
505
516
|
if (fullSchema.title && !responseSchema.title) responseSchema.title = fullSchema.title;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as ObjectId } from "./core-62oo3N1C.mjs";
|
|
2
|
-
import { l as Plugin, p as RepositoryContext } from "./repository-
|
|
2
|
+
import { l as Plugin, p as RepositoryContext } from "./repository-Q16ErTu8.mjs";
|
|
3
3
|
import mongoose, { Connection } from "mongoose";
|
|
4
4
|
import { CacheAdapter as CacheAdapter$1, CacheOptions, RepositoryCacheHandle, RepositoryCachePluginOptions, cachePlugin } from "@classytic/repo-core/cache";
|
|
5
5
|
import { TenantConfig } from "@classytic/repo-core/tenant";
|
|
@@ -26,8 +26,39 @@ interface LookupOptions {
|
|
|
26
26
|
select?: string | readonly string[] | Record<string, 0 | 1>;
|
|
27
27
|
/** Additional pipeline to run on the joined collection */
|
|
28
28
|
pipeline?: PipelineStage[];
|
|
29
|
-
/**
|
|
30
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Let variables for the pipeline form.
|
|
31
|
+
*
|
|
32
|
+
* Values are aggregation EXPRESSIONS, not just field paths — `'$skuRef'` and
|
|
33
|
+
* `{ $toObjectId: '$skuRef' }` are both valid. This was typed
|
|
34
|
+
* `Record<string, string>`, which made every cross-type correlation
|
|
35
|
+
* inexpressible through the typed API and pushed callers to hand-write raw
|
|
36
|
+
* pipelines or give up on the join entirely.
|
|
37
|
+
*/
|
|
38
|
+
let?: Record<string, unknown>;
|
|
39
|
+
/**
|
|
40
|
+
* Coerce `localField` before comparing it to `foreignField`.
|
|
41
|
+
*
|
|
42
|
+
* ## Why this exists
|
|
43
|
+
*
|
|
44
|
+
* A `$lookup` compares values with STRICT BSON type equality. A `string`
|
|
45
|
+
* holding `"6a43c8fd…"` never equals an `ObjectId("6a43c8fd…")`, so the join
|
|
46
|
+
* matches nothing — and a `$lookup` that matches nothing is not an error, it
|
|
47
|
+
* is an empty array. The row renders without its joined data and every count
|
|
48
|
+
* downstream is plausibly, silently wrong.
|
|
49
|
+
*
|
|
50
|
+
* That is not hypothetical: flow stores `skuRef` as an opaque `string` (it
|
|
51
|
+
* must also admit non-ObjectId SKU codes) while `catalog_products._id` is an
|
|
52
|
+
* `ObjectId`. The naive form returned 0 matches for every row; the coerced
|
|
53
|
+
* form returned all of them. The workaround was a second round-trip resolver
|
|
54
|
+
* per request.
|
|
55
|
+
*
|
|
56
|
+
* `'objectId'` compiles to a `$convert` with `onError: null` — NOT
|
|
57
|
+
* `$toObjectId`, which THROWS on the first unconvertible value and would take
|
|
58
|
+
* down the whole query for one legacy SKU code. A row whose ref cannot be an
|
|
59
|
+
* ObjectId simply does not join, which is the same outcome as a genuine miss.
|
|
60
|
+
*/
|
|
61
|
+
coerce?: 'objectId';
|
|
31
62
|
/** Query filter to apply before join (legacy, for aggregate.ts compatibility) */
|
|
32
63
|
query?: Record<string, unknown>;
|
|
33
64
|
/** Query options (legacy, for aggregate.ts compatibility) */
|
|
@@ -75,6 +106,14 @@ declare class LookupBuilder {
|
|
|
75
106
|
* Defaults to the collection name if not specified
|
|
76
107
|
*/
|
|
77
108
|
as(fieldName: string): this;
|
|
109
|
+
/**
|
|
110
|
+
* Coerce the local field before comparing it to the foreign field.
|
|
111
|
+
*
|
|
112
|
+
* Use when the two sides are stored as different BSON types — most commonly a
|
|
113
|
+
* `string` reference against an `ObjectId` `_id`. Without it the join matches
|
|
114
|
+
* nothing and reports no error. See {@link LookupOptions.coerce}.
|
|
115
|
+
*/
|
|
116
|
+
coerce(kind: NonNullable<LookupOptions['coerce']>): this;
|
|
78
117
|
/**
|
|
79
118
|
* Mark this lookup as returning a single document
|
|
80
119
|
* Automatically unwraps the array result to a single object or null
|
|
@@ -98,7 +137,7 @@ declare class LookupBuilder {
|
|
|
98
137
|
* Set let variables for use in pipeline
|
|
99
138
|
* Allows referencing local document fields in the pipeline
|
|
100
139
|
*/
|
|
101
|
-
let(variables: Record<string,
|
|
140
|
+
let(variables: Record<string, unknown>): this;
|
|
102
141
|
/**
|
|
103
142
|
* Control pipeline sanitization (default: true)
|
|
104
143
|
* Set to false for auto-generated pipelines that are known safe
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { n as Repository } from "../Repository-BAzymuOz.mjs";
|
|
2
|
+
import { Connection, Model } from "mongoose";
|
|
3
|
+
//#region src/outbox/index.d.ts
|
|
4
|
+
/** Delivery states. `dead_letter` is terminal and operator-visible. */
|
|
5
|
+
declare const OUTBOX_STATUSES: readonly ["pending", "delivered", "dead_letter"];
|
|
6
|
+
interface OutboxModelOptions {
|
|
7
|
+
/** Collection name. Default `event_outbox`. */
|
|
8
|
+
collection?: string;
|
|
9
|
+
/** Mongoose model name. Default `EventOutbox`. Must be unique per connection. */
|
|
10
|
+
modelName?: string;
|
|
11
|
+
/**
|
|
12
|
+
* How long a DELIVERED row is retained before the TTL monitor removes it.
|
|
13
|
+
* Default 7 days. Pending and dead-lettered rows are never auto-removed —
|
|
14
|
+
* expiring an undelivered event would destroy the guarantee the table exists for.
|
|
15
|
+
*/
|
|
16
|
+
ttlSeconds?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Column holding the "not claimable before" timestamp. Default `visibleAt`.
|
|
19
|
+
*
|
|
20
|
+
* Configurable because an existing table cannot be renamed without rebuilding the claim
|
|
21
|
+
* index — the one index whose absence turns a busy outbox into a collection scan. One live
|
|
22
|
+
* host uses `nextVisibleAt`; it passes that here and keeps its data and indexes untouched.
|
|
23
|
+
* The same value must be given to `repositoryAsOutboxStore`, or the writer and the claimer
|
|
24
|
+
* disagree about which column means "visible" and the queue silently stops draining.
|
|
25
|
+
*/
|
|
26
|
+
visibleAtField?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build (or return) the outbox model on `connection`.
|
|
30
|
+
*
|
|
31
|
+
* Idempotent per connection: calling twice returns the same model rather than throwing
|
|
32
|
+
* `OverwriteModelError`, so a host may build it lazily from several call sites.
|
|
33
|
+
*/
|
|
34
|
+
declare function createOutboxModel(connection: Connection, options?: OutboxModelOptions): Model<Record<string, unknown>>;
|
|
35
|
+
/**
|
|
36
|
+
* The repository `repositoryAsOutboxStore` needs, with the plugin set it actually calls.
|
|
37
|
+
*
|
|
38
|
+
* The adapter drives `create` / `getOne` / `getAll` / `findOneAndUpdate` (base `Repository`)
|
|
39
|
+
* plus `deleteMany`, which is contributed by `batchOperationsPlugin` THROUGH the method
|
|
40
|
+
* registry. Both plugins are therefore required, and in that pairing — a host that passes only
|
|
41
|
+
* `batchOperationsPlugin()` gets a repository whose `deleteMany` is never registered.
|
|
42
|
+
*
|
|
43
|
+
* This existed as three hand-written lines in a host, which is two lines too many to get wrong
|
|
44
|
+
* silently: the wrong plugin list does not fail at boot, it fails the first time a delivered row
|
|
45
|
+
* is swept, on a code path nobody watches. `Repository` validates the registered set at
|
|
46
|
+
* construction and throws naming what is missing, so composing it HERE moves that check to
|
|
47
|
+
* every consumer instead of the one host that remembered.
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* const store = repositoryAsOutboxStore(
|
|
51
|
+
* createOutboxRepository(connection), // this
|
|
52
|
+
* { visibleAtField: 'nextVisibleAt' }, // iff you passed the same to createOutboxModel
|
|
53
|
+
* );
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* `plugins` appends to the required set rather than replacing it — for an observability or
|
|
57
|
+
* cache plugin a deployment wants on this table too.
|
|
58
|
+
*/
|
|
59
|
+
declare function createOutboxRepository(connection: Connection, options?: OutboxModelOptions & {
|
|
60
|
+
plugins?: readonly unknown[];
|
|
61
|
+
}): Repository<Record<string, unknown>>;
|
|
62
|
+
//#endregion
|
|
63
|
+
export { OUTBOX_STATUSES, OutboxModelOptions, createOutboxModel, createOutboxRepository };
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { n as Repository } from "../Repository-BQ3FEnOa.mjs";
|
|
2
|
+
import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "../method-registry.plugin-DblM7V6j.mjs";
|
|
3
|
+
import { Schema } from "mongoose";
|
|
4
|
+
//#region src/outbox/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* The mongo-backed OUTBOX table — schema, indexes and repository.
|
|
7
|
+
*
|
|
8
|
+
* ## Why this is here
|
|
9
|
+
*
|
|
10
|
+
* Two ERP hosts independently hand-rolled this: 465 lines in one, 301 in the other, both
|
|
11
|
+
* implementing the same `OutboxStore` contract over the same database. The second host's source
|
|
12
|
+
* even carries the comment *"be-prod learned that the hard way"* about a partial-index pitfall —
|
|
13
|
+
* the LESSON transferred and the code was rewritten anyway. That is the failure mode worth
|
|
14
|
+
* naming: duplication here does not fail loudly, it drifts, and this is the table that guarantees
|
|
15
|
+
* a collected payment is eventually recorded.
|
|
16
|
+
*
|
|
17
|
+
* ## What this module is, and is not
|
|
18
|
+
*
|
|
19
|
+
* It is the MONGO half only: a schema, the four indexes that make claim/dedupe/retention correct,
|
|
20
|
+
* and a repository over them. It does NOT implement `OutboxStore` — `@classytic/arc`'s
|
|
21
|
+
* `repositoryAsOutboxStore` already does that, generically, over any repository. Keeping the two
|
|
22
|
+
* apart is what avoids a dependency cycle (mongokit does not depend on arc) and what lets a
|
|
23
|
+
* non-mongo kit reuse the contract half.
|
|
24
|
+
*
|
|
25
|
+
* A host composes three lines:
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* const model = createOutboxModel(connection, { visibleAtField: 'nextVisibleAt' });
|
|
29
|
+
* const repository = new Repository(model);
|
|
30
|
+
* const store = repositoryAsOutboxStore(repository, { visibleAtField: 'nextVisibleAt' });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
/** Delivery states. `dead_letter` is terminal and operator-visible. */
|
|
34
|
+
const OUTBOX_STATUSES = [
|
|
35
|
+
"pending",
|
|
36
|
+
"delivered",
|
|
37
|
+
"dead_letter"
|
|
38
|
+
];
|
|
39
|
+
const DEFAULT_TTL_SECONDS = 604800;
|
|
40
|
+
/**
|
|
41
|
+
* Build (or return) the outbox model on `connection`.
|
|
42
|
+
*
|
|
43
|
+
* Idempotent per connection: calling twice returns the same model rather than throwing
|
|
44
|
+
* `OverwriteModelError`, so a host may build it lazily from several call sites.
|
|
45
|
+
*/
|
|
46
|
+
function createOutboxModel(connection, options = {}) {
|
|
47
|
+
const { collection = "event_outbox", modelName = "EventOutbox", ttlSeconds = DEFAULT_TTL_SECONDS, visibleAtField = "visibleAt" } = options;
|
|
48
|
+
const existing = connection.models[modelName];
|
|
49
|
+
if (existing) return existing;
|
|
50
|
+
/**
|
|
51
|
+
* The document shape is `repositoryAsOutboxStore`'s, not an invention of this module.
|
|
52
|
+
*
|
|
53
|
+
* Two things about it surprise people coming from a hand-rolled table, and both are
|
|
54
|
+
* deliberate:
|
|
55
|
+
*
|
|
56
|
+
* 1. **`_id` IS the event id** (a string), not an ObjectId with a separate `eventId`
|
|
57
|
+
* column. That is what makes `save` idempotent for free — a redelivered event is a
|
|
58
|
+
* duplicate-key error the adapter swallows, with no read-then-write race to lose.
|
|
59
|
+
* 2. **The whole `event` is nested**, rather than split into `payload` + `meta`. The relay
|
|
60
|
+
* republishes the event verbatim; splitting it means reassembling it on the way out, and
|
|
61
|
+
* a reassembly that drifts from the original is a corrupted replay nobody can detect.
|
|
62
|
+
*
|
|
63
|
+
* A host with an EXISTING outbox in a different shape cannot simply point this at its
|
|
64
|
+
* collection — the ids are of a different type. That is a data migration, not a config
|
|
65
|
+
* change, and it must be planned rather than discovered.
|
|
66
|
+
*/
|
|
67
|
+
const schema = new Schema({
|
|
68
|
+
/** The event id itself. String, because the adapter writes `event.meta.id` here. */
|
|
69
|
+
_id: {
|
|
70
|
+
type: String,
|
|
71
|
+
required: true
|
|
72
|
+
},
|
|
73
|
+
/** The event, verbatim, as the relay will republish it. */
|
|
74
|
+
event: {
|
|
75
|
+
type: Schema.Types.Mixed,
|
|
76
|
+
required: true
|
|
77
|
+
},
|
|
78
|
+
type: {
|
|
79
|
+
type: String,
|
|
80
|
+
required: true
|
|
81
|
+
},
|
|
82
|
+
status: {
|
|
83
|
+
type: String,
|
|
84
|
+
enum: [...OUTBOX_STATUSES],
|
|
85
|
+
default: "pending"
|
|
86
|
+
},
|
|
87
|
+
attempts: {
|
|
88
|
+
type: Number,
|
|
89
|
+
default: 0
|
|
90
|
+
},
|
|
91
|
+
/**
|
|
92
|
+
* Caller-supplied dedupe key. `null` when absent — see the partial index below for
|
|
93
|
+
* why null must not participate in the unique constraint.
|
|
94
|
+
*/
|
|
95
|
+
dedupeKey: {
|
|
96
|
+
type: String,
|
|
97
|
+
default: null
|
|
98
|
+
},
|
|
99
|
+
partitionKey: {
|
|
100
|
+
type: String,
|
|
101
|
+
default: null
|
|
102
|
+
},
|
|
103
|
+
headers: {
|
|
104
|
+
type: Schema.Types.Mixed,
|
|
105
|
+
default: null
|
|
106
|
+
},
|
|
107
|
+
leaseOwner: {
|
|
108
|
+
type: String,
|
|
109
|
+
default: null
|
|
110
|
+
},
|
|
111
|
+
leaseExpiresAt: {
|
|
112
|
+
type: Date,
|
|
113
|
+
default: null
|
|
114
|
+
},
|
|
115
|
+
[visibleAtField]: {
|
|
116
|
+
type: Date,
|
|
117
|
+
default: Date.now
|
|
118
|
+
},
|
|
119
|
+
deliveredAt: {
|
|
120
|
+
type: Date,
|
|
121
|
+
default: null
|
|
122
|
+
},
|
|
123
|
+
firstFailedAt: {
|
|
124
|
+
type: Date,
|
|
125
|
+
default: null
|
|
126
|
+
},
|
|
127
|
+
lastFailedAt: {
|
|
128
|
+
type: Date,
|
|
129
|
+
default: null
|
|
130
|
+
},
|
|
131
|
+
lastError: {
|
|
132
|
+
type: Schema.Types.Mixed,
|
|
133
|
+
default: null
|
|
134
|
+
},
|
|
135
|
+
createdAt: {
|
|
136
|
+
type: Date,
|
|
137
|
+
default: Date.now
|
|
138
|
+
}
|
|
139
|
+
}, {
|
|
140
|
+
/**
|
|
141
|
+
* `strict: false` so a host may stamp extra columns without a schema change, and
|
|
142
|
+
* `timestamps: false` because `createdAt` is set by the adapter — letting mongoose
|
|
143
|
+
* manage it would add an `updatedAt` the claim index does not cover.
|
|
144
|
+
*
|
|
145
|
+
* `_id: false` disables mongoose's ObjectId auto-generation; the adapter always
|
|
146
|
+
* supplies the id.
|
|
147
|
+
*/
|
|
148
|
+
strict: false,
|
|
149
|
+
timestamps: false,
|
|
150
|
+
collection,
|
|
151
|
+
versionKey: false,
|
|
152
|
+
_id: false
|
|
153
|
+
});
|
|
154
|
+
/**
|
|
155
|
+
* THE CLAIM INDEX. The relay's hot query is
|
|
156
|
+
* `status = 'pending' AND <visibleAt> <= now`, ordered by `createdAt`.
|
|
157
|
+
* Without this the relay table-scans on every tick.
|
|
158
|
+
*/
|
|
159
|
+
schema.index({
|
|
160
|
+
status: 1,
|
|
161
|
+
[visibleAtField]: 1,
|
|
162
|
+
createdAt: 1
|
|
163
|
+
});
|
|
164
|
+
/**
|
|
165
|
+
* Retention. Scoped to `deliveredAt`, which is only ever set on success — so a pending or
|
|
166
|
+
* dead-lettered row has `null` and is never expired. A TTL on `createdAt` would silently
|
|
167
|
+
* delete undelivered events, which is the one thing this table must never do.
|
|
168
|
+
*/
|
|
169
|
+
schema.index({ deliveredAt: 1 }, { expireAfterSeconds: ttlSeconds });
|
|
170
|
+
/**
|
|
171
|
+
* Dedupe. `unique` + `partialFilterExpression` on the TYPE, not on `$ne: null`.
|
|
172
|
+
*
|
|
173
|
+
* Mongo rejects `unique` combined with a `sparse: true` + partial filter ("cannot mix"), and
|
|
174
|
+
* the failure is that the index silently never builds — so duplicate events insert happily
|
|
175
|
+
* and the outbox's idempotency guarantee is gone with nothing in the logs. Filtering on
|
|
176
|
+
* `$type: 'string'` indexes only rows that actually carry a key.
|
|
177
|
+
*/
|
|
178
|
+
schema.index({ dedupeKey: 1 }, {
|
|
179
|
+
unique: true,
|
|
180
|
+
partialFilterExpression: { dedupeKey: { $type: "string" } }
|
|
181
|
+
});
|
|
182
|
+
/**
|
|
183
|
+
* The DEAD-LETTER read. `getDeadLettered` is part of the `OutboxStore` contract and is what
|
|
184
|
+
* an operator alert polls — hourly, forever, on a table that grows. Partial, because the
|
|
185
|
+
* rows an operator reads are a vanishing fraction of the rows written, and a full index on
|
|
186
|
+
* `status` would be almost entirely `delivered` entries nobody queries by status alone.
|
|
187
|
+
*
|
|
188
|
+
* A host that dropped this still worked: the alert just collection-scanned every hour, which
|
|
189
|
+
* is invisible until the table is large and then is a periodic production stall.
|
|
190
|
+
*/
|
|
191
|
+
schema.index({
|
|
192
|
+
status: 1,
|
|
193
|
+
lastFailedAt: 1
|
|
194
|
+
}, { partialFilterExpression: { status: "dead_letter" } });
|
|
195
|
+
/**
|
|
196
|
+
* There is deliberately NO `claimToken` index.
|
|
197
|
+
*
|
|
198
|
+
* One host's fork claims with a generated token; `repositoryAsOutboxStore` claims with a
|
|
199
|
+
* LEASE (`leaseOwner` + `leaseExpiresAt`), already covered above. An index copied across
|
|
200
|
+
* from that fork would be permanently empty here — free to write, but it advertises a
|
|
201
|
+
* column this shape never sets, which is how the next reader concludes the two tables are
|
|
202
|
+
* interchangeable.
|
|
203
|
+
*/
|
|
204
|
+
return connection.model(modelName, schema, collection);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The repository `repositoryAsOutboxStore` needs, with the plugin set it actually calls.
|
|
208
|
+
*
|
|
209
|
+
* The adapter drives `create` / `getOne` / `getAll` / `findOneAndUpdate` (base `Repository`)
|
|
210
|
+
* plus `deleteMany`, which is contributed by `batchOperationsPlugin` THROUGH the method
|
|
211
|
+
* registry. Both plugins are therefore required, and in that pairing — a host that passes only
|
|
212
|
+
* `batchOperationsPlugin()` gets a repository whose `deleteMany` is never registered.
|
|
213
|
+
*
|
|
214
|
+
* This existed as three hand-written lines in a host, which is two lines too many to get wrong
|
|
215
|
+
* silently: the wrong plugin list does not fail at boot, it fails the first time a delivered row
|
|
216
|
+
* is swept, on a code path nobody watches. `Repository` validates the registered set at
|
|
217
|
+
* construction and throws naming what is missing, so composing it HERE moves that check to
|
|
218
|
+
* every consumer instead of the one host that remembered.
|
|
219
|
+
*
|
|
220
|
+
* ```ts
|
|
221
|
+
* const store = repositoryAsOutboxStore(
|
|
222
|
+
* createOutboxRepository(connection), // this
|
|
223
|
+
* { visibleAtField: 'nextVisibleAt' }, // iff you passed the same to createOutboxModel
|
|
224
|
+
* );
|
|
225
|
+
* ```
|
|
226
|
+
*
|
|
227
|
+
* `plugins` appends to the required set rather than replacing it — for an observability or
|
|
228
|
+
* cache plugin a deployment wants on this table too.
|
|
229
|
+
*/
|
|
230
|
+
function createOutboxRepository(connection, options = {}) {
|
|
231
|
+
const { plugins = [], ...modelOptions } = options;
|
|
232
|
+
return new Repository(createOutboxModel(connection, modelOptions), [
|
|
233
|
+
methodRegistryPlugin(),
|
|
234
|
+
batchOperationsPlugin(),
|
|
235
|
+
...plugins
|
|
236
|
+
]);
|
|
237
|
+
}
|
|
238
|
+
//#endregion
|
|
239
|
+
export { OUTBOX_STATUSES, createOutboxModel, createOutboxRepository };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as PaginationEngine } from "../PaginationEngine-
|
|
1
|
+
import { t as PaginationEngine } from "../PaginationEngine-CuONvwCM.mjs";
|
|
2
2
|
export { PaginationEngine };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as PopulateSpec, i as ObjectId, l as SortSpec, s as SelectSpec } from "./core-62oo3N1C.mjs";
|
|
2
|
-
import { h as RepositoryInstance, p as RepositoryContext } from "./repository-
|
|
2
|
+
import { h as RepositoryInstance, p as RepositoryContext } from "./repository-Q16ErTu8.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) */
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { S as RepositoryPluginName, b as MONGOKIT_PLUGIN_NAMES, l as Plugin, x as MongoKitPluginName } from "../repository-
|
|
2
|
-
import { A as ensureAuditTrailReady, C as AuditTrailMethods, D as AuditTrailQueryOptions, E as AuditTrailQuery, O as EnsureAuditTrailReadyOptions, S as AuditQueryResult, T as AuditTrailOptions, _ as RepositoryCachePluginOptions, a as DateSequentialIdOptions, b as AuditOperation, c as SequentialIdOptions, d as getNextSequence, f as prefixedId, g as RepositoryCacheHandle, h as CacheOptions, i as CustomIdOptions, k as auditTrailPlugin, l as customIdPlugin, m as CacheAdapter, o as IdGenerator, p as sequentialId, r as multiTenantPlugin, s as PrefixedIdOptions, t as MultiTenantOptions, u as dateSequentialId, v as cachePlugin, w as AuditTrailMode, x as AuditQueryOptions, y as AuditEntry } from "../multi-tenant.plugin-
|
|
3
|
-
import { A as
|
|
1
|
+
import { S as RepositoryPluginName, b as MONGOKIT_PLUGIN_NAMES, l as Plugin, x as MongoKitPluginName } from "../repository-Q16ErTu8.mjs";
|
|
2
|
+
import { A as ensureAuditTrailReady, C as AuditTrailMethods, D as AuditTrailQueryOptions, E as AuditTrailQuery, O as EnsureAuditTrailReadyOptions, S as AuditQueryResult, T as AuditTrailOptions, _ as RepositoryCachePluginOptions, a as DateSequentialIdOptions, b as AuditOperation, c as SequentialIdOptions, d as getNextSequence, f as prefixedId, g as RepositoryCacheHandle, h as CacheOptions, i as CustomIdOptions, k as auditTrailPlugin, l as customIdPlugin, m as CacheAdapter, o as IdGenerator, p as sequentialId, r as multiTenantPlugin, s as PrefixedIdOptions, t as MultiTenantOptions, u as dateSequentialId, v as cachePlugin, w as AuditTrailMode, x as AuditQueryOptions, y as AuditEntry } from "../multi-tenant.plugin-2VtSSm1Y.mjs";
|
|
3
|
+
import { A as IdempotencyStoreMethods, B as auditLogPlugin, C as LeaseMethods, D as ImmutableStatesPluginOptions, E as ImmutableClaimView, F as elasticSearchPlugin, H as appendOnlyPlugin, 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 AggregateHelpersMethods, V as AppendOnlyPluginOptions, W as aggregateHelpersPlugin, _ 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, z as batchOperationsPlugin } from "../validation-chain.plugin-BSshLJTA.mjs";
|
|
4
4
|
import { ChangeLogStore } from "@classytic/repo-core/sync";
|
|
5
5
|
//#region src/plugins/change-log.plugin.d.ts
|
|
6
6
|
interface ChangeLogPluginOptions {
|
|
@@ -15,4 +15,4 @@ interface ChangeLogPluginOptions {
|
|
|
15
15
|
}
|
|
16
16
|
declare function changeLogPlugin(options: ChangeLogPluginOptions): Plugin;
|
|
17
17
|
//#endregion
|
|
18
|
-
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 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 };
|
|
18
|
+
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 };
|
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import {
|
|
1
|
+
import { a as customIdPlugin, c as prefixedId, d as AuditTrailQuery, f as auditTrailPlugin, i as multiTenantPlugin, l as sequentialId, m as auditLogPlugin, n as softDeletePlugin, o as dateSequentialId, p as ensureAuditTrailReady, s as getNextSequence, t as timestampPlugin, u as cachePlugin } from "../timestamp.plugin-i9Qxm6kn.mjs";
|
|
2
|
+
import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "../method-registry.plugin-DblM7V6j.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-CpcB6dOd.mjs";
|
|
3
4
|
//#region src/plugins/change-log.plugin.ts
|
|
4
5
|
const PLUGIN_NAME = "changeLog";
|
|
5
6
|
function isSkipped(context) {
|