@web-ts-toolkit/moo 0.43.0 → 0.47.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.
- package/README.md +122 -7
- package/index.d.mts +2 -2
- package/index.d.ts +2 -2
- package/index.js +386 -57
- package/index.mjs +386 -57
- package/is.d.mts +2 -2
- package/is.d.ts +2 -2
- package/is.js +8 -7
- package/is.mjs +8 -7
- package/llms.txt +33 -5
- package/package.json +3 -3
- package/plugins/cascade-delete.d.mts +151 -4
- package/plugins/cascade-delete.d.ts +151 -4
- package/plugins/cascade-delete.js +354 -33
- package/plugins/cascade-delete.mjs +354 -33
- package/plugins/index.d.mts +1 -1
- package/plugins/index.d.ts +1 -1
- package/plugins/index.js +354 -33
- package/plugins/index.mjs +354 -33
- package/plugins/keycloak-user-sync.d.mts +32 -4
- package/plugins/keycloak-user-sync.d.ts +32 -4
- package/plugins/keycloak-user-sync.js +207 -45
- package/plugins/keycloak-user-sync.mjs +207 -45
- package/plugins/model-function.d.mts +46 -0
- package/plugins/model-function.d.ts +46 -0
- package/plugins/new-document.d.mts +21 -2
- package/plugins/new-document.d.ts +21 -2
- package/schema.d.mts +34 -5
- package/schema.d.ts +34 -5
- package/schema.js +24 -17
- package/schema.mjs +24 -17
|
@@ -1,14 +1,58 @@
|
|
|
1
1
|
import { Schema } from 'mongoose';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* MongoDB query operators accepted for a single document field in
|
|
5
|
+
* {@link QueryFilter}. The runtime forwards relationship and full filters
|
|
6
|
+
* straight to Mongoose `find()`, so operator objects (for example
|
|
7
|
+
* `{ price: { $gt: 5 } }`) are evaluated by MongoDB rather than by this
|
|
8
|
+
* package. Each field therefore accepts either its plain document value or
|
|
9
|
+
* an operator object; top-level logical operators (`$and`/`$or`/`$not`/`$nor`)
|
|
10
|
+
* remain available through the trailing `Record<string, unknown>` index.
|
|
11
|
+
*/
|
|
12
|
+
type QueryOperators<TValue = unknown> = {
|
|
13
|
+
$eq?: TValue;
|
|
14
|
+
$ne?: TValue;
|
|
15
|
+
$gt?: TValue;
|
|
16
|
+
$gte?: TValue;
|
|
17
|
+
$lt?: TValue;
|
|
18
|
+
$lte?: TValue;
|
|
19
|
+
$in?: TValue[];
|
|
20
|
+
$nin?: TValue[];
|
|
21
|
+
$exists?: boolean;
|
|
22
|
+
$regex?: string | RegExp;
|
|
23
|
+
$type?: string | string[];
|
|
24
|
+
$not?: QueryOperators<TValue> | RegExp;
|
|
25
|
+
$size?: number;
|
|
26
|
+
$all?: TValue[];
|
|
27
|
+
$elemMatch?: Record<string, unknown>;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Typed dependent-collection filter. Each declared document field accepts its
|
|
31
|
+
* plain value, a {@link QueryOperators} object, or `null`; undeclared paths
|
|
32
|
+
* (including `$and`/`$or` operator payloads composed by
|
|
33
|
+
* `extraForeignFilter`) are accepted through the index signature and passed
|
|
34
|
+
* to Mongoose unchanged.
|
|
35
|
+
*/
|
|
36
|
+
type QueryFilter<TDocument extends Record<string, unknown> = Record<string, unknown>> = {
|
|
37
|
+
[K in keyof TDocument]?: TDocument[K] | QueryOperators<TDocument[K]> | null;
|
|
38
|
+
} & Record<string, unknown>;
|
|
39
|
+
/**
|
|
40
|
+
* Dependents grouped by configured model name. The no-argument
|
|
41
|
+
* `findDependents()`/`findOrphans()` overloads return a `Partial` map:
|
|
42
|
+
* entries whose lookup is unsupported (for example `findOrphans()` for a
|
|
43
|
+
* non-`_id` local key, dotted foreign path, dynamic `refPath`, or
|
|
44
|
+
* `foreignFilter`-only relationship) or whose resolver produced a
|
|
45
|
+
* nullish/non-object result are omitted rather than reported as empty
|
|
46
|
+
* arrays, matching the `mergeResults` runtime behavior.
|
|
47
|
+
*/
|
|
4
48
|
type CascadeDeleteDependencyMap<TModelName extends string, TDependentDocument = unknown> = Record<TModelName, TDependentDocument[]>;
|
|
5
49
|
type CascadeDeleteDocumentMethods<TModelName extends string, TDependentDocument = unknown> = {
|
|
6
|
-
findDependents(): Promise<CascadeDeleteDependencyMap<TModelName, TDependentDocument
|
|
50
|
+
findDependents(): Promise<Partial<CascadeDeleteDependencyMap<TModelName, TDependentDocument>> & Record<string, unknown[]>>;
|
|
7
51
|
findDependents(modelName: TModelName): Promise<TDependentDocument[]>;
|
|
8
52
|
findDependents(modelName: string): Promise<unknown[] | null>;
|
|
9
53
|
};
|
|
10
54
|
type CascadeDeleteModelStatics<TModelName extends string, TDependentDocument = unknown> = {
|
|
11
|
-
findOrphans(): Promise<CascadeDeleteDependencyMap<TModelName, TDependentDocument
|
|
55
|
+
findOrphans(): Promise<Partial<CascadeDeleteDependencyMap<TModelName, TDependentDocument>> & Record<string, unknown[]>>;
|
|
12
56
|
findOrphans(modelName: TModelName): Promise<TDependentDocument[] | null>;
|
|
13
57
|
findOrphans(modelName: string): Promise<unknown[] | null>;
|
|
14
58
|
};
|
|
@@ -19,7 +63,110 @@ interface CascadeDeletePluginOptions<TModelName extends string = string, TDepend
|
|
|
19
63
|
foreignField?: string;
|
|
20
64
|
foreignFilter?: FilterResolver<TDependentDocument>;
|
|
21
65
|
extraForeignFilter?: FilterResolver<TDependentDocument>;
|
|
66
|
+
/**
|
|
67
|
+
* Maximum dependent deletes allowed in flight at once for this relationship.
|
|
68
|
+
* Must be an integer >= 1. Defaults to 8. Inside an active transaction the
|
|
69
|
+
* effective limit is forced to 1 (MOO-04 serialization) regardless of this
|
|
70
|
+
* value. Public `findDependents()` is unaffected: it still returns an array.
|
|
71
|
+
*/
|
|
72
|
+
maxConcurrency?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Maximum dependent `_id`s acquired per internal ID-page query. Must be an
|
|
75
|
+
* integer >= 1. Defaults to 100. Bounds acquisition memory; deletion work is
|
|
76
|
+
* never all materialized or scheduled up front.
|
|
77
|
+
*/
|
|
78
|
+
batchSize?: number;
|
|
22
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Cascades document `deleteOne()` to configured dependent models.
|
|
82
|
+
*
|
|
83
|
+
* Supported operations: only document `deleteOne()` (and legacy document
|
|
84
|
+
* `remove()` for dependents) is intercepted, through
|
|
85
|
+
* `pre('deleteOne', { document: true, query: false })` and
|
|
86
|
+
* `post('deleteOne', { document: true, query: false })`. Query helpers such
|
|
87
|
+
* as `deleteMany()`, `findOneAndDelete()`, `updateOne()`, and query
|
|
88
|
+
* `deleteOne()` bypass the cascade entirely. `findDependents()` is a
|
|
89
|
+
* document method; `findOrphans()` is a model static with the support matrix
|
|
90
|
+
* documented on {@link CascadeDeleteDependencyMap} (unsupported shapes
|
|
91
|
+
* resolve to `null` and are omitted from the no-argument map).
|
|
92
|
+
*
|
|
93
|
+
* Post-delete failure timing: the `post('deleteOne')` hook runs after the
|
|
94
|
+
* parent document is already removed. A later dependent failure therefore
|
|
95
|
+
* rejects the parent `deleteOne()` promise without restoring the parent —
|
|
96
|
+
* see the traversal contract below for the transactional abort path.
|
|
97
|
+
*
|
|
98
|
+
* Supported behavior: dependent models resolve through the owning model's
|
|
99
|
+
* connection (duplicate names on other connections stay isolated); the
|
|
100
|
+
* effective session (explicit `deleteOne({ session })` option, else the
|
|
101
|
+
* document-bound `$session()`) flows through dependent reads and deletes,
|
|
102
|
+
* including nested cascades; inside an active transaction dependent deletes run
|
|
103
|
+
* serially on that session so replica-set commit/abort covers parent and
|
|
104
|
+
* dependents together. Unsupported modes fail before the parent is removed: a
|
|
105
|
+
* session from a different connection/client, or an active transaction on a
|
|
106
|
+
* standalone (`Single`) topology.
|
|
107
|
+
*
|
|
108
|
+
* Predicate fail-closed contract (MOO-05):
|
|
109
|
+
*
|
|
110
|
+
* - Relationship mode (`localField` + `foreignField`) never issues a
|
|
111
|
+
* destructive query without a valid relationship. Missing/`null`/empty
|
|
112
|
+
* local keys (including empty reference arrays and arrays containing only
|
|
113
|
+
* nullish entries) resolve to zero dependents and delete nothing, so
|
|
114
|
+
* unrelated records with a missing/`null` foreign key survive. A local field
|
|
115
|
+
* that was omitted by a projection is distinguished from an intentionally
|
|
116
|
+
* empty relation: when the path is provably deselected (`$isSelected()`
|
|
117
|
+
* returns `false`) the lookup throws instead of silently reporting zero
|
|
118
|
+
* dependents.
|
|
119
|
+
* - Supplemental `extraForeignFilter` constraints are composed conjunctively
|
|
120
|
+
* (`{ $and: [relationship, extra] }`). An extra filter on the relationship
|
|
121
|
+
* field therefore intersects instead of replacing or widening the deletion
|
|
122
|
+
* set; `$or`/`$and`/operator payloads inside the extra filter stay
|
|
123
|
+
* constrained by the relationship predicate. An absent, nullish,
|
|
124
|
+
* non-object, or empty extra filter leaves the relationship unchanged.
|
|
125
|
+
* - Explicit full-filter mode (`foreignFilter`) is a separately documented
|
|
126
|
+
* contract: the resolved filter is used as-is with no relationship
|
|
127
|
+
* predicate, and any configured `extraForeignFilter` is ignored. A resolver
|
|
128
|
+
* returning `null`/`undefined`/non-object resolves to zero dependents.
|
|
129
|
+
* Maintainer decision: an intentionally empty full filter (`{}`) is rejected
|
|
130
|
+
* by default — statically at `schema.plugin(...)` time and dynamically when
|
|
131
|
+
* a resolver returns `{}` — because it would match the whole dependent
|
|
132
|
+
* collection. There is currently no broad-delete opt-in flag.
|
|
133
|
+
* - Option validation runs at `schema.plugin(...)` time: `model` must be a
|
|
134
|
+
* non-empty string, relationship mode requires both `localField` and
|
|
135
|
+
* `foreignField`, static `foreignFilter`/`extraForeignFilter` values must be
|
|
136
|
+
* plain objects (or resolver functions), and a static empty `foreignFilter`
|
|
137
|
+
* throws. Invalid runtime resolver results fail closed as described above.
|
|
138
|
+
*
|
|
139
|
+
* Traversal/bounds contract (MOO-06):
|
|
140
|
+
*
|
|
141
|
+
* - Deletion never uses bulk writes: every dependent is removed through its
|
|
142
|
+
* own hydrated document `deleteOne()` (or legacy `remove()`), so nested
|
|
143
|
+
* cascade middleware and custom document hooks always run with full field
|
|
144
|
+
* state. Public `findDependents()` keeps its array-returning API for
|
|
145
|
+
* convenience; internal deletion traversal pages `_id`s (`batchSize`,
|
|
146
|
+
* default 100) and loads full documents per page instead.
|
|
147
|
+
* - In-flight deletes are bounded by `maxConcurrency` (default 8, integer
|
|
148
|
+
* >= 1). Inside an active transaction the effective limit is forced to 1 to
|
|
149
|
+
* preserve MOO-04 serialization. No new deletes start after the first
|
|
150
|
+
* failure (fail-fast).
|
|
151
|
+
* - Partial failure: `post('deleteOne')` runs after the parent is already
|
|
152
|
+
* removed. Outside a transaction a dependent failure rejects the parent
|
|
153
|
+
* `deleteOne()` promise but cannot restore the parent or already-deleted
|
|
154
|
+
* dependents; remaining batches are never fetched. Inside a transaction the
|
|
155
|
+
* caller observes the rejection before commit and can abort to restore
|
|
156
|
+
* parent and dependents together. A dependent row that is already gone when
|
|
157
|
+
* re-loaded (concurrent diamond, delayed retry) is skipped, not treated as
|
|
158
|
+
* a failure.
|
|
159
|
+
* - Repeated references to the same `_id` within one relationship delete that
|
|
160
|
+
* row once per level (deduplicated by `_id`). Diamonds (two middles sharing
|
|
161
|
+
* one leaf) and cycles (A <-> B) always terminate: deletions are monotonic
|
|
162
|
+
* (deleted rows never reappear in later pages/levels) and every level
|
|
163
|
+
* re-queries live state, so already-deleted parents resolve to zero further
|
|
164
|
+
* dependents. Cross-level diamonds run leaf hooks at least once; concurrent
|
|
165
|
+
* copies may each fire leaf middleware before either observes the other's
|
|
166
|
+
* delete (at-least-once, never suppressed via bulk paths). No performance
|
|
167
|
+
* claim beyond the measured fan-out experiment recorded with the tests is
|
|
168
|
+
* made here.
|
|
169
|
+
*/
|
|
23
170
|
declare function cascadeDeletePlugin<TModelName extends string = string, TDependentDocument extends Record<string, unknown> = Record<string, unknown>>(schema: Schema, options: CascadeDeletePluginOptions<TModelName, TDependentDocument>): void;
|
|
24
171
|
|
|
25
|
-
export { type CascadeDeleteDependencyMap, type CascadeDeleteDocumentMethods, type CascadeDeleteModelStatics, type CascadeDeletePluginOptions, type FilterResolver, type QueryFilter, cascadeDeletePlugin };
|
|
172
|
+
export { type CascadeDeleteDependencyMap, type CascadeDeleteDocumentMethods, type CascadeDeleteModelStatics, type CascadeDeletePluginOptions, type FilterResolver, type QueryFilter, type QueryOperators, cascadeDeletePlugin };
|
|
@@ -1,14 +1,58 @@
|
|
|
1
1
|
import { Schema } from 'mongoose';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* MongoDB query operators accepted for a single document field in
|
|
5
|
+
* {@link QueryFilter}. The runtime forwards relationship and full filters
|
|
6
|
+
* straight to Mongoose `find()`, so operator objects (for example
|
|
7
|
+
* `{ price: { $gt: 5 } }`) are evaluated by MongoDB rather than by this
|
|
8
|
+
* package. Each field therefore accepts either its plain document value or
|
|
9
|
+
* an operator object; top-level logical operators (`$and`/`$or`/`$not`/`$nor`)
|
|
10
|
+
* remain available through the trailing `Record<string, unknown>` index.
|
|
11
|
+
*/
|
|
12
|
+
type QueryOperators<TValue = unknown> = {
|
|
13
|
+
$eq?: TValue;
|
|
14
|
+
$ne?: TValue;
|
|
15
|
+
$gt?: TValue;
|
|
16
|
+
$gte?: TValue;
|
|
17
|
+
$lt?: TValue;
|
|
18
|
+
$lte?: TValue;
|
|
19
|
+
$in?: TValue[];
|
|
20
|
+
$nin?: TValue[];
|
|
21
|
+
$exists?: boolean;
|
|
22
|
+
$regex?: string | RegExp;
|
|
23
|
+
$type?: string | string[];
|
|
24
|
+
$not?: QueryOperators<TValue> | RegExp;
|
|
25
|
+
$size?: number;
|
|
26
|
+
$all?: TValue[];
|
|
27
|
+
$elemMatch?: Record<string, unknown>;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Typed dependent-collection filter. Each declared document field accepts its
|
|
31
|
+
* plain value, a {@link QueryOperators} object, or `null`; undeclared paths
|
|
32
|
+
* (including `$and`/`$or` operator payloads composed by
|
|
33
|
+
* `extraForeignFilter`) are accepted through the index signature and passed
|
|
34
|
+
* to Mongoose unchanged.
|
|
35
|
+
*/
|
|
36
|
+
type QueryFilter<TDocument extends Record<string, unknown> = Record<string, unknown>> = {
|
|
37
|
+
[K in keyof TDocument]?: TDocument[K] | QueryOperators<TDocument[K]> | null;
|
|
38
|
+
} & Record<string, unknown>;
|
|
39
|
+
/**
|
|
40
|
+
* Dependents grouped by configured model name. The no-argument
|
|
41
|
+
* `findDependents()`/`findOrphans()` overloads return a `Partial` map:
|
|
42
|
+
* entries whose lookup is unsupported (for example `findOrphans()` for a
|
|
43
|
+
* non-`_id` local key, dotted foreign path, dynamic `refPath`, or
|
|
44
|
+
* `foreignFilter`-only relationship) or whose resolver produced a
|
|
45
|
+
* nullish/non-object result are omitted rather than reported as empty
|
|
46
|
+
* arrays, matching the `mergeResults` runtime behavior.
|
|
47
|
+
*/
|
|
4
48
|
type CascadeDeleteDependencyMap<TModelName extends string, TDependentDocument = unknown> = Record<TModelName, TDependentDocument[]>;
|
|
5
49
|
type CascadeDeleteDocumentMethods<TModelName extends string, TDependentDocument = unknown> = {
|
|
6
|
-
findDependents(): Promise<CascadeDeleteDependencyMap<TModelName, TDependentDocument
|
|
50
|
+
findDependents(): Promise<Partial<CascadeDeleteDependencyMap<TModelName, TDependentDocument>> & Record<string, unknown[]>>;
|
|
7
51
|
findDependents(modelName: TModelName): Promise<TDependentDocument[]>;
|
|
8
52
|
findDependents(modelName: string): Promise<unknown[] | null>;
|
|
9
53
|
};
|
|
10
54
|
type CascadeDeleteModelStatics<TModelName extends string, TDependentDocument = unknown> = {
|
|
11
|
-
findOrphans(): Promise<CascadeDeleteDependencyMap<TModelName, TDependentDocument
|
|
55
|
+
findOrphans(): Promise<Partial<CascadeDeleteDependencyMap<TModelName, TDependentDocument>> & Record<string, unknown[]>>;
|
|
12
56
|
findOrphans(modelName: TModelName): Promise<TDependentDocument[] | null>;
|
|
13
57
|
findOrphans(modelName: string): Promise<unknown[] | null>;
|
|
14
58
|
};
|
|
@@ -19,7 +63,110 @@ interface CascadeDeletePluginOptions<TModelName extends string = string, TDepend
|
|
|
19
63
|
foreignField?: string;
|
|
20
64
|
foreignFilter?: FilterResolver<TDependentDocument>;
|
|
21
65
|
extraForeignFilter?: FilterResolver<TDependentDocument>;
|
|
66
|
+
/**
|
|
67
|
+
* Maximum dependent deletes allowed in flight at once for this relationship.
|
|
68
|
+
* Must be an integer >= 1. Defaults to 8. Inside an active transaction the
|
|
69
|
+
* effective limit is forced to 1 (MOO-04 serialization) regardless of this
|
|
70
|
+
* value. Public `findDependents()` is unaffected: it still returns an array.
|
|
71
|
+
*/
|
|
72
|
+
maxConcurrency?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Maximum dependent `_id`s acquired per internal ID-page query. Must be an
|
|
75
|
+
* integer >= 1. Defaults to 100. Bounds acquisition memory; deletion work is
|
|
76
|
+
* never all materialized or scheduled up front.
|
|
77
|
+
*/
|
|
78
|
+
batchSize?: number;
|
|
22
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Cascades document `deleteOne()` to configured dependent models.
|
|
82
|
+
*
|
|
83
|
+
* Supported operations: only document `deleteOne()` (and legacy document
|
|
84
|
+
* `remove()` for dependents) is intercepted, through
|
|
85
|
+
* `pre('deleteOne', { document: true, query: false })` and
|
|
86
|
+
* `post('deleteOne', { document: true, query: false })`. Query helpers such
|
|
87
|
+
* as `deleteMany()`, `findOneAndDelete()`, `updateOne()`, and query
|
|
88
|
+
* `deleteOne()` bypass the cascade entirely. `findDependents()` is a
|
|
89
|
+
* document method; `findOrphans()` is a model static with the support matrix
|
|
90
|
+
* documented on {@link CascadeDeleteDependencyMap} (unsupported shapes
|
|
91
|
+
* resolve to `null` and are omitted from the no-argument map).
|
|
92
|
+
*
|
|
93
|
+
* Post-delete failure timing: the `post('deleteOne')` hook runs after the
|
|
94
|
+
* parent document is already removed. A later dependent failure therefore
|
|
95
|
+
* rejects the parent `deleteOne()` promise without restoring the parent —
|
|
96
|
+
* see the traversal contract below for the transactional abort path.
|
|
97
|
+
*
|
|
98
|
+
* Supported behavior: dependent models resolve through the owning model's
|
|
99
|
+
* connection (duplicate names on other connections stay isolated); the
|
|
100
|
+
* effective session (explicit `deleteOne({ session })` option, else the
|
|
101
|
+
* document-bound `$session()`) flows through dependent reads and deletes,
|
|
102
|
+
* including nested cascades; inside an active transaction dependent deletes run
|
|
103
|
+
* serially on that session so replica-set commit/abort covers parent and
|
|
104
|
+
* dependents together. Unsupported modes fail before the parent is removed: a
|
|
105
|
+
* session from a different connection/client, or an active transaction on a
|
|
106
|
+
* standalone (`Single`) topology.
|
|
107
|
+
*
|
|
108
|
+
* Predicate fail-closed contract (MOO-05):
|
|
109
|
+
*
|
|
110
|
+
* - Relationship mode (`localField` + `foreignField`) never issues a
|
|
111
|
+
* destructive query without a valid relationship. Missing/`null`/empty
|
|
112
|
+
* local keys (including empty reference arrays and arrays containing only
|
|
113
|
+
* nullish entries) resolve to zero dependents and delete nothing, so
|
|
114
|
+
* unrelated records with a missing/`null` foreign key survive. A local field
|
|
115
|
+
* that was omitted by a projection is distinguished from an intentionally
|
|
116
|
+
* empty relation: when the path is provably deselected (`$isSelected()`
|
|
117
|
+
* returns `false`) the lookup throws instead of silently reporting zero
|
|
118
|
+
* dependents.
|
|
119
|
+
* - Supplemental `extraForeignFilter` constraints are composed conjunctively
|
|
120
|
+
* (`{ $and: [relationship, extra] }`). An extra filter on the relationship
|
|
121
|
+
* field therefore intersects instead of replacing or widening the deletion
|
|
122
|
+
* set; `$or`/`$and`/operator payloads inside the extra filter stay
|
|
123
|
+
* constrained by the relationship predicate. An absent, nullish,
|
|
124
|
+
* non-object, or empty extra filter leaves the relationship unchanged.
|
|
125
|
+
* - Explicit full-filter mode (`foreignFilter`) is a separately documented
|
|
126
|
+
* contract: the resolved filter is used as-is with no relationship
|
|
127
|
+
* predicate, and any configured `extraForeignFilter` is ignored. A resolver
|
|
128
|
+
* returning `null`/`undefined`/non-object resolves to zero dependents.
|
|
129
|
+
* Maintainer decision: an intentionally empty full filter (`{}`) is rejected
|
|
130
|
+
* by default — statically at `schema.plugin(...)` time and dynamically when
|
|
131
|
+
* a resolver returns `{}` — because it would match the whole dependent
|
|
132
|
+
* collection. There is currently no broad-delete opt-in flag.
|
|
133
|
+
* - Option validation runs at `schema.plugin(...)` time: `model` must be a
|
|
134
|
+
* non-empty string, relationship mode requires both `localField` and
|
|
135
|
+
* `foreignField`, static `foreignFilter`/`extraForeignFilter` values must be
|
|
136
|
+
* plain objects (or resolver functions), and a static empty `foreignFilter`
|
|
137
|
+
* throws. Invalid runtime resolver results fail closed as described above.
|
|
138
|
+
*
|
|
139
|
+
* Traversal/bounds contract (MOO-06):
|
|
140
|
+
*
|
|
141
|
+
* - Deletion never uses bulk writes: every dependent is removed through its
|
|
142
|
+
* own hydrated document `deleteOne()` (or legacy `remove()`), so nested
|
|
143
|
+
* cascade middleware and custom document hooks always run with full field
|
|
144
|
+
* state. Public `findDependents()` keeps its array-returning API for
|
|
145
|
+
* convenience; internal deletion traversal pages `_id`s (`batchSize`,
|
|
146
|
+
* default 100) and loads full documents per page instead.
|
|
147
|
+
* - In-flight deletes are bounded by `maxConcurrency` (default 8, integer
|
|
148
|
+
* >= 1). Inside an active transaction the effective limit is forced to 1 to
|
|
149
|
+
* preserve MOO-04 serialization. No new deletes start after the first
|
|
150
|
+
* failure (fail-fast).
|
|
151
|
+
* - Partial failure: `post('deleteOne')` runs after the parent is already
|
|
152
|
+
* removed. Outside a transaction a dependent failure rejects the parent
|
|
153
|
+
* `deleteOne()` promise but cannot restore the parent or already-deleted
|
|
154
|
+
* dependents; remaining batches are never fetched. Inside a transaction the
|
|
155
|
+
* caller observes the rejection before commit and can abort to restore
|
|
156
|
+
* parent and dependents together. A dependent row that is already gone when
|
|
157
|
+
* re-loaded (concurrent diamond, delayed retry) is skipped, not treated as
|
|
158
|
+
* a failure.
|
|
159
|
+
* - Repeated references to the same `_id` within one relationship delete that
|
|
160
|
+
* row once per level (deduplicated by `_id`). Diamonds (two middles sharing
|
|
161
|
+
* one leaf) and cycles (A <-> B) always terminate: deletions are monotonic
|
|
162
|
+
* (deleted rows never reappear in later pages/levels) and every level
|
|
163
|
+
* re-queries live state, so already-deleted parents resolve to zero further
|
|
164
|
+
* dependents. Cross-level diamonds run leaf hooks at least once; concurrent
|
|
165
|
+
* copies may each fire leaf middleware before either observes the other's
|
|
166
|
+
* delete (at-least-once, never suppressed via bulk paths). No performance
|
|
167
|
+
* claim beyond the measured fan-out experiment recorded with the tests is
|
|
168
|
+
* made here.
|
|
169
|
+
*/
|
|
23
170
|
declare function cascadeDeletePlugin<TModelName extends string = string, TDependentDocument extends Record<string, unknown> = Record<string, unknown>>(schema: Schema, options: CascadeDeletePluginOptions<TModelName, TDependentDocument>): void;
|
|
24
171
|
|
|
25
|
-
export { type CascadeDeleteDependencyMap, type CascadeDeleteDocumentMethods, type CascadeDeleteModelStatics, type CascadeDeletePluginOptions, type FilterResolver, type QueryFilter, cascadeDeletePlugin };
|
|
172
|
+
export { type CascadeDeleteDependencyMap, type CascadeDeleteDocumentMethods, type CascadeDeleteModelStatics, type CascadeDeletePluginOptions, type FilterResolver, type QueryFilter, type QueryOperators, cascadeDeletePlugin };
|