@budibase/backend-core 2.32.7 → 2.32.9

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 (37) hide show
  1. package/dist/index.js +729 -615
  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.meta.json +1 -1
  6. package/dist/src/cache/appMetadata.js +3 -3
  7. package/dist/src/cache/appMetadata.js.map +1 -1
  8. package/dist/src/context/mainContext.d.ts +3 -1
  9. package/dist/src/context/mainContext.js +19 -0
  10. package/dist/src/context/mainContext.js.map +1 -1
  11. package/dist/src/context/types.d.ts +2 -1
  12. package/dist/src/db/couch/DatabaseImpl.d.ts +1 -0
  13. package/dist/src/db/couch/DatabaseImpl.js +35 -5
  14. package/dist/src/db/couch/DatabaseImpl.js.map +1 -1
  15. package/dist/src/db/lucene.js +0 -1
  16. package/dist/src/db/lucene.js.map +1 -1
  17. package/dist/src/docIds/params.d.ts +2 -3
  18. package/dist/src/docIds/params.js +6 -3
  19. package/dist/src/docIds/params.js.map +1 -1
  20. package/dist/src/features/index.js +3 -2
  21. package/dist/src/features/index.js.map +1 -1
  22. package/dist/src/index.d.ts +2 -0
  23. package/dist/src/sql/sql.js +45 -9
  24. package/dist/src/sql/sql.js.map +1 -1
  25. package/dist/src/users/users.d.ts +1 -2
  26. package/dist/src/users/users.js +0 -24
  27. package/dist/src/users/users.js.map +1 -1
  28. package/package.json +4 -4
  29. package/src/cache/appMetadata.ts +3 -3
  30. package/src/context/mainContext.ts +18 -1
  31. package/src/context/types.ts +2 -1
  32. package/src/db/couch/DatabaseImpl.ts +35 -6
  33. package/src/db/lucene.ts +0 -1
  34. package/src/docIds/params.ts +8 -6
  35. package/src/features/index.ts +3 -1
  36. package/src/sql/sql.ts +46 -6
  37. package/src/users/users.ts +0 -29
package/dist/index.js CHANGED
@@ -54894,6 +54894,7 @@ __export(helpers_exports, {
54894
54894
  isGoogleSheets: () => isGoogleSheets,
54895
54895
  isSQL: () => isSQL,
54896
54896
  schema: () => schema_exports,
54897
+ views: () => views_exports,
54897
54898
  withTimeout: () => withTimeout
54898
54899
  });
54899
54900
 
@@ -55044,6 +55045,7 @@ __export(schema_exports, {
55044
55045
  decodeNonAscii: () => decodeNonAscii,
55045
55046
  encodeNonAscii: () => encodeNonAscii,
55046
55047
  isDeprecatedSingleUserColumn: () => isDeprecatedSingleUserColumn,
55048
+ isNumeric: () => isNumeric,
55047
55049
  isRequired: () => isRequired
55048
55050
  });
55049
55051
  function isDeprecatedSingleUserColumn(schema) {
@@ -55065,9 +55067,38 @@ function decodeNonAscii(str) {
55065
55067
  (match, p1) => String.fromCharCode(parseInt(p1, 16))
55066
55068
  );
55067
55069
  }
55070
+ function isNumeric(field) {
55071
+ return field.type === "number" /* NUMBER */ || field.type === "bigint" /* BIGINT */;
55072
+ }
55068
55073
 
55069
- // ../shared-core/src/filters.ts
55074
+ // ../shared-core/src/helpers/views.ts
55075
+ var views_exports = {};
55076
+ __export(views_exports, {
55077
+ basicFields: () => basicFields,
55078
+ calculationFields: () => calculationFields,
55079
+ isBasicViewField: () => isBasicViewField,
55080
+ isCalculationField: () => isCalculationField,
55081
+ isCalculationView: () => isCalculationView
55082
+ });
55070
55083
  var import_lodash = require("lodash");
55084
+ function isCalculationField(field) {
55085
+ return "calculationType" in field;
55086
+ }
55087
+ function isBasicViewField(field) {
55088
+ return !isCalculationField(field);
55089
+ }
55090
+ function isCalculationView(view) {
55091
+ return Object.values(view.schema || {}).some(isCalculationField);
55092
+ }
55093
+ function calculationFields(view) {
55094
+ return (0, import_lodash.pickBy)(view.schema || {}, isCalculationField);
55095
+ }
55096
+ function basicFields(view) {
55097
+ return (0, import_lodash.pickBy)(view.schema || {}, (field) => !isCalculationField(field));
55098
+ }
55099
+
55100
+ // ../shared-core/src/filters.ts
55101
+ var import_lodash2 = require("lodash");
55071
55102
  var HBS_REGEX = /{{([^{].*?)}}/g;
55072
55103
  var LOGICAL_OPERATORS = Object.values(LogicalOperator);
55073
55104
  var SEARCH_OPERATORS = [
@@ -55136,7 +55167,7 @@ var NoEmptyFilterStrings = [
55136
55167
  ];
55137
55168
  function recurseLogicalOperators(filters, fn) {
55138
55169
  for (const logical of LOGICAL_OPERATORS) {
55139
- if (filters[logical]) {
55170
+ if (filters?.[logical]) {
55140
55171
  filters[logical].conditions = filters[logical].conditions.map(
55141
55172
  (condition) => fn(condition)
55142
55173
  );
@@ -55345,7 +55376,7 @@ var buildQuery = (filter) => {
55345
55376
  function fixupFilterArrays(filters) {
55346
55377
  for (const searchField of Object.values(ArrayOperator)) {
55347
55378
  const field = filters[searchField];
55348
- if (field == null || !(0, import_lodash.isPlainObject)(field)) {
55379
+ if (field == null || !(0, import_lodash2.isPlainObject)(field)) {
55349
55380
  continue;
55350
55381
  }
55351
55382
  for (const key of Object.keys(field)) {
@@ -55432,10 +55463,10 @@ function runQuery(docs, query) {
55432
55463
  if (docValue == null || docValue === "") {
55433
55464
  return false;
55434
55465
  }
55435
- if ((0, import_lodash.isPlainObject)(testValue.low) && (0, import_lodash.isEmpty)(testValue.low)) {
55466
+ if ((0, import_lodash2.isPlainObject)(testValue.low) && (0, import_lodash2.isEmpty)(testValue.low)) {
55436
55467
  testValue.low = void 0;
55437
55468
  }
55438
- if ((0, import_lodash.isPlainObject)(testValue.high) && (0, import_lodash.isEmpty)(testValue.high)) {
55469
+ if ((0, import_lodash2.isPlainObject)(testValue.high) && (0, import_lodash2.isEmpty)(testValue.high)) {
55439
55470
  testValue.high = void 0;
55440
55471
  }
55441
55472
  if (testValue.low == null && testValue.high == null) {
@@ -55687,6 +55718,7 @@ var utils_exports = {};
55687
55718
  __export(utils_exports, {
55688
55719
  filterValueToLabel: () => filterValueToLabel,
55689
55720
  hasSchema: () => hasSchema,
55721
+ isSupportedUserSearch: () => isSupportedUserSearch,
55690
55722
  parallelForeach: () => parallelForeach,
55691
55723
  trimOtherProps: () => trimOtherProps,
55692
55724
  unreachable: () => unreachable
@@ -55742,6 +55774,29 @@ function trimOtherProps(object, allowedProps) {
55742
55774
  );
55743
55775
  return result;
55744
55776
  }
55777
+ function isSupportedUserSearch(query) {
55778
+ const allowed = [
55779
+ { op: "string" /* STRING */, key: "email" },
55780
+ { op: "equal" /* EQUAL */, key: "_id" },
55781
+ { op: "oneOf" /* ONE_OF */, key: "_id" }
55782
+ ];
55783
+ for (let [key, operation] of Object.entries(query)) {
55784
+ if (typeof operation !== "object") {
55785
+ return false;
55786
+ }
55787
+ const fields = Object.keys(operation || {});
55788
+ if (fields.length === 0) {
55789
+ continue;
55790
+ }
55791
+ const allowedOperation = allowed.find(
55792
+ (allow) => allow.op === key && fields.length === 1 && fields[0] === allow.key
55793
+ );
55794
+ if (!allowedOperation) {
55795
+ return false;
55796
+ }
55797
+ }
55798
+ return true;
55799
+ }
55745
55800
 
55746
55801
  // ../shared-core/src/sdk/index.ts
55747
55802
  var sdk_exports = {};
@@ -56132,6 +56187,7 @@ __export(context_exports, {
56132
56187
  getProdAppDB: () => getProdAppDB,
56133
56188
  getProdAppId: () => getProdAppId,
56134
56189
  getScimDBName: () => getScimDBName,
56190
+ getTableForView: () => getTableForView,
56135
56191
  getTenantIDFromAppID: () => getTenantIDFromAppID,
56136
56192
  getTenantId: () => getTenantId,
56137
56193
  identity: () => identity_exports,
@@ -56139,7 +56195,8 @@ __export(context_exports, {
56139
56195
  isScim: () => isScim,
56140
56196
  isTenancyEnabled: () => isTenancyEnabled,
56141
56197
  isTenantIdSet: () => isTenantIdSet,
56142
- setFeatureFlags: () => setFeatureFlags
56198
+ setFeatureFlags: () => setFeatureFlags,
56199
+ setTableForView: () => setTableForView
56143
56200
  });
56144
56201
 
56145
56202
  // src/context/identity.ts
@@ -56193,7 +56250,7 @@ function getAccountUserId(account) {
56193
56250
 
56194
56251
  // src/environment.ts
56195
56252
  var import_fs = require("fs");
56196
- var import_lodash2 = require("lodash");
56253
+ var import_lodash3 = require("lodash");
56197
56254
  function isTest() {
56198
56255
  return isJest();
56199
56256
  }
@@ -56361,7 +56418,7 @@ var environment = {
56361
56418
  OPENAI_API_KEY: process.env.OPENAI_API_KEY
56362
56419
  };
56363
56420
  function setEnv(newEnvVars) {
56364
- const oldEnv = (0, import_lodash2.cloneDeep)(environment);
56421
+ const oldEnv = (0, import_lodash3.cloneDeep)(environment);
56365
56422
  let key;
56366
56423
  for (key in newEnvVars) {
56367
56424
  environment._set(key, newEnvVars[key]);
@@ -56913,6 +56970,7 @@ __export(db_exports, {
56913
56970
  isProdAppID: () => isProdAppID,
56914
56971
  isSameAppID: () => isSameAppID,
56915
56972
  isTableId: () => isTableId,
56973
+ isViewId: () => isViewId,
56916
56974
  paginatedSearch: () => paginatedSearch,
56917
56975
  pagination: () => pagination,
56918
56976
  prefixRoleID: () => prefixRoleID,
@@ -58453,6 +58511,7 @@ var AppState = /* @__PURE__ */ ((AppState2) => {
58453
58511
  return AppState2;
58454
58512
  })(AppState || {});
58455
58513
  var EXPIRY_SECONDS = 3600;
58514
+ var INVALID_EXPIRY_SECONDS = 60;
58456
58515
  async function populateFromDB(appId) {
58457
58516
  return doWithDB(
58458
58517
  appId,
@@ -58475,7 +58534,7 @@ async function getAppMetadata(appId) {
58475
58534
  } catch (err) {
58476
58535
  if (err && err.status === 404) {
58477
58536
  metadata = { state: "invalid" /* INVALID */ };
58478
- expiry = void 0;
58537
+ expiry = INVALID_EXPIRY_SECONDS;
58479
58538
  } else {
58480
58539
  throw err;
58481
58540
  }
@@ -58530,6 +58589,7 @@ __export(docIds_exports, {
58530
58589
  isDatasourceId: () => isDatasourceId,
58531
58590
  isGlobalUserID: () => isGlobalUserID,
58532
58591
  isTableId: () => isTableId,
58592
+ isViewId: () => isViewId,
58533
58593
  prefixRoleID: () => prefixRoleID
58534
58594
  });
58535
58595
 
@@ -58615,8 +58675,11 @@ function getQueryIndex(viewName) {
58615
58675
  var isTableId = (id) => {
58616
58676
  return !!id && (id.startsWith(`${"ta" /* TABLE */}${SEPARATOR}`) || id.startsWith(`${"datasource_plus" /* DATASOURCE_PLUS */}${SEPARATOR}`));
58617
58677
  };
58678
+ function isViewId(id) {
58679
+ return !!id && id.startsWith(`${"view" /* VIEW */}${SEPARATOR}`);
58680
+ }
58618
58681
  var isDatasourceId = (id) => {
58619
- return id && id.startsWith(`${"datasource" /* DATASOURCE */}${SEPARATOR}`);
58682
+ return !!id && id.startsWith(`${"datasource" /* DATASOURCE */}${SEPARATOR}`);
58620
58683
  };
58621
58684
  function getWorkspaceParams(id = "", otherProps = {}) {
58622
58685
  return {
@@ -59815,87 +59878,467 @@ __export(features_exports, {
59815
59878
  });
59816
59879
  var import_posthog_node = require("posthog-node");
59817
59880
  var import_dd_trace3 = __toESM(require("dd-trace"));
59818
- var posthog;
59819
- function init4(opts) {
59820
- if (environment_default.POSTHOG_TOKEN && environment_default.POSTHOG_API_HOST && !environment_default.SELF_HOSTED && environment_default.POSTHOG_FEATURE_FLAGS_ENABLED) {
59821
- console.log("initializing posthog client...");
59822
- posthog = new import_posthog_node.PostHog(environment_default.POSTHOG_TOKEN, {
59823
- host: environment_default.POSTHOG_API_HOST,
59824
- personalApiKey: environment_default.POSTHOG_PERSONAL_TOKEN,
59825
- ...opts
59826
- });
59827
- } else {
59828
- console.log("posthog disabled");
59881
+
59882
+ // src/utils/index.ts
59883
+ var utils_exports4 = {};
59884
+ __export(utils_exports4, {
59885
+ Duration: () => Duration,
59886
+ DurationType: () => DurationType,
59887
+ clearCookie: () => clearCookie,
59888
+ compare: () => compare,
59889
+ getAppIdFromCtx: () => getAppIdFromCtx,
59890
+ getCookie: () => getCookie,
59891
+ hasCircularStructure: () => hasCircularStructure,
59892
+ hash: () => hash,
59893
+ isAudited: () => isAudited,
59894
+ isClient: () => isClient,
59895
+ isPublicApiRequest: () => isPublicApiRequest,
59896
+ isServingApp: () => isServingApp,
59897
+ isServingBuilder: () => isServingBuilder,
59898
+ isServingBuilderPreview: () => isServingBuilderPreview,
59899
+ isValidInternalAPIKey: () => isValidInternalAPIKey,
59900
+ newid: () => newid,
59901
+ openJwt: () => openJwt,
59902
+ resolveAppUrl: () => resolveAppUrl,
59903
+ setCookie: () => setCookie,
59904
+ timeout: () => timeout,
59905
+ validEmail: () => validEmail
59906
+ });
59907
+
59908
+ // src/utils/hashing.ts
59909
+ var bcrypt = environment_default.JS_BCRYPT ? require("bcryptjs") : require("bcrypt");
59910
+ var SALT_ROUNDS = environment_default.SALT_ROUNDS || 10;
59911
+ async function hash(data) {
59912
+ const salt = await bcrypt.genSalt(SALT_ROUNDS);
59913
+ return bcrypt.hash(data, salt);
59914
+ }
59915
+ async function compare(data, encrypted) {
59916
+ return bcrypt.compare(data, encrypted);
59917
+ }
59918
+
59919
+ // src/tenancy/index.ts
59920
+ var tenancy_exports = {};
59921
+ __export(tenancy_exports, {
59922
+ addTenantToUrl: () => addTenantToUrl,
59923
+ getTenantDB: () => getTenantDB,
59924
+ getTenantIDFromCtx: () => getTenantIDFromCtx,
59925
+ getTenantInfo: () => getTenantInfo,
59926
+ isUserInAppTenant: () => isUserInAppTenant,
59927
+ saveTenantInfo: () => saveTenantInfo
59928
+ });
59929
+
59930
+ // src/tenancy/db.ts
59931
+ function getTenantDB(tenantId) {
59932
+ return getDB(getGlobalDBName(tenantId));
59933
+ }
59934
+ async function saveTenantInfo(tenantInfo) {
59935
+ const db = getTenantDB(tenantInfo.tenantId);
59936
+ return db.put({
59937
+ _id: "tenant_info",
59938
+ ...tenantInfo
59939
+ });
59940
+ }
59941
+ async function getTenantInfo(tenantId) {
59942
+ try {
59943
+ const db = getTenantDB(tenantId);
59944
+ const tenantInfo = await db.get("tenant_info");
59945
+ delete tenantInfo.owner.password;
59946
+ return tenantInfo;
59947
+ } catch {
59948
+ return void 0;
59829
59949
  }
59830
59950
  }
59831
- function shutdown2() {
59832
- posthog?.shutdown();
59951
+
59952
+ // src/tenancy/tenancy.ts
59953
+ function addTenantToUrl(url) {
59954
+ const tenantId = getTenantId();
59955
+ if (isMultiTenant()) {
59956
+ const char = url.indexOf("?") === -1 ? "?" : "&";
59957
+ url += `${char}tenantId=${tenantId}`;
59958
+ }
59959
+ return url;
59833
59960
  }
59834
- var Flag = class {
59835
- constructor(defaultValue) {
59836
- this.defaultValue = defaultValue;
59961
+ var isUserInAppTenant = (appId, user) => {
59962
+ let userTenantId;
59963
+ if (user) {
59964
+ userTenantId = user.tenantId || DEFAULT_TENANT_ID;
59965
+ } else {
59966
+ userTenantId = getTenantId();
59837
59967
  }
59838
- static boolean(defaultValue) {
59839
- return new BooleanFlag(defaultValue);
59968
+ const tenantId = getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID;
59969
+ return tenantId === userTenantId;
59970
+ };
59971
+ var ALL_STRATEGIES = Object.values(TenantResolutionStrategy);
59972
+ var getTenantIDFromCtx = (ctx, opts) => {
59973
+ if (!isMultiTenant()) {
59974
+ return DEFAULT_TENANT_ID;
59840
59975
  }
59841
- static string(defaultValue) {
59842
- return new StringFlag(defaultValue);
59976
+ if (opts.allowNoTenant === void 0) {
59977
+ opts.allowNoTenant = false;
59843
59978
  }
59844
- static number(defaultValue) {
59845
- return new NumberFlag(defaultValue);
59979
+ if (!opts.includeStrategies) {
59980
+ opts.includeStrategies = ALL_STRATEGIES;
59846
59981
  }
59847
- };
59848
- var BooleanFlag = class extends Flag {
59849
- parse(value) {
59850
- if (typeof value === "string") {
59851
- return ["true", "t", "1"].includes(value.toLowerCase());
59982
+ if (!opts.excludeStrategies) {
59983
+ opts.excludeStrategies = [];
59984
+ }
59985
+ const isAllowed = (strategy) => {
59986
+ if (opts.excludeStrategies?.includes(strategy)) {
59987
+ return false;
59852
59988
  }
59853
- if (typeof value === "boolean") {
59854
- return value;
59989
+ if (opts.includeStrategies?.includes(strategy)) {
59990
+ return true;
59991
+ }
59992
+ };
59993
+ if (isAllowed("user" /* USER */)) {
59994
+ const userTenantId = ctx.user?.tenantId;
59995
+ if (userTenantId) {
59996
+ return userTenantId;
59855
59997
  }
59856
- throw new Error(`could not parse value "${value}" as boolean`);
59857
59998
  }
59858
- };
59859
- var StringFlag = class extends Flag {
59860
- parse(value) {
59861
- if (typeof value === "string") {
59862
- return value;
59999
+ if (isAllowed("header" /* HEADER */)) {
60000
+ const headerTenantId = ctx.request.headers["x-budibase-tenant-id" /* TENANT_ID */];
60001
+ if (headerTenantId) {
60002
+ return headerTenantId;
59863
60003
  }
59864
- throw new Error(`could not parse value "${value}" as string`);
59865
60004
  }
59866
- };
59867
- var NumberFlag = class extends Flag {
59868
- parse(value) {
59869
- if (typeof value === "number") {
59870
- return value;
60005
+ if (isAllowed("query" /* QUERY */)) {
60006
+ const queryTenantId = ctx.request.query.tenantId;
60007
+ if (queryTenantId) {
60008
+ return queryTenantId;
59871
60009
  }
59872
- if (typeof value === "string") {
59873
- const parsed = parseFloat(value);
59874
- if (!isNaN(parsed)) {
59875
- return parsed;
60010
+ }
60011
+ if (isAllowed("subdomain" /* SUBDOMAIN */)) {
60012
+ let platformHost;
60013
+ try {
60014
+ platformHost = new URL(getPlatformURL()).host.split(":")[0];
60015
+ } catch (err) {
60016
+ if (err.code !== "ERR_INVALID_URL") {
60017
+ throw err;
60018
+ }
60019
+ }
60020
+ const requestHost = ctx.host;
60021
+ if (platformHost && requestHost.includes(platformHost)) {
60022
+ const tenantId = requestHost.substring(
60023
+ 0,
60024
+ requestHost.indexOf(`.${platformHost}`)
60025
+ );
60026
+ if (tenantId) {
60027
+ return tenantId;
59876
60028
  }
59877
60029
  }
59878
- throw new Error(`could not parse value "${value}" as number`);
59879
- }
59880
- };
59881
- var FlagSet = class {
59882
- constructor(flagSchema) {
59883
- this.flagSchema = flagSchema;
59884
- this.setId = crypto.randomUUID();
59885
- }
59886
- defaults() {
59887
- return Object.keys(this.flagSchema).reduce((acc, key) => {
59888
- const typedKey = key;
59889
- acc[typedKey] = this.flagSchema[key].defaultValue;
59890
- return acc;
59891
- }, {});
59892
60030
  }
59893
- isFlagName(name) {
59894
- return this.flagSchema[name] !== void 0;
60031
+ if (isAllowed("path" /* PATH */)) {
60032
+ const match = ctx.matched.find(
60033
+ (m) => !!m.paramNames.find((p) => p.name === "tenantId")
60034
+ );
60035
+ const ctxUrl = ctx.originalUrl;
60036
+ let url;
60037
+ if (ctxUrl.includes("?")) {
60038
+ url = ctxUrl.split("?")[0];
60039
+ } else {
60040
+ url = ctxUrl;
60041
+ }
60042
+ if (match) {
60043
+ const params2 = match.params(url, match.captures(url), {});
60044
+ if (params2.tenantId) {
60045
+ return params2.tenantId;
60046
+ }
60047
+ }
59895
60048
  }
59896
- async get(key, ctx) {
59897
- const flags2 = await this.fetch(ctx);
59898
- return flags2[key];
60049
+ if (!opts.allowNoTenant) {
60050
+ ctx.throw(403, "Tenant id not set");
60051
+ }
60052
+ return void 0;
60053
+ };
60054
+
60055
+ // src/utils/utils.ts
60056
+ var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
60057
+ var APP_PREFIX3 = "app" /* APP */ + SEPARATOR;
60058
+ var PROD_APP_PREFIX = "/app/";
60059
+ var BUILDER_PREVIEW_PATH = "/app/preview";
60060
+ var BUILDER_PREFIX = "/builder";
60061
+ var BUILDER_APP_PREFIX = `${BUILDER_PREFIX}/app/`;
60062
+ var PUBLIC_API_PREFIX = "/api/public/v";
60063
+ function confirmAppId(possibleAppId) {
60064
+ return possibleAppId && possibleAppId.startsWith(APP_PREFIX3) ? possibleAppId : void 0;
60065
+ }
60066
+ async function resolveAppUrl(ctx) {
60067
+ const appUrl = ctx.path.split("/")[2];
60068
+ let possibleAppUrl = `/${appUrl.toLowerCase()}`;
60069
+ let tenantId = getTenantId();
60070
+ if (!environment_default.isDev() && environment_default.MULTI_TENANCY) {
60071
+ tenantId = getTenantIDFromCtx(ctx, {
60072
+ includeStrategies: ["subdomain" /* SUBDOMAIN */]
60073
+ });
60074
+ }
60075
+ const apps = await doInTenant(
60076
+ tenantId,
60077
+ () => getAllApps({ dev: false })
60078
+ );
60079
+ const app = apps.filter(
60080
+ (a) => a.url && a.url.toLowerCase() === possibleAppUrl
60081
+ )[0];
60082
+ return app && app.appId ? app.appId : void 0;
60083
+ }
60084
+ function isServingApp(ctx) {
60085
+ if (ctx.path.startsWith(`/${APP_PREFIX3}`)) {
60086
+ return true;
60087
+ }
60088
+ return ctx.path.startsWith(PROD_APP_PREFIX);
60089
+ }
60090
+ function isServingBuilder(ctx) {
60091
+ return ctx.path.startsWith(BUILDER_APP_PREFIX);
60092
+ }
60093
+ function isServingBuilderPreview(ctx) {
60094
+ return ctx.path.startsWith(BUILDER_PREVIEW_PATH);
60095
+ }
60096
+ function isPublicApiRequest(ctx) {
60097
+ return ctx.path.startsWith(PUBLIC_API_PREFIX);
60098
+ }
60099
+ async function getAppIdFromCtx(ctx) {
60100
+ const options2 = [ctx.request.headers["x-budibase-app-id" /* APP_ID */]];
60101
+ let appId;
60102
+ for (let option of options2) {
60103
+ appId = confirmAppId(option);
60104
+ if (appId) {
60105
+ break;
60106
+ }
60107
+ }
60108
+ if (!appId && ctx.request.body && ctx.request.body.appId) {
60109
+ appId = confirmAppId(ctx.request.body.appId);
60110
+ }
60111
+ const pathId = parseAppIdFromUrlPath(ctx.path);
60112
+ if (!appId && pathId) {
60113
+ appId = confirmAppId(pathId);
60114
+ }
60115
+ const isBuilderPreview = ctx.path.startsWith(BUILDER_PREVIEW_PATH);
60116
+ const isViewingProdApp = ctx.path.startsWith(PROD_APP_PREFIX) && !isBuilderPreview;
60117
+ if (!appId && isViewingProdApp) {
60118
+ appId = confirmAppId(await resolveAppUrl(ctx));
60119
+ }
60120
+ const referer = ctx.request.headers.referer;
60121
+ if (!appId && referer?.includes(BUILDER_APP_PREFIX)) {
60122
+ const refererId = parseAppIdFromUrlPath(ctx.request.headers.referer);
60123
+ appId = confirmAppId(refererId);
60124
+ }
60125
+ return appId;
60126
+ }
60127
+ function parseAppIdFromUrlPath(url) {
60128
+ if (!url) {
60129
+ return;
60130
+ }
60131
+ return url.split("?")[0].split("/").find((subPath) => subPath.startsWith(APP_PREFIX3));
60132
+ }
60133
+ function openJwt(token) {
60134
+ if (!token) {
60135
+ return void 0;
60136
+ }
60137
+ try {
60138
+ return import_jsonwebtoken.default.verify(token, environment_default.JWT_SECRET);
60139
+ } catch (e) {
60140
+ if (environment_default.JWT_SECRET_FALLBACK) {
60141
+ return import_jsonwebtoken.default.verify(token, environment_default.JWT_SECRET_FALLBACK);
60142
+ } else {
60143
+ throw e;
60144
+ }
60145
+ }
60146
+ }
60147
+ function isValidInternalAPIKey(apiKey) {
60148
+ if (environment_default.INTERNAL_API_KEY && environment_default.INTERNAL_API_KEY === apiKey) {
60149
+ return true;
60150
+ }
60151
+ return !!(environment_default.INTERNAL_API_KEY_FALLBACK && environment_default.INTERNAL_API_KEY_FALLBACK === apiKey);
60152
+ }
60153
+ function getCookie(ctx, name) {
60154
+ const cookie = ctx.cookies.get(name);
60155
+ if (!cookie) {
60156
+ return void 0;
60157
+ }
60158
+ return openJwt(cookie);
60159
+ }
60160
+ function setCookie(ctx, value, name = "builder", opts = { sign: true }) {
60161
+ if (value && opts && opts.sign) {
60162
+ value = import_jsonwebtoken.default.sign(value, environment_default.JWT_SECRET);
60163
+ }
60164
+ const config = {
60165
+ expires: MAX_VALID_DATE,
60166
+ path: "/",
60167
+ httpOnly: false,
60168
+ overwrite: true
60169
+ };
60170
+ if (environment_default.COOKIE_DOMAIN) {
60171
+ config.domain = environment_default.COOKIE_DOMAIN;
60172
+ }
60173
+ ctx.cookies.set(name, value, config);
60174
+ }
60175
+ function clearCookie(ctx, name) {
60176
+ setCookie(ctx, null, name);
60177
+ }
60178
+ function isClient(ctx) {
60179
+ return ctx.headers["x-budibase-type" /* TYPE */] === "client";
60180
+ }
60181
+ function timeout(timeMs) {
60182
+ return new Promise((resolve) => setTimeout(resolve, timeMs));
60183
+ }
60184
+ function isAudited(event) {
60185
+ return !!AuditedEventFriendlyName[event];
60186
+ }
60187
+ function hasCircularStructure(json) {
60188
+ if (typeof json !== "object") {
60189
+ return false;
60190
+ }
60191
+ try {
60192
+ JSON.stringify(json);
60193
+ } catch (err) {
60194
+ if (err instanceof Error && err?.message.includes("circular structure")) {
60195
+ return true;
60196
+ }
60197
+ }
60198
+ return false;
60199
+ }
60200
+
60201
+ // src/utils/stringUtils.ts
60202
+ function validEmail(value) {
60203
+ return value && !!value.match(
60204
+ /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
60205
+ );
60206
+ }
60207
+
60208
+ // src/utils/Duration.ts
60209
+ var DurationType = /* @__PURE__ */ ((DurationType2) => {
60210
+ DurationType2["MILLISECONDS"] = "milliseconds";
60211
+ DurationType2["SECONDS"] = "seconds";
60212
+ DurationType2["MINUTES"] = "minutes";
60213
+ DurationType2["HOURS"] = "hours";
60214
+ DurationType2["DAYS"] = "days";
60215
+ return DurationType2;
60216
+ })(DurationType || {});
60217
+ var conversion = {
60218
+ milliseconds: 1,
60219
+ seconds: 1e3,
60220
+ minutes: 60 * 1e3,
60221
+ hours: 60 * 60 * 1e3,
60222
+ days: 24 * 60 * 60 * 1e3
60223
+ };
60224
+ var Duration = class _Duration {
60225
+ static convert(from, to, duration) {
60226
+ const milliseconds = duration * conversion[from];
60227
+ return milliseconds / conversion[to];
60228
+ }
60229
+ static from(from, duration) {
60230
+ return {
60231
+ to: (to) => {
60232
+ return _Duration.convert(from, to, duration);
60233
+ },
60234
+ toMs: () => {
60235
+ return _Duration.convert(from, "milliseconds" /* MILLISECONDS */, duration);
60236
+ },
60237
+ toSeconds: () => {
60238
+ return _Duration.convert(from, "seconds" /* SECONDS */, duration);
60239
+ }
60240
+ };
60241
+ }
60242
+ static fromSeconds(duration) {
60243
+ return _Duration.from("seconds" /* SECONDS */, duration);
60244
+ }
60245
+ static fromMinutes(duration) {
60246
+ return _Duration.from("minutes" /* MINUTES */, duration);
60247
+ }
60248
+ static fromHours(duration) {
60249
+ return _Duration.from("hours" /* HOURS */, duration);
60250
+ }
60251
+ static fromDays(duration) {
60252
+ return _Duration.from("days" /* DAYS */, duration);
60253
+ }
60254
+ static fromMilliseconds(duration) {
60255
+ return _Duration.from("milliseconds" /* MILLISECONDS */, duration);
60256
+ }
60257
+ };
60258
+
60259
+ // src/features/index.ts
60260
+ var posthog;
60261
+ function init4(opts) {
60262
+ if (environment_default.POSTHOG_TOKEN && environment_default.POSTHOG_API_HOST && !environment_default.SELF_HOSTED && environment_default.POSTHOG_FEATURE_FLAGS_ENABLED) {
60263
+ console.log("initializing posthog client...");
60264
+ posthog = new import_posthog_node.PostHog(environment_default.POSTHOG_TOKEN, {
60265
+ host: environment_default.POSTHOG_API_HOST,
60266
+ personalApiKey: environment_default.POSTHOG_PERSONAL_TOKEN,
60267
+ featureFlagsPollingInterval: Duration.fromMinutes(3).toMs(),
60268
+ ...opts
60269
+ });
60270
+ } else {
60271
+ console.log("posthog disabled");
60272
+ }
60273
+ }
60274
+ function shutdown2() {
60275
+ posthog?.shutdown();
60276
+ }
60277
+ var Flag = class {
60278
+ constructor(defaultValue) {
60279
+ this.defaultValue = defaultValue;
60280
+ }
60281
+ static boolean(defaultValue) {
60282
+ return new BooleanFlag(defaultValue);
60283
+ }
60284
+ static string(defaultValue) {
60285
+ return new StringFlag(defaultValue);
60286
+ }
60287
+ static number(defaultValue) {
60288
+ return new NumberFlag(defaultValue);
60289
+ }
60290
+ };
60291
+ var BooleanFlag = class extends Flag {
60292
+ parse(value) {
60293
+ if (typeof value === "string") {
60294
+ return ["true", "t", "1"].includes(value.toLowerCase());
60295
+ }
60296
+ if (typeof value === "boolean") {
60297
+ return value;
60298
+ }
60299
+ throw new Error(`could not parse value "${value}" as boolean`);
60300
+ }
60301
+ };
60302
+ var StringFlag = class extends Flag {
60303
+ parse(value) {
60304
+ if (typeof value === "string") {
60305
+ return value;
60306
+ }
60307
+ throw new Error(`could not parse value "${value}" as string`);
60308
+ }
60309
+ };
60310
+ var NumberFlag = class extends Flag {
60311
+ parse(value) {
60312
+ if (typeof value === "number") {
60313
+ return value;
60314
+ }
60315
+ if (typeof value === "string") {
60316
+ const parsed = parseFloat(value);
60317
+ if (!isNaN(parsed)) {
60318
+ return parsed;
60319
+ }
60320
+ }
60321
+ throw new Error(`could not parse value "${value}" as number`);
60322
+ }
60323
+ };
60324
+ var FlagSet = class {
60325
+ constructor(flagSchema) {
60326
+ this.flagSchema = flagSchema;
60327
+ this.setId = crypto.randomUUID();
60328
+ }
60329
+ defaults() {
60330
+ return Object.keys(this.flagSchema).reduce((acc, key) => {
60331
+ const typedKey = key;
60332
+ acc[typedKey] = this.flagSchema[key].defaultValue;
60333
+ return acc;
60334
+ }, {});
60335
+ }
60336
+ isFlagName(name) {
60337
+ return this.flagSchema[name] !== void 0;
60338
+ }
60339
+ async get(key, ctx) {
60340
+ const flags2 = await this.fetch(ctx);
60341
+ return flags2[key];
59899
60342
  }
59900
60343
  async isEnabled(key, ctx) {
59901
60344
  const flags2 = await this.fetch(ctx);
@@ -59997,7 +60440,7 @@ var flags = new FlagSet({
59997
60440
  AUTOMATION_BRANCHING: Flag.boolean(environment_default.isDev()),
59998
60441
  SQS: Flag.boolean(environment_default.isDev()),
59999
60442
  ["AI_CUSTOM_CONFIGS" /* AI_CUSTOM_CONFIGS */]: Flag.boolean(environment_default.isDev()),
60000
- ["ENRICHED_RELATIONSHIPS" /* ENRICHED_RELATIONSHIPS */]: Flag.boolean(false)
60443
+ ["ENRICHED_RELATIONSHIPS" /* ENRICHED_RELATIONSHIPS */]: Flag.boolean(environment_default.isDev())
60001
60444
  });
60002
60445
 
60003
60446
  // src/db/couch/DatabaseImpl.ts
@@ -60098,7 +60541,7 @@ var DatabaseImpl = class _DatabaseImpl {
60098
60541
  return this.getDb();
60099
60542
  }
60100
60543
  // this function fetches the DB and handles if DB creation is needed
60101
- async performCall(call) {
60544
+ async performCallWithDBCreation(call) {
60102
60545
  const db = this.getDb();
60103
60546
  const fnc = await call(db);
60104
60547
  try {
@@ -60106,11 +60549,20 @@ var DatabaseImpl = class _DatabaseImpl {
60106
60549
  } catch (err) {
60107
60550
  if (err.statusCode === 404 && err.reason === DATABASE_NOT_FOUND) {
60108
60551
  await this.checkAndCreateDb();
60109
- return await this.performCall(call);
60552
+ return await this.performCallWithDBCreation(call);
60110
60553
  }
60111
60554
  throw new CouchDBError(`CouchDB error: ${err.message}`, err);
60112
60555
  }
60113
60556
  }
60557
+ async performCall(call) {
60558
+ const db = this.getDb();
60559
+ const fnc = await call(db);
60560
+ try {
60561
+ return await fnc();
60562
+ } catch (err) {
60563
+ throw new CouchDBError(`CouchDB error: ${err.message}`, err);
60564
+ }
60565
+ }
60114
60566
  async get(id) {
60115
60567
  return this.performCall((db) => {
60116
60568
  if (!id) {
@@ -60194,7 +60646,7 @@ var DatabaseImpl = class _DatabaseImpl {
60194
60646
  if (!document._id) {
60195
60647
  throw new Error("Cannot store document without _id field.");
60196
60648
  }
60197
- return this.performCall(async (db) => {
60649
+ return this.performCallWithDBCreation(async (db) => {
60198
60650
  if (!document.createdAt) {
60199
60651
  document.createdAt = (/* @__PURE__ */ new Date()).toISOString();
60200
60652
  }
@@ -60216,7 +60668,7 @@ var DatabaseImpl = class _DatabaseImpl {
60216
60668
  }
60217
60669
  async bulkDocs(documents) {
60218
60670
  const now = (/* @__PURE__ */ new Date()).toISOString();
60219
- return this.performCall((db) => {
60671
+ return this.performCallWithDBCreation((db) => {
60220
60672
  return () => db.bulk({
60221
60673
  docs: documents.map((d) => ({ createdAt: now, ...d, updatedAt: now }))
60222
60674
  });
@@ -60224,7 +60676,21 @@ var DatabaseImpl = class _DatabaseImpl {
60224
60676
  }
60225
60677
  async allDocs(params2) {
60226
60678
  return this.performCall((db) => {
60227
- return () => db.list(params2);
60679
+ return async () => {
60680
+ try {
60681
+ return await db.list(params2);
60682
+ } catch (err) {
60683
+ if (err.reason === DATABASE_NOT_FOUND) {
60684
+ return {
60685
+ offset: 0,
60686
+ total_rows: 0,
60687
+ rows: []
60688
+ };
60689
+ } else {
60690
+ throw err;
60691
+ }
60692
+ }
60693
+ };
60228
60694
  });
60229
60695
  }
60230
60696
  async _sqlQuery(url, method, body2) {
@@ -60638,6 +61104,21 @@ function setFeatureFlags(key, value) {
60638
61104
  context.featureFlagCache ??= {};
60639
61105
  context.featureFlagCache[key] = value;
60640
61106
  }
61107
+ function getTableForView(viewId) {
61108
+ const context = getCurrentContext();
61109
+ if (!context) {
61110
+ return;
61111
+ }
61112
+ return context.viewToTableCache?.[viewId];
61113
+ }
61114
+ function setTableForView(viewId, table) {
61115
+ const context = getCurrentContext();
61116
+ if (!context) {
61117
+ return;
61118
+ }
61119
+ context.viewToTableCache ??= {};
61120
+ context.viewToTableCache[viewId] = table;
61121
+ }
60641
61122
 
60642
61123
  // src/cache/base/index.ts
60643
61124
  function generateTenantKey(key) {
@@ -60720,209 +61201,73 @@ var BaseCache = class {
60720
61201
  }
60721
61202
  /**
60722
61203
  * Read from the cache. Write to the cache if not exists.
60723
- */
60724
- async withCache(key, ttl = null, fetchFn, opts = { useTenancy: true }) {
60725
- const cachedValue = await this.get(key, opts);
60726
- if (cachedValue) {
60727
- return cachedValue;
60728
- }
60729
- try {
60730
- const fetchedValue = await fetchFn();
60731
- await this.store(key, fetchedValue, ttl, opts);
60732
- return fetchedValue;
60733
- } catch (err) {
60734
- console.error("Error fetching before cache - ", err);
60735
- throw err;
60736
- }
60737
- }
60738
- async bustCache(key) {
60739
- const client = await this.getClient();
60740
- try {
60741
- await client.delete(generateTenantKey(key));
60742
- } catch (err) {
60743
- console.error("Error busting cache - ", err);
60744
- throw err;
60745
- }
60746
- }
60747
- /**
60748
- * Delete the entry if the provided value matches the stored one.
60749
- */
60750
- async deleteIfValue(key, value, opts = { useTenancy: true }) {
60751
- key = opts.useTenancy ? generateTenantKey(key) : key;
60752
- const client = await this.getClient();
60753
- await client.deleteIfValue(key, value);
60754
- }
60755
- };
60756
-
60757
- // src/cache/generic.ts
60758
- var GENERIC = new BaseCache();
60759
- var CacheKey = /* @__PURE__ */ ((CacheKey2) => {
60760
- CacheKey2["CHECKLIST"] = "checklist";
60761
- CacheKey2["INSTALLATION"] = "installation";
60762
- CacheKey2["ANALYTICS_ENABLED"] = "analyticsEnabled";
60763
- CacheKey2["UNIQUE_TENANT_ID"] = "uniqueTenantId";
60764
- CacheKey2["EVENTS"] = "events";
60765
- CacheKey2["BACKFILL_METADATA"] = "backfillMetadata";
60766
- CacheKey2["EVENTS_RATE_LIMIT"] = "eventsRateLimit";
60767
- return CacheKey2;
60768
- })(CacheKey || {});
60769
- var TTL = /* @__PURE__ */ ((TTL2) => {
60770
- TTL2[TTL2["ONE_MINUTE"] = 600] = "ONE_MINUTE";
60771
- TTL2[TTL2["ONE_HOUR"] = 3600] = "ONE_HOUR";
60772
- TTL2[TTL2["ONE_DAY"] = 86400] = "ONE_DAY";
60773
- return TTL2;
60774
- })(TTL || {});
60775
- var keys = (...args) => GENERIC.keys(...args);
60776
- var get2 = (...args) => GENERIC.get(...args);
60777
- var store = (...args) => GENERIC.store(...args);
60778
- var destroy = (...args) => GENERIC.delete(...args);
60779
- var withCache = (...args) => GENERIC.withCache(...args);
60780
- var bustCache = (...args) => GENERIC.bustCache(...args);
60781
-
60782
- // src/cache/user.ts
60783
- var user_exports = {};
60784
- __export(user_exports, {
60785
- getUser: () => getUser,
60786
- getUsers: () => getUsers,
60787
- invalidateUser: () => invalidateUser
60788
- });
60789
-
60790
- // src/tenancy/index.ts
60791
- var tenancy_exports = {};
60792
- __export(tenancy_exports, {
60793
- addTenantToUrl: () => addTenantToUrl,
60794
- getTenantDB: () => getTenantDB,
60795
- getTenantIDFromCtx: () => getTenantIDFromCtx,
60796
- getTenantInfo: () => getTenantInfo,
60797
- isUserInAppTenant: () => isUserInAppTenant,
60798
- saveTenantInfo: () => saveTenantInfo
60799
- });
60800
-
60801
- // src/tenancy/db.ts
60802
- function getTenantDB(tenantId) {
60803
- return getDB(getGlobalDBName(tenantId));
60804
- }
60805
- async function saveTenantInfo(tenantInfo) {
60806
- const db = getTenantDB(tenantInfo.tenantId);
60807
- return db.put({
60808
- _id: "tenant_info",
60809
- ...tenantInfo
60810
- });
60811
- }
60812
- async function getTenantInfo(tenantId) {
60813
- try {
60814
- const db = getTenantDB(tenantId);
60815
- const tenantInfo = await db.get("tenant_info");
60816
- delete tenantInfo.owner.password;
60817
- return tenantInfo;
60818
- } catch {
60819
- return void 0;
60820
- }
60821
- }
60822
-
60823
- // src/tenancy/tenancy.ts
60824
- function addTenantToUrl(url) {
60825
- const tenantId = getTenantId();
60826
- if (isMultiTenant()) {
60827
- const char = url.indexOf("?") === -1 ? "?" : "&";
60828
- url += `${char}tenantId=${tenantId}`;
60829
- }
60830
- return url;
60831
- }
60832
- var isUserInAppTenant = (appId, user) => {
60833
- let userTenantId;
60834
- if (user) {
60835
- userTenantId = user.tenantId || DEFAULT_TENANT_ID;
60836
- } else {
60837
- userTenantId = getTenantId();
60838
- }
60839
- const tenantId = getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID;
60840
- return tenantId === userTenantId;
60841
- };
60842
- var ALL_STRATEGIES = Object.values(TenantResolutionStrategy);
60843
- var getTenantIDFromCtx = (ctx, opts) => {
60844
- if (!isMultiTenant()) {
60845
- return DEFAULT_TENANT_ID;
60846
- }
60847
- if (opts.allowNoTenant === void 0) {
60848
- opts.allowNoTenant = false;
60849
- }
60850
- if (!opts.includeStrategies) {
60851
- opts.includeStrategies = ALL_STRATEGIES;
60852
- }
60853
- if (!opts.excludeStrategies) {
60854
- opts.excludeStrategies = [];
60855
- }
60856
- const isAllowed = (strategy) => {
60857
- if (opts.excludeStrategies?.includes(strategy)) {
60858
- return false;
60859
- }
60860
- if (opts.includeStrategies?.includes(strategy)) {
60861
- return true;
60862
- }
60863
- };
60864
- if (isAllowed("user" /* USER */)) {
60865
- const userTenantId = ctx.user?.tenantId;
60866
- if (userTenantId) {
60867
- return userTenantId;
60868
- }
60869
- }
60870
- if (isAllowed("header" /* HEADER */)) {
60871
- const headerTenantId = ctx.request.headers["x-budibase-tenant-id" /* TENANT_ID */];
60872
- if (headerTenantId) {
60873
- return headerTenantId;
60874
- }
60875
- }
60876
- if (isAllowed("query" /* QUERY */)) {
60877
- const queryTenantId = ctx.request.query.tenantId;
60878
- if (queryTenantId) {
60879
- return queryTenantId;
61204
+ */
61205
+ async withCache(key, ttl = null, fetchFn, opts = { useTenancy: true }) {
61206
+ const cachedValue = await this.get(key, opts);
61207
+ if (cachedValue) {
61208
+ return cachedValue;
60880
61209
  }
60881
- }
60882
- if (isAllowed("subdomain" /* SUBDOMAIN */)) {
60883
- let platformHost;
60884
61210
  try {
60885
- platformHost = new URL(getPlatformURL()).host.split(":")[0];
61211
+ const fetchedValue = await fetchFn();
61212
+ await this.store(key, fetchedValue, ttl, opts);
61213
+ return fetchedValue;
60886
61214
  } catch (err) {
60887
- if (err.code !== "ERR_INVALID_URL") {
60888
- throw err;
60889
- }
60890
- }
60891
- const requestHost = ctx.host;
60892
- if (platformHost && requestHost.includes(platformHost)) {
60893
- const tenantId = requestHost.substring(
60894
- 0,
60895
- requestHost.indexOf(`.${platformHost}`)
60896
- );
60897
- if (tenantId) {
60898
- return tenantId;
60899
- }
61215
+ console.error("Error fetching before cache - ", err);
61216
+ throw err;
60900
61217
  }
60901
61218
  }
60902
- if (isAllowed("path" /* PATH */)) {
60903
- const match = ctx.matched.find(
60904
- (m) => !!m.paramNames.find((p) => p.name === "tenantId")
60905
- );
60906
- const ctxUrl = ctx.originalUrl;
60907
- let url;
60908
- if (ctxUrl.includes("?")) {
60909
- url = ctxUrl.split("?")[0];
60910
- } else {
60911
- url = ctxUrl;
60912
- }
60913
- if (match) {
60914
- const params2 = match.params(url, match.captures(url), {});
60915
- if (params2.tenantId) {
60916
- return params2.tenantId;
60917
- }
61219
+ async bustCache(key) {
61220
+ const client = await this.getClient();
61221
+ try {
61222
+ await client.delete(generateTenantKey(key));
61223
+ } catch (err) {
61224
+ console.error("Error busting cache - ", err);
61225
+ throw err;
60918
61226
  }
60919
61227
  }
60920
- if (!opts.allowNoTenant) {
60921
- ctx.throw(403, "Tenant id not set");
61228
+ /**
61229
+ * Delete the entry if the provided value matches the stored one.
61230
+ */
61231
+ async deleteIfValue(key, value, opts = { useTenancy: true }) {
61232
+ key = opts.useTenancy ? generateTenantKey(key) : key;
61233
+ const client = await this.getClient();
61234
+ await client.deleteIfValue(key, value);
60922
61235
  }
60923
- return void 0;
60924
61236
  };
60925
61237
 
61238
+ // src/cache/generic.ts
61239
+ var GENERIC = new BaseCache();
61240
+ var CacheKey = /* @__PURE__ */ ((CacheKey2) => {
61241
+ CacheKey2["CHECKLIST"] = "checklist";
61242
+ CacheKey2["INSTALLATION"] = "installation";
61243
+ CacheKey2["ANALYTICS_ENABLED"] = "analyticsEnabled";
61244
+ CacheKey2["UNIQUE_TENANT_ID"] = "uniqueTenantId";
61245
+ CacheKey2["EVENTS"] = "events";
61246
+ CacheKey2["BACKFILL_METADATA"] = "backfillMetadata";
61247
+ CacheKey2["EVENTS_RATE_LIMIT"] = "eventsRateLimit";
61248
+ return CacheKey2;
61249
+ })(CacheKey || {});
61250
+ var TTL = /* @__PURE__ */ ((TTL2) => {
61251
+ TTL2[TTL2["ONE_MINUTE"] = 600] = "ONE_MINUTE";
61252
+ TTL2[TTL2["ONE_HOUR"] = 3600] = "ONE_HOUR";
61253
+ TTL2[TTL2["ONE_DAY"] = 86400] = "ONE_DAY";
61254
+ return TTL2;
61255
+ })(TTL || {});
61256
+ var keys = (...args) => GENERIC.keys(...args);
61257
+ var get2 = (...args) => GENERIC.get(...args);
61258
+ var store = (...args) => GENERIC.store(...args);
61259
+ var destroy = (...args) => GENERIC.delete(...args);
61260
+ var withCache = (...args) => GENERIC.withCache(...args);
61261
+ var bustCache = (...args) => GENERIC.bustCache(...args);
61262
+
61263
+ // src/cache/user.ts
61264
+ var user_exports = {};
61265
+ __export(user_exports, {
61266
+ getUser: () => getUser,
61267
+ getUsers: () => getUsers,
61268
+ invalidateUser: () => invalidateUser
61269
+ });
61270
+
60926
61271
  // src/platform/index.ts
60927
61272
  var platform_exports = {};
60928
61273
  __export(platform_exports, {
@@ -60955,339 +61300,96 @@ async function lookupTenantId(userId) {
60955
61300
  const user = await getUserDoc(userId);
60956
61301
  return user.tenantId;
60957
61302
  }
60958
- async function getUserDoc(emailOrId) {
60959
- const db = getPlatformDB();
60960
- return db.get(emailOrId);
60961
- }
60962
- async function updateUserDoc(platformUser) {
60963
- const db = getPlatformDB();
60964
- await db.put(platformUser);
60965
- }
60966
- function newUserIdDoc(id, tenantId) {
60967
- return {
60968
- _id: id,
60969
- tenantId
60970
- };
60971
- }
60972
- function newUserEmailDoc(userId, email, tenantId) {
60973
- return {
60974
- _id: email,
60975
- userId,
60976
- tenantId
60977
- };
60978
- }
60979
- function newUserSsoIdDoc(ssoId, email, userId, tenantId) {
60980
- return {
60981
- _id: ssoId,
60982
- userId,
60983
- email,
60984
- tenantId
60985
- };
60986
- }
60987
- async function addUserDoc(emailOrId, newDocFn) {
60988
- const db = getPlatformDB();
60989
- let user;
60990
- try {
60991
- await db.get(emailOrId);
60992
- } catch (e) {
60993
- if (e.status === 404) {
60994
- user = newDocFn();
60995
- await db.put(user);
60996
- } else {
60997
- throw e;
60998
- }
60999
- }
61000
- }
61001
- async function addSsoUser(ssoId, email, userId, tenantId) {
61002
- return addUserDoc(
61003
- ssoId,
61004
- () => newUserSsoIdDoc(ssoId, email, userId, tenantId)
61005
- );
61006
- }
61007
- async function addUser(tenantId, userId, email, ssoId) {
61008
- const promises = [
61009
- addUserDoc(userId, () => newUserIdDoc(userId, tenantId)),
61010
- addUserDoc(email, () => newUserEmailDoc(userId, email, tenantId))
61011
- ];
61012
- if (ssoId) {
61013
- promises.push(addSsoUser(ssoId, email, userId, tenantId));
61014
- }
61015
- await Promise.all(promises);
61016
- }
61017
- async function removeUser(user) {
61018
- const db = getPlatformDB();
61019
- const keys2 = [user._id, user.email];
61020
- const userDocs = await db.allDocs({
61021
- keys: keys2,
61022
- include_docs: true
61023
- });
61024
- await db.bulkRemove(
61025
- userDocs.rows.map((row) => row.doc),
61026
- { silenceErrors: true }
61027
- );
61028
- }
61029
-
61030
- // src/platform/tenants.ts
61031
- var tenants_exports = {};
61032
- __export(tenants_exports, {
61033
- addTenant: () => addTenant,
61034
- exists: () => exists,
61035
- getTenantIds: () => getTenantIds,
61036
- removeTenant: () => removeTenant,
61037
- tenacyLockOptions: () => tenacyLockOptions
61038
- });
61039
-
61040
- // src/redis/redlockImpl.ts
61041
- var redlockImpl_exports = {};
61042
- __export(redlockImpl_exports, {
61043
- AUTO_EXTEND_POLLING_MS: () => AUTO_EXTEND_POLLING_MS,
61044
- doWithLock: () => doWithLock,
61045
- newRedlock: () => newRedlock
61046
- });
61047
- var import_redlock = __toESM(require("redlock"));
61048
-
61049
- // src/utils/index.ts
61050
- var utils_exports4 = {};
61051
- __export(utils_exports4, {
61052
- Duration: () => Duration,
61053
- DurationType: () => DurationType,
61054
- clearCookie: () => clearCookie,
61055
- compare: () => compare,
61056
- getAppIdFromCtx: () => getAppIdFromCtx,
61057
- getCookie: () => getCookie,
61058
- hasCircularStructure: () => hasCircularStructure,
61059
- hash: () => hash,
61060
- isAudited: () => isAudited,
61061
- isClient: () => isClient,
61062
- isPublicApiRequest: () => isPublicApiRequest,
61063
- isServingApp: () => isServingApp,
61064
- isServingBuilder: () => isServingBuilder,
61065
- isServingBuilderPreview: () => isServingBuilderPreview,
61066
- isValidInternalAPIKey: () => isValidInternalAPIKey,
61067
- newid: () => newid,
61068
- openJwt: () => openJwt,
61069
- resolveAppUrl: () => resolveAppUrl,
61070
- setCookie: () => setCookie,
61071
- timeout: () => timeout,
61072
- validEmail: () => validEmail
61073
- });
61074
-
61075
- // src/utils/hashing.ts
61076
- var bcrypt = environment_default.JS_BCRYPT ? require("bcryptjs") : require("bcrypt");
61077
- var SALT_ROUNDS = environment_default.SALT_ROUNDS || 10;
61078
- async function hash(data) {
61079
- const salt = await bcrypt.genSalt(SALT_ROUNDS);
61080
- return bcrypt.hash(data, salt);
61081
- }
61082
- async function compare(data, encrypted) {
61083
- return bcrypt.compare(data, encrypted);
61084
- }
61085
-
61086
- // src/utils/utils.ts
61087
- var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
61088
- var APP_PREFIX3 = "app" /* APP */ + SEPARATOR;
61089
- var PROD_APP_PREFIX = "/app/";
61090
- var BUILDER_PREVIEW_PATH = "/app/preview";
61091
- var BUILDER_PREFIX = "/builder";
61092
- var BUILDER_APP_PREFIX = `${BUILDER_PREFIX}/app/`;
61093
- var PUBLIC_API_PREFIX = "/api/public/v";
61094
- function confirmAppId(possibleAppId) {
61095
- return possibleAppId && possibleAppId.startsWith(APP_PREFIX3) ? possibleAppId : void 0;
61096
- }
61097
- async function resolveAppUrl(ctx) {
61098
- const appUrl = ctx.path.split("/")[2];
61099
- let possibleAppUrl = `/${appUrl.toLowerCase()}`;
61100
- let tenantId = getTenantId();
61101
- if (!environment_default.isDev() && environment_default.MULTI_TENANCY) {
61102
- tenantId = getTenantIDFromCtx(ctx, {
61103
- includeStrategies: ["subdomain" /* SUBDOMAIN */]
61104
- });
61105
- }
61106
- const apps = await doInTenant(
61107
- tenantId,
61108
- () => getAllApps({ dev: false })
61109
- );
61110
- const app = apps.filter(
61111
- (a) => a.url && a.url.toLowerCase() === possibleAppUrl
61112
- )[0];
61113
- return app && app.appId ? app.appId : void 0;
61114
- }
61115
- function isServingApp(ctx) {
61116
- if (ctx.path.startsWith(`/${APP_PREFIX3}`)) {
61117
- return true;
61118
- }
61119
- return ctx.path.startsWith(PROD_APP_PREFIX);
61120
- }
61121
- function isServingBuilder(ctx) {
61122
- return ctx.path.startsWith(BUILDER_APP_PREFIX);
61303
+ async function getUserDoc(emailOrId) {
61304
+ const db = getPlatformDB();
61305
+ return db.get(emailOrId);
61123
61306
  }
61124
- function isServingBuilderPreview(ctx) {
61125
- return ctx.path.startsWith(BUILDER_PREVIEW_PATH);
61307
+ async function updateUserDoc(platformUser) {
61308
+ const db = getPlatformDB();
61309
+ await db.put(platformUser);
61126
61310
  }
61127
- function isPublicApiRequest(ctx) {
61128
- return ctx.path.startsWith(PUBLIC_API_PREFIX);
61311
+ function newUserIdDoc(id, tenantId) {
61312
+ return {
61313
+ _id: id,
61314
+ tenantId
61315
+ };
61129
61316
  }
61130
- async function getAppIdFromCtx(ctx) {
61131
- const options2 = [ctx.request.headers["x-budibase-app-id" /* APP_ID */]];
61132
- let appId;
61133
- for (let option of options2) {
61134
- appId = confirmAppId(option);
61135
- if (appId) {
61136
- break;
61137
- }
61138
- }
61139
- if (!appId && ctx.request.body && ctx.request.body.appId) {
61140
- appId = confirmAppId(ctx.request.body.appId);
61141
- }
61142
- const pathId = parseAppIdFromUrlPath(ctx.path);
61143
- if (!appId && pathId) {
61144
- appId = confirmAppId(pathId);
61145
- }
61146
- const isBuilderPreview = ctx.path.startsWith(BUILDER_PREVIEW_PATH);
61147
- const isViewingProdApp = ctx.path.startsWith(PROD_APP_PREFIX) && !isBuilderPreview;
61148
- if (!appId && isViewingProdApp) {
61149
- appId = confirmAppId(await resolveAppUrl(ctx));
61150
- }
61151
- const referer = ctx.request.headers.referer;
61152
- if (!appId && referer?.includes(BUILDER_APP_PREFIX)) {
61153
- const refererId = parseAppIdFromUrlPath(ctx.request.headers.referer);
61154
- appId = confirmAppId(refererId);
61155
- }
61156
- return appId;
61317
+ function newUserEmailDoc(userId, email, tenantId) {
61318
+ return {
61319
+ _id: email,
61320
+ userId,
61321
+ tenantId
61322
+ };
61157
61323
  }
61158
- function parseAppIdFromUrlPath(url) {
61159
- if (!url) {
61160
- return;
61161
- }
61162
- return url.split("?")[0].split("/").find((subPath) => subPath.startsWith(APP_PREFIX3));
61324
+ function newUserSsoIdDoc(ssoId, email, userId, tenantId) {
61325
+ return {
61326
+ _id: ssoId,
61327
+ userId,
61328
+ email,
61329
+ tenantId
61330
+ };
61163
61331
  }
61164
- function openJwt(token) {
61165
- if (!token) {
61166
- return void 0;
61167
- }
61332
+ async function addUserDoc(emailOrId, newDocFn) {
61333
+ const db = getPlatformDB();
61334
+ let user;
61168
61335
  try {
61169
- return import_jsonwebtoken.default.verify(token, environment_default.JWT_SECRET);
61336
+ await db.get(emailOrId);
61170
61337
  } catch (e) {
61171
- if (environment_default.JWT_SECRET_FALLBACK) {
61172
- return import_jsonwebtoken.default.verify(token, environment_default.JWT_SECRET_FALLBACK);
61338
+ if (e.status === 404) {
61339
+ user = newDocFn();
61340
+ await db.put(user);
61173
61341
  } else {
61174
61342
  throw e;
61175
61343
  }
61176
61344
  }
61177
61345
  }
61178
- function isValidInternalAPIKey(apiKey) {
61179
- if (environment_default.INTERNAL_API_KEY && environment_default.INTERNAL_API_KEY === apiKey) {
61180
- return true;
61181
- }
61182
- return !!(environment_default.INTERNAL_API_KEY_FALLBACK && environment_default.INTERNAL_API_KEY_FALLBACK === apiKey);
61183
- }
61184
- function getCookie(ctx, name) {
61185
- const cookie = ctx.cookies.get(name);
61186
- if (!cookie) {
61187
- return void 0;
61188
- }
61189
- return openJwt(cookie);
61190
- }
61191
- function setCookie(ctx, value, name = "builder", opts = { sign: true }) {
61192
- if (value && opts && opts.sign) {
61193
- value = import_jsonwebtoken.default.sign(value, environment_default.JWT_SECRET);
61194
- }
61195
- const config = {
61196
- expires: MAX_VALID_DATE,
61197
- path: "/",
61198
- httpOnly: false,
61199
- overwrite: true
61200
- };
61201
- if (environment_default.COOKIE_DOMAIN) {
61202
- config.domain = environment_default.COOKIE_DOMAIN;
61203
- }
61204
- ctx.cookies.set(name, value, config);
61205
- }
61206
- function clearCookie(ctx, name) {
61207
- setCookie(ctx, null, name);
61208
- }
61209
- function isClient(ctx) {
61210
- return ctx.headers["x-budibase-type" /* TYPE */] === "client";
61211
- }
61212
- function timeout(timeMs) {
61213
- return new Promise((resolve) => setTimeout(resolve, timeMs));
61214
- }
61215
- function isAudited(event) {
61216
- return !!AuditedEventFriendlyName[event];
61346
+ async function addSsoUser(ssoId, email, userId, tenantId) {
61347
+ return addUserDoc(
61348
+ ssoId,
61349
+ () => newUserSsoIdDoc(ssoId, email, userId, tenantId)
61350
+ );
61217
61351
  }
61218
- function hasCircularStructure(json) {
61219
- if (typeof json !== "object") {
61220
- return false;
61221
- }
61222
- try {
61223
- JSON.stringify(json);
61224
- } catch (err) {
61225
- if (err instanceof Error && err?.message.includes("circular structure")) {
61226
- return true;
61227
- }
61352
+ async function addUser(tenantId, userId, email, ssoId) {
61353
+ const promises = [
61354
+ addUserDoc(userId, () => newUserIdDoc(userId, tenantId)),
61355
+ addUserDoc(email, () => newUserEmailDoc(userId, email, tenantId))
61356
+ ];
61357
+ if (ssoId) {
61358
+ promises.push(addSsoUser(ssoId, email, userId, tenantId));
61228
61359
  }
61229
- return false;
61360
+ await Promise.all(promises);
61230
61361
  }
61231
-
61232
- // src/utils/stringUtils.ts
61233
- function validEmail(value) {
61234
- return value && !!value.match(
61235
- /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
61362
+ async function removeUser(user) {
61363
+ const db = getPlatformDB();
61364
+ const keys2 = [user._id, user.email];
61365
+ const userDocs = await db.allDocs({
61366
+ keys: keys2,
61367
+ include_docs: true
61368
+ });
61369
+ await db.bulkRemove(
61370
+ userDocs.rows.map((row) => row.doc),
61371
+ { silenceErrors: true }
61236
61372
  );
61237
61373
  }
61238
61374
 
61239
- // src/utils/Duration.ts
61240
- var DurationType = /* @__PURE__ */ ((DurationType2) => {
61241
- DurationType2["MILLISECONDS"] = "milliseconds";
61242
- DurationType2["SECONDS"] = "seconds";
61243
- DurationType2["MINUTES"] = "minutes";
61244
- DurationType2["HOURS"] = "hours";
61245
- DurationType2["DAYS"] = "days";
61246
- return DurationType2;
61247
- })(DurationType || {});
61248
- var conversion = {
61249
- milliseconds: 1,
61250
- seconds: 1e3,
61251
- minutes: 60 * 1e3,
61252
- hours: 60 * 60 * 1e3,
61253
- days: 24 * 60 * 60 * 1e3
61254
- };
61255
- var Duration = class _Duration {
61256
- static convert(from, to, duration) {
61257
- const milliseconds = duration * conversion[from];
61258
- return milliseconds / conversion[to];
61259
- }
61260
- static from(from, duration) {
61261
- return {
61262
- to: (to) => {
61263
- return _Duration.convert(from, to, duration);
61264
- },
61265
- toMs: () => {
61266
- return _Duration.convert(from, "milliseconds" /* MILLISECONDS */, duration);
61267
- },
61268
- toSeconds: () => {
61269
- return _Duration.convert(from, "seconds" /* SECONDS */, duration);
61270
- }
61271
- };
61272
- }
61273
- static fromSeconds(duration) {
61274
- return _Duration.from("seconds" /* SECONDS */, duration);
61275
- }
61276
- static fromMinutes(duration) {
61277
- return _Duration.from("minutes" /* MINUTES */, duration);
61278
- }
61279
- static fromHours(duration) {
61280
- return _Duration.from("hours" /* HOURS */, duration);
61281
- }
61282
- static fromDays(duration) {
61283
- return _Duration.from("days" /* DAYS */, duration);
61284
- }
61285
- static fromMilliseconds(duration) {
61286
- return _Duration.from("milliseconds" /* MILLISECONDS */, duration);
61287
- }
61288
- };
61375
+ // src/platform/tenants.ts
61376
+ var tenants_exports = {};
61377
+ __export(tenants_exports, {
61378
+ addTenant: () => addTenant,
61379
+ exists: () => exists,
61380
+ getTenantIds: () => getTenantIds,
61381
+ removeTenant: () => removeTenant,
61382
+ tenacyLockOptions: () => tenacyLockOptions
61383
+ });
61289
61384
 
61290
61385
  // src/redis/redlockImpl.ts
61386
+ var redlockImpl_exports = {};
61387
+ __export(redlockImpl_exports, {
61388
+ AUTO_EXTEND_POLLING_MS: () => AUTO_EXTEND_POLLING_MS,
61389
+ doWithLock: () => doWithLock,
61390
+ newRedlock: () => newRedlock
61391
+ });
61392
+ var import_redlock = __toESM(require("redlock"));
61291
61393
  async function getClient(type, opts) {
61292
61394
  if (type === "custom" /* CUSTOM */) {
61293
61395
  return newRedlock(opts);
@@ -61610,7 +61712,6 @@ __export(users_exports3, {
61610
61712
  isBuilder: () => isBuilder2,
61611
61713
  isCreator: () => isCreator2,
61612
61714
  isGlobalBuilder: () => isGlobalBuilder2,
61613
- isSupportedUserSearch: () => isSupportedUserSearch,
61614
61715
  paginatedUsers: () => paginatedUsers,
61615
61716
  removeAppBuilder: () => removeAppBuilder,
61616
61717
  removePortalUserPermissions: () => removePortalUserPermissions,
@@ -65484,29 +65585,6 @@ function removeUserPassword(users) {
65484
65585
  }
65485
65586
  return users;
65486
65587
  }
65487
- function isSupportedUserSearch(query) {
65488
- const allowed = [
65489
- { op: "string" /* STRING */, key: "email" },
65490
- { op: "equal" /* EQUAL */, key: "_id" },
65491
- { op: "oneOf" /* ONE_OF */, key: "_id" }
65492
- ];
65493
- for (let [key, operation] of Object.entries(query)) {
65494
- if (typeof operation !== "object") {
65495
- return false;
65496
- }
65497
- const fields = Object.keys(operation || {});
65498
- if (fields.length === 0) {
65499
- continue;
65500
- }
65501
- const allowedOperation = allowed.find(
65502
- (allow) => allow.op === key && fields.length === 1 && fields[0] === allow.key
65503
- );
65504
- if (!allowedOperation) {
65505
- return false;
65506
- }
65507
- }
65508
- return true;
65509
- }
65510
65588
  async function bulkGetGlobalUsersById(userIds, opts) {
65511
65589
  const db = getGlobalDB();
65512
65590
  let users = (await db.allDocs({
@@ -68070,7 +68148,7 @@ var SqlTableQueryBuilder = class {
68070
68148
  var sqlTable_default = SqlTableQueryBuilder;
68071
68149
 
68072
68150
  // src/sql/sql.ts
68073
- var import_lodash3 = require("lodash");
68151
+ var import_lodash4 = require("lodash");
68074
68152
  function getBaseLimit() {
68075
68153
  const envLimit = environment_default.SQL_MAX_ROWS ? parseInt(environment_default.SQL_MAX_ROWS) : null;
68076
68154
  return envLimit || 5e3;
@@ -68259,7 +68337,7 @@ var InternalBuilder = class {
68259
68337
  return body2;
68260
68338
  }
68261
68339
  parseFilters(filters) {
68262
- filters = (0, import_lodash3.cloneDeep)(filters);
68340
+ filters = (0, import_lodash4.cloneDeep)(filters);
68263
68341
  for (const op of Object.values(BasicOperator)) {
68264
68342
  const filter = filters[op];
68265
68343
  if (!filter) {
@@ -68681,8 +68759,36 @@ var InternalBuilder = class {
68681
68759
  }
68682
68760
  return query.countDistinct(`${aliased}.${primary[0]} as total`);
68683
68761
  }
68762
+ addAggregations(query, aggregations) {
68763
+ const fields = this.query.resource?.fields || [];
68764
+ if (fields.length > 0) {
68765
+ query = query.groupBy(fields.map((field) => `${this.table.name}.${field}`));
68766
+ }
68767
+ for (const aggregation of aggregations) {
68768
+ const op = aggregation.calculationType;
68769
+ const field = `${this.table.name}.${aggregation.field} as ${aggregation.name}`;
68770
+ switch (op) {
68771
+ case "count" /* COUNT */:
68772
+ query = query.count(field);
68773
+ break;
68774
+ case "sum" /* SUM */:
68775
+ query = query.sum(field);
68776
+ break;
68777
+ case "avg" /* AVG */:
68778
+ query = query.avg(field);
68779
+ break;
68780
+ case "min" /* MIN */:
68781
+ query = query.min(field);
68782
+ break;
68783
+ case "max" /* MAX */:
68784
+ query = query.max(field);
68785
+ break;
68786
+ }
68787
+ }
68788
+ return query;
68789
+ }
68684
68790
  addSorting(query) {
68685
- let { sort: sort2 } = this.query;
68791
+ let { sort: sort2, resource } = this.query;
68686
68792
  const primaryKey = this.table.primary;
68687
68793
  const tableName = getTableName(this.table);
68688
68794
  const aliases = this.query.tableAliases;
@@ -68707,7 +68813,8 @@ var InternalBuilder = class {
68707
68813
  }
68708
68814
  }
68709
68815
  }
68710
- if (!sort2 || sort2[primaryKey[0]] === void 0) {
68816
+ const hasAggregations = (resource?.aggregations?.length ?? 0) > 0;
68817
+ if (!hasAggregations && (!sort2 || sort2[primaryKey[0]] === void 0)) {
68711
68818
  query = query.orderBy(`${aliased}.${primaryKey[0]}`);
68712
68819
  }
68713
68820
  return query;
@@ -68981,7 +69088,14 @@ var InternalBuilder = class {
68981
69088
  query = query.offset(foundOffset);
68982
69089
  }
68983
69090
  }
68984
- query = !counting ? query.select(this.generateSelectStatement()) : this.addDistinctCount(query);
69091
+ const aggregations = this.query.resource?.aggregations || [];
69092
+ if (counting) {
69093
+ query = this.addDistinctCount(query);
69094
+ } else if (aggregations.length > 0) {
69095
+ query = this.addAggregations(query, aggregations);
69096
+ } else {
69097
+ query = query.select(this.generateSelectStatement());
69098
+ }
68985
69099
  if (!counting) {
68986
69100
  query = this.addSorting(query);
68987
69101
  }