json-rest-api 1.0.10 → 1.0.11

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 (117) hide show
  1. package/.claude/settings.local.json +9 -1
  2. package/AGENTS.md +34 -0
  3. package/CLAUDE.md +26 -1
  4. package/CRUSH.md +55 -0
  5. package/README.md +3 -3
  6. package/TODO.todo +8 -0
  7. package/docs/API.md +17 -18
  8. package/docs/COMPARISON.md +18 -13
  9. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +17 -21
  10. package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +33 -2
  11. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +61 -7
  12. package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +58 -61
  13. package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +37 -37
  14. package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +103 -87
  15. package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +37 -34
  16. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +45 -42
  17. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +3 -3
  18. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +1 -1
  19. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +5 -5
  20. package/docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md +498 -755
  21. package/docs/GUIDE/GUIDE_6_Positioning.md +1 -3
  22. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +4 -5
  23. package/docs/GUIDE/GUIDE_X_Bulk_Operations.md +15 -6
  24. package/docs/GUIDE/GUIDE_X_Cors.md +2 -5
  25. package/docs/GUIDE/GUIDE_X_File_Uploads.md +2 -3
  26. package/docs/GUIDE/GUIDE_X_Multihome.md +1 -4
  27. package/docs/GUIDE/GUIDE_X_SocketIO.md +0 -2
  28. package/docs/GUIDE/GUIDE_X_URL_Management.md +317 -0
  29. package/docs/GUIDE/index.md +3 -0
  30. package/docs/QUICKSTART.md +12 -12
  31. package/docs/index.md +2 -2
  32. package/docs/mini-guides/file-uploads-complete.md +0 -1
  33. package/examples/url-override-example.js +137 -0
  34. package/index.js +7 -1
  35. package/package.json +5 -9
  36. package/plugins/core/bulk-operations-plugin.js +2 -0
  37. package/plugins/core/connectors/express-plugin.js +43 -24
  38. package/plugins/core/connectors/lib/request-helpers.js +2 -2
  39. package/plugins/core/lib/anyapi/anyapi-registry.js +821 -0
  40. package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +647 -0
  41. package/plugins/core/lib/anyapi/schema-utils.js +186 -0
  42. package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +113 -0
  43. package/plugins/core/lib/dbTablesOperations.js +305 -0
  44. package/plugins/core/lib/querying/knex-cross-table-search.js +64 -108
  45. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +21 -27
  46. package/plugins/core/lib/querying/knex-pagination-helpers.js +36 -20
  47. package/plugins/core/lib/querying/knex-query-helpers-base.js +49 -14
  48. package/plugins/core/lib/querying/knex-query-helpers.js +378 -222
  49. package/plugins/core/lib/querying/knex-relationship-includes.js +262 -87
  50. package/plugins/core/lib/querying/url-helpers.js +119 -0
  51. package/plugins/core/lib/querying-writing/compile-schemas.js +36 -28
  52. package/plugins/core/lib/querying-writing/database-capabilities.js +10 -9
  53. package/plugins/core/lib/querying-writing/knex-field-helpers.js +9 -3
  54. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +2 -2
  55. package/plugins/core/lib/querying-writing/payload-validators.js +10 -16
  56. package/plugins/core/lib/querying-writing/schema-helpers.js +52 -60
  57. package/plugins/core/lib/querying-writing/scope-validations.js +78 -22
  58. package/plugins/core/lib/querying-writing/simplified-helpers.js +208 -61
  59. package/plugins/core/lib/storage/storage-adapter.js +160 -0
  60. package/plugins/core/lib/writing/relationship-processor.js +4 -11
  61. package/plugins/core/rest-api-access.js +456 -0
  62. package/plugins/core/rest-api-anyapi-knex-plugin.js +2285 -0
  63. package/plugins/core/rest-api-knex-plugin.js +226 -87
  64. package/plugins/core/rest-api-multihome-plugin.js +89 -27
  65. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +0 -1
  66. package/plugins/core/rest-api-plugin-methods/check-permissions.js +1 -5
  67. package/plugins/core/rest-api-plugin-methods/common.js +46 -10
  68. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +31 -27
  69. package/plugins/core/rest-api-plugin-methods/delete.js +1 -4
  70. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +13 -1
  71. package/plugins/core/rest-api-plugin-methods/get-related.js +114 -56
  72. package/plugins/core/rest-api-plugin-methods/get-relationship.js +4 -3
  73. package/plugins/core/rest-api-plugin-methods/get.js +2 -5
  74. package/plugins/core/rest-api-plugin-methods/patch.js +15 -16
  75. package/plugins/core/rest-api-plugin-methods/post-relationship.js +21 -14
  76. package/plugins/core/rest-api-plugin-methods/post.js +23 -11
  77. package/plugins/core/rest-api-plugin-methods/put.js +18 -21
  78. package/plugins/core/rest-api-plugin-methods/query.js +3 -4
  79. package/plugins/core/rest-api-plugin-methods/release.js +13 -0
  80. package/plugins/core/rest-api-plugin.js +3 -4
  81. package/plugins/core/rest-api-positioning-plugin.js +253 -76
  82. package/plugins/core/socketio-plugin.js +436 -502
  83. package/quickTest.js +2 -2
  84. package/scripts/debug-anyapi-review-filter.mjs +63 -0
  85. package/scripts/inspect-anyapi-descriptor-country.mjs +19 -0
  86. package/scripts/inspect-anyapi-descriptor.mjs +19 -0
  87. package/test-debug.js +59 -0
  88. package/tests/README.md +7 -1
  89. package/tests/anyapi-basic.test.js +102 -0
  90. package/tests/anyapi-cursor-pagination.test.js +144 -0
  91. package/tests/computed-fields.test.js +27 -12
  92. package/tests/cors.test.js +5 -6
  93. package/tests/curl.test.js +1 -1
  94. package/tests/custom-idproperty-relationships.test.js +37 -29
  95. package/tests/field-getters.test.js +25 -7
  96. package/tests/fixtures/api-configs.js +576 -150
  97. package/tests/helpers/storage-mode.js +65 -0
  98. package/tests/helpers/test-utils.js +85 -3
  99. package/tests/include-depth-validation.test.js +35 -1
  100. package/tests/nested-includes.test.js +130 -103
  101. package/tests/pagination-cursor-multifield.test.js +17 -13
  102. package/tests/pagination-enhanced.test.js +19 -24
  103. package/tests/polymorphic-simple.test.js +9 -9
  104. package/tests/positioning-advanced.test.js +19 -19
  105. package/tests/positioning-extended.test.js +25 -25
  106. package/tests/positioning-multifilter.test.js +62 -62
  107. package/tests/positioning.test.js +7 -7
  108. package/tests/queries.test.js +5 -5
  109. package/tests/query-limits.test.js +77 -92
  110. package/tests/rest-api-access.test.js +206 -0
  111. package/tests/searchschema-merge.test.js +182 -0
  112. package/tests/socketio.test.js +33 -14
  113. package/tests/virtual-fields.test.js +0 -1
  114. package/docs/DOCS_ALL.md +0 -21894
  115. package/plugins/core/jwt-auth-plugin.js +0 -897
  116. package/plugins/core/lib/jwt-auth-helpers.js +0 -130
  117. package/tests/jwt-auth.test.js +0 -692
@@ -0,0 +1,647 @@
1
+ import { resolveFieldInfo } from '../utils/descriptor-helpers.js';
2
+
3
+ const JOIN_METHODS = new Set([
4
+ 'join',
5
+ 'innerJoin',
6
+ 'leftJoin',
7
+ 'leftOuterJoin',
8
+ 'rightJoin',
9
+ 'rightOuterJoin',
10
+ 'outerJoin',
11
+ 'crossJoin',
12
+ ]);
13
+
14
+ const JOIN_ON_METHODS = new Set([
15
+ 'on',
16
+ 'andOn',
17
+ 'orOn',
18
+ 'onVal',
19
+ 'andOnVal',
20
+ 'orOnVal',
21
+ ]);
22
+
23
+ const JOIN_ON_IN_METHODS = new Set([
24
+ 'onIn',
25
+ 'orOnIn',
26
+ 'onNotIn',
27
+ 'orOnNotIn',
28
+ ]);
29
+
30
+ const JOIN_ON_NULL_METHODS = new Set([
31
+ 'onNull',
32
+ 'orOnNull',
33
+ 'onNotNull',
34
+ 'orOnNotNull',
35
+ ]);
36
+
37
+ const JOIN_ON_BETWEEN_METHODS = new Set([
38
+ 'onBetween',
39
+ 'orOnBetween',
40
+ 'onNotBetween',
41
+ 'orOnNotBetween',
42
+ ]);
43
+
44
+ const WHERE_METHODS = new Set([
45
+ 'where',
46
+ 'andWhere',
47
+ 'orWhere',
48
+ 'whereNot',
49
+ 'andWhereNot',
50
+ 'orWhereNot',
51
+ 'having',
52
+ 'orHaving',
53
+ ]);
54
+
55
+ const WHERE_IN_METHODS = new Set([
56
+ 'whereIn',
57
+ 'orWhereIn',
58
+ 'whereNotIn',
59
+ 'orWhereNotIn',
60
+ ]);
61
+
62
+ const BETWEEN_METHODS = new Set(['whereBetween', 'orWhereBetween', 'whereNotBetween', 'orWhereNotBetween']);
63
+
64
+ const NULL_METHODS = new Set(['whereNull', 'orWhereNull', 'whereNotNull', 'orWhereNotNull']);
65
+
66
+ const ORDER_METHODS = new Set(['orderBy', 'orderByRaw']);
67
+
68
+ const GROUP_METHODS = new Set(['groupBy', 'groupByRaw']);
69
+
70
+ const SELECT_METHODS = new Set(['select', 'columns', 'distinct']);
71
+
72
+ const AGGREGATE_METHODS = new Set(['count', 'min', 'max', 'sum', 'avg']);
73
+
74
+ const CALLBACK_FIRST_METHODS = new Set([
75
+ 'where',
76
+ 'andWhere',
77
+ 'orWhere',
78
+ 'having',
79
+ 'orHaving',
80
+ ]);
81
+
82
+ const isFunction = (value) => typeof value === 'function';
83
+ const isObject = (value) => value && typeof value === 'object' && !Array.isArray(value);
84
+
85
+ const SQL_OPERATORS = new Set([
86
+ '=',
87
+ '!=',
88
+ '<>',
89
+ '<',
90
+ '<=',
91
+ '>',
92
+ '>=',
93
+ 'like',
94
+ 'not like',
95
+ 'ilike',
96
+ 'not ilike',
97
+ 'is',
98
+ 'is not',
99
+ 'is null',
100
+ 'is not null',
101
+ ]);
102
+
103
+ const parseTableExpression = (expression) => {
104
+ if (expression && typeof expression === 'object' && !Array.isArray(expression)) {
105
+ const [alias, tableName] = Object.entries(expression)[0] || [];
106
+ if (!alias) {
107
+ return { original: expression };
108
+ }
109
+ return { alias, tableName, original: expression };
110
+ }
111
+
112
+ if (typeof expression !== 'string') {
113
+ return { original: expression };
114
+ }
115
+
116
+ const raw = expression.trim();
117
+ if (raw === '') {
118
+ return { original: expression };
119
+ }
120
+
121
+ const asMatch = raw.split(/\s+as\s+/i);
122
+ if (asMatch.length === 2) {
123
+ return { tableName: asMatch[0].trim(), alias: asMatch[1].trim(), original: expression };
124
+ }
125
+
126
+ const parts = raw.split(/\s+/);
127
+ if (parts.length === 2) {
128
+ return { tableName: parts[0].trim(), alias: parts[1].trim(), original: expression };
129
+ }
130
+
131
+ return { tableName: raw, alias: raw, original: expression };
132
+ };
133
+
134
+ const translateObjectKeys = (adapter, obj) => {
135
+ const result = {};
136
+ for (const [key, value] of Object.entries(obj)) {
137
+ result[adapter.translateColumn(key)] = value;
138
+ }
139
+ return result;
140
+ };
141
+
142
+ const translateJoinObject = (adapter, obj) => {
143
+ const result = {};
144
+ for (const [lhs, rhs] of Object.entries(obj)) {
145
+ const translatedKey = typeof lhs === 'string' ? adapter.translateColumn(lhs) : lhs;
146
+ const translatedValue = typeof rhs === 'string' ? adapter.translateColumn(rhs) : rhs;
147
+ result[translatedKey] = translatedValue;
148
+ }
149
+ return result;
150
+ };
151
+
152
+ const maybeWrapCallback = (adapter, callback) => {
153
+ if (!isFunction(callback)) return callback;
154
+ return function wrappedCallback(...args) {
155
+ return callback.apply(adapter.proxy, args);
156
+ };
157
+ };
158
+
159
+ const isRaw = (value) => Boolean(value && typeof value === 'object' && typeof value.toSQL === 'function' && value.toSQL().method);
160
+
161
+ const ensureArray = (value) => (Array.isArray(value) ? value : [value]);
162
+
163
+ export async function preloadRelatedDescriptors({ registry, descriptor }) {
164
+ const visited = new Map();
165
+ const queue = [descriptor.resource];
166
+ visited.set(descriptor.resource, descriptor);
167
+
168
+ while (queue.length > 0) {
169
+ const resource = queue.pop();
170
+ const current = visited.get(resource);
171
+ if (!current) continue;
172
+
173
+ const relatedTargets = new Set();
174
+
175
+ Object.values(current.belongsTo || {}).forEach((info) => {
176
+ if (info?.target) relatedTargets.add(info.target);
177
+ });
178
+
179
+ Object.values(current.relationships || {}).forEach((info) => {
180
+ if (info?.target) relatedTargets.add(info.target);
181
+ });
182
+
183
+ Object.values(current.manyToMany || {}).forEach((info) => {
184
+ if (info?.target) relatedTargets.add(info.target);
185
+ if (info?.through) relatedTargets.add(info.through);
186
+ });
187
+
188
+ Object.values(current.polymorphicBelongsTo || {}).forEach((info) => {
189
+ ensureArray(info?.types || []).forEach((type) => relatedTargets.add(type));
190
+ });
191
+
192
+ for (const target of relatedTargets) {
193
+ if (!target || visited.has(target)) continue;
194
+ const nextDescriptor = await registry.getDescriptor(current.tenant, target);
195
+ if (!nextDescriptor) continue;
196
+ visited.set(target, nextDescriptor);
197
+ queue.push(target);
198
+ }
199
+ }
200
+
201
+ return visited;
202
+ }
203
+
204
+ export class AnyapiQueryAdapter {
205
+ constructor({
206
+ descriptor,
207
+ db,
208
+ registry,
209
+ descriptorsMap,
210
+ resourceToTableName,
211
+ tableNameToResource,
212
+ log,
213
+ }) {
214
+ this.descriptor = descriptor;
215
+ this.db = db;
216
+ this.registry = registry;
217
+ this.descriptorsMap = descriptorsMap;
218
+ this.resourceToTableName = resourceToTableName || new Map();
219
+ this.tableNameToResource = tableNameToResource || new Map();
220
+ this.log = log || console;
221
+
222
+ this.primaryAlias = this.resourceToTableName.get(descriptor.resource) || descriptor.resource;
223
+ this.aliasDescriptors = new Map();
224
+ this.aliasDescriptors.set(this.primaryAlias, descriptor);
225
+
226
+ this.aliasDescriptors.set(descriptor.resource, descriptor);
227
+ this.aliasActualNames = new Map();
228
+ this.aliasActualNames.set(this.primaryAlias, this.primaryAlias);
229
+ this.aliasActualNames.set(descriptor.resource, this.primaryAlias);
230
+
231
+ const physicalTableName = this.resourceToTableName.get(descriptor.resource);
232
+ if (physicalTableName) {
233
+ this.aliasDescriptors.set(physicalTableName, descriptor);
234
+ this.aliasActualNames.set(physicalTableName, this.primaryAlias);
235
+ }
236
+
237
+ this.builders = new WeakMap();
238
+
239
+ this.builder = db({ [this.primaryAlias]: descriptor.canonical.tableName })
240
+ .where(`${this.primaryAlias}.${descriptor.canonical.tenantColumn}`, descriptor.tenant)
241
+ .where(`${this.primaryAlias}.${descriptor.canonical.resourceColumn}`, descriptor.resource);
242
+
243
+ this.proxy = this.#createProxy(this.builder);
244
+ }
245
+
246
+ get tableAlias() {
247
+ return this.primaryAlias;
248
+ }
249
+
250
+ get query() {
251
+ return this.proxy;
252
+ }
253
+
254
+ translateColumn(reference) {
255
+ if (typeof reference !== 'string') {
256
+ return reference;
257
+ }
258
+
259
+ const trimmed = reference.trim();
260
+ if (trimmed === '*' || trimmed === '') {
261
+ return reference;
262
+ }
263
+
264
+ const parts = trimmed.split('.');
265
+ let requestedAlias;
266
+ let field;
267
+
268
+ if (parts.length === 1) {
269
+ requestedAlias = this.primaryAlias;
270
+ field = parts[0];
271
+ } else {
272
+ requestedAlias = parts.shift();
273
+ field = parts.join('.');
274
+ }
275
+
276
+ if (!field) {
277
+ return reference;
278
+ }
279
+
280
+ let descriptor = this.aliasDescriptors.get(requestedAlias);
281
+ if (!descriptor) {
282
+ const resource = this.tableNameToResource.get(requestedAlias) || requestedAlias;
283
+ descriptor = this.descriptorsMap.get(resource);
284
+ if (descriptor) {
285
+ this.aliasDescriptors.set(requestedAlias, descriptor);
286
+ const actualAlias = this.resourceToTableName.get(resource) || resource;
287
+ this.aliasActualNames.set(requestedAlias, actualAlias);
288
+ }
289
+ }
290
+
291
+ if (!descriptor) {
292
+ return reference;
293
+ }
294
+
295
+ const info = resolveFieldInfo(descriptor, field);
296
+ if (!info?.column) {
297
+ return reference;
298
+ }
299
+
300
+ const actualAlias = this.aliasActualNames.get(requestedAlias)
301
+ || this.resourceToTableName.get(descriptor.resource)
302
+ || descriptor.resource;
303
+
304
+ return `${actualAlias}.${info.column}`;
305
+ }
306
+
307
+ translateColumns(columns) {
308
+ if (Array.isArray(columns)) {
309
+ return columns.map((column) => this.translateColumn(column));
310
+ }
311
+ return this.translateColumn(columns);
312
+ }
313
+
314
+ registerAlias(alias, descriptor) {
315
+ if (!alias || !descriptor) return;
316
+ this.aliasDescriptors.set(alias, descriptor);
317
+ this.aliasActualNames.set(alias, alias);
318
+ }
319
+
320
+ #translateWhereArgs(method, args) {
321
+ if (args.length === 0) return args;
322
+ const [first, ...rest] = args;
323
+
324
+ if (isFunction(first)) {
325
+ return [maybeWrapCallback(this, first), ...rest];
326
+ }
327
+
328
+ if (isObject(first) && !isRaw(first)) {
329
+ return [translateObjectKeys(this, first), ...rest];
330
+ }
331
+
332
+ if (Array.isArray(first)) {
333
+ return [first.map((item) => (typeof item === 'string' ? this.translateColumn(item) : item)), ...rest];
334
+ }
335
+
336
+ const translatedFirst = this.translateColumn(first);
337
+
338
+ if (rest.length > 0 && isFunction(rest[rest.length - 1])) {
339
+ const newRest = [...rest];
340
+ newRest[newRest.length - 1] = maybeWrapCallback(this, rest[rest.length - 1]);
341
+ return [translatedFirst, ...newRest];
342
+ }
343
+
344
+ return [translatedFirst, ...rest];
345
+ }
346
+
347
+ #translateWhereInArgs(args) {
348
+ if (args.length === 0) return args;
349
+ const [column, values, ...rest] = args;
350
+ const translatedColumn = this.translateColumn(column);
351
+
352
+ if (isFunction(values)) {
353
+ return [translatedColumn, maybeWrapCallback(this, values), ...rest];
354
+ }
355
+
356
+ return [translatedColumn, values, ...rest];
357
+ }
358
+
359
+ #translateBetweenArgs(args) {
360
+ if (args.length === 0) return args;
361
+ const [column, range, ...rest] = args;
362
+ return [this.translateColumn(column), range, ...rest];
363
+ }
364
+
365
+ #translateNullArgs(args) {
366
+ if (args.length === 0) return args;
367
+ const [column, ...rest] = args;
368
+ return [this.translateColumn(column), ...rest];
369
+ }
370
+
371
+ #translateOrderArgs(method, args) {
372
+ if (args.length === 0) return args;
373
+
374
+ if (method === 'orderByRaw') {
375
+ return args;
376
+ }
377
+
378
+ const [column, direction, ...rest] = args;
379
+
380
+ if (Array.isArray(column)) {
381
+ return [column.map((entry) => {
382
+ if (typeof entry === 'string') {
383
+ return this.translateColumn(entry);
384
+ }
385
+ if (isObject(entry)) {
386
+ const [col, dir] = Object.entries(entry)[0] || [];
387
+ if (!col) return entry;
388
+ return { [this.translateColumn(col)]: dir };
389
+ }
390
+ return entry;
391
+ }), direction, ...rest];
392
+ }
393
+
394
+ if (isObject(column) && column.column) {
395
+ return [{ ...column, column: this.translateColumn(column.column) }, direction, ...rest];
396
+ }
397
+
398
+ return [this.translateColumn(column), direction, ...rest];
399
+ }
400
+
401
+ #translateGroupArgs(args) {
402
+ if (args.length === 0) return args;
403
+ const [first, ...rest] = args;
404
+ if (Array.isArray(first)) {
405
+ return [first.map((entry) => (typeof entry === 'string' ? this.translateColumn(entry) : entry)), ...rest];
406
+ }
407
+ return [this.translateColumn(first), ...rest];
408
+ }
409
+
410
+ #translateSelectArgs(args) {
411
+ if (args.length === 0) return args;
412
+ const [first, ...rest] = args;
413
+
414
+ if (Array.isArray(first)) {
415
+ return [first.map((entry) => (typeof entry === 'string' ? this.translateColumn(entry) : entry)), ...rest];
416
+ }
417
+
418
+ if (typeof first === 'string') {
419
+ return [this.translateColumn(first), ...rest];
420
+ }
421
+
422
+ return [first, ...rest];
423
+ }
424
+
425
+ #translateAggregateArgs(args) {
426
+ if (args.length === 0) return args;
427
+ const [column, options] = args;
428
+ if (Array.isArray(column)) {
429
+ return [column.map((entry) => (typeof entry === 'string' ? this.translateColumn(entry) : entry)), options];
430
+ }
431
+ if (typeof column === 'string') {
432
+ return [this.translateColumn(column), options];
433
+ }
434
+ return args;
435
+ }
436
+
437
+ #translateOnArgs(args) {
438
+ if (args.length === 0) return args;
439
+ const [first, ...rest] = args;
440
+
441
+ if (isFunction(first)) {
442
+ return [maybeWrapCallback(this, first), ...rest];
443
+ }
444
+
445
+ if (isObject(first) && !isRaw(first)) {
446
+ return [translateJoinObject(this, first), ...rest];
447
+ }
448
+
449
+ const translatedFirst = typeof first === 'string' ? this.translateColumn(first) : first;
450
+
451
+ if (rest.length === 0) {
452
+ return [translatedFirst];
453
+ }
454
+
455
+ const translatedRest = rest.map((value, index) => {
456
+ if (isFunction(value)) {
457
+ return maybeWrapCallback(this, value);
458
+ }
459
+ if (typeof value !== 'string') {
460
+ return value;
461
+ }
462
+ const lower = value.toLowerCase();
463
+ if (SQL_OPERATORS.has(lower)) {
464
+ return value;
465
+ }
466
+ return this.translateColumn(value);
467
+ });
468
+
469
+ return [translatedFirst, ...translatedRest];
470
+ }
471
+
472
+ #translateOnInArgs(args) {
473
+ if (args.length === 0) return args;
474
+ const [column, values, ...rest] = args;
475
+ const translatedColumn = typeof column === 'string' ? this.translateColumn(column) : column;
476
+
477
+ if (isFunction(values)) {
478
+ return [translatedColumn, maybeWrapCallback(this, values), ...rest];
479
+ }
480
+
481
+ return [translatedColumn, values, ...rest];
482
+ }
483
+
484
+ #translateOnBetweenArgs(args) {
485
+ if (args.length === 0) return args;
486
+ const [column, range, ...rest] = args;
487
+ return [typeof column === 'string' ? this.translateColumn(column) : column, range, ...rest];
488
+ }
489
+
490
+ #translateOnNullArgs(args) {
491
+ if (args.length === 0) return args;
492
+ const [column, ...rest] = args;
493
+ return [typeof column === 'string' ? this.translateColumn(column) : column, ...rest];
494
+ }
495
+
496
+ #addResourceConstraints(joinClause, alias, descriptor) {
497
+ joinClause.on(`${alias}.${descriptor.canonical.tenantColumn}`, this.db.raw('?', [descriptor.tenant]));
498
+ joinClause.andOn(`${alias}.${descriptor.canonical.resourceColumn}`, this.db.raw('?', [descriptor.resource]));
499
+ }
500
+
501
+ #createJoinProxy(joinClause) {
502
+ const adapter = this;
503
+ const handler = {
504
+ get(target, prop, receiver) {
505
+ const original = target[prop];
506
+ if (typeof original !== 'function') {
507
+ return Reflect.get(target, prop, receiver);
508
+ }
509
+ return (...args) => {
510
+ let translatedArgs = args;
511
+
512
+ if (WHERE_METHODS.has(prop)) {
513
+ translatedArgs = adapter.#translateWhereArgs(prop, args);
514
+ } else if (WHERE_IN_METHODS.has(prop)) {
515
+ translatedArgs = adapter.#translateWhereInArgs(args);
516
+ } else if (BETWEEN_METHODS.has(prop)) {
517
+ translatedArgs = adapter.#translateBetweenArgs(args);
518
+ } else if (NULL_METHODS.has(prop)) {
519
+ translatedArgs = adapter.#translateNullArgs(args);
520
+ } else if (JOIN_ON_METHODS.has(prop)) {
521
+ translatedArgs = adapter.#translateOnArgs(args);
522
+ } else if (JOIN_ON_IN_METHODS.has(prop)) {
523
+ translatedArgs = adapter.#translateOnInArgs(args);
524
+ } else if (JOIN_ON_BETWEEN_METHODS.has(prop)) {
525
+ translatedArgs = adapter.#translateOnBetweenArgs(args);
526
+ } else if (JOIN_ON_NULL_METHODS.has(prop)) {
527
+ translatedArgs = adapter.#translateOnNullArgs(args);
528
+ }
529
+
530
+ const result = original.apply(target, translatedArgs);
531
+ return result === target ? receiver : result;
532
+ };
533
+ },
534
+ };
535
+
536
+ return new Proxy(joinClause, handler);
537
+ }
538
+
539
+ #handleJoin(method, args) {
540
+ if (args.length === 0) return args;
541
+ const [tableExpression, ...rest] = args;
542
+ const parsed = parseTableExpression(tableExpression);
543
+
544
+ if (!parsed.tableName || !parsed.alias) {
545
+ return args;
546
+ }
547
+
548
+ const resource = this.tableNameToResource.get(parsed.tableName) || parsed.tableName;
549
+ const descriptor = this.descriptorsMap.get(resource);
550
+
551
+ if (!descriptor) {
552
+ return args;
553
+ }
554
+
555
+ this.registerAlias(parsed.alias, descriptor);
556
+
557
+ const joinTarget = { [parsed.alias]: descriptor.canonical.tableName };
558
+
559
+ if (rest.length === 0) {
560
+ return [joinTarget];
561
+ }
562
+
563
+ const [first, ...remaining] = rest;
564
+
565
+ if (isFunction(first)) {
566
+ const joinCallback = first;
567
+ const wrappedCallback = (joinClause) => {
568
+ this.#addResourceConstraints(joinClause, parsed.alias, descriptor);
569
+ const proxyClause = this.#createJoinProxy(joinClause);
570
+ return joinCallback.call(proxyClause, proxyClause);
571
+ };
572
+ return [joinTarget, wrappedCallback, ...remaining];
573
+ }
574
+
575
+ const translatedArgs = rest.map((value, index) => {
576
+ if (index === 0 || index === 1) {
577
+ return this.translateColumn(value);
578
+ }
579
+ return value;
580
+ });
581
+
582
+ return [joinTarget, ...translatedArgs];
583
+ }
584
+
585
+ #createProxy(builder) {
586
+ const adapter = this;
587
+ const handler = {
588
+ get(target, prop, receiver) {
589
+ if (prop === '__adapter__') {
590
+ return adapter;
591
+ }
592
+
593
+ const value = target[prop];
594
+ if (typeof value !== 'function') {
595
+ return Reflect.get(target, prop, receiver);
596
+ }
597
+
598
+ if (JOIN_METHODS.has(prop)) {
599
+ return (...args) => {
600
+ const translatedArgs = adapter.#handleJoin(prop, args);
601
+ const result = value.apply(target, translatedArgs);
602
+ return result === target ? receiver : result;
603
+ };
604
+ }
605
+
606
+ return (...args) => {
607
+ let translatedArgs = args;
608
+
609
+ if (WHERE_METHODS.has(prop)) {
610
+ translatedArgs = adapter.#translateWhereArgs(prop, args);
611
+ } else if (WHERE_IN_METHODS.has(prop)) {
612
+ translatedArgs = adapter.#translateWhereInArgs(args);
613
+ } else if (BETWEEN_METHODS.has(prop)) {
614
+ translatedArgs = adapter.#translateBetweenArgs(args);
615
+ } else if (NULL_METHODS.has(prop)) {
616
+ translatedArgs = adapter.#translateNullArgs(args);
617
+ } else if (JOIN_ON_METHODS.has(prop)) {
618
+ translatedArgs = adapter.#translateOnArgs(args);
619
+ } else if (JOIN_ON_IN_METHODS.has(prop)) {
620
+ translatedArgs = adapter.#translateOnInArgs(args);
621
+ } else if (JOIN_ON_BETWEEN_METHODS.has(prop)) {
622
+ translatedArgs = adapter.#translateOnBetweenArgs(args);
623
+ } else if (JOIN_ON_NULL_METHODS.has(prop)) {
624
+ translatedArgs = adapter.#translateOnNullArgs(args);
625
+ } else if (ORDER_METHODS.has(prop)) {
626
+ translatedArgs = adapter.#translateOrderArgs(prop, args);
627
+ } else if (GROUP_METHODS.has(prop)) {
628
+ translatedArgs = adapter.#translateGroupArgs(args);
629
+ } else if (SELECT_METHODS.has(prop)) {
630
+ translatedArgs = adapter.#translateSelectArgs(args);
631
+ } else if (AGGREGATE_METHODS.has(prop)) {
632
+ translatedArgs = adapter.#translateAggregateArgs(args);
633
+ } else if (CALLBACK_FIRST_METHODS.has(prop) && args.length && isFunction(args[0])) {
634
+ translatedArgs = [maybeWrapCallback(adapter, args[0]), ...args.slice(1)];
635
+ }
636
+
637
+ const result = value.apply(target, translatedArgs);
638
+ return result === target ? receiver : result;
639
+ };
640
+ },
641
+ };
642
+
643
+ const proxy = new Proxy(builder, handler);
644
+ this.builders.set(proxy, builder);
645
+ return proxy;
646
+ }
647
+ }