@imqueue/pg-sequelize 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/CONTRIBUTING.md +58 -0
  3. package/CONTRIBUTION-TERMS.md +79 -0
  4. package/LICENSE +585 -0
  5. package/README.md +94 -0
  6. package/SECURITY.md +41 -0
  7. package/index.d.ts +86 -0
  8. package/index.js +87 -0
  9. package/package.json +75 -0
  10. package/src/BaseModel.d.ts +695 -0
  11. package/src/BaseModel.js +917 -0
  12. package/src/Graph.d.ts +215 -0
  13. package/src/Graph.js +257 -0
  14. package/src/decorators/AssociatedWith.d.ts +94 -0
  15. package/src/decorators/AssociatedWith.js +71 -0
  16. package/src/decorators/ColumnIndex.d.ts +206 -0
  17. package/src/decorators/ColumnIndex.js +98 -0
  18. package/src/decorators/CreatedBy.d.ts +27 -0
  19. package/src/decorators/CreatedBy.js +84 -0
  20. package/src/decorators/DeletedBy.d.ts +30 -0
  21. package/src/decorators/DeletedBy.js +89 -0
  22. package/src/decorators/DynamicView.d.ts +124 -0
  23. package/src/decorators/DynamicView.js +113 -0
  24. package/src/decorators/Emittable.d.ts +39 -0
  25. package/src/decorators/Emittable.js +42 -0
  26. package/src/decorators/NullableIndex.d.ts +77 -0
  27. package/src/decorators/NullableIndex.js +64 -0
  28. package/src/decorators/UpdatedBy.d.ts +27 -0
  29. package/src/decorators/UpdatedBy.js +105 -0
  30. package/src/decorators/View.d.ts +87 -0
  31. package/src/decorators/View.js +93 -0
  32. package/src/decorators/index.d.ts +32 -0
  33. package/src/decorators/index.js +33 -0
  34. package/src/helpers/index.d.ts +24 -0
  35. package/src/helpers/index.js +25 -0
  36. package/src/helpers/js.d.ts +61 -0
  37. package/src/helpers/js.js +88 -0
  38. package/src/helpers/query.d.ts +445 -0
  39. package/src/helpers/query.js +1095 -0
  40. package/src/index.d.ts +162 -0
  41. package/src/index.js +223 -0
  42. package/src/types/DataPage.d.ts +52 -0
  43. package/src/types/DataPage.js +2 -0
  44. package/src/types/FieldsInput.d.ts +41 -0
  45. package/src/types/FieldsInput.js +75 -0
  46. package/src/types/FilterInput.d.ts +136 -0
  47. package/src/types/FilterInput.js +291 -0
  48. package/src/types/JsonObject.d.ts +16 -0
  49. package/src/types/JsonObject.js +50 -0
  50. package/src/types/OrderByInput.d.ts +45 -0
  51. package/src/types/OrderByInput.js +80 -0
  52. package/src/types/PaginationInput.d.ts +44 -0
  53. package/src/types/PaginationInput.js +90 -0
  54. package/src/types/index.d.ts +30 -0
  55. package/src/types/index.js +31 -0
  56. package/src/types/ranges/DateRange.d.ts +27 -0
  57. package/src/types/ranges/DateRange.js +69 -0
  58. package/src/types/ranges/IRange.d.ts +47 -0
  59. package/src/types/ranges/IRange.js +2 -0
  60. package/src/types/ranges/NumericRange.d.ts +19 -0
  61. package/src/types/ranges/NumericRange.js +61 -0
  62. package/src/types/ranges/index.d.ts +26 -0
  63. package/src/types/ranges/index.js +27 -0
package/src/Graph.d.ts ADDED
@@ -0,0 +1,215 @@
1
+ /*!
2
+ * @imqueue/pg-sequelize - Sequelize ORM refines for @imqueue
3
+ *
4
+ * I'm Queue Software Project
5
+ * Copyright (C) 2025 imqueue.com <support@imqueue.com>
6
+ *
7
+ * This program is free software: you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation, either version 3 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
19
+ *
20
+ * If you want to use this code in a closed source (commercial) project, you can
21
+ * purchase a proprietary commercial license. Please contact us at
22
+ * <support@imqueue.com> to get commercial licensing options.
23
+ */
24
+ /**
25
+ * A graph as adjacency lists: each vertex mapped to the vertices it points at.
26
+ */
27
+ export type GraphMap<T> = Map<T, T[]>;
28
+ /**
29
+ * Called once per vertex during a traversal.
30
+ *
31
+ * @remarks
32
+ * Returning `false` prunes the walk at that vertex — its own edges are not
33
+ * followed — rather than ending the traversal. Anything else, `undefined`
34
+ * included, carries on. The second argument is the live visited map, so a
35
+ * callback can see what has already been reached, and add to it to steer the
36
+ * walk away from a vertex it does not want visited.
37
+ */
38
+ export type GraphForeachCallback<T> = (vertex: T, visited: Map<T, boolean>) => false | void;
39
+ /**
40
+ * A directed, unweighted graph with depth-first traversal and cycle detection.
41
+ *
42
+ * @remarks
43
+ * Small on purpose. It exists so model associations can be walked as a graph —
44
+ * `BaseModel.toGraph()` builds one — and the question worth asking of that graph is
45
+ * whether it has a cycle, because a cycle is a chain of `include`s that can be asked
46
+ * to include itself.
47
+ *
48
+ * Directed, despite what this said for a long time: an edge is recorded only on the
49
+ * vertex it starts from, so `addEdge(a, b)` does not make `hasEdge(b, a)` true. The
50
+ * cycle detection is the standard directed-graph one, with a recursion stack
51
+ * alongside the visited set, and it would be wrong for an undirected graph.
52
+ *
53
+ * Vertices are used as `Map` keys, so identity is what distinguishes them — model
54
+ * classes work, structurally equal objects do not.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const graph = Lead.toGraph();
59
+ *
60
+ * if (graph.isCycled()) {
61
+ * // some association path leads back to where it started
62
+ * }
63
+ * ```
64
+ */
65
+ export declare class Graph<T> {
66
+ /** The adjacency lists backing this graph. */
67
+ private list;
68
+ /**
69
+ * Adds vertices with no edges.
70
+ *
71
+ * @remarks
72
+ * Not idempotent: a vertex that is already present has its edges RESET, so guard
73
+ * with {@link Graph.hasVertex} when a vertex may already be there. Adding an edge
74
+ * from an unknown vertex adds it for you, which is the usual way one appears.
75
+ *
76
+ * @param vertex - Vertices to add.
77
+ * @returns This graph, for chaining.
78
+ */
79
+ addVertex(...vertex: T[]): Graph<T>;
80
+ /**
81
+ * Removes vertices along with the edges leading out of them.
82
+ *
83
+ * @remarks
84
+ * Edges pointing AT a removed vertex are left behind, so other vertices can go on
85
+ * naming one that is gone — and a walk that follows such an edge simply finds no
86
+ * further edges rather than failing.
87
+ *
88
+ * @param vertex - Vertices to remove.
89
+ * @returns This graph, for chaining.
90
+ */
91
+ delVertex(...vertex: T[]): Graph<T>;
92
+ /**
93
+ * Adds edges from one vertex to others.
94
+ *
95
+ * @remarks
96
+ * Directed: only `fromVertex` records them. It is added to the graph first if it
97
+ * is not there yet, while the targets are not — an edge may point at a vertex the
98
+ * graph does not otherwise know. Duplicate edges are kept as duplicates.
99
+ *
100
+ * @param fromVertex - Vertex the edges start from.
101
+ * @param toVertex - Vertices they point at.
102
+ * @returns This graph, for chaining.
103
+ */
104
+ addEdge(fromVertex: T, ...toVertex: T[]): Graph<T>;
105
+ /**
106
+ * Removes edges from one vertex to others.
107
+ *
108
+ * @remarks
109
+ * Every occurrence of each target is removed, so a duplicated edge goes entirely.
110
+ * A vertex with no edges, or one that is not in the graph, is left alone.
111
+ *
112
+ * @param fromVertex - Vertex to remove edges from.
113
+ * @param toVertex - Vertices to stop pointing at.
114
+ * @returns This graph, for chaining.
115
+ */
116
+ delEdge(fromVertex: T, ...toVertex: T[]): Graph<T>;
117
+ /**
118
+ * Whether one vertex points at another.
119
+ *
120
+ * @remarks
121
+ * Directed, so the order of the arguments matters: this asks about an edge from
122
+ * `vertex` to `edge` and says nothing about the other direction.
123
+ *
124
+ * @param vertex - Vertex the edge would start from.
125
+ * @param edge - Vertex it would point at.
126
+ * @returns `true` when that edge is present.
127
+ */
128
+ hasEdge(vertex: T, edge: T): boolean;
129
+ /**
130
+ * Whether a vertex is in this graph.
131
+ *
132
+ * @remarks
133
+ * By identity, since vertices are `Map` keys. A vertex that is only pointed at by
134
+ * an edge and never added is not in the graph.
135
+ *
136
+ * @param vertex - Vertex to look for.
137
+ * @returns `true` when the graph holds it.
138
+ */
139
+ hasVertex(vertex: T): boolean;
140
+ /**
141
+ * Visits every vertex once, depth first.
142
+ *
143
+ * @remarks
144
+ * Walks from each vertex in turn, sharing one visited set across all of them — so
145
+ * the callback sees each vertex exactly once no matter how many paths reach it,
146
+ * and a disconnected part of the graph is covered too. A callback returning
147
+ * `false` prunes that branch; the traversal moves on to the next vertex rather
148
+ * than stopping.
149
+ *
150
+ * @param callback - Called once per vertex.
151
+ * @returns This graph, for chaining.
152
+ */
153
+ forEach(callback: GraphForeachCallback<T>): Graph<T>;
154
+ /**
155
+ * Walks depth first from one vertex.
156
+ *
157
+ * @remarks
158
+ * Follows edges as far as they go, marking each vertex as it arrives and never
159
+ * arriving twice, which is what makes it safe on a cyclic graph. Passing a visited
160
+ * map of your own both continues an earlier walk and lets you exclude vertices by
161
+ * marking them before starting.
162
+ *
163
+ * @param vertex - Vertex to start from.
164
+ * @param callback - Called once per vertex reached; returning `false` prunes.
165
+ * @param visited - Vertices already reached. A fresh map by default.
166
+ * @returns This graph, for chaining.
167
+ */
168
+ walk(vertex: T, callback?: GraphForeachCallback<T>, visited?: Map<T, boolean>): Graph<T>;
169
+ /**
170
+ * Every vertex reachable from one vertex, in the order a depth-first walk finds
171
+ * them.
172
+ *
173
+ * @remarks
174
+ * The reachable SET, not the longest path — each vertex appears once however many
175
+ * routes lead to it, and the starting vertex is the first entry. Reading it as a
176
+ * path is what makes a cyclic graph look as though it terminates.
177
+ *
178
+ * @param vertex - Vertex to start from.
179
+ * @returns An iterator over the reachable vertices.
180
+ */
181
+ path(vertex: T): IterableIterator<T>;
182
+ /**
183
+ * Whether any path in this graph leads back to where it started.
184
+ *
185
+ * @remarks
186
+ * Checks from every vertex, so a cycle in a part of the graph nothing else reaches
187
+ * is still found. A self-edge counts. For model associations this is the question
188
+ * that matters: a cycle is an `include` chain that can be asked to include itself.
189
+ *
190
+ * @returns `true` when the graph contains a cycle.
191
+ */
192
+ isCycled(): boolean;
193
+ /**
194
+ * The vertices in this graph, in insertion order.
195
+ *
196
+ * @returns An iterator over the vertices.
197
+ */
198
+ vertices(): IterableIterator<T>;
199
+ /**
200
+ * Looks for a cycle reachable from one vertex.
201
+ *
202
+ * @remarks
203
+ * Depth-first with a recursion stack beside the visited set: reaching a vertex
204
+ * that is still on the stack means the path has come back on itself, whereas
205
+ * reaching one that is merely visited means it was explored already and holds no
206
+ * cycle. The stack entry is cleared on the way out, which is what keeps two
207
+ * separate paths through one vertex from reading as a cycle.
208
+ *
209
+ * @param vertex - Vertex to search from.
210
+ * @param visited - Vertices explored in this run, added to as it goes.
211
+ * @param stack - Vertices on the current path.
212
+ * @returns `true` when a cycle is reachable from `vertex`.
213
+ */
214
+ private detectCycle;
215
+ }
package/src/Graph.js ADDED
@@ -0,0 +1,257 @@
1
+ /**
2
+ * A directed, unweighted graph with depth-first traversal and cycle detection.
3
+ *
4
+ * @remarks
5
+ * Small on purpose. It exists so model associations can be walked as a graph —
6
+ * `BaseModel.toGraph()` builds one — and the question worth asking of that graph is
7
+ * whether it has a cycle, because a cycle is a chain of `include`s that can be asked
8
+ * to include itself.
9
+ *
10
+ * Directed, despite what this said for a long time: an edge is recorded only on the
11
+ * vertex it starts from, so `addEdge(a, b)` does not make `hasEdge(b, a)` true. The
12
+ * cycle detection is the standard directed-graph one, with a recursion stack
13
+ * alongside the visited set, and it would be wrong for an undirected graph.
14
+ *
15
+ * Vertices are used as `Map` keys, so identity is what distinguishes them — model
16
+ * classes work, structurally equal objects do not.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const graph = Lead.toGraph();
21
+ *
22
+ * if (graph.isCycled()) {
23
+ * // some association path leads back to where it started
24
+ * }
25
+ * ```
26
+ */
27
+ export class Graph {
28
+ /** The adjacency lists backing this graph. */
29
+ list = new Map();
30
+ /**
31
+ * Adds vertices with no edges.
32
+ *
33
+ * @remarks
34
+ * Not idempotent: a vertex that is already present has its edges RESET, so guard
35
+ * with {@link Graph.hasVertex} when a vertex may already be there. Adding an edge
36
+ * from an unknown vertex adds it for you, which is the usual way one appears.
37
+ *
38
+ * @param vertex - Vertices to add.
39
+ * @returns This graph, for chaining.
40
+ */
41
+ addVertex(...vertex) {
42
+ for (const v of vertex) {
43
+ this.list.set(v, []);
44
+ }
45
+ return this;
46
+ }
47
+ /**
48
+ * Removes vertices along with the edges leading out of them.
49
+ *
50
+ * @remarks
51
+ * Edges pointing AT a removed vertex are left behind, so other vertices can go on
52
+ * naming one that is gone — and a walk that follows such an edge simply finds no
53
+ * further edges rather than failing.
54
+ *
55
+ * @param vertex - Vertices to remove.
56
+ * @returns This graph, for chaining.
57
+ */
58
+ delVertex(...vertex) {
59
+ for (const v of vertex) {
60
+ this.list.delete(v);
61
+ }
62
+ return this;
63
+ }
64
+ /**
65
+ * Adds edges from one vertex to others.
66
+ *
67
+ * @remarks
68
+ * Directed: only `fromVertex` records them. It is added to the graph first if it
69
+ * is not there yet, while the targets are not — an edge may point at a vertex the
70
+ * graph does not otherwise know. Duplicate edges are kept as duplicates.
71
+ *
72
+ * @param fromVertex - Vertex the edges start from.
73
+ * @param toVertex - Vertices they point at.
74
+ * @returns This graph, for chaining.
75
+ */
76
+ addEdge(fromVertex, ...toVertex) {
77
+ let edges = this.list.get(fromVertex);
78
+ if (!edges) {
79
+ this.addVertex(fromVertex);
80
+ edges = this.list.get(fromVertex);
81
+ }
82
+ edges.push(...toVertex);
83
+ return this;
84
+ }
85
+ /**
86
+ * Removes edges from one vertex to others.
87
+ *
88
+ * @remarks
89
+ * Every occurrence of each target is removed, so a duplicated edge goes entirely.
90
+ * A vertex with no edges, or one that is not in the graph, is left alone.
91
+ *
92
+ * @param fromVertex - Vertex to remove edges from.
93
+ * @param toVertex - Vertices to stop pointing at.
94
+ * @returns This graph, for chaining.
95
+ */
96
+ delEdge(fromVertex, ...toVertex) {
97
+ const edges = this.list.get(fromVertex);
98
+ if (!(edges && edges.length)) {
99
+ return this;
100
+ }
101
+ for (const vertex of toVertex) {
102
+ while (~edges.indexOf(vertex)) {
103
+ edges.splice(edges.indexOf(vertex), 1);
104
+ }
105
+ }
106
+ return this;
107
+ }
108
+ /**
109
+ * Whether one vertex points at another.
110
+ *
111
+ * @remarks
112
+ * Directed, so the order of the arguments matters: this asks about an edge from
113
+ * `vertex` to `edge` and says nothing about the other direction.
114
+ *
115
+ * @param vertex - Vertex the edge would start from.
116
+ * @param edge - Vertex it would point at.
117
+ * @returns `true` when that edge is present.
118
+ */
119
+ hasEdge(vertex, edge) {
120
+ return !!~(this.list.get(vertex) || []).indexOf(edge);
121
+ }
122
+ /**
123
+ * Whether a vertex is in this graph.
124
+ *
125
+ * @remarks
126
+ * By identity, since vertices are `Map` keys. A vertex that is only pointed at by
127
+ * an edge and never added is not in the graph.
128
+ *
129
+ * @param vertex - Vertex to look for.
130
+ * @returns `true` when the graph holds it.
131
+ */
132
+ hasVertex(vertex) {
133
+ return this.list.has(vertex);
134
+ }
135
+ /**
136
+ * Visits every vertex once, depth first.
137
+ *
138
+ * @remarks
139
+ * Walks from each vertex in turn, sharing one visited set across all of them — so
140
+ * the callback sees each vertex exactly once no matter how many paths reach it,
141
+ * and a disconnected part of the graph is covered too. A callback returning
142
+ * `false` prunes that branch; the traversal moves on to the next vertex rather
143
+ * than stopping.
144
+ *
145
+ * @param callback - Called once per vertex.
146
+ * @returns This graph, for chaining.
147
+ */
148
+ forEach(callback) {
149
+ const visited = new Map();
150
+ for (const node of this.list.keys()) {
151
+ this.walk(node, callback, visited);
152
+ }
153
+ return this;
154
+ }
155
+ /**
156
+ * Walks depth first from one vertex.
157
+ *
158
+ * @remarks
159
+ * Follows edges as far as they go, marking each vertex as it arrives and never
160
+ * arriving twice, which is what makes it safe on a cyclic graph. Passing a visited
161
+ * map of your own both continues an earlier walk and lets you exclude vertices by
162
+ * marking them before starting.
163
+ *
164
+ * @param vertex - Vertex to start from.
165
+ * @param callback - Called once per vertex reached; returning `false` prunes.
166
+ * @param visited - Vertices already reached. A fresh map by default.
167
+ * @returns This graph, for chaining.
168
+ */
169
+ walk(vertex, callback, visited = new Map()) {
170
+ if (!visited.get(vertex)) {
171
+ visited.set(vertex, true);
172
+ if (callback && callback(vertex, visited) === false) {
173
+ return this;
174
+ }
175
+ for (const neighbor of this.list.get(vertex) || []) {
176
+ this.walk(neighbor, callback, visited);
177
+ }
178
+ }
179
+ return this;
180
+ }
181
+ /**
182
+ * Every vertex reachable from one vertex, in the order a depth-first walk finds
183
+ * them.
184
+ *
185
+ * @remarks
186
+ * The reachable SET, not the longest path — each vertex appears once however many
187
+ * routes lead to it, and the starting vertex is the first entry. Reading it as a
188
+ * path is what makes a cyclic graph look as though it terminates.
189
+ *
190
+ * @param vertex - Vertex to start from.
191
+ * @returns An iterator over the reachable vertices.
192
+ */
193
+ path(vertex) {
194
+ const visited = new Map();
195
+ this.walk(vertex, undefined, visited);
196
+ return visited.keys();
197
+ }
198
+ /**
199
+ * Whether any path in this graph leads back to where it started.
200
+ *
201
+ * @remarks
202
+ * Checks from every vertex, so a cycle in a part of the graph nothing else reaches
203
+ * is still found. A self-edge counts. For model associations this is the question
204
+ * that matters: a cycle is an `include` chain that can be asked to include itself.
205
+ *
206
+ * @returns `true` when the graph contains a cycle.
207
+ */
208
+ isCycled() {
209
+ const visited = new Map();
210
+ const stack = new Map();
211
+ for (const node of this.list.keys()) {
212
+ if (this.detectCycle(node, visited, stack)) {
213
+ return true;
214
+ }
215
+ }
216
+ return false;
217
+ }
218
+ /**
219
+ * The vertices in this graph, in insertion order.
220
+ *
221
+ * @returns An iterator over the vertices.
222
+ */
223
+ vertices() {
224
+ return this.list.keys();
225
+ }
226
+ /**
227
+ * Looks for a cycle reachable from one vertex.
228
+ *
229
+ * @remarks
230
+ * Depth-first with a recursion stack beside the visited set: reaching a vertex
231
+ * that is still on the stack means the path has come back on itself, whereas
232
+ * reaching one that is merely visited means it was explored already and holds no
233
+ * cycle. The stack entry is cleared on the way out, which is what keeps two
234
+ * separate paths through one vertex from reading as a cycle.
235
+ *
236
+ * @param vertex - Vertex to search from.
237
+ * @param visited - Vertices explored in this run, added to as it goes.
238
+ * @param stack - Vertices on the current path.
239
+ * @returns `true` when a cycle is reachable from `vertex`.
240
+ */
241
+ detectCycle(vertex, visited, stack) {
242
+ if (!visited.get(vertex)) {
243
+ visited.set(vertex, true);
244
+ stack.set(vertex, true);
245
+ for (const currentNode of this.list.get(vertex) || []) {
246
+ if ((!visited.get(currentNode) &&
247
+ this.detectCycle(currentNode, visited, stack)) ||
248
+ stack.get(currentNode)) {
249
+ return true;
250
+ }
251
+ }
252
+ }
253
+ stack.set(vertex, false);
254
+ return false;
255
+ }
256
+ }
257
+ //# sourceMappingURL=Graph.js.map
@@ -0,0 +1,94 @@
1
+ /*!
2
+ * @imqueue/pg-sequelize - Sequelize ORM refines for @imqueue
3
+ *
4
+ * I'm Queue Software Project
5
+ * Copyright (C) 2025 imqueue.com <support@imqueue.com>
6
+ *
7
+ * This program is free software: you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation, either version 3 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
19
+ *
20
+ * If you want to use this code in a closed source (commercial) project, you can
21
+ * purchase a proprietary commercial license. Please contact us at
22
+ * <support@imqueue.com> to get commercial licensing options.
23
+ */
24
+ /**
25
+ * Describes the association a filter field stands for.
26
+ */
27
+ export interface IAssociated {
28
+ /** The model on the other side of the association. */
29
+ model: any;
30
+ /**
31
+ * The input class describing that model's own filter fields.
32
+ *
33
+ * @remarks
34
+ * Read recursively, so a filter can nest as deep as the input classes do.
35
+ */
36
+ input: any;
37
+ /**
38
+ * Association name on the model, when it differs from the field name.
39
+ *
40
+ * @remarks
41
+ * Recorded and then never read: nothing in this package consumes it today, so
42
+ * the field name and the association name have to match. Left in place because
43
+ * removing it from the public type would break callers that set it.
44
+ */
45
+ modelFieldName?: string;
46
+ }
47
+ /**
48
+ * Marks a field of a filter input as standing for an association rather than for a
49
+ * column.
50
+ *
51
+ * @remarks
52
+ * This is what lets a caller's filter reach through a relation. `query.toWhereOptions`
53
+ * instantiates the input class, and any property it finds carrying one of these
54
+ * descriptors becomes a required `include` on the associated model, with the nested
55
+ * filter resolved against that model's own input class — recursively, so the nesting
56
+ * can go as deep as the input classes do. Without it the nested object would be
57
+ * treated as a column filter and produce a where clause on a column that does not
58
+ * exist.
59
+ *
60
+ * Declare the field with `declare`, and do not instantiate the input class yourself.
61
+ * The descriptor lives on the prototype as a read-only, non-enumerable property, so a
62
+ * real field declaration would shadow it with `undefined` under
63
+ * `useDefineForClassFields` and the association would be silently ignored — and an
64
+ * assignment to it throws in strict mode. These classes are descriptions of a wire
65
+ * shape, not containers for one.
66
+ *
67
+ * The thunk is resolved on first read rather than at decoration time, which is what
68
+ * makes it worth being a thunk: input classes and models routinely import each other,
69
+ * and a decorator body runs while those modules are still initialising, so resolving
70
+ * eagerly could capture `undefined` as the model. First read happens on the first
71
+ * query, by which point every module is loaded.
72
+ *
73
+ * @param cb - Returns the association descriptor.
74
+ * @returns A property decorator.
75
+ * @example
76
+ * ```typescript
77
+ * export class PaymentListInput {
78
+ * @property(() => `number[] | ${FilterInput.name}`, true)
79
+ * declare public amount?: number[] | FilterInput;
80
+ *
81
+ * @property('PaymentTypeListInput', true)
82
+ * @AssociatedWith(() => ({
83
+ * model: PaymentType,
84
+ * input: PaymentTypeListInput,
85
+ * }))
86
+ * declare public paymentType?: PaymentTypeListInput;
87
+ * }
88
+ *
89
+ * // A filter of { paymentType: { name: 'card' } } now becomes a required join on
90
+ * // PaymentType with the name filter applied there.
91
+ * const options = query.toWhereOptions(filter, PaymentListInput);
92
+ * ```
93
+ */
94
+ export declare function AssociatedWith(cb: () => IAssociated): (target: any, key: string) => any;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Marks a field of a filter input as standing for an association rather than for a
3
+ * column.
4
+ *
5
+ * @remarks
6
+ * This is what lets a caller's filter reach through a relation. `query.toWhereOptions`
7
+ * instantiates the input class, and any property it finds carrying one of these
8
+ * descriptors becomes a required `include` on the associated model, with the nested
9
+ * filter resolved against that model's own input class — recursively, so the nesting
10
+ * can go as deep as the input classes do. Without it the nested object would be
11
+ * treated as a column filter and produce a where clause on a column that does not
12
+ * exist.
13
+ *
14
+ * Declare the field with `declare`, and do not instantiate the input class yourself.
15
+ * The descriptor lives on the prototype as a read-only, non-enumerable property, so a
16
+ * real field declaration would shadow it with `undefined` under
17
+ * `useDefineForClassFields` and the association would be silently ignored — and an
18
+ * assignment to it throws in strict mode. These classes are descriptions of a wire
19
+ * shape, not containers for one.
20
+ *
21
+ * The thunk is resolved on first read rather than at decoration time, which is what
22
+ * makes it worth being a thunk: input classes and models routinely import each other,
23
+ * and a decorator body runs while those modules are still initialising, so resolving
24
+ * eagerly could capture `undefined` as the model. First read happens on the first
25
+ * query, by which point every module is loaded.
26
+ *
27
+ * @param cb - Returns the association descriptor.
28
+ * @returns A property decorator.
29
+ * @example
30
+ * ```typescript
31
+ * export class PaymentListInput {
32
+ * @property(() => `number[] | ${FilterInput.name}`, true)
33
+ * declare public amount?: number[] | FilterInput;
34
+ *
35
+ * @property('PaymentTypeListInput', true)
36
+ * @AssociatedWith(() => ({
37
+ * model: PaymentType,
38
+ * input: PaymentTypeListInput,
39
+ * }))
40
+ * declare public paymentType?: PaymentTypeListInput;
41
+ * }
42
+ *
43
+ * // A filter of { paymentType: { name: 'card' } } now becomes a required join on
44
+ * // PaymentType with the name filter applied there.
45
+ * const options = query.toWhereOptions(filter, PaymentListInput);
46
+ * ```
47
+ */
48
+ export function AssociatedWith(cb) {
49
+ return (target, key) => {
50
+ let resolved;
51
+ let done = false;
52
+ Object.defineProperty(target, key, {
53
+ get() {
54
+ if (!done) {
55
+ done = true;
56
+ const association = cb();
57
+ if (association) {
58
+ resolved = {
59
+ model: association.model,
60
+ input: association.input,
61
+ key: association.modelFieldName || key,
62
+ };
63
+ }
64
+ }
65
+ return resolved;
66
+ },
67
+ });
68
+ return target;
69
+ };
70
+ }
71
+ //# sourceMappingURL=AssociatedWith.js.map