@budibase/backend-core 2.31.7 → 2.32.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 (36) hide show
  1. package/dist/index.js +380 -155
  2. package/dist/index.js.map +4 -4
  3. package/dist/index.js.meta.json +1 -1
  4. package/dist/package.json +4 -4
  5. package/dist/plugins.js.map +2 -2
  6. package/dist/plugins.js.meta.json +1 -1
  7. package/dist/src/environment.d.ts +1 -0
  8. package/dist/src/environment.js +1 -1
  9. package/dist/src/environment.js.map +1 -1
  10. package/dist/src/events/publishers/ai.d.ts +7 -0
  11. package/dist/src/events/publishers/ai.js +30 -0
  12. package/dist/src/events/publishers/ai.js.map +1 -0
  13. package/dist/src/events/publishers/index.d.ts +1 -0
  14. package/dist/src/events/publishers/index.js +3 -1
  15. package/dist/src/events/publishers/index.js.map +1 -1
  16. package/dist/src/features/index.d.ts +2 -0
  17. package/dist/src/features/index.js +2 -0
  18. package/dist/src/features/index.js.map +1 -1
  19. package/dist/src/sql/sql.js +343 -148
  20. package/dist/src/sql/sql.js.map +1 -1
  21. package/dist/src/sql/utils.d.ts +2 -1
  22. package/dist/src/sql/utils.js +14 -0
  23. package/dist/src/sql/utils.js.map +1 -1
  24. package/dist/tests/core/utilities/structures/licenses.js +10 -0
  25. package/dist/tests/core/utilities/structures/licenses.js.map +1 -1
  26. package/dist/tests/core/utilities/structures/quotas.js +4 -0
  27. package/dist/tests/core/utilities/structures/quotas.js.map +1 -1
  28. package/package.json +4 -4
  29. package/src/environment.ts +1 -0
  30. package/src/events/publishers/ai.ts +21 -0
  31. package/src/events/publishers/index.ts +1 -0
  32. package/src/features/index.ts +2 -0
  33. package/src/sql/sql.ts +474 -211
  34. package/src/sql/utils.ts +29 -1
  35. package/tests/core/utilities/structures/licenses.ts +10 -0
  36. package/tests/core/utilities/structures/quotas.ts +4 -0
package/dist/index.js CHANGED
@@ -54288,6 +54288,9 @@ var AuditedEventFriendlyName = {
54288
54288
  // EMAIL
54289
54289
  ["email:smtp:created" /* EMAIL_SMTP_CREATED */]: `Email configuration created`,
54290
54290
  ["email:smtp:updated" /* EMAIL_SMTP_UPDATED */]: `Email configuration updated`,
54291
+ // AI
54292
+ ["ai:config:created" /* AI_CONFIG_CREATED */]: `AI configuration created`,
54293
+ ["ai:config:updated" /* AI_CONFIG_UPDATED */]: `AI configuration updated`,
54291
54294
  // AUTH
54292
54295
  ["auth:sso:created" /* AUTH_SSO_CREATED */]: `SSO configuration created`,
54293
54296
  ["auth:sso:updated" /* AUTH_SSO_UPDATED */]: `SSO configuration updated`,
@@ -55353,7 +55356,7 @@ function fixupFilterArrays(filters) {
55353
55356
  recurseLogicalOperators(filters, fixupFilterArrays);
55354
55357
  return filters;
55355
55358
  }
55356
- var search = (docs, query) => {
55359
+ function search(docs, query) {
55357
55360
  let result = runQuery(docs, query.query);
55358
55361
  if (query.sort) {
55359
55362
  result = sort(result, query.sort, query.sortOrder || "ascending" /* ASCENDING */);
@@ -55367,8 +55370,8 @@ var search = (docs, query) => {
55367
55370
  response.totalRows = totalRows;
55368
55371
  }
55369
55372
  return response;
55370
- };
55371
- var runQuery = (docs, query, findInDoc = deepGet) => {
55373
+ }
55374
+ function runQuery(docs, query) {
55372
55375
  if (!docs || !Array.isArray(docs)) {
55373
55376
  return [];
55374
55377
  }
@@ -55382,7 +55385,7 @@ var runQuery = (docs, query, findInDoc = deepGet) => {
55382
55385
  }
55383
55386
  const match = (type, test) => (doc) => {
55384
55387
  for (const [key, testValue] of Object.entries(query[type] || {})) {
55385
- const valueToCheck = isLogicalSearchOperator(type) ? doc : findInDoc(doc, removeKeyNumbering(key));
55388
+ const valueToCheck = isLogicalSearchOperator(type) ? doc : deepGet(doc, removeKeyNumbering(key));
55386
55389
  const result = test(valueToCheck, testValue);
55387
55390
  if (query.allOr && result) {
55388
55391
  return true;
@@ -55606,8 +55609,8 @@ var runQuery = (docs, query, findInDoc = deepGet) => {
55606
55609
  }
55607
55610
  };
55608
55611
  return docs.filter(docMatch);
55609
- };
55610
- var sort = (docs, sort2, sortOrder, sortType = "string" /* STRING */) => {
55612
+ }
55613
+ function sort(docs, sort2, sortOrder, sortType = "string" /* STRING */) {
55611
55614
  if (!sort2 || !sortOrder || !sortType) {
55612
55615
  return docs;
55613
55616
  }
@@ -55629,14 +55632,14 @@ var sort = (docs, sort2, sortOrder, sortType = "string" /* STRING */) => {
55629
55632
  }
55630
55633
  return result;
55631
55634
  });
55632
- };
55633
- var limit = (docs, limit2) => {
55635
+ }
55636
+ function limit(docs, limit2) {
55634
55637
  const numLimit = parseFloat(limit2);
55635
55638
  if (isNaN(numLimit)) {
55636
55639
  return docs;
55637
55640
  }
55638
55641
  return docs.slice(0, numLimit);
55639
- };
55642
+ }
55640
55643
  var hasFilters = (query) => {
55641
55644
  if (!query) {
55642
55645
  return false;
@@ -56297,6 +56300,7 @@ var environment = {
56297
56300
  // Couch/search
56298
56301
  SQL_LOGGING_ENABLE: process.env.SQL_LOGGING_ENABLE,
56299
56302
  SQL_MAX_ROWS: process.env.SQL_MAX_ROWS,
56303
+ SQL_MAX_RELATED_ROWS: process.env.MAX_RELATED_ROWS,
56300
56304
  // smtp
56301
56305
  SMTP_FALLBACK_ENABLED: process.env.SMTP_FALLBACK_ENABLED,
56302
56306
  SMTP_USER: process.env.SMTP_USER,
@@ -56788,7 +56792,8 @@ __export(utils_exports3, {
56788
56792
  isValidFilter: () => isValidFilter,
56789
56793
  isValidISODateString: () => isValidISODateString,
56790
56794
  isValidTime: () => isValidTime,
56791
- sqlLog: () => sqlLog
56795
+ sqlLog: () => sqlLog,
56796
+ validateManyToMany: () => validateManyToMany
56792
56797
  });
56793
56798
 
56794
56799
  // src/db/index.ts
@@ -59762,6 +59767,15 @@ function sqlLog(client, query, values2) {
59762
59767
  }
59763
59768
  console.log(string);
59764
59769
  }
59770
+ function isValidManyToManyRelationship(relationship) {
59771
+ return !!relationship.through && !!relationship.fromPrimary && !!relationship.from && !!relationship.toPrimary && !!relationship.to;
59772
+ }
59773
+ function validateManyToMany(relationship) {
59774
+ if (isValidManyToManyRelationship(relationship)) {
59775
+ return relationship;
59776
+ }
59777
+ return void 0;
59778
+ }
59765
59779
 
59766
59780
  // src/features/index.ts
59767
59781
  var features_exports = {};
@@ -59953,7 +59967,9 @@ var FlagSet = class {
59953
59967
  };
59954
59968
  var flags = new FlagSet({
59955
59969
  DEFAULT_VALUES: Flag.boolean(environment_default.isDev()),
59970
+ AUTOMATION_BRANCHING: Flag.boolean(environment_default.isDev()),
59956
59971
  SQS: Flag.boolean(environment_default.isDev()),
59972
+ ["AI_CUSTOM_CONFIGS" /* AI_CUSTOM_CONFIGS */]: Flag.boolean(environment_default.isDev()),
59957
59973
  ["ENRICHED_RELATIONSHIPS" /* ENRICHED_RELATIONSHIPS */]: Flag.boolean(false)
59958
59974
  });
59959
59975
 
@@ -62307,6 +62323,7 @@ async function getAccountHolderFromUserIds(userIds) {
62307
62323
  var events_exports = {};
62308
62324
  __export(events_exports, {
62309
62325
  account: () => account_default,
62326
+ ai: () => ai_default,
62310
62327
  analytics: () => analytics_exports,
62311
62328
  app: () => app_default,
62312
62329
  auditLog: () => auditLog_default,
@@ -63891,6 +63908,20 @@ var email_default = {
63891
63908
  SMTPUpdated
63892
63909
  };
63893
63910
 
63911
+ // src/events/publishers/ai.ts
63912
+ async function AIConfigCreated(timestamp) {
63913
+ const properties = {};
63914
+ await publishEvent("ai:config:created" /* AI_CONFIG_CREATED */, properties, timestamp);
63915
+ }
63916
+ async function AIConfigUpdated() {
63917
+ const properties = {};
63918
+ await publishEvent("ai:config:updated" /* AI_CONFIG_UPDATED */, properties);
63919
+ }
63920
+ var ai_default = {
63921
+ AIConfigCreated,
63922
+ AIConfigUpdated
63923
+ };
63924
+
63894
63925
  // src/events/publishers/license.ts
63895
63926
  async function planChanged(account, opts) {
63896
63927
  const properties = {
@@ -67953,10 +67984,15 @@ var sqlTable_default = SqlTableQueryBuilder;
67953
67984
 
67954
67985
  // src/sql/sql.ts
67955
67986
  var import_lodash3 = require("lodash");
67987
+ var MAX_SQS_RELATIONSHIP_FIELDS = 63;
67956
67988
  function getBaseLimit() {
67957
67989
  const envLimit = environment_default.SQL_MAX_ROWS ? parseInt(environment_default.SQL_MAX_ROWS) : null;
67958
67990
  return envLimit || 5e3;
67959
67991
  }
67992
+ function getRelationshipLimit() {
67993
+ const envLimit = environment_default.SQL_MAX_RELATED_ROWS ? parseInt(environment_default.SQL_MAX_RELATED_ROWS) : null;
67994
+ return envLimit || 500;
67995
+ }
67960
67996
  function getTableName(table) {
67961
67997
  if (table?.sourceType === "internal" /* INTERNAL */ || table?.sourceId === INTERNAL_TABLE_SOURCE_ID) {
67962
67998
  return table?._id;
@@ -67981,6 +68017,15 @@ function convertBooleans(query) {
67981
68017
  }
67982
68018
  var InternalBuilder = class {
67983
68019
  constructor(client, knex3, query) {
68020
+ // states the various situations in which we need a full mapped select statement
68021
+ this.SPECIAL_SELECT_CASES = {
68022
+ POSTGRES_MONEY: (field) => {
68023
+ return this.client === "pg" /* POSTGRES */ && field?.externalType?.includes("money");
68024
+ },
68025
+ MSSQL_DATES: (field) => {
68026
+ return this.client === "mssql" /* MS_SQL */ && field?.type === "datetime" /* DATETIME */ && field.timeOnly;
68027
+ }
68028
+ };
67984
68029
  this.client = client;
67985
68030
  this.query = query;
67986
68031
  this.knex = knex3;
@@ -68015,48 +68060,50 @@ var InternalBuilder = class {
68015
68060
  quotedIdentifier(key) {
68016
68061
  return key.split(".").map((part) => this.quote(part)).join(".");
68017
68062
  }
68063
+ isFullSelectStatementRequired() {
68064
+ const { meta } = this.query;
68065
+ for (let column of Object.values(meta.table.schema)) {
68066
+ if (this.SPECIAL_SELECT_CASES.POSTGRES_MONEY(column)) {
68067
+ return true;
68068
+ } else if (this.SPECIAL_SELECT_CASES.MSSQL_DATES(column)) {
68069
+ return true;
68070
+ }
68071
+ }
68072
+ return false;
68073
+ }
68018
68074
  generateSelectStatement() {
68019
- const { resource, meta } = this.query;
68075
+ const { meta, endpoint, resource, tableAliases } = this.query;
68020
68076
  if (!resource || !resource.fields || resource.fields.length === 0) {
68021
68077
  return "*";
68022
68078
  }
68079
+ const alias = tableAliases?.[endpoint.entityId] ? tableAliases?.[endpoint.entityId] : endpoint.entityId;
68023
68080
  const schema = meta.table.schema;
68081
+ if (!this.isFullSelectStatementRequired()) {
68082
+ return [this.knex.raw(`${this.quote(alias)}.*`)];
68083
+ }
68024
68084
  return resource.fields.map((field) => {
68025
68085
  const parts = field.split(/\./g);
68026
68086
  let table = void 0;
68027
- let column = void 0;
68028
- if (parts.length === 1) {
68029
- column = parts[0];
68030
- }
68031
- if (parts.length === 2) {
68032
- table = parts[0];
68033
- column = parts[1];
68034
- }
68035
- if (parts.length > 2) {
68087
+ let column = parts[0];
68088
+ if (parts.length > 1) {
68036
68089
  table = parts[0];
68037
68090
  column = parts.slice(1).join(".");
68038
68091
  }
68039
- if (!column) {
68040
- throw new Error(`Invalid field name: ${field}`);
68041
- }
68092
+ return { table, column, field };
68093
+ }).filter(({ table }) => !table || table === alias).map(({ table, column, field }) => {
68042
68094
  const columnSchema = schema[column];
68043
- if (this.client === "pg" /* POSTGRES */ && columnSchema?.externalType?.includes("money")) {
68095
+ if (this.SPECIAL_SELECT_CASES.POSTGRES_MONEY(columnSchema)) {
68044
68096
  return this.knex.raw(
68045
68097
  `${this.quotedIdentifier(
68046
68098
  [table, column].join(".")
68047
68099
  )}::money::numeric as ${this.quote(field)}`
68048
68100
  );
68049
68101
  }
68050
- if (this.client === "mssql" /* MS_SQL */ && columnSchema?.type === "datetime" /* DATETIME */ && columnSchema.timeOnly) {
68102
+ if (this.SPECIAL_SELECT_CASES.MSSQL_DATES(columnSchema)) {
68051
68103
  return this.knex.raw(`CONVERT(varchar, ${field}, 108) as "${field}"`);
68052
68104
  }
68053
- if (table) {
68054
- return this.knex.raw(
68055
- `${this.quote(table)}.${this.quote(column)} as ${this.quote(field)}`
68056
- );
68057
- } else {
68058
- return this.knex.raw(`${this.quote(field)} as ${this.quote(field)}`);
68059
- }
68105
+ const quoted = table ? `${this.quote(table)}.${this.quote(column)}` : this.quote(field);
68106
+ return this.knex.raw(quoted);
68060
68107
  });
68061
68108
  }
68062
68109
  // OracleDB can't use character-large-objects (CLOBs) in WHERE clauses,
@@ -68167,28 +68214,94 @@ var InternalBuilder = class {
68167
68214
  }
68168
68215
  return filters;
68169
68216
  }
68217
+ addJoinFieldCheck(query, relationship) {
68218
+ const document = relationship.from?.split(".")[0] || "";
68219
+ return query.andWhere(`${document}.fieldName`, "=", relationship.column);
68220
+ }
68221
+ addRelationshipForFilter(query, filterKey, whereCb) {
68222
+ const mainKnex = this.knex;
68223
+ const { relationships, endpoint, tableAliases: aliases } = this.query;
68224
+ const tableName = endpoint.entityId;
68225
+ const fromAlias = aliases?.[tableName] || tableName;
68226
+ const matches2 = (possibleTable) => filterKey.startsWith(`${possibleTable}`);
68227
+ if (!relationships) {
68228
+ return query;
68229
+ }
68230
+ for (const relationship of relationships) {
68231
+ const relatedTableName = relationship.tableName;
68232
+ const toAlias = aliases?.[relatedTableName] || relatedTableName;
68233
+ if ((matches2(relatedTableName) || matches2(toAlias)) && relationship.to && relationship.tableName) {
68234
+ let subQuery = mainKnex.select(mainKnex.raw(1)).from({ [toAlias]: relatedTableName });
68235
+ const manyToMany = validateManyToMany(relationship);
68236
+ if (manyToMany) {
68237
+ const throughAlias = aliases?.[manyToMany.through] || relationship.through;
68238
+ let throughTable = this.tableNameWithSchema(manyToMany.through, {
68239
+ alias: throughAlias,
68240
+ schema: endpoint.schema
68241
+ });
68242
+ subQuery = subQuery.innerJoin(throughTable, function() {
68243
+ this.on(
68244
+ `${toAlias}.${manyToMany.toPrimary}`,
68245
+ "=",
68246
+ `${throughAlias}.${manyToMany.to}`
68247
+ );
68248
+ }).where(
68249
+ `${throughAlias}.${manyToMany.from}`,
68250
+ "=",
68251
+ mainKnex.raw(
68252
+ this.quotedIdentifier(`${fromAlias}.${manyToMany.fromPrimary}`)
68253
+ )
68254
+ );
68255
+ if (this.client === "sqlite3" /* SQL_LITE */) {
68256
+ subQuery = this.addJoinFieldCheck(subQuery, manyToMany);
68257
+ }
68258
+ } else {
68259
+ subQuery = subQuery.where(
68260
+ `${toAlias}.${relationship.to}`,
68261
+ "=",
68262
+ mainKnex.raw(
68263
+ this.quotedIdentifier(`${fromAlias}.${relationship.from}`)
68264
+ )
68265
+ );
68266
+ }
68267
+ query = query.whereExists(whereCb(subQuery));
68268
+ break;
68269
+ }
68270
+ }
68271
+ return query;
68272
+ }
68170
68273
  // right now we only do filters on the specific table being queried
68171
68274
  addFilters(query, filters, opts) {
68172
68275
  if (!filters) {
68173
68276
  return query;
68174
68277
  }
68278
+ const builder = this;
68175
68279
  filters = this.parseFilters({ ...filters });
68176
68280
  const aliases = this.query.tableAliases;
68177
68281
  const allOr = filters.allOr;
68178
- const tableName = this.client === "sqlite3" /* SQL_LITE */ ? this.table._id : this.table.name;
68282
+ const isSqlite = this.client === "sqlite3" /* SQL_LITE */;
68283
+ const tableName = isSqlite ? this.table._id : this.table.name;
68179
68284
  function getTableAlias(name) {
68180
68285
  const alias = aliases?.[name];
68181
68286
  return alias || name;
68182
68287
  }
68183
68288
  function iterate(structure, fn, complexKeyFn) {
68289
+ const handleRelationship = (q2, key, value) => {
68290
+ const [filterTableName, ...otherProperties] = key.split(".");
68291
+ const property = otherProperties.join(".");
68292
+ const alias = getTableAlias(filterTableName);
68293
+ return fn(q2, alias ? `${alias}.${property}` : property, value);
68294
+ };
68184
68295
  for (const key in structure) {
68185
68296
  const value = structure[key];
68186
68297
  const updatedKey = removeKeyNumbering2(key);
68187
68298
  const isRelationshipField = updatedKey.includes(".");
68299
+ const shouldProcessRelationship = opts?.relationship && isRelationshipField;
68188
68300
  let castedTypeValue;
68189
68301
  if (key === "_complexIdOperator" /* COMPLEX_ID_OPERATOR */ && (castedTypeValue = structure[key]) && complexKeyFn) {
68190
68302
  const alias = getTableAlias(tableName);
68191
- complexKeyFn(
68303
+ query = complexKeyFn(
68304
+ query,
68192
68305
  castedTypeValue.id.map(
68193
68306
  (x) => alias ? `${alias}.${x}` : x
68194
68307
  ),
@@ -68196,23 +68309,26 @@ var InternalBuilder = class {
68196
68309
  );
68197
68310
  } else if (!isRelationshipField) {
68198
68311
  const alias = getTableAlias(tableName);
68199
- fn(alias ? `${alias}.${updatedKey}` : updatedKey, value);
68200
- }
68201
- if (opts?.relationship && isRelationshipField) {
68202
- const [filterTableName, property] = updatedKey.split(".");
68203
- const alias = getTableAlias(filterTableName);
68204
- fn(alias ? `${alias}.${property}` : property, value);
68312
+ query = fn(
68313
+ query,
68314
+ alias ? `${alias}.${updatedKey}` : updatedKey,
68315
+ value
68316
+ );
68317
+ } else if (shouldProcessRelationship) {
68318
+ query = builder.addRelationshipForFilter(query, updatedKey, (q2) => {
68319
+ return handleRelationship(q2, updatedKey, value);
68320
+ });
68205
68321
  }
68206
68322
  }
68207
68323
  }
68208
- const like = (key, value) => {
68324
+ const like = (q2, key, value) => {
68209
68325
  const fuzzyOr = filters?.fuzzyOr;
68210
68326
  const fnc = fuzzyOr || allOr ? "orWhere" : "where";
68211
68327
  if (this.client === "pg" /* POSTGRES */) {
68212
- query = query[fnc](key, "ilike", `%${value}%`);
68328
+ return q2[fnc](key, "ilike", `%${value}%`);
68213
68329
  } else {
68214
68330
  const rawFnc = `${fnc}Raw`;
68215
- query = query[rawFnc](`LOWER(${this.quotedIdentifier(key)}) LIKE ?`, [
68331
+ return q2[rawFnc](`LOWER(${this.quotedIdentifier(key)}) LIKE ?`, [
68216
68332
  `%${value.toLowerCase()}%`
68217
68333
  ]);
68218
68334
  }
@@ -68229,13 +68345,13 @@ var InternalBuilder = class {
68229
68345
  return `[${value.join(",")}]`;
68230
68346
  }
68231
68347
  if (this.client === "pg" /* POSTGRES */) {
68232
- iterate(mode, (key, value) => {
68348
+ iterate(mode, (q2, key, value) => {
68233
68349
  const wrap = any ? "" : "'";
68234
68350
  const op = any ? "\\?| array" : "@>";
68235
68351
  const fieldNames = key.split(/\./g);
68236
68352
  const table = fieldNames[0];
68237
68353
  const col = fieldNames[1];
68238
- query = query[rawFnc](
68354
+ return q2[rawFnc](
68239
68355
  `${not}COALESCE("${table}"."${col}"::jsonb ${op} ${wrap}${stringifyArray(
68240
68356
  value,
68241
68357
  any ? "'" : '"'
@@ -68244,8 +68360,8 @@ var InternalBuilder = class {
68244
68360
  });
68245
68361
  } else if (this.client === "mysql2" /* MY_SQL */) {
68246
68362
  const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS";
68247
- iterate(mode, (key, value) => {
68248
- query = query[rawFnc](
68363
+ iterate(mode, (q2, key, value) => {
68364
+ return q2[rawFnc](
68249
68365
  `${not}COALESCE(${jsonFnc}(${key}, '${stringifyArray(
68250
68366
  value
68251
68367
  )}'), FALSE)`
@@ -68253,7 +68369,7 @@ var InternalBuilder = class {
68253
68369
  });
68254
68370
  } else {
68255
68371
  const andOr = mode === filters?.containsAny ? " OR " : " AND ";
68256
- iterate(mode, (key, value) => {
68372
+ iterate(mode, (q2, key, value) => {
68257
68373
  let statement = "";
68258
68374
  const identifier = this.quotedIdentifier(key);
68259
68375
  for (let i in value) {
@@ -68265,15 +68381,15 @@ var InternalBuilder = class {
68265
68381
  statement += `${statement ? andOr : ""}COALESCE(LOWER(${identifier}), '') LIKE ?`;
68266
68382
  }
68267
68383
  if (statement === "") {
68268
- return;
68384
+ return q2;
68269
68385
  }
68270
68386
  if (not) {
68271
- query = query[rawFnc](
68387
+ return q2[rawFnc](
68272
68388
  `(NOT (${statement}) OR ${identifier} IS NULL)`,
68273
68389
  value
68274
68390
  );
68275
68391
  } else {
68276
- query = query[rawFnc](statement, value);
68392
+ return q2[rawFnc](statement, value);
68277
68393
  }
68278
68394
  });
68279
68395
  }
@@ -68300,35 +68416,35 @@ var InternalBuilder = class {
68300
68416
  const fnc = allOr ? "orWhereIn" : "whereIn";
68301
68417
  iterate(
68302
68418
  filters.oneOf,
68303
- (key, array) => {
68419
+ (q2, key, array) => {
68304
68420
  if (this.client === "oracledb" /* ORACLE */) {
68305
68421
  key = this.convertClobs(key);
68306
68422
  array = Array.isArray(array) ? array : [array];
68307
68423
  const binding = new Array(array.length).fill("?").join(",");
68308
- query = query.whereRaw(`${key} IN (${binding})`, array);
68424
+ return q2.whereRaw(`${key} IN (${binding})`, array);
68309
68425
  } else {
68310
- query = query[fnc](key, Array.isArray(array) ? array : [array]);
68426
+ return q2[fnc](key, Array.isArray(array) ? array : [array]);
68311
68427
  }
68312
68428
  },
68313
- (key, array) => {
68429
+ (q2, key, array) => {
68314
68430
  if (this.client === "oracledb" /* ORACLE */) {
68315
68431
  const keyStr = `(${key.map((k) => this.convertClobs(k)).join(",")})`;
68316
68432
  const binding = `(${array.map((a) => `(${new Array(a.length).fill("?").join(",")})`).join(",")})`;
68317
- query = query.whereRaw(`${keyStr} IN ${binding}`, array.flat());
68433
+ return q2.whereRaw(`${keyStr} IN ${binding}`, array.flat());
68318
68434
  } else {
68319
- query = query[fnc](key, Array.isArray(array) ? array : [array]);
68435
+ return q2[fnc](key, Array.isArray(array) ? array : [array]);
68320
68436
  }
68321
68437
  }
68322
68438
  );
68323
68439
  }
68324
68440
  if (filters.string) {
68325
- iterate(filters.string, (key, value) => {
68441
+ iterate(filters.string, (q2, key, value) => {
68326
68442
  const fnc = allOr ? "orWhere" : "where";
68327
68443
  if (this.client === "pg" /* POSTGRES */) {
68328
- query = query[fnc](key, "ilike", `${value}%`);
68444
+ return q2[fnc](key, "ilike", `${value}%`);
68329
68445
  } else {
68330
68446
  const rawFnc = `${fnc}Raw`;
68331
- query = query[rawFnc](`LOWER(${this.quotedIdentifier(key)}) LIKE ?`, [
68447
+ return q2[rawFnc](`LOWER(${this.quotedIdentifier(key)}) LIKE ?`, [
68332
68448
  `${value.toLowerCase()}%`
68333
68449
  ]);
68334
68450
  }
@@ -68338,7 +68454,7 @@ var InternalBuilder = class {
68338
68454
  iterate(filters.fuzzy, like);
68339
68455
  }
68340
68456
  if (filters.range) {
68341
- iterate(filters.range, (key, value) => {
68457
+ iterate(filters.range, (q2, key, value) => {
68342
68458
  const isEmptyObject = (val) => {
68343
68459
  return val && Object.keys(val).length === 0 && Object.getPrototypeOf(val) === Object.prototype;
68344
68460
  };
@@ -68355,91 +68471,87 @@ var InternalBuilder = class {
68355
68471
  }
68356
68472
  if (lowValid && highValid) {
68357
68473
  if (schema?.type === "bigint" /* BIGINT */ && this.client === "sqlite3" /* SQL_LITE */) {
68358
- query = query.whereRaw(
68474
+ return q2.whereRaw(
68359
68475
  `CAST(${key} AS INTEGER) BETWEEN CAST(? AS INTEGER) AND CAST(? AS INTEGER)`,
68360
68476
  [value.low, value.high]
68361
68477
  );
68362
68478
  } else {
68363
68479
  const fnc = allOr ? "orWhereBetween" : "whereBetween";
68364
- query = query[fnc](key, [value.low, value.high]);
68480
+ return q2[fnc](key, [value.low, value.high]);
68365
68481
  }
68366
68482
  } else if (lowValid) {
68367
68483
  if (schema?.type === "bigint" /* BIGINT */ && this.client === "sqlite3" /* SQL_LITE */) {
68368
- query = query.whereRaw(
68369
- `CAST(${key} AS INTEGER) >= CAST(? AS INTEGER)`,
68370
- [value.low]
68371
- );
68484
+ return q2.whereRaw(`CAST(${key} AS INTEGER) >= CAST(? AS INTEGER)`, [
68485
+ value.low
68486
+ ]);
68372
68487
  } else {
68373
68488
  const fnc = allOr ? "orWhere" : "where";
68374
- query = query[fnc](key, ">=", value.low);
68489
+ return q2[fnc](key, ">=", value.low);
68375
68490
  }
68376
68491
  } else if (highValid) {
68377
68492
  if (schema?.type === "bigint" /* BIGINT */ && this.client === "sqlite3" /* SQL_LITE */) {
68378
- query = query.whereRaw(
68379
- `CAST(${key} AS INTEGER) <= CAST(? AS INTEGER)`,
68380
- [value.high]
68381
- );
68493
+ return q2.whereRaw(`CAST(${key} AS INTEGER) <= CAST(? AS INTEGER)`, [
68494
+ value.high
68495
+ ]);
68382
68496
  } else {
68383
68497
  const fnc = allOr ? "orWhere" : "where";
68384
- query = query[fnc](key, "<=", value.high);
68498
+ return q2[fnc](key, "<=", value.high);
68385
68499
  }
68386
68500
  }
68501
+ return q2;
68387
68502
  });
68388
68503
  }
68389
68504
  if (filters.equal) {
68390
- iterate(filters.equal, (key, value) => {
68505
+ iterate(filters.equal, (q2, key, value) => {
68391
68506
  const fnc = allOr ? "orWhereRaw" : "whereRaw";
68392
68507
  if (this.client === "mssql" /* MS_SQL */) {
68393
- query = query[fnc](
68508
+ return q2[fnc](
68394
68509
  `CASE WHEN ${this.quotedIdentifier(key)} = ? THEN 1 ELSE 0 END = 1`,
68395
68510
  [value]
68396
68511
  );
68397
68512
  } else if (this.client === "oracledb" /* ORACLE */) {
68398
68513
  const identifier = this.convertClobs(key);
68399
- query = query[fnc](
68400
- `(${identifier} IS NOT NULL AND ${identifier} = ?)`,
68401
- [value]
68402
- );
68514
+ return q2[fnc](`(${identifier} IS NOT NULL AND ${identifier} = ?)`, [
68515
+ value
68516
+ ]);
68403
68517
  } else {
68404
- query = query[fnc](
68405
- `COALESCE(${this.quotedIdentifier(key)} = ?, FALSE)`,
68406
- [value]
68407
- );
68518
+ return q2[fnc](`COALESCE(${this.quotedIdentifier(key)} = ?, FALSE)`, [
68519
+ value
68520
+ ]);
68408
68521
  }
68409
68522
  });
68410
68523
  }
68411
68524
  if (filters.notEqual) {
68412
- iterate(filters.notEqual, (key, value) => {
68525
+ iterate(filters.notEqual, (q2, key, value) => {
68413
68526
  const fnc = allOr ? "orWhereRaw" : "whereRaw";
68414
68527
  if (this.client === "mssql" /* MS_SQL */) {
68415
- query = query[fnc](
68528
+ return q2[fnc](
68416
68529
  `CASE WHEN ${this.quotedIdentifier(key)} = ? THEN 1 ELSE 0 END = 0`,
68417
68530
  [value]
68418
68531
  );
68419
68532
  } else if (this.client === "oracledb" /* ORACLE */) {
68420
68533
  const identifier = this.convertClobs(key);
68421
- query = query[fnc](
68534
+ return q2[fnc](
68422
68535
  `(${identifier} IS NOT NULL AND ${identifier} != ?) OR ${identifier} IS NULL`,
68423
68536
  [value]
68424
68537
  );
68425
68538
  } else {
68426
- query = query[fnc](
68427
- `COALESCE(${this.quotedIdentifier(key)} != ?, TRUE)`,
68428
- [value]
68429
- );
68539
+ return q2[fnc](`COALESCE(${this.quotedIdentifier(key)} != ?, TRUE)`, [
68540
+ value
68541
+ ]);
68430
68542
  }
68431
68543
  });
68432
68544
  }
68433
68545
  if (filters.empty) {
68434
- iterate(filters.empty, (key) => {
68546
+ iterate(filters.empty, (q2, key) => {
68435
68547
  const fnc = allOr ? "orWhereNull" : "whereNull";
68436
- query = query[fnc](key);
68548
+ return q2[fnc](key);
68437
68549
  });
68438
68550
  }
68439
68551
  if (filters.notEmpty) {
68440
- iterate(filters.notEmpty, (key) => {
68552
+ iterate(filters.notEmpty, (q2, key) => {
68441
68553
  const fnc = allOr ? "orWhereNotNull" : "whereNotNull";
68442
- query = query[fnc](key);
68554
+ return q2[fnc](key);
68443
68555
  });
68444
68556
  }
68445
68557
  if (filters.contains) {
@@ -68507,10 +68619,166 @@ var InternalBuilder = class {
68507
68619
  }
68508
68620
  return withSchema;
68509
68621
  }
68510
- addRelationships(query, fromTable, relationships, schema, aliases) {
68511
- if (!relationships) {
68512
- return query;
68622
+ addJsonRelationships(query, fromTable, relationships) {
68623
+ const sqlClient = this.client;
68624
+ const knex3 = this.knex;
68625
+ const { resource, tableAliases: aliases, endpoint } = this.query;
68626
+ const fields = resource?.fields || [];
68627
+ const jsonField = (field) => {
68628
+ const parts = field.split(".");
68629
+ let tableField, unaliased;
68630
+ if (parts.length > 1) {
68631
+ const alias = parts.shift();
68632
+ unaliased = parts.join(".");
68633
+ tableField = `${this.quote(alias)}.${this.quote(unaliased)}`;
68634
+ } else {
68635
+ unaliased = parts.join(".");
68636
+ tableField = this.quote(unaliased);
68637
+ }
68638
+ let separator = ",";
68639
+ switch (sqlClient) {
68640
+ case "oracledb" /* ORACLE */:
68641
+ separator = " VALUE ";
68642
+ break;
68643
+ case "mssql" /* MS_SQL */:
68644
+ separator = ":";
68645
+ }
68646
+ return `'${unaliased}'${separator}${tableField}`;
68647
+ };
68648
+ for (let relationship of relationships) {
68649
+ const {
68650
+ tableName: toTable,
68651
+ through: throughTable,
68652
+ to: toKey,
68653
+ from: fromKey,
68654
+ fromPrimary,
68655
+ toPrimary
68656
+ } = relationship;
68657
+ if (!toTable || !fromTable) {
68658
+ continue;
68659
+ }
68660
+ const toAlias = aliases?.[toTable] || toTable, fromAlias = aliases?.[fromTable] || fromTable;
68661
+ let toTableWithSchema = this.tableNameWithSchema(toTable, {
68662
+ alias: toAlias,
68663
+ schema: endpoint.schema
68664
+ });
68665
+ let relationshipFields = fields.filter(
68666
+ (field) => field.split(".")[0] === toAlias
68667
+ );
68668
+ if (this.client === "sqlite3" /* SQL_LITE */) {
68669
+ relationshipFields = relationshipFields.slice(
68670
+ 0,
68671
+ MAX_SQS_RELATIONSHIP_FIELDS
68672
+ );
68673
+ }
68674
+ const fieldList = relationshipFields.map((field) => jsonField(field)).join(",");
68675
+ const primaryKey = `${toAlias}.${toPrimary || toKey}`;
68676
+ let subQuery = knex3.from(toTableWithSchema).limit(getRelationshipLimit()).orderBy(primaryKey);
68677
+ if (throughTable && toPrimary && fromPrimary) {
68678
+ const throughAlias = aliases?.[throughTable] || throughTable;
68679
+ let throughTableWithSchema = this.tableNameWithSchema(throughTable, {
68680
+ alias: throughAlias,
68681
+ schema: endpoint.schema
68682
+ });
68683
+ subQuery = subQuery.join(throughTableWithSchema, function() {
68684
+ this.on(`${toAlias}.${toPrimary}`, "=", `${throughAlias}.${toKey}`);
68685
+ }).where(
68686
+ `${throughAlias}.${fromKey}`,
68687
+ "=",
68688
+ knex3.raw(this.quotedIdentifier(`${fromAlias}.${fromPrimary}`))
68689
+ );
68690
+ } else {
68691
+ subQuery = subQuery.where(
68692
+ `${toAlias}.${toKey}`,
68693
+ "=",
68694
+ knex3.raw(this.quotedIdentifier(`${fromAlias}.${fromKey}`))
68695
+ );
68696
+ }
68697
+ const standardWrap = (select) => {
68698
+ subQuery = subQuery.select(`${toAlias}.*`);
68699
+ return knex3.select(knex3.raw(select)).from({
68700
+ [toAlias]: subQuery
68701
+ });
68702
+ };
68703
+ let wrapperQuery;
68704
+ switch (sqlClient) {
68705
+ case "sqlite3" /* SQL_LITE */:
68706
+ subQuery = this.addJoinFieldCheck(subQuery, relationship);
68707
+ wrapperQuery = standardWrap(
68708
+ `json_group_array(json_object(${fieldList}))`
68709
+ );
68710
+ break;
68711
+ case "pg" /* POSTGRES */:
68712
+ wrapperQuery = standardWrap(
68713
+ `json_agg(json_build_object(${fieldList}))`
68714
+ );
68715
+ break;
68716
+ case "mysql2" /* MY_SQL */:
68717
+ wrapperQuery = subQuery.select(
68718
+ knex3.raw(`json_arrayagg(json_object(${fieldList}))`)
68719
+ );
68720
+ break;
68721
+ case "oracledb" /* ORACLE */:
68722
+ wrapperQuery = standardWrap(
68723
+ `json_arrayagg(json_object(${fieldList}))`
68724
+ );
68725
+ break;
68726
+ case "mssql" /* MS_SQL */:
68727
+ wrapperQuery = knex3.raw(
68728
+ `(SELECT ${this.quote(toAlias)} = (${knex3.select(`${fromAlias}.*`).from({
68729
+ [fromAlias]: subQuery.select(`${toAlias}.*`)
68730
+ })} FOR JSON PATH))`
68731
+ );
68732
+ break;
68733
+ default:
68734
+ throw new Error(`JSON relationships not implement for ${sqlClient}`);
68735
+ }
68736
+ query = query.select({ [relationship.column]: wrapperQuery });
68513
68737
  }
68738
+ return query;
68739
+ }
68740
+ addJoin(query, tables, columns) {
68741
+ const { tableAliases: aliases, endpoint } = this.query;
68742
+ const schema = endpoint.schema;
68743
+ const toTable = tables.to, fromTable = tables.from, throughTable = tables.through;
68744
+ const toAlias = aliases?.[toTable] || toTable, throughAlias = throughTable && aliases?.[throughTable] || throughTable, fromAlias = aliases?.[fromTable] || fromTable;
68745
+ let toTableWithSchema = this.tableNameWithSchema(toTable, {
68746
+ alias: toAlias,
68747
+ schema
68748
+ });
68749
+ let throughTableWithSchema = throughTable ? this.tableNameWithSchema(throughTable, {
68750
+ alias: throughAlias,
68751
+ schema
68752
+ }) : void 0;
68753
+ if (!throughTable) {
68754
+ query = query.leftJoin(toTableWithSchema, function() {
68755
+ for (let relationship of columns) {
68756
+ const from = relationship.from, to = relationship.to;
68757
+ this.orOn(`${fromAlias}.${from}`, "=", `${toAlias}.${to}`);
68758
+ }
68759
+ });
68760
+ } else {
68761
+ query = query.leftJoin(throughTableWithSchema, function() {
68762
+ for (let relationship of columns) {
68763
+ const fromPrimary = relationship.fromPrimary;
68764
+ const from = relationship.from;
68765
+ this.orOn(
68766
+ `${fromAlias}.${fromPrimary}`,
68767
+ "=",
68768
+ `${throughAlias}.${from}`
68769
+ );
68770
+ }
68771
+ }).leftJoin(toTableWithSchema, function() {
68772
+ for (let relationship of columns) {
68773
+ const toPrimary = relationship.toPrimary;
68774
+ const to = relationship.to;
68775
+ this.orOn(`${toAlias}.${toPrimary}`, `${throughAlias}.${to}`);
68776
+ }
68777
+ });
68778
+ }
68779
+ return query;
68780
+ }
68781
+ addRelationships(query, fromTable, relationships) {
68514
68782
  const tableSets = {};
68515
68783
  for (let relationship of relationships) {
68516
68784
  const keyObj = {
@@ -68529,41 +68797,15 @@ var InternalBuilder = class {
68529
68797
  }
68530
68798
  for (let [key, relationships2] of Object.entries(tableSets)) {
68531
68799
  const { toTable, throughTable } = JSON.parse(key);
68532
- const toAlias = aliases?.[toTable] || toTable, throughAlias = aliases?.[throughTable] || throughTable, fromAlias = aliases?.[fromTable] || fromTable;
68533
- let toTableWithSchema = this.tableNameWithSchema(toTable, {
68534
- alias: toAlias,
68535
- schema
68536
- });
68537
- let throughTableWithSchema = this.tableNameWithSchema(throughTable, {
68538
- alias: throughAlias,
68539
- schema
68540
- });
68541
- if (!throughTable) {
68542
- query = query.leftJoin(toTableWithSchema, function() {
68543
- for (let relationship of relationships2) {
68544
- const from = relationship.from, to = relationship.to;
68545
- this.orOn(`${fromAlias}.${from}`, "=", `${toAlias}.${to}`);
68546
- }
68547
- });
68548
- } else {
68549
- query = query.leftJoin(throughTableWithSchema, function() {
68550
- for (let relationship of relationships2) {
68551
- const fromPrimary = relationship.fromPrimary;
68552
- const from = relationship.from;
68553
- this.orOn(
68554
- `${fromAlias}.${fromPrimary}`,
68555
- "=",
68556
- `${throughAlias}.${from}`
68557
- );
68558
- }
68559
- }).leftJoin(toTableWithSchema, function() {
68560
- for (let relationship of relationships2) {
68561
- const toPrimary = relationship.toPrimary;
68562
- const to = relationship.to;
68563
- this.orOn(`${toAlias}.${toPrimary}`, `${throughAlias}.${to}`);
68564
- }
68565
- });
68566
- }
68800
+ query = this.addJoin(
68801
+ query,
68802
+ {
68803
+ from: fromTable,
68804
+ to: toTable,
68805
+ through: throughTable
68806
+ },
68807
+ relationships2
68808
+ );
68567
68809
  }
68568
68810
  return query;
68569
68811
  }
@@ -68639,7 +68881,7 @@ var InternalBuilder = class {
68639
68881
  return query.upsert(parsedBody);
68640
68882
  }
68641
68883
  read(opts = {}) {
68642
- let { endpoint, filters, paginate, relationships, tableAliases } = this.query;
68884
+ let { endpoint, filters, paginate, relationships } = this.query;
68643
68885
  const { limits } = opts;
68644
68886
  const counting = endpoint.operation === "COUNT" /* COUNT */;
68645
68887
  const tableName = endpoint.entityId;
@@ -68664,30 +68906,13 @@ var InternalBuilder = class {
68664
68906
  if (foundOffset != null) {
68665
68907
  query = query.offset(foundOffset);
68666
68908
  }
68667
- query = this.addSorting(query);
68668
68909
  }
68669
- query = this.addFilters(query, filters);
68670
- const alias = tableAliases?.[tableName] || tableName;
68671
- let preQuery = this.knex({
68672
- // the typescript definition for the knex constructor doesn't support this
68673
- // syntax, but it is the only way to alias a pre-query result as part of
68674
- // a query - there is an alias dictionary type, but it assumes it can only
68675
- // be a table name, not a pre-query
68676
- [alias]: query
68677
- });
68678
- preQuery = !counting ? preQuery.select(this.generateSelectStatement()) : this.addDistinctCount(preQuery);
68679
- if (this.client !== "mssql" /* MS_SQL */ && !counting) {
68680
- preQuery = this.addSorting(preQuery);
68910
+ query = !counting ? query.select(this.generateSelectStatement()) : this.addDistinctCount(query);
68911
+ if (!counting) {
68912
+ query = this.addSorting(query);
68681
68913
  }
68682
- query = this.addRelationships(
68683
- preQuery,
68684
- tableName,
68685
- relationships,
68686
- endpoint.schema,
68687
- tableAliases
68688
- );
68689
- if (limits?.base) {
68690
- query = query.limit(limits.base);
68914
+ if (relationships) {
68915
+ query = this.addJsonRelationships(query, tableName, relationships);
68691
68916
  }
68692
68917
  return this.addFilters(query, filters, { relationship: true });
68693
68918
  }