@bairock/lenz 0.0.14 → 0.0.16

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 (120) hide show
  1. package/README.md +119 -8
  2. package/dist/cli/commands/generate.d.ts.map +1 -1
  3. package/dist/cli/commands/generate.js +66 -79
  4. package/dist/cli/commands/generate.js.map +1 -1
  5. package/dist/cli/commands/init.d.ts.map +1 -1
  6. package/dist/cli/commands/init.js +22 -30
  7. package/dist/cli/commands/init.js.map +1 -1
  8. package/dist/cli/commands/studio.js +15 -21
  9. package/dist/cli/commands/studio.js.map +1 -1
  10. package/dist/cli/index.js +11 -18
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/config/index.d.ts +4 -6
  13. package/dist/config/index.d.ts.map +1 -1
  14. package/dist/config/index.js +31 -15
  15. package/dist/config/index.js.map +1 -1
  16. package/dist/engine/CodeGenerator.d.ts +8 -28
  17. package/dist/engine/CodeGenerator.d.ts.map +1 -1
  18. package/dist/engine/CodeGenerator.js +26 -1935
  19. package/dist/engine/CodeGenerator.js.map +1 -1
  20. package/dist/engine/GraphQLParseHelpers.d.ts +25 -0
  21. package/dist/engine/GraphQLParseHelpers.d.ts.map +1 -0
  22. package/dist/engine/GraphQLParseHelpers.js +128 -0
  23. package/dist/engine/GraphQLParseHelpers.js.map +1 -0
  24. package/dist/engine/GraphQLParser.d.ts +23 -8
  25. package/dist/engine/GraphQLParser.d.ts.map +1 -1
  26. package/dist/engine/GraphQLParser.js +165 -248
  27. package/dist/engine/GraphQLParser.js.map +1 -1
  28. package/dist/engine/GraphQLRelationAnalyzer.d.ts +10 -0
  29. package/dist/engine/GraphQLRelationAnalyzer.d.ts.map +1 -0
  30. package/dist/engine/GraphQLRelationAnalyzer.js +117 -0
  31. package/dist/engine/GraphQLRelationAnalyzer.js.map +1 -0
  32. package/dist/engine/LenzEngine.d.ts +4 -4
  33. package/dist/engine/LenzEngine.d.ts.map +1 -1
  34. package/dist/engine/LenzEngine.js +59 -46
  35. package/dist/engine/LenzEngine.js.map +1 -1
  36. package/dist/engine/SchemaRelationValidator.d.ts +15 -0
  37. package/dist/engine/SchemaRelationValidator.d.ts.map +1 -0
  38. package/dist/engine/SchemaRelationValidator.js +133 -0
  39. package/dist/engine/SchemaRelationValidator.js.map +1 -0
  40. package/dist/engine/SchemaValidator.d.ts +12 -11
  41. package/dist/engine/SchemaValidator.d.ts.map +1 -1
  42. package/dist/engine/SchemaValidator.js +163 -169
  43. package/dist/engine/SchemaValidator.js.map +1 -1
  44. package/dist/engine/directives.d.ts +10 -0
  45. package/dist/engine/directives.d.ts.map +1 -1
  46. package/dist/engine/directives.js +192 -47
  47. package/dist/engine/directives.js.map +1 -1
  48. package/dist/engine/generators/ClientGenerator.d.ts +7 -0
  49. package/dist/engine/generators/ClientGenerator.d.ts.map +1 -0
  50. package/dist/engine/generators/ClientGenerator.js +386 -0
  51. package/dist/engine/generators/ClientGenerator.js.map +1 -0
  52. package/dist/engine/generators/DelegateGenerator.d.ts +9 -0
  53. package/dist/engine/generators/DelegateGenerator.d.ts.map +1 -0
  54. package/dist/engine/generators/DelegateGenerator.js +453 -0
  55. package/dist/engine/generators/DelegateGenerator.js.map +1 -0
  56. package/dist/engine/generators/DelegateHelpers.d.ts +7 -0
  57. package/dist/engine/generators/DelegateHelpers.d.ts.map +1 -0
  58. package/dist/engine/generators/DelegateHelpers.js +144 -0
  59. package/dist/engine/generators/DelegateHelpers.js.map +1 -0
  60. package/dist/engine/generators/DelegateRelations.d.ts +11 -0
  61. package/dist/engine/generators/DelegateRelations.d.ts.map +1 -0
  62. package/dist/engine/generators/DelegateRelations.js +794 -0
  63. package/dist/engine/generators/DelegateRelations.js.map +1 -0
  64. package/dist/engine/generators/DelegateTemplateBody.d.ts +8 -0
  65. package/dist/engine/generators/DelegateTemplateBody.d.ts.map +1 -0
  66. package/dist/engine/generators/DelegateTemplateBody.js +776 -0
  67. package/dist/engine/generators/DelegateTemplateBody.js.map +1 -0
  68. package/dist/engine/generators/GenerateRuntimeErrors.d.ts +2 -0
  69. package/dist/engine/generators/GenerateRuntimeErrors.d.ts.map +1 -0
  70. package/dist/engine/generators/GenerateRuntimeErrors.js +140 -0
  71. package/dist/engine/generators/GenerateRuntimeErrors.js.map +1 -0
  72. package/dist/engine/generators/GenerateRuntimeIndex.d.ts +2 -0
  73. package/dist/engine/generators/GenerateRuntimeIndex.d.ts.map +1 -0
  74. package/dist/engine/generators/GenerateRuntimeIndex.js +21 -0
  75. package/dist/engine/generators/GenerateRuntimeIndex.js.map +1 -0
  76. package/dist/engine/generators/GenerateRuntimeLogger.d.ts +2 -0
  77. package/dist/engine/generators/GenerateRuntimeLogger.d.ts.map +1 -0
  78. package/dist/engine/generators/GenerateRuntimeLogger.js +125 -0
  79. package/dist/engine/generators/GenerateRuntimeLogger.js.map +1 -0
  80. package/dist/engine/generators/GenerateRuntimePagination.d.ts +2 -0
  81. package/dist/engine/generators/GenerateRuntimePagination.d.ts.map +1 -0
  82. package/dist/engine/generators/GenerateRuntimePagination.js +159 -0
  83. package/dist/engine/generators/GenerateRuntimePagination.js.map +1 -0
  84. package/dist/engine/generators/GenerateRuntimeQuery.d.ts +2 -0
  85. package/dist/engine/generators/GenerateRuntimeQuery.d.ts.map +1 -0
  86. package/dist/engine/generators/GenerateRuntimeQuery.js +427 -0
  87. package/dist/engine/generators/GenerateRuntimeQuery.js.map +1 -0
  88. package/dist/engine/generators/GenerateRuntimeRelations.d.ts +2 -0
  89. package/dist/engine/generators/GenerateRuntimeRelations.d.ts.map +1 -0
  90. package/dist/engine/generators/GenerateRuntimeRelations.js +130 -0
  91. package/dist/engine/generators/GenerateRuntimeRelations.js.map +1 -0
  92. package/dist/engine/generators/RuntimeGenerator.d.ts +16 -0
  93. package/dist/engine/generators/RuntimeGenerator.d.ts.map +1 -0
  94. package/dist/engine/generators/RuntimeGenerator.js +16 -0
  95. package/dist/engine/generators/RuntimeGenerator.js.map +1 -0
  96. package/dist/engine/generators/TypeFilterTypes.d.ts +2 -0
  97. package/dist/engine/generators/TypeFilterTypes.d.ts.map +1 -0
  98. package/dist/engine/generators/TypeFilterTypes.js +220 -0
  99. package/dist/engine/generators/TypeFilterTypes.js.map +1 -0
  100. package/dist/engine/generators/TypeGenerator.d.ts +16 -0
  101. package/dist/engine/generators/TypeGenerator.d.ts.map +1 -0
  102. package/dist/engine/generators/TypeGenerator.js +493 -0
  103. package/dist/engine/generators/TypeGenerator.js.map +1 -0
  104. package/dist/engine/generators/helpers.d.ts +13 -0
  105. package/dist/engine/generators/helpers.d.ts.map +1 -0
  106. package/dist/engine/generators/helpers.js +316 -0
  107. package/dist/engine/generators/helpers.js.map +1 -0
  108. package/dist/engine/index.d.ts +3 -3
  109. package/dist/engine/index.d.ts.map +1 -1
  110. package/dist/engine/index.js +3 -9
  111. package/dist/engine/index.js.map +1 -1
  112. package/dist/errors/index.d.ts +3 -0
  113. package/dist/errors/index.d.ts.map +1 -1
  114. package/dist/errors/index.js +25 -32
  115. package/dist/errors/index.js.map +1 -1
  116. package/dist/index.d.ts +4 -5
  117. package/dist/index.d.ts.map +1 -1
  118. package/dist/index.js +10 -30
  119. package/dist/index.js.map +1 -1
  120. package/package.json +6 -7
@@ -0,0 +1,427 @@
1
+ export function generateRuntimeQuery() {
2
+ return `// This file was auto-generated by Lenz. Do not edit manually.
3
+ // @generated
4
+
5
+ import { ObjectId, Filter, UpdateFilter } from 'mongodb'
6
+ import type { WhereInput, QueryOptions, SelectInput } from '../types'
7
+ import { PaginationHelper } from './pagination'
8
+
9
+ export class QueryBuilder {
10
+ static buildWhere<T>(where: WhereInput<T>): Filter<any> {
11
+ const filter: Filter<any> = {}
12
+
13
+ for (const [key, value] of Object.entries(where || {})) {
14
+ if (key === 'id') {
15
+ if (typeof value === 'object' && value !== null) {
16
+ this.applyOperators(filter, '_id', value)
17
+ } else {
18
+ filter._id = this.normalizeId(value)
19
+ }
20
+ } else if (key === 'search') {
21
+ if (typeof value === 'string' && value.length > 0) {
22
+ filter.$text = { $search: value }
23
+ }
24
+ } else if (key === 'AND') {
25
+ if (value !== null && typeof value === 'object') {
26
+ const conditions = Array.isArray(value) ? value : [value]
27
+ if (conditions.length > 0) {
28
+ filter.$and = conditions.map(cond => this.buildWhere(cond))
29
+ }
30
+ }
31
+ } else if (key === 'OR') {
32
+ if (value !== null && typeof value === 'object') {
33
+ const conditions = Array.isArray(value) ? value : [value]
34
+ if (conditions.length > 0) {
35
+ filter.$or = conditions.map(cond => this.buildWhere(cond))
36
+ }
37
+ }
38
+ } else if (key === 'NOT') {
39
+ if (value !== null && typeof value === 'object') {
40
+ const conditions = Array.isArray(value) ? value : [value]
41
+ if (conditions.length > 0) {
42
+ filter.$nor = conditions.map(cond => this.buildWhere(cond))
43
+ }
44
+ }
45
+ } else if (typeof value === 'object' && value !== null) {
46
+ this.applyOperators(filter, key, value)
47
+ } else {
48
+ filter[key] = value
49
+ }
50
+ }
51
+
52
+ return filter
53
+ }
54
+
55
+ static buildCursorCondition(
56
+ cursor: string | ObjectId,
57
+ orderBy: any = { _id: 'asc' }
58
+ ): Filter<any> {
59
+ const cursorId = typeof cursor === 'string'
60
+ ? PaginationHelper.parseCursor(cursor)
61
+ : cursor.toString()
62
+
63
+ const isDesc = (v: any) => v === 'desc' || v === -1;
64
+ const isAsc = (v: any) => v === 'asc' || v === 1 || v === undefined;
65
+
66
+ if (orderBy._id || (!Object.keys(orderBy).length)) {
67
+ const cursorObjectId = (() => { try { return new ObjectId(cursorId); } catch { return cursorId; } })();
68
+ const dir = isDesc(orderBy._id) ? '$lt' : '$gt';
69
+ return { _id: { [dir]: cursorObjectId } }
70
+ }
71
+
72
+ const orderField = Object.keys(orderBy)[0]
73
+ const orderDirection = orderBy[orderField]
74
+
75
+ return {
76
+ [orderField]: isDesc(orderDirection)
77
+ ? { $lt: cursorId }
78
+ : { $gt: cursorId }
79
+ }
80
+ }
81
+
82
+ static buildProjection<T>(
83
+ select: SelectInput | undefined,
84
+ hiddenFields: string[] = [],
85
+ omit?: SelectInput,
86
+ fieldMapping?: Record<string, string>
87
+ ): any {
88
+ const mapField = (name: string): string => fieldMapping?.[name] || name;
89
+
90
+ if (omit && !select) {
91
+ const projection: any = {};
92
+ for (const [key, value] of Object.entries(omit)) {
93
+ if (value) projection[mapField(key)] = 0;
94
+ }
95
+ hiddenFields.forEach(field => { projection[mapField(field)] = 0; });
96
+ return Object.keys(projection).length > 0 ? projection : undefined;
97
+ }
98
+
99
+ if (!select) {
100
+ if (hiddenFields.length === 0) return undefined;
101
+ const projection: any = {};
102
+ hiddenFields.forEach(field => {
103
+ projection[mapField(field)] = 0;
104
+ });
105
+ return projection;
106
+ }
107
+
108
+ const projection: any = {};
109
+ const processSelect = (sel: SelectInput, prefix = '') => {
110
+ for (const [key, value] of Object.entries(sel)) {
111
+ const mappedKey = prefix ? key : mapField(key);
112
+ const fullPath = prefix ? \`\${prefix}.\${key}\` : mappedKey;
113
+
114
+ if (typeof value === 'boolean') {
115
+ projection[fullPath] = value ? 1 : 0;
116
+ } else {
117
+ processSelect(value, fullPath);
118
+ }
119
+ }
120
+ };
121
+
122
+ processSelect(select);
123
+
124
+ // Don't add hiddenFields as 0 here — MongoDB rejects mixed 1/0 projections (error 31253).
125
+ // With inclusion projection, hidden fields not in select are automatically excluded.
126
+
127
+ return projection;
128
+ }
129
+
130
+ static buildOptions<T>(options: QueryOptions<T>, hiddenFields: string[] = [], fieldMapping?: Record<string, string>): any {
131
+ const result: any = {}
132
+
133
+ if (options.skip !== undefined) result.skip = options.skip
134
+ if (options.take !== undefined) result.limit = options.take
135
+
136
+ if (options.orderBy) {
137
+ result.sort = this.buildSort(options.orderBy)
138
+ }
139
+
140
+ if (options.session) {
141
+ result.session = options.session
142
+ }
143
+
144
+ const projection = this.buildProjection(options.select, hiddenFields, options.omit, fieldMapping)
145
+ if (projection) {
146
+ result.projection = projection
147
+ }
148
+
149
+ return result
150
+ }
151
+
152
+ static buildSort(orderBy: any): any {
153
+ if (Array.isArray(orderBy)) {
154
+ return orderBy.reduce((acc, curr) => ({ ...acc, ...this.buildSort(curr) }), {})
155
+ }
156
+
157
+ const sort: any = {}
158
+ for (const [field, direction] of Object.entries(orderBy)) {
159
+ const mongoField = field === 'id' ? '_id' : field
160
+ if (direction === 'asc' || direction === 1) {
161
+ sort[mongoField] = 1
162
+ } else if (direction === 'desc' || direction === -1) {
163
+ sort[mongoField] = -1
164
+ }
165
+ }
166
+ return sort
167
+ }
168
+
169
+ private static applyOperators(filter: any, field: string, operators: any): void {
170
+ let targetField = field;
171
+ const pathArr = operators.path;
172
+ if (Array.isArray(pathArr) && pathArr.length > 0) {
173
+ targetField = pathArr.reduce((acc: string, p: string) => \`\${acc}.\${p}\`, field);
174
+ }
175
+
176
+ const mode = operators.mode;
177
+ const isInsensitive = mode === 'insensitive';
178
+
179
+ const mongoOperators: Record<string, string> = {
180
+ equals: '$eq',
181
+ not: '$ne',
182
+ in: '$in',
183
+ notIn: '$nin',
184
+ lt: '$lt',
185
+ lte: '$lte',
186
+ gt: '$gt',
187
+ gte: '$gte',
188
+ contains: '$regex',
189
+ startsWith: '$regex',
190
+ endsWith: '$regex'
191
+ }
192
+
193
+ for (const [op, value] of Object.entries(operators)) {
194
+ if (op === 'path') continue;
195
+ if (op === 'mode') continue;
196
+
197
+ if (op === 'string_contains') {
198
+ filter[targetField] = { $regex: String(value).replace(/[.*+?^\${}()|[\]\\\\]/g, '\\\\$&'), $options: 'i' };
199
+ continue;
200
+ }
201
+ if (op === 'string_starts_with') {
202
+ filter[targetField] = { $regex: \`^\${String(value).replace(/[.*+?^\${}()|[\]\\\\]/g, '\\\\$&')}\`, $options: 'i' };
203
+ continue;
204
+ }
205
+ if (op === 'string_ends_with') {
206
+ filter[targetField] = { $regex: \`\${String(value).replace(/[.*+?^\${}()|[\]\\\\]/g, '\\\\$&')}\$\`, $options: 'i' };
207
+ continue;
208
+ }
209
+
210
+ if (op === 'has') {
211
+ filter[targetField] = { $elemMatch: { $eq: value } };
212
+ continue;
213
+ }
214
+ if (op === 'hasEvery') {
215
+ if (Array.isArray(value)) {
216
+ filter[targetField] = { $all: value };
217
+ }
218
+ continue;
219
+ }
220
+ if (op === 'hasSome') {
221
+ if (Array.isArray(value)) {
222
+ filter[targetField] = { $in: value };
223
+ }
224
+ continue;
225
+ }
226
+ if (op === 'isEmpty') {
227
+ if (value) {
228
+ filter[targetField] = { $size: 0 };
229
+ } else {
230
+ // $not cannot contain $size — use $nor instead
231
+ filter[targetField] = { $nor: [{ $exists: false }, { $size: 0 }] };
232
+ }
233
+ continue;
234
+ }
235
+ if (op === 'equals') {
236
+ if (Array.isArray(value)) {
237
+ filter[targetField] = { $eq: value };
238
+ continue;
239
+ }
240
+ // Non-array values fall through to generic $eq handler below
241
+ }
242
+
243
+ // Geo-spatial operators (MongoDB)
244
+ if (op === 'near' || op === 'nearSphere') {
245
+ const mongoOp = op === 'near' ? '$near' : '$nearSphere';
246
+ if (value && typeof value === 'object') {
247
+ const geoValue: any = {};
248
+ if (value.type && value.coordinates) {
249
+ geoValue.$geometry = { type: value.type, coordinates: value.coordinates };
250
+ }
251
+ if (value.maxDistance !== undefined) geoValue.$maxDistance = value.maxDistance;
252
+ if (value.minDistance !== undefined) geoValue.$minDistance = value.minDistance;
253
+ filter[targetField] = { [mongoOp]: geoValue };
254
+ }
255
+ continue;
256
+ }
257
+ if (op === 'geoWithin') {
258
+ if (value && typeof value === 'object') {
259
+ filter[targetField] = { $geoWithin: value };
260
+ }
261
+ continue;
262
+ }
263
+ if (op === 'geoIntersects') {
264
+ if (value && typeof value === 'object' && value.$geometry) {
265
+ filter[targetField] = { $geoIntersects: { $geometry: value.$geometry } };
266
+ }
267
+ continue;
268
+ }
269
+ if (op === 'geoContains') {
270
+ if (value && typeof value === 'object' && value.$geometry) {
271
+ filter[targetField] = { $geoContains: { $geometry: value.$geometry } };
272
+ }
273
+ continue;
274
+ }
275
+
276
+ if (op === 'array_contains') {
277
+ filter[targetField] = value;
278
+ continue;
279
+ }
280
+ if (op === 'array_starts_with') {
281
+ filter[targetField] = { $expr: { $eq: [{ $arrayElemAt: [{ $ifNull: ['$' + targetField, []] }, 0] }, value] } };
282
+ continue;
283
+ }
284
+ if (op === 'array_ends_with') {
285
+ filter[targetField] = { $expr: { $eq: [{ $arrayElemAt: [{ $ifNull: ['$' + targetField, []] }, -1] }, value] } };
286
+ continue;
287
+ }
288
+
289
+ const mongoOp = mongoOperators[op]
290
+
291
+ if (mongoOp) {
292
+ if (op === 'contains') {
293
+ filter[targetField] = { $regex: String(value).replace(/[.*+?^\${}()|[\]\\\\]/g, '\\\\$&'), $options: isInsensitive ? 'i' : '' }
294
+ } else if (op === 'startsWith') {
295
+ filter[targetField] = { $regex: \`^\${String(value).replace(/[.*+?^\${}()|[\]\\\\]/g, '\\\\$&')}\`, $options: isInsensitive ? 'i' : '' }
296
+ } else if (op === 'endsWith') {
297
+ filter[targetField] = { $regex: \`\${String(value).replace(/[.*+?^\${}()|[\]\\\\]/g, '\\\\$&')}\$\`, $options: isInsensitive ? 'i' : '' }
298
+ } else if (isInsensitive && (op === 'equals' || op === 'not')) {
299
+ const escapedValue = String(value).replace(/[.*+?^\${}()|[\]\\\\]/g, '\\\\$&');
300
+ if (op === 'equals') {
301
+ filter[targetField] = { $regex: new RegExp(\`^\${escapedValue}\$\`, 'i') };
302
+ } else {
303
+ filter[targetField] = { $not: { $regex: \`^\${escapedValue}\$\`, $options: 'i' } };
304
+ }
305
+ } else if (isInsensitive && (op === 'in' || op === 'notIn')) {
306
+ if (Array.isArray(value)) {
307
+ const regexes = value.map(v => new RegExp(\`^\${String(v).replace(/[.*+?^\${}()|[\]\\\\]/g, '\\\\$&')}\$\`, 'i'));
308
+ filter[targetField] = { [op === 'in' ? '$in' : '$nin']: regexes };
309
+ }
310
+ } else {
311
+ if (!filter[targetField]) filter[targetField] = {}
312
+ const normalizedValue = targetField === '_id' ? this.normalizeId(value) : value
313
+ filter[targetField][mongoOp] = normalizedValue
314
+ }
315
+ }
316
+ }
317
+ }
318
+
319
+ static normalizeId(id: string | ObjectId | (string | ObjectId)[]): ObjectId | string | (ObjectId | string)[] {
320
+ try {
321
+ if (Array.isArray(id)) {
322
+ return id.map(item => this.normalizeId(item) as ObjectId | string)
323
+ }
324
+ if (typeof id === 'string' && /^[0-9a-fA-F]{24}$/.test(id)) {
325
+ return new ObjectId(id)
326
+ }
327
+ return id
328
+ } catch {
329
+ return id
330
+ }
331
+ }
332
+
333
+ static buildUpdate(data: any): UpdateFilter<any> {
334
+ const update: UpdateFilter<any> = {}
335
+
336
+ const setOperations: any = {}
337
+ const mongoOperators: any = {}
338
+
339
+ const atomicOperators = ['increment', 'decrement', 'multiply', 'divide'];
340
+ const arrayOperators = ['push', 'pull', 'addToSet', 'pop', 'pullAll'];
341
+ const operatorMap: Record<string, string> = {
342
+ push: '$push',
343
+ pull: '$pull',
344
+ addToSet: '$addToSet',
345
+ pop: '$pop',
346
+ pullAll: '$pullAll'
347
+ };
348
+
349
+ for (const [key, value] of Object.entries(data)) {
350
+ if (key.startsWith('$')) {
351
+ mongoOperators[key] = value
352
+ } else {
353
+ if (typeof value === 'object' && value !== null) {
354
+ const keys = Object.keys(value)
355
+
356
+ // Atomic number operations: increment, decrement, multiply, divide
357
+ if (keys.every(k => atomicOperators.includes(k))) {
358
+ const inc: Record<string, number> = {};
359
+ const mul: Record<string, number> = {};
360
+ for (const op of keys) {
361
+ const opValue = value[op];
362
+ if (op === 'increment') { inc[key] = opValue; }
363
+ else if (op === 'decrement') { inc[key] = -opValue; }
364
+ else if (op === 'multiply') { mul[key] = opValue; }
365
+ else if (op === 'divide') { mul[key] = 1 / opValue; }
366
+ }
367
+ if (Object.keys(inc).length > 0) {
368
+ if (!mongoOperators.$inc) mongoOperators.$inc = {};
369
+ Object.assign(mongoOperators.$inc, inc);
370
+ }
371
+ if (Object.keys(mul).length > 0) {
372
+ if (!mongoOperators.$mul) mongoOperators.$mul = {};
373
+ Object.assign(mongoOperators.$mul, mul);
374
+ }
375
+ continue;
376
+ }
377
+
378
+ if (keys.length === 1 && arrayOperators.includes(keys[0])) {
379
+ const op = keys[0]
380
+ const opValue = value[op]
381
+
382
+ if (op === 'push' || op === 'addToSet') {
383
+ if (typeof opValue === 'object' && opValue !== null && Object.keys(opValue).length === 1 && opValue.each !== undefined) {
384
+ const mongoOp = operatorMap[op]
385
+ if (!mongoOperators[mongoOp]) {
386
+ mongoOperators[mongoOp] = {}
387
+ }
388
+ mongoOperators[mongoOp][key] = { $each: opValue.each }
389
+ continue
390
+ }
391
+ }
392
+
393
+ const mongoOp = operatorMap[op]
394
+ if (!mongoOperators[mongoOp]) {
395
+ mongoOperators[mongoOp] = {}
396
+ }
397
+
398
+ // push, addToSet, pull — sub-ops ($each, $position, $in, etc.) need $ prefix for MongoDB
399
+ if (['push', 'addToSet', 'pull'].includes(op) && typeof opValue === 'object' && opValue !== null && !Array.isArray(opValue)) {
400
+ const prefixed: any = {};
401
+ for (const [k, v] of Object.entries(opValue)) {
402
+ prefixed[k.startsWith('$') ? k : '$' + k] = v;
403
+ }
404
+ mongoOperators[mongoOp][key] = prefixed;
405
+ } else {
406
+ mongoOperators[mongoOp][key] = opValue;
407
+ }
408
+ continue
409
+ }
410
+ }
411
+
412
+ setOperations[key] = value
413
+ }
414
+ }
415
+
416
+ if (Object.keys(setOperations).length > 0) {
417
+ update.$set = setOperations
418
+ }
419
+
420
+ Object.assign(update, mongoOperators)
421
+
422
+ return update
423
+ }
424
+ }
425
+ `;
426
+ }
427
+ //# sourceMappingURL=GenerateRuntimeQuery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenerateRuntimeQuery.js","sourceRoot":"","sources":["../../../src/engine/generators/GenerateRuntimeQuery.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,oBAAoB;IAChC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuaV,CAAC;AACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function generateRuntimeRelations(): string;
2
+ //# sourceMappingURL=GenerateRuntimeRelations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenerateRuntimeRelations.d.ts","sourceRoot":"","sources":["../../../src/engine/generators/GenerateRuntimeRelations.ts"],"names":[],"mappings":"AAAA,wBAAgB,wBAAwB,IAAI,MAAM,CAgIjD"}
@@ -0,0 +1,130 @@
1
+ export function generateRuntimeRelations() {
2
+ return `// This file was auto-generated by Lenz. Do not edit manually.
3
+ // @generated
4
+
5
+ import { Db, ObjectId } from 'mongodb'
6
+ import { QueryBuilder } from './query'
7
+
8
+ export class RelationResolver {
9
+ static async resolveOneToOne(
10
+ db: Db,
11
+ sourceCollection: string,
12
+ targetCollection: string,
13
+ sourceId: string,
14
+ foreignKey: string
15
+ ): Promise<any> {
16
+ const collection = db.collection(targetCollection)
17
+ const id = QueryBuilder.normalizeId(sourceId)
18
+ return await collection.findOne({ [foreignKey]: id })
19
+ }
20
+
21
+ static async resolveOneToMany(
22
+ db: Db,
23
+ sourceCollection: string,
24
+ targetCollection: string,
25
+ sourceId: string,
26
+ foreignKey: string
27
+ ): Promise<any[]> {
28
+ const collection = db.collection(targetCollection)
29
+ const id = QueryBuilder.normalizeId(sourceId)
30
+ return await collection.find({ [foreignKey]: id }).toArray()
31
+ }
32
+
33
+ static async resolveManyToMany(
34
+ db: Db,
35
+ sourceCollection: string,
36
+ targetCollection: string,
37
+ joinCollection: string,
38
+ sourceField: string,
39
+ targetField: string,
40
+ sourceId: string,
41
+ where?: any,
42
+ orderBy?: any,
43
+ take?: number,
44
+ skip?: number,
45
+ select?: any
46
+ ): Promise<any[]> {
47
+ const joinCol = db.collection(joinCollection)
48
+ const targetCol = db.collection(targetCollection)
49
+
50
+ const connections = await joinCol.find({
51
+ [sourceField]: sourceId
52
+ }).toArray()
53
+
54
+ const targetIds = connections.map(c => c[targetField])
55
+
56
+ if (targetIds.length === 0) {
57
+ return []
58
+ }
59
+
60
+ let query: any = { _id: { $in: targetIds.map(id => {
61
+ try { return new ObjectId(id) } catch { return id }
62
+ }) } }
63
+
64
+ if (where && Object.keys(where).length > 0) {
65
+ query = { $and: [query, where] }
66
+ }
67
+
68
+ let cursor = targetCol.find(query)
69
+
70
+ if (orderBy) {
71
+ cursor = cursor.sort(orderBy)
72
+ }
73
+ if (skip !== undefined && skip !== null) {
74
+ cursor = cursor.skip(skip)
75
+ }
76
+ if (take !== undefined && take !== null) {
77
+ cursor = cursor.limit(take)
78
+ }
79
+ if (select) {
80
+ const projection = QueryBuilder.buildProjection(select, [])
81
+ if (projection) {
82
+ cursor = cursor.project(projection)
83
+ }
84
+ }
85
+
86
+ return await cursor.toArray()
87
+ }
88
+
89
+ static async countManyToMany(
90
+ db: Db,
91
+ joinCollection: string,
92
+ sourceField: string,
93
+ sourceId: string
94
+ ): Promise<number> {
95
+ const joinCol = db.collection(joinCollection)
96
+ return await joinCol.countDocuments({ [sourceField]: sourceId })
97
+ }
98
+
99
+ static formatDocument(doc: any): any {
100
+ if (!doc) return doc
101
+ const formatted = { ...doc }
102
+ if (formatted._id) {
103
+ formatted.id = formatted._id.toString()
104
+ delete formatted._id
105
+ }
106
+ // Convert BSON types to JS types (depth-first, recursing into arrays and nested objects)
107
+ for (const [key, val] of Object.entries(formatted)) {
108
+ if (Array.isArray(val)) {
109
+ formatted[key] = val.map(item =>
110
+ item !== null && typeof item === 'object' ? this.formatDocument(item) : item
111
+ );
112
+ } else if (val !== null && typeof val === 'object') {
113
+ if (val instanceof ObjectId || val._bsontype === 'ObjectId') {
114
+ formatted[key] = val.toString()
115
+ } else if (val._bsontype === 'Long') {
116
+ formatted[key] = val.toBigInt()
117
+ } else if (val._bsontype === 'Binary') {
118
+ formatted[key] = Buffer.from(val.buffer)
119
+ } else if (!(val instanceof Date) && !(val instanceof Buffer)) {
120
+ // Recurse into nested objects (embedded documents)
121
+ formatted[key] = this.formatDocument(val)
122
+ }
123
+ }
124
+ }
125
+ return formatted
126
+ }
127
+ }
128
+ `;
129
+ }
130
+ //# sourceMappingURL=GenerateRuntimeRelations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenerateRuntimeRelations.js","sourceRoot":"","sources":["../../../src/engine/generators/GenerateRuntimeRelations.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,wBAAwB;IACpC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8HV,CAAC;AACF,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { generateRuntimePagination } from './GenerateRuntimePagination.js';
2
+ import { generateRuntimeErrors } from './GenerateRuntimeErrors.js';
3
+ import { generateRuntimeLogger } from './GenerateRuntimeLogger.js';
4
+ import { generateRuntimeIndex } from './GenerateRuntimeIndex.js';
5
+ import { generateRuntimeQuery } from './GenerateRuntimeQuery.js';
6
+ import { generateRuntimeRelations } from './GenerateRuntimeRelations.js';
7
+ export { generateRuntimePagination, generateRuntimeErrors, generateRuntimeLogger, generateRuntimeIndex, generateRuntimeQuery, generateRuntimeRelations };
8
+ export declare class RuntimeGenerator {
9
+ generateRuntimePagination(): string;
10
+ generateRuntimeErrors(): string;
11
+ generateRuntimeLogger(): string;
12
+ generateRuntimeIndex(): string;
13
+ generateRuntimeQuery(): string;
14
+ generateRuntimeRelations(): string;
15
+ }
16
+ //# sourceMappingURL=RuntimeGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RuntimeGenerator.d.ts","sourceRoot":"","sources":["../../../src/engine/generators/RuntimeGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACzB,CAAC;AAEF,qBAAa,gBAAgB;IAC3B,yBAAyB;IACzB,qBAAqB;IACrB,qBAAqB;IACrB,oBAAoB;IACpB,oBAAoB;IACpB,wBAAwB;CACzB"}
@@ -0,0 +1,16 @@
1
+ import { generateRuntimePagination } from './GenerateRuntimePagination.js';
2
+ import { generateRuntimeErrors } from './GenerateRuntimeErrors.js';
3
+ import { generateRuntimeLogger } from './GenerateRuntimeLogger.js';
4
+ import { generateRuntimeIndex } from './GenerateRuntimeIndex.js';
5
+ import { generateRuntimeQuery } from './GenerateRuntimeQuery.js';
6
+ import { generateRuntimeRelations } from './GenerateRuntimeRelations.js';
7
+ export { generateRuntimePagination, generateRuntimeErrors, generateRuntimeLogger, generateRuntimeIndex, generateRuntimeQuery, generateRuntimeRelations };
8
+ export class RuntimeGenerator {
9
+ generateRuntimePagination() { return generateRuntimePagination(); }
10
+ generateRuntimeErrors() { return generateRuntimeErrors(); }
11
+ generateRuntimeLogger() { return generateRuntimeLogger(); }
12
+ generateRuntimeIndex() { return generateRuntimeIndex(); }
13
+ generateRuntimeQuery() { return generateRuntimeQuery(); }
14
+ generateRuntimeRelations() { return generateRuntimeRelations(); }
15
+ }
16
+ //# sourceMappingURL=RuntimeGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RuntimeGenerator.js","sourceRoot":"","sources":["../../../src/engine/generators/RuntimeGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACzB,CAAC;AAEF,MAAM,OAAO,gBAAgB;IAC3B,yBAAyB,KAAK,OAAO,yBAAyB,EAAE,CAAC,CAAC,CAAC;IACnE,qBAAqB,KAAK,OAAO,qBAAqB,EAAE,CAAC,CAAC,CAAC;IAC3D,qBAAqB,KAAK,OAAO,qBAAqB,EAAE,CAAC,CAAC,CAAC;IAC3D,oBAAoB,KAAK,OAAO,oBAAoB,EAAE,CAAC,CAAC,CAAC;IACzD,oBAAoB,KAAK,OAAO,oBAAoB,EAAE,CAAC,CAAC,CAAC;IACzD,wBAAwB,KAAK,OAAO,wBAAwB,EAAE,CAAC,CAAC,CAAC;CAClE"}
@@ -0,0 +1,2 @@
1
+ export declare function generateFilterTypes(): string;
2
+ //# sourceMappingURL=TypeFilterTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TypeFilterTypes.d.ts","sourceRoot":"","sources":["../../../src/engine/generators/TypeFilterTypes.ts"],"names":[],"mappings":"AAAA,wBAAgB,mBAAmB,IAAI,MAAM,CA0N5C"}