@budibase/backend-core 2.32.6 → 2.32.8

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.
package/dist/index.js CHANGED
@@ -54228,6 +54228,7 @@ var configs_exports = {};
54228
54228
  __export(configs_exports, {
54229
54229
  analyticsEnabled: () => analyticsEnabled,
54230
54230
  generateConfigID: () => generateConfigID,
54231
+ getAIConfig: () => getAIConfig,
54231
54232
  getConfig: () => getConfig,
54232
54233
  getDefaultGoogleConfig: () => getDefaultGoogleConfig,
54233
54234
  getGoogleConfig: () => getGoogleConfig,
@@ -58452,6 +58453,7 @@ var AppState = /* @__PURE__ */ ((AppState2) => {
58452
58453
  return AppState2;
58453
58454
  })(AppState || {});
58454
58455
  var EXPIRY_SECONDS = 3600;
58456
+ var INVALID_EXPIRY_SECONDS = 60;
58455
58457
  async function populateFromDB(appId) {
58456
58458
  return doWithDB(
58457
58459
  appId,
@@ -58474,7 +58476,7 @@ async function getAppMetadata(appId) {
58474
58476
  } catch (err) {
58475
58477
  if (err && err.status === 404) {
58476
58478
  metadata = { state: "invalid" /* INVALID */ };
58477
- expiry = void 0;
58479
+ expiry = INVALID_EXPIRY_SECONDS;
58478
58480
  } else {
58479
58481
  throw err;
58480
58482
  }
@@ -59814,6 +59816,385 @@ __export(features_exports, {
59814
59816
  });
59815
59817
  var import_posthog_node = require("posthog-node");
59816
59818
  var import_dd_trace3 = __toESM(require("dd-trace"));
59819
+
59820
+ // src/utils/index.ts
59821
+ var utils_exports4 = {};
59822
+ __export(utils_exports4, {
59823
+ Duration: () => Duration,
59824
+ DurationType: () => DurationType,
59825
+ clearCookie: () => clearCookie,
59826
+ compare: () => compare,
59827
+ getAppIdFromCtx: () => getAppIdFromCtx,
59828
+ getCookie: () => getCookie,
59829
+ hasCircularStructure: () => hasCircularStructure,
59830
+ hash: () => hash,
59831
+ isAudited: () => isAudited,
59832
+ isClient: () => isClient,
59833
+ isPublicApiRequest: () => isPublicApiRequest,
59834
+ isServingApp: () => isServingApp,
59835
+ isServingBuilder: () => isServingBuilder,
59836
+ isServingBuilderPreview: () => isServingBuilderPreview,
59837
+ isValidInternalAPIKey: () => isValidInternalAPIKey,
59838
+ newid: () => newid,
59839
+ openJwt: () => openJwt,
59840
+ resolveAppUrl: () => resolveAppUrl,
59841
+ setCookie: () => setCookie,
59842
+ timeout: () => timeout,
59843
+ validEmail: () => validEmail
59844
+ });
59845
+
59846
+ // src/utils/hashing.ts
59847
+ var bcrypt = environment_default.JS_BCRYPT ? require("bcryptjs") : require("bcrypt");
59848
+ var SALT_ROUNDS = environment_default.SALT_ROUNDS || 10;
59849
+ async function hash(data) {
59850
+ const salt = await bcrypt.genSalt(SALT_ROUNDS);
59851
+ return bcrypt.hash(data, salt);
59852
+ }
59853
+ async function compare(data, encrypted) {
59854
+ return bcrypt.compare(data, encrypted);
59855
+ }
59856
+
59857
+ // src/tenancy/index.ts
59858
+ var tenancy_exports = {};
59859
+ __export(tenancy_exports, {
59860
+ addTenantToUrl: () => addTenantToUrl,
59861
+ getTenantDB: () => getTenantDB,
59862
+ getTenantIDFromCtx: () => getTenantIDFromCtx,
59863
+ getTenantInfo: () => getTenantInfo,
59864
+ isUserInAppTenant: () => isUserInAppTenant,
59865
+ saveTenantInfo: () => saveTenantInfo
59866
+ });
59867
+
59868
+ // src/tenancy/db.ts
59869
+ function getTenantDB(tenantId) {
59870
+ return getDB(getGlobalDBName(tenantId));
59871
+ }
59872
+ async function saveTenantInfo(tenantInfo) {
59873
+ const db = getTenantDB(tenantInfo.tenantId);
59874
+ return db.put({
59875
+ _id: "tenant_info",
59876
+ ...tenantInfo
59877
+ });
59878
+ }
59879
+ async function getTenantInfo(tenantId) {
59880
+ try {
59881
+ const db = getTenantDB(tenantId);
59882
+ const tenantInfo = await db.get("tenant_info");
59883
+ delete tenantInfo.owner.password;
59884
+ return tenantInfo;
59885
+ } catch {
59886
+ return void 0;
59887
+ }
59888
+ }
59889
+
59890
+ // src/tenancy/tenancy.ts
59891
+ function addTenantToUrl(url) {
59892
+ const tenantId = getTenantId();
59893
+ if (isMultiTenant()) {
59894
+ const char = url.indexOf("?") === -1 ? "?" : "&";
59895
+ url += `${char}tenantId=${tenantId}`;
59896
+ }
59897
+ return url;
59898
+ }
59899
+ var isUserInAppTenant = (appId, user) => {
59900
+ let userTenantId;
59901
+ if (user) {
59902
+ userTenantId = user.tenantId || DEFAULT_TENANT_ID;
59903
+ } else {
59904
+ userTenantId = getTenantId();
59905
+ }
59906
+ const tenantId = getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID;
59907
+ return tenantId === userTenantId;
59908
+ };
59909
+ var ALL_STRATEGIES = Object.values(TenantResolutionStrategy);
59910
+ var getTenantIDFromCtx = (ctx, opts) => {
59911
+ if (!isMultiTenant()) {
59912
+ return DEFAULT_TENANT_ID;
59913
+ }
59914
+ if (opts.allowNoTenant === void 0) {
59915
+ opts.allowNoTenant = false;
59916
+ }
59917
+ if (!opts.includeStrategies) {
59918
+ opts.includeStrategies = ALL_STRATEGIES;
59919
+ }
59920
+ if (!opts.excludeStrategies) {
59921
+ opts.excludeStrategies = [];
59922
+ }
59923
+ const isAllowed = (strategy) => {
59924
+ if (opts.excludeStrategies?.includes(strategy)) {
59925
+ return false;
59926
+ }
59927
+ if (opts.includeStrategies?.includes(strategy)) {
59928
+ return true;
59929
+ }
59930
+ };
59931
+ if (isAllowed("user" /* USER */)) {
59932
+ const userTenantId = ctx.user?.tenantId;
59933
+ if (userTenantId) {
59934
+ return userTenantId;
59935
+ }
59936
+ }
59937
+ if (isAllowed("header" /* HEADER */)) {
59938
+ const headerTenantId = ctx.request.headers["x-budibase-tenant-id" /* TENANT_ID */];
59939
+ if (headerTenantId) {
59940
+ return headerTenantId;
59941
+ }
59942
+ }
59943
+ if (isAllowed("query" /* QUERY */)) {
59944
+ const queryTenantId = ctx.request.query.tenantId;
59945
+ if (queryTenantId) {
59946
+ return queryTenantId;
59947
+ }
59948
+ }
59949
+ if (isAllowed("subdomain" /* SUBDOMAIN */)) {
59950
+ let platformHost;
59951
+ try {
59952
+ platformHost = new URL(getPlatformURL()).host.split(":")[0];
59953
+ } catch (err) {
59954
+ if (err.code !== "ERR_INVALID_URL") {
59955
+ throw err;
59956
+ }
59957
+ }
59958
+ const requestHost = ctx.host;
59959
+ if (platformHost && requestHost.includes(platformHost)) {
59960
+ const tenantId = requestHost.substring(
59961
+ 0,
59962
+ requestHost.indexOf(`.${platformHost}`)
59963
+ );
59964
+ if (tenantId) {
59965
+ return tenantId;
59966
+ }
59967
+ }
59968
+ }
59969
+ if (isAllowed("path" /* PATH */)) {
59970
+ const match = ctx.matched.find(
59971
+ (m) => !!m.paramNames.find((p) => p.name === "tenantId")
59972
+ );
59973
+ const ctxUrl = ctx.originalUrl;
59974
+ let url;
59975
+ if (ctxUrl.includes("?")) {
59976
+ url = ctxUrl.split("?")[0];
59977
+ } else {
59978
+ url = ctxUrl;
59979
+ }
59980
+ if (match) {
59981
+ const params2 = match.params(url, match.captures(url), {});
59982
+ if (params2.tenantId) {
59983
+ return params2.tenantId;
59984
+ }
59985
+ }
59986
+ }
59987
+ if (!opts.allowNoTenant) {
59988
+ ctx.throw(403, "Tenant id not set");
59989
+ }
59990
+ return void 0;
59991
+ };
59992
+
59993
+ // src/utils/utils.ts
59994
+ var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
59995
+ var APP_PREFIX3 = "app" /* APP */ + SEPARATOR;
59996
+ var PROD_APP_PREFIX = "/app/";
59997
+ var BUILDER_PREVIEW_PATH = "/app/preview";
59998
+ var BUILDER_PREFIX = "/builder";
59999
+ var BUILDER_APP_PREFIX = `${BUILDER_PREFIX}/app/`;
60000
+ var PUBLIC_API_PREFIX = "/api/public/v";
60001
+ function confirmAppId(possibleAppId) {
60002
+ return possibleAppId && possibleAppId.startsWith(APP_PREFIX3) ? possibleAppId : void 0;
60003
+ }
60004
+ async function resolveAppUrl(ctx) {
60005
+ const appUrl = ctx.path.split("/")[2];
60006
+ let possibleAppUrl = `/${appUrl.toLowerCase()}`;
60007
+ let tenantId = getTenantId();
60008
+ if (!environment_default.isDev() && environment_default.MULTI_TENANCY) {
60009
+ tenantId = getTenantIDFromCtx(ctx, {
60010
+ includeStrategies: ["subdomain" /* SUBDOMAIN */]
60011
+ });
60012
+ }
60013
+ const apps = await doInTenant(
60014
+ tenantId,
60015
+ () => getAllApps({ dev: false })
60016
+ );
60017
+ const app = apps.filter(
60018
+ (a) => a.url && a.url.toLowerCase() === possibleAppUrl
60019
+ )[0];
60020
+ return app && app.appId ? app.appId : void 0;
60021
+ }
60022
+ function isServingApp(ctx) {
60023
+ if (ctx.path.startsWith(`/${APP_PREFIX3}`)) {
60024
+ return true;
60025
+ }
60026
+ return ctx.path.startsWith(PROD_APP_PREFIX);
60027
+ }
60028
+ function isServingBuilder(ctx) {
60029
+ return ctx.path.startsWith(BUILDER_APP_PREFIX);
60030
+ }
60031
+ function isServingBuilderPreview(ctx) {
60032
+ return ctx.path.startsWith(BUILDER_PREVIEW_PATH);
60033
+ }
60034
+ function isPublicApiRequest(ctx) {
60035
+ return ctx.path.startsWith(PUBLIC_API_PREFIX);
60036
+ }
60037
+ async function getAppIdFromCtx(ctx) {
60038
+ const options2 = [ctx.request.headers["x-budibase-app-id" /* APP_ID */]];
60039
+ let appId;
60040
+ for (let option of options2) {
60041
+ appId = confirmAppId(option);
60042
+ if (appId) {
60043
+ break;
60044
+ }
60045
+ }
60046
+ if (!appId && ctx.request.body && ctx.request.body.appId) {
60047
+ appId = confirmAppId(ctx.request.body.appId);
60048
+ }
60049
+ const pathId = parseAppIdFromUrlPath(ctx.path);
60050
+ if (!appId && pathId) {
60051
+ appId = confirmAppId(pathId);
60052
+ }
60053
+ const isBuilderPreview = ctx.path.startsWith(BUILDER_PREVIEW_PATH);
60054
+ const isViewingProdApp = ctx.path.startsWith(PROD_APP_PREFIX) && !isBuilderPreview;
60055
+ if (!appId && isViewingProdApp) {
60056
+ appId = confirmAppId(await resolveAppUrl(ctx));
60057
+ }
60058
+ const referer = ctx.request.headers.referer;
60059
+ if (!appId && referer?.includes(BUILDER_APP_PREFIX)) {
60060
+ const refererId = parseAppIdFromUrlPath(ctx.request.headers.referer);
60061
+ appId = confirmAppId(refererId);
60062
+ }
60063
+ return appId;
60064
+ }
60065
+ function parseAppIdFromUrlPath(url) {
60066
+ if (!url) {
60067
+ return;
60068
+ }
60069
+ return url.split("?")[0].split("/").find((subPath) => subPath.startsWith(APP_PREFIX3));
60070
+ }
60071
+ function openJwt(token) {
60072
+ if (!token) {
60073
+ return void 0;
60074
+ }
60075
+ try {
60076
+ return import_jsonwebtoken.default.verify(token, environment_default.JWT_SECRET);
60077
+ } catch (e) {
60078
+ if (environment_default.JWT_SECRET_FALLBACK) {
60079
+ return import_jsonwebtoken.default.verify(token, environment_default.JWT_SECRET_FALLBACK);
60080
+ } else {
60081
+ throw e;
60082
+ }
60083
+ }
60084
+ }
60085
+ function isValidInternalAPIKey(apiKey) {
60086
+ if (environment_default.INTERNAL_API_KEY && environment_default.INTERNAL_API_KEY === apiKey) {
60087
+ return true;
60088
+ }
60089
+ return !!(environment_default.INTERNAL_API_KEY_FALLBACK && environment_default.INTERNAL_API_KEY_FALLBACK === apiKey);
60090
+ }
60091
+ function getCookie(ctx, name) {
60092
+ const cookie = ctx.cookies.get(name);
60093
+ if (!cookie) {
60094
+ return void 0;
60095
+ }
60096
+ return openJwt(cookie);
60097
+ }
60098
+ function setCookie(ctx, value, name = "builder", opts = { sign: true }) {
60099
+ if (value && opts && opts.sign) {
60100
+ value = import_jsonwebtoken.default.sign(value, environment_default.JWT_SECRET);
60101
+ }
60102
+ const config = {
60103
+ expires: MAX_VALID_DATE,
60104
+ path: "/",
60105
+ httpOnly: false,
60106
+ overwrite: true
60107
+ };
60108
+ if (environment_default.COOKIE_DOMAIN) {
60109
+ config.domain = environment_default.COOKIE_DOMAIN;
60110
+ }
60111
+ ctx.cookies.set(name, value, config);
60112
+ }
60113
+ function clearCookie(ctx, name) {
60114
+ setCookie(ctx, null, name);
60115
+ }
60116
+ function isClient(ctx) {
60117
+ return ctx.headers["x-budibase-type" /* TYPE */] === "client";
60118
+ }
60119
+ function timeout(timeMs) {
60120
+ return new Promise((resolve) => setTimeout(resolve, timeMs));
60121
+ }
60122
+ function isAudited(event) {
60123
+ return !!AuditedEventFriendlyName[event];
60124
+ }
60125
+ function hasCircularStructure(json) {
60126
+ if (typeof json !== "object") {
60127
+ return false;
60128
+ }
60129
+ try {
60130
+ JSON.stringify(json);
60131
+ } catch (err) {
60132
+ if (err instanceof Error && err?.message.includes("circular structure")) {
60133
+ return true;
60134
+ }
60135
+ }
60136
+ return false;
60137
+ }
60138
+
60139
+ // src/utils/stringUtils.ts
60140
+ function validEmail(value) {
60141
+ return value && !!value.match(
60142
+ /^(([^<>()[\]\\.,;:\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,}))$/
60143
+ );
60144
+ }
60145
+
60146
+ // src/utils/Duration.ts
60147
+ var DurationType = /* @__PURE__ */ ((DurationType2) => {
60148
+ DurationType2["MILLISECONDS"] = "milliseconds";
60149
+ DurationType2["SECONDS"] = "seconds";
60150
+ DurationType2["MINUTES"] = "minutes";
60151
+ DurationType2["HOURS"] = "hours";
60152
+ DurationType2["DAYS"] = "days";
60153
+ return DurationType2;
60154
+ })(DurationType || {});
60155
+ var conversion = {
60156
+ milliseconds: 1,
60157
+ seconds: 1e3,
60158
+ minutes: 60 * 1e3,
60159
+ hours: 60 * 60 * 1e3,
60160
+ days: 24 * 60 * 60 * 1e3
60161
+ };
60162
+ var Duration = class _Duration {
60163
+ static convert(from, to, duration) {
60164
+ const milliseconds = duration * conversion[from];
60165
+ return milliseconds / conversion[to];
60166
+ }
60167
+ static from(from, duration) {
60168
+ return {
60169
+ to: (to) => {
60170
+ return _Duration.convert(from, to, duration);
60171
+ },
60172
+ toMs: () => {
60173
+ return _Duration.convert(from, "milliseconds" /* MILLISECONDS */, duration);
60174
+ },
60175
+ toSeconds: () => {
60176
+ return _Duration.convert(from, "seconds" /* SECONDS */, duration);
60177
+ }
60178
+ };
60179
+ }
60180
+ static fromSeconds(duration) {
60181
+ return _Duration.from("seconds" /* SECONDS */, duration);
60182
+ }
60183
+ static fromMinutes(duration) {
60184
+ return _Duration.from("minutes" /* MINUTES */, duration);
60185
+ }
60186
+ static fromHours(duration) {
60187
+ return _Duration.from("hours" /* HOURS */, duration);
60188
+ }
60189
+ static fromDays(duration) {
60190
+ return _Duration.from("days" /* DAYS */, duration);
60191
+ }
60192
+ static fromMilliseconds(duration) {
60193
+ return _Duration.from("milliseconds" /* MILLISECONDS */, duration);
60194
+ }
60195
+ };
60196
+
60197
+ // src/features/index.ts
59817
60198
  var posthog;
59818
60199
  function init4(opts) {
59819
60200
  if (environment_default.POSTHOG_TOKEN && environment_default.POSTHOG_API_HOST && !environment_default.SELF_HOSTED && environment_default.POSTHOG_FEATURE_FLAGS_ENABLED) {
@@ -59821,6 +60202,7 @@ function init4(opts) {
59821
60202
  posthog = new import_posthog_node.PostHog(environment_default.POSTHOG_TOKEN, {
59822
60203
  host: environment_default.POSTHOG_API_HOST,
59823
60204
  personalApiKey: environment_default.POSTHOG_PERSONAL_TOKEN,
60205
+ featureFlagsPollingInterval: Duration.fromMinutes(3).toMs(),
59824
60206
  ...opts
59825
60207
  });
59826
60208
  } else {
@@ -60097,7 +60479,7 @@ var DatabaseImpl = class _DatabaseImpl {
60097
60479
  return this.getDb();
60098
60480
  }
60099
60481
  // this function fetches the DB and handles if DB creation is needed
60100
- async performCall(call) {
60482
+ async performCallWithDBCreation(call) {
60101
60483
  const db = this.getDb();
60102
60484
  const fnc = await call(db);
60103
60485
  try {
@@ -60105,11 +60487,20 @@ var DatabaseImpl = class _DatabaseImpl {
60105
60487
  } catch (err) {
60106
60488
  if (err.statusCode === 404 && err.reason === DATABASE_NOT_FOUND) {
60107
60489
  await this.checkAndCreateDb();
60108
- return await this.performCall(call);
60490
+ return await this.performCallWithDBCreation(call);
60109
60491
  }
60110
60492
  throw new CouchDBError(`CouchDB error: ${err.message}`, err);
60111
60493
  }
60112
60494
  }
60495
+ async performCall(call) {
60496
+ const db = this.getDb();
60497
+ const fnc = await call(db);
60498
+ try {
60499
+ return await fnc();
60500
+ } catch (err) {
60501
+ throw new CouchDBError(`CouchDB error: ${err.message}`, err);
60502
+ }
60503
+ }
60113
60504
  async get(id) {
60114
60505
  return this.performCall((db) => {
60115
60506
  if (!id) {
@@ -60193,7 +60584,7 @@ var DatabaseImpl = class _DatabaseImpl {
60193
60584
  if (!document._id) {
60194
60585
  throw new Error("Cannot store document without _id field.");
60195
60586
  }
60196
- return this.performCall(async (db) => {
60587
+ return this.performCallWithDBCreation(async (db) => {
60197
60588
  if (!document.createdAt) {
60198
60589
  document.createdAt = (/* @__PURE__ */ new Date()).toISOString();
60199
60590
  }
@@ -60215,7 +60606,7 @@ var DatabaseImpl = class _DatabaseImpl {
60215
60606
  }
60216
60607
  async bulkDocs(documents) {
60217
60608
  const now = (/* @__PURE__ */ new Date()).toISOString();
60218
- return this.performCall((db) => {
60609
+ return this.performCallWithDBCreation((db) => {
60219
60610
  return () => db.bulk({
60220
60611
  docs: documents.map((d) => ({ createdAt: now, ...d, updatedAt: now }))
60221
60612
  });
@@ -60223,7 +60614,21 @@ var DatabaseImpl = class _DatabaseImpl {
60223
60614
  }
60224
60615
  async allDocs(params2) {
60225
60616
  return this.performCall((db) => {
60226
- return () => db.list(params2);
60617
+ return async () => {
60618
+ try {
60619
+ return await db.list(params2);
60620
+ } catch (err) {
60621
+ if (err.reason === DATABASE_NOT_FOUND) {
60622
+ return {
60623
+ offset: 0,
60624
+ total_rows: 0,
60625
+ rows: []
60626
+ };
60627
+ } else {
60628
+ throw err;
60629
+ }
60630
+ }
60631
+ };
60227
60632
  });
60228
60633
  }
60229
60634
  async _sqlQuery(url, method, body2) {
@@ -60786,142 +61191,6 @@ __export(user_exports, {
60786
61191
  invalidateUser: () => invalidateUser
60787
61192
  });
60788
61193
 
60789
- // src/tenancy/index.ts
60790
- var tenancy_exports = {};
60791
- __export(tenancy_exports, {
60792
- addTenantToUrl: () => addTenantToUrl,
60793
- getTenantDB: () => getTenantDB,
60794
- getTenantIDFromCtx: () => getTenantIDFromCtx,
60795
- getTenantInfo: () => getTenantInfo,
60796
- isUserInAppTenant: () => isUserInAppTenant,
60797
- saveTenantInfo: () => saveTenantInfo
60798
- });
60799
-
60800
- // src/tenancy/db.ts
60801
- function getTenantDB(tenantId) {
60802
- return getDB(getGlobalDBName(tenantId));
60803
- }
60804
- async function saveTenantInfo(tenantInfo) {
60805
- const db = getTenantDB(tenantInfo.tenantId);
60806
- return db.put({
60807
- _id: "tenant_info",
60808
- ...tenantInfo
60809
- });
60810
- }
60811
- async function getTenantInfo(tenantId) {
60812
- try {
60813
- const db = getTenantDB(tenantId);
60814
- const tenantInfo = await db.get("tenant_info");
60815
- delete tenantInfo.owner.password;
60816
- return tenantInfo;
60817
- } catch {
60818
- return void 0;
60819
- }
60820
- }
60821
-
60822
- // src/tenancy/tenancy.ts
60823
- function addTenantToUrl(url) {
60824
- const tenantId = getTenantId();
60825
- if (isMultiTenant()) {
60826
- const char = url.indexOf("?") === -1 ? "?" : "&";
60827
- url += `${char}tenantId=${tenantId}`;
60828
- }
60829
- return url;
60830
- }
60831
- var isUserInAppTenant = (appId, user) => {
60832
- let userTenantId;
60833
- if (user) {
60834
- userTenantId = user.tenantId || DEFAULT_TENANT_ID;
60835
- } else {
60836
- userTenantId = getTenantId();
60837
- }
60838
- const tenantId = getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID;
60839
- return tenantId === userTenantId;
60840
- };
60841
- var ALL_STRATEGIES = Object.values(TenantResolutionStrategy);
60842
- var getTenantIDFromCtx = (ctx, opts) => {
60843
- if (!isMultiTenant()) {
60844
- return DEFAULT_TENANT_ID;
60845
- }
60846
- if (opts.allowNoTenant === void 0) {
60847
- opts.allowNoTenant = false;
60848
- }
60849
- if (!opts.includeStrategies) {
60850
- opts.includeStrategies = ALL_STRATEGIES;
60851
- }
60852
- if (!opts.excludeStrategies) {
60853
- opts.excludeStrategies = [];
60854
- }
60855
- const isAllowed = (strategy) => {
60856
- if (opts.excludeStrategies?.includes(strategy)) {
60857
- return false;
60858
- }
60859
- if (opts.includeStrategies?.includes(strategy)) {
60860
- return true;
60861
- }
60862
- };
60863
- if (isAllowed("user" /* USER */)) {
60864
- const userTenantId = ctx.user?.tenantId;
60865
- if (userTenantId) {
60866
- return userTenantId;
60867
- }
60868
- }
60869
- if (isAllowed("header" /* HEADER */)) {
60870
- const headerTenantId = ctx.request.headers["x-budibase-tenant-id" /* TENANT_ID */];
60871
- if (headerTenantId) {
60872
- return headerTenantId;
60873
- }
60874
- }
60875
- if (isAllowed("query" /* QUERY */)) {
60876
- const queryTenantId = ctx.request.query.tenantId;
60877
- if (queryTenantId) {
60878
- return queryTenantId;
60879
- }
60880
- }
60881
- if (isAllowed("subdomain" /* SUBDOMAIN */)) {
60882
- let platformHost;
60883
- try {
60884
- platformHost = new URL(getPlatformURL()).host.split(":")[0];
60885
- } catch (err) {
60886
- if (err.code !== "ERR_INVALID_URL") {
60887
- throw err;
60888
- }
60889
- }
60890
- const requestHost = ctx.host;
60891
- if (platformHost && requestHost.includes(platformHost)) {
60892
- const tenantId = requestHost.substring(
60893
- 0,
60894
- requestHost.indexOf(`.${platformHost}`)
60895
- );
60896
- if (tenantId) {
60897
- return tenantId;
60898
- }
60899
- }
60900
- }
60901
- if (isAllowed("path" /* PATH */)) {
60902
- const match = ctx.matched.find(
60903
- (m) => !!m.paramNames.find((p) => p.name === "tenantId")
60904
- );
60905
- const ctxUrl = ctx.originalUrl;
60906
- let url;
60907
- if (ctxUrl.includes("?")) {
60908
- url = ctxUrl.split("?")[0];
60909
- } else {
60910
- url = ctxUrl;
60911
- }
60912
- if (match) {
60913
- const params2 = match.params(url, match.captures(url), {});
60914
- if (params2.tenantId) {
60915
- return params2.tenantId;
60916
- }
60917
- }
60918
- }
60919
- if (!opts.allowNoTenant) {
60920
- ctx.throw(403, "Tenant id not set");
60921
- }
60922
- return void 0;
60923
- };
60924
-
60925
61194
  // src/platform/index.ts
60926
61195
  var platform_exports = {};
60927
61196
  __export(platform_exports, {
@@ -61044,249 +61313,6 @@ __export(redlockImpl_exports, {
61044
61313
  newRedlock: () => newRedlock
61045
61314
  });
61046
61315
  var import_redlock = __toESM(require("redlock"));
61047
-
61048
- // src/utils/index.ts
61049
- var utils_exports4 = {};
61050
- __export(utils_exports4, {
61051
- Duration: () => Duration,
61052
- DurationType: () => DurationType,
61053
- clearCookie: () => clearCookie,
61054
- compare: () => compare,
61055
- getAppIdFromCtx: () => getAppIdFromCtx,
61056
- getCookie: () => getCookie,
61057
- hasCircularStructure: () => hasCircularStructure,
61058
- hash: () => hash,
61059
- isAudited: () => isAudited,
61060
- isClient: () => isClient,
61061
- isPublicApiRequest: () => isPublicApiRequest,
61062
- isServingApp: () => isServingApp,
61063
- isServingBuilder: () => isServingBuilder,
61064
- isServingBuilderPreview: () => isServingBuilderPreview,
61065
- isValidInternalAPIKey: () => isValidInternalAPIKey,
61066
- newid: () => newid,
61067
- openJwt: () => openJwt,
61068
- resolveAppUrl: () => resolveAppUrl,
61069
- setCookie: () => setCookie,
61070
- timeout: () => timeout,
61071
- validEmail: () => validEmail
61072
- });
61073
-
61074
- // src/utils/hashing.ts
61075
- var bcrypt = environment_default.JS_BCRYPT ? require("bcryptjs") : require("bcrypt");
61076
- var SALT_ROUNDS = environment_default.SALT_ROUNDS || 10;
61077
- async function hash(data) {
61078
- const salt = await bcrypt.genSalt(SALT_ROUNDS);
61079
- return bcrypt.hash(data, salt);
61080
- }
61081
- async function compare(data, encrypted) {
61082
- return bcrypt.compare(data, encrypted);
61083
- }
61084
-
61085
- // src/utils/utils.ts
61086
- var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
61087
- var APP_PREFIX3 = "app" /* APP */ + SEPARATOR;
61088
- var PROD_APP_PREFIX = "/app/";
61089
- var BUILDER_PREVIEW_PATH = "/app/preview";
61090
- var BUILDER_PREFIX = "/builder";
61091
- var BUILDER_APP_PREFIX = `${BUILDER_PREFIX}/app/`;
61092
- var PUBLIC_API_PREFIX = "/api/public/v";
61093
- function confirmAppId(possibleAppId) {
61094
- return possibleAppId && possibleAppId.startsWith(APP_PREFIX3) ? possibleAppId : void 0;
61095
- }
61096
- async function resolveAppUrl(ctx) {
61097
- const appUrl = ctx.path.split("/")[2];
61098
- let possibleAppUrl = `/${appUrl.toLowerCase()}`;
61099
- let tenantId = getTenantId();
61100
- if (!environment_default.isDev() && environment_default.MULTI_TENANCY) {
61101
- tenantId = getTenantIDFromCtx(ctx, {
61102
- includeStrategies: ["subdomain" /* SUBDOMAIN */]
61103
- });
61104
- }
61105
- const apps = await doInTenant(
61106
- tenantId,
61107
- () => getAllApps({ dev: false })
61108
- );
61109
- const app = apps.filter(
61110
- (a) => a.url && a.url.toLowerCase() === possibleAppUrl
61111
- )[0];
61112
- return app && app.appId ? app.appId : void 0;
61113
- }
61114
- function isServingApp(ctx) {
61115
- if (ctx.path.startsWith(`/${APP_PREFIX3}`)) {
61116
- return true;
61117
- }
61118
- return ctx.path.startsWith(PROD_APP_PREFIX);
61119
- }
61120
- function isServingBuilder(ctx) {
61121
- return ctx.path.startsWith(BUILDER_APP_PREFIX);
61122
- }
61123
- function isServingBuilderPreview(ctx) {
61124
- return ctx.path.startsWith(BUILDER_PREVIEW_PATH);
61125
- }
61126
- function isPublicApiRequest(ctx) {
61127
- return ctx.path.startsWith(PUBLIC_API_PREFIX);
61128
- }
61129
- async function getAppIdFromCtx(ctx) {
61130
- const options2 = [ctx.request.headers["x-budibase-app-id" /* APP_ID */]];
61131
- let appId;
61132
- for (let option of options2) {
61133
- appId = confirmAppId(option);
61134
- if (appId) {
61135
- break;
61136
- }
61137
- }
61138
- if (!appId && ctx.request.body && ctx.request.body.appId) {
61139
- appId = confirmAppId(ctx.request.body.appId);
61140
- }
61141
- const pathId = parseAppIdFromUrlPath(ctx.path);
61142
- if (!appId && pathId) {
61143
- appId = confirmAppId(pathId);
61144
- }
61145
- const isBuilderPreview = ctx.path.startsWith(BUILDER_PREVIEW_PATH);
61146
- const isViewingProdApp = ctx.path.startsWith(PROD_APP_PREFIX) && !isBuilderPreview;
61147
- if (!appId && isViewingProdApp) {
61148
- appId = confirmAppId(await resolveAppUrl(ctx));
61149
- }
61150
- const referer = ctx.request.headers.referer;
61151
- if (!appId && referer?.includes(BUILDER_APP_PREFIX)) {
61152
- const refererId = parseAppIdFromUrlPath(ctx.request.headers.referer);
61153
- appId = confirmAppId(refererId);
61154
- }
61155
- return appId;
61156
- }
61157
- function parseAppIdFromUrlPath(url) {
61158
- if (!url) {
61159
- return;
61160
- }
61161
- return url.split("?")[0].split("/").find((subPath) => subPath.startsWith(APP_PREFIX3));
61162
- }
61163
- function openJwt(token) {
61164
- if (!token) {
61165
- return void 0;
61166
- }
61167
- try {
61168
- return import_jsonwebtoken.default.verify(token, environment_default.JWT_SECRET);
61169
- } catch (e) {
61170
- if (environment_default.JWT_SECRET_FALLBACK) {
61171
- return import_jsonwebtoken.default.verify(token, environment_default.JWT_SECRET_FALLBACK);
61172
- } else {
61173
- throw e;
61174
- }
61175
- }
61176
- }
61177
- function isValidInternalAPIKey(apiKey) {
61178
- if (environment_default.INTERNAL_API_KEY && environment_default.INTERNAL_API_KEY === apiKey) {
61179
- return true;
61180
- }
61181
- return !!(environment_default.INTERNAL_API_KEY_FALLBACK && environment_default.INTERNAL_API_KEY_FALLBACK === apiKey);
61182
- }
61183
- function getCookie(ctx, name) {
61184
- const cookie = ctx.cookies.get(name);
61185
- if (!cookie) {
61186
- return void 0;
61187
- }
61188
- return openJwt(cookie);
61189
- }
61190
- function setCookie(ctx, value, name = "builder", opts = { sign: true }) {
61191
- if (value && opts && opts.sign) {
61192
- value = import_jsonwebtoken.default.sign(value, environment_default.JWT_SECRET);
61193
- }
61194
- const config = {
61195
- expires: MAX_VALID_DATE,
61196
- path: "/",
61197
- httpOnly: false,
61198
- overwrite: true
61199
- };
61200
- if (environment_default.COOKIE_DOMAIN) {
61201
- config.domain = environment_default.COOKIE_DOMAIN;
61202
- }
61203
- ctx.cookies.set(name, value, config);
61204
- }
61205
- function clearCookie(ctx, name) {
61206
- setCookie(ctx, null, name);
61207
- }
61208
- function isClient(ctx) {
61209
- return ctx.headers["x-budibase-type" /* TYPE */] === "client";
61210
- }
61211
- function timeout(timeMs) {
61212
- return new Promise((resolve) => setTimeout(resolve, timeMs));
61213
- }
61214
- function isAudited(event) {
61215
- return !!AuditedEventFriendlyName[event];
61216
- }
61217
- function hasCircularStructure(json) {
61218
- if (typeof json !== "object") {
61219
- return false;
61220
- }
61221
- try {
61222
- JSON.stringify(json);
61223
- } catch (err) {
61224
- if (err instanceof Error && err?.message.includes("circular structure")) {
61225
- return true;
61226
- }
61227
- }
61228
- return false;
61229
- }
61230
-
61231
- // src/utils/stringUtils.ts
61232
- function validEmail(value) {
61233
- return value && !!value.match(
61234
- /^(([^<>()[\]\\.,;:\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,}))$/
61235
- );
61236
- }
61237
-
61238
- // src/utils/Duration.ts
61239
- var DurationType = /* @__PURE__ */ ((DurationType2) => {
61240
- DurationType2["MILLISECONDS"] = "milliseconds";
61241
- DurationType2["SECONDS"] = "seconds";
61242
- DurationType2["MINUTES"] = "minutes";
61243
- DurationType2["HOURS"] = "hours";
61244
- DurationType2["DAYS"] = "days";
61245
- return DurationType2;
61246
- })(DurationType || {});
61247
- var conversion = {
61248
- milliseconds: 1,
61249
- seconds: 1e3,
61250
- minutes: 60 * 1e3,
61251
- hours: 60 * 60 * 1e3,
61252
- days: 24 * 60 * 60 * 1e3
61253
- };
61254
- var Duration = class _Duration {
61255
- static convert(from, to, duration) {
61256
- const milliseconds = duration * conversion[from];
61257
- return milliseconds / conversion[to];
61258
- }
61259
- static from(from, duration) {
61260
- return {
61261
- to: (to) => {
61262
- return _Duration.convert(from, to, duration);
61263
- },
61264
- toMs: () => {
61265
- return _Duration.convert(from, "milliseconds" /* MILLISECONDS */, duration);
61266
- },
61267
- toSeconds: () => {
61268
- return _Duration.convert(from, "seconds" /* SECONDS */, duration);
61269
- }
61270
- };
61271
- }
61272
- static fromSeconds(duration) {
61273
- return _Duration.from("seconds" /* SECONDS */, duration);
61274
- }
61275
- static fromMinutes(duration) {
61276
- return _Duration.from("minutes" /* MINUTES */, duration);
61277
- }
61278
- static fromHours(duration) {
61279
- return _Duration.from("hours" /* HOURS */, duration);
61280
- }
61281
- static fromDays(duration) {
61282
- return _Duration.from("days" /* DAYS */, duration);
61283
- }
61284
- static fromMilliseconds(duration) {
61285
- return _Duration.from("milliseconds" /* MILLISECONDS */, duration);
61286
- }
61287
- };
61288
-
61289
- // src/redis/redlockImpl.ts
61290
61316
  async function getClient(type, opts) {
61291
61317
  if (type === "custom" /* CUSTOM */) {
61292
61318
  return newRedlock(opts);
@@ -66198,6 +66224,9 @@ async function getSCIMConfig() {
66198
66224
  const config = await getConfig("scim" /* SCIM */);
66199
66225
  return config?.config;
66200
66226
  }
66227
+ async function getAIConfig() {
66228
+ return getConfig("ai" /* AI */);
66229
+ }
66201
66230
 
66202
66231
  // src/migrations/index.ts
66203
66232
  var migrations_exports = {};
@@ -67984,9 +68013,17 @@ var SqlTableQueryBuilder = class {
67984
68013
  constructor(client) {
67985
68014
  this.sqlClient = client;
67986
68015
  }
67987
- getSqlClient() {
68016
+ getBaseSqlClient() {
67988
68017
  return this.sqlClient;
67989
68018
  }
68019
+ getSqlClient() {
68020
+ return this.extendedSqlClient || this.sqlClient;
68021
+ }
68022
+ // if working in a database like MySQL with many variants (MariaDB)
68023
+ // we can set another client which overrides the base one
68024
+ setExtendedSqlClient(client) {
68025
+ this.extendedSqlClient = client;
68026
+ }
67990
68027
  /**
67991
68028
  * @param json the input JSON structure from which an SQL query will be built.
67992
68029
  * @return the operation that was found in the JSON.
@@ -68138,6 +68175,7 @@ var InternalBuilder = class {
68138
68175
  return `"${str}"`;
68139
68176
  case "mssql" /* MS_SQL */:
68140
68177
  return `[${str}]`;
68178
+ case "mariadb" /* MARIADB */:
68141
68179
  case "mysql2" /* MY_SQL */:
68142
68180
  return `\`${str}\``;
68143
68181
  }
@@ -68445,7 +68483,7 @@ var InternalBuilder = class {
68445
68483
  )}${wrap}, FALSE)`
68446
68484
  );
68447
68485
  });
68448
- } else if (this.client === "mysql2" /* MY_SQL */) {
68486
+ } else if (this.client === "mysql2" /* MY_SQL */ || this.client === "mariadb" /* MARIADB */) {
68449
68487
  const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS";
68450
68488
  iterate(mode, (q2, key, value) => {
68451
68489
  return q2[rawFnc](
@@ -68748,7 +68786,7 @@ var InternalBuilder = class {
68748
68786
  continue;
68749
68787
  }
68750
68788
  const relatedTable = meta.tables?.[toTable];
68751
- const toAlias = aliases?.[toTable] || toTable, fromAlias = aliases?.[fromTable] || fromTable;
68789
+ const toAlias = aliases?.[toTable] || toTable, fromAlias = aliases?.[fromTable] || fromTable, throughAlias = throughTable && aliases?.[throughTable] || throughTable;
68752
68790
  let toTableWithSchema = this.tableNameWithSchema(toTable, {
68753
68791
  alias: toAlias,
68754
68792
  schema: endpoint.schema
@@ -68767,29 +68805,25 @@ var InternalBuilder = class {
68767
68805
  );
68768
68806
  const fieldList = relationshipFields.map((field) => this.buildJsonField(field)).join(",");
68769
68807
  const primaryKey = `${toAlias}.${toPrimary || toKey}`;
68770
- let subQuery = knex3.from(toTableWithSchema).limit(getRelationshipLimit()).orderBy(primaryKey);
68771
- if (throughTable && toPrimary && fromPrimary) {
68772
- const throughAlias = aliases?.[throughTable] || throughTable;
68808
+ let subQuery = knex3.from(toTableWithSchema).orderBy(primaryKey);
68809
+ const isManyToMany = throughTable && toPrimary && fromPrimary;
68810
+ let correlatedTo = isManyToMany ? `${throughAlias}.${fromKey}` : `${toAlias}.${toKey}`, correlatedFrom = isManyToMany ? `${fromAlias}.${fromPrimary}` : `${fromAlias}.${fromKey}`;
68811
+ if (isManyToMany) {
68773
68812
  let throughTableWithSchema = this.tableNameWithSchema(throughTable, {
68774
68813
  alias: throughAlias,
68775
68814
  schema: endpoint.schema
68776
68815
  });
68777
68816
  subQuery = subQuery.join(throughTableWithSchema, function() {
68778
68817
  this.on(`${toAlias}.${toPrimary}`, "=", `${throughAlias}.${toKey}`);
68779
- }).where(
68780
- `${throughAlias}.${fromKey}`,
68781
- "=",
68782
- knex3.raw(this.quotedIdentifier(`${fromAlias}.${fromPrimary}`))
68783
- );
68784
- } else {
68785
- subQuery = subQuery.where(
68786
- `${toAlias}.${toKey}`,
68787
- "=",
68788
- knex3.raw(this.quotedIdentifier(`${fromAlias}.${fromKey}`))
68789
- );
68818
+ });
68790
68819
  }
68820
+ subQuery = subQuery.where(
68821
+ correlatedTo,
68822
+ "=",
68823
+ knex3.raw(this.quotedIdentifier(correlatedFrom))
68824
+ );
68791
68825
  const standardWrap = (select) => {
68792
- subQuery = subQuery.select(`${toAlias}.*`);
68826
+ subQuery = subQuery.select(`${toAlias}.*`).limit(getRelationshipLimit());
68793
68827
  return knex3.select(knex3.raw(select)).from({
68794
68828
  [toAlias]: subQuery
68795
68829
  });
@@ -68807,11 +68841,14 @@ var InternalBuilder = class {
68807
68841
  `json_agg(json_build_object(${fieldList}))`
68808
68842
  );
68809
68843
  break;
68810
- case "mysql2" /* MY_SQL */:
68844
+ case "mariadb" /* MARIADB */:
68811
68845
  wrapperQuery = subQuery.select(
68812
- knex3.raw(`json_arrayagg(json_object(${fieldList}))`)
68846
+ knex3.raw(
68847
+ `json_arrayagg(json_object(${fieldList}) LIMIT ${getRelationshipLimit()})`
68848
+ )
68813
68849
  );
68814
68850
  break;
68851
+ case "mysql2" /* MY_SQL */:
68815
68852
  case "oracledb" /* ORACLE */:
68816
68853
  wrapperQuery = standardWrap(
68817
68854
  `json_arrayagg(json_object(${fieldList}))`
@@ -68820,7 +68857,7 @@ var InternalBuilder = class {
68820
68857
  case "mssql" /* MS_SQL */:
68821
68858
  wrapperQuery = knex3.raw(
68822
68859
  `(SELECT ${this.quote(toAlias)} = (${knex3.select(`${fromAlias}.*`).from({
68823
- [fromAlias]: subQuery.select(`${toAlias}.*`)
68860
+ [fromAlias]: subQuery.select(`${toAlias}.*`).limit(getRelationshipLimit())
68824
68861
  })} FOR JSON PATH))`
68825
68862
  );
68826
68863
  break;
@@ -68931,7 +68968,7 @@ var InternalBuilder = class {
68931
68968
  return query;
68932
68969
  }
68933
68970
  const parsedBody = body2.map((row) => this.parseBody(row));
68934
- if (this.client === "pg" /* POSTGRES */ || this.client === "sqlite3" /* SQL_LITE */ || this.client === "mysql2" /* MY_SQL */) {
68971
+ if (this.client === "pg" /* POSTGRES */ || this.client === "sqlite3" /* SQL_LITE */ || this.client === "mysql2" /* MY_SQL */ || this.client === "mariadb" /* MARIADB */) {
68935
68972
  const primary = this.table.primary;
68936
68973
  if (!primary) {
68937
68974
  throw new Error("Primary key is required for upsert");
@@ -69035,7 +69072,7 @@ var SqlQueryBuilder = class extends sqlTable_default {
69035
69072
  _query(json, opts = {}) {
69036
69073
  const sqlClient = this.getSqlClient();
69037
69074
  const config = {
69038
- client: sqlClient
69075
+ client: this.getBaseSqlClient()
69039
69076
  };
69040
69077
  if (sqlClient === "sqlite3" /* SQL_LITE */ || sqlClient === "oracledb" /* ORACLE */) {
69041
69078
  config.useNullAsDefault = true;
@@ -69137,7 +69174,7 @@ var SqlQueryBuilder = class extends sqlTable_default {
69137
69174
  let id;
69138
69175
  if (sqlClient === "mssql" /* MS_SQL */) {
69139
69176
  id = results?.[0].id;
69140
- } else if (sqlClient === "mysql2" /* MY_SQL */) {
69177
+ } else if (sqlClient === "mysql2" /* MY_SQL */ || sqlClient === "mariadb" /* MARIADB */) {
69141
69178
  id = results?.insertId;
69142
69179
  }
69143
69180
  row = processFn(