@budibase/backend-core 2.32.12 → 2.32.14

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 (34) hide show
  1. package/dist/index.js +129 -52
  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/db/couch/DatabaseImpl.d.ts +1 -0
  7. package/dist/src/db/couch/DatabaseImpl.js +15 -5
  8. package/dist/src/db/couch/DatabaseImpl.js.map +1 -1
  9. package/dist/src/features/features.d.ts +47 -0
  10. package/dist/src/features/features.js +269 -0
  11. package/dist/src/features/features.js.map +1 -0
  12. package/dist/src/features/index.d.ts +2 -39
  13. package/dist/src/features/index.js +6 -235
  14. package/dist/src/features/index.js.map +1 -1
  15. package/dist/src/features/tests/utils.d.ts +3 -0
  16. package/dist/src/features/tests/utils.js +56 -0
  17. package/dist/src/features/tests/utils.js.map +1 -0
  18. package/dist/src/middleware/passport/sso/sso.js +0 -25
  19. package/dist/src/middleware/passport/sso/sso.js.map +1 -1
  20. package/dist/src/users/users.js +8 -1
  21. package/dist/src/users/users.js.map +1 -1
  22. package/dist/tests/core/utilities/structures/accounts.js +2 -2
  23. package/dist/tests/core/utilities/structures/accounts.js.map +1 -1
  24. package/dist/tests/core/utilities/structures/users.js +2 -5
  25. package/dist/tests/core/utilities/structures/users.js.map +1 -1
  26. package/package.json +4 -4
  27. package/src/db/couch/DatabaseImpl.ts +20 -6
  28. package/src/features/features.ts +300 -0
  29. package/src/features/index.ts +2 -281
  30. package/src/features/tests/utils.ts +64 -0
  31. package/src/middleware/passport/sso/sso.ts +0 -24
  32. package/src/users/users.ts +10 -2
  33. package/tests/core/utilities/structures/accounts.ts +0 -4
  34. package/tests/core/utilities/structures/users.ts +0 -5
package/dist/index.js CHANGED
@@ -61260,7 +61260,7 @@ __export(src_exports, {
61260
61260
  sql: () => sql_exports,
61261
61261
  tenancy: () => tenancy,
61262
61262
  timers: () => timers_exports,
61263
- userUtils: () => utils_exports5,
61263
+ userUtils: () => utils_exports6,
61264
61264
  users: () => users_exports3,
61265
61265
  utils: () => utils_exports4,
61266
61266
  withEnv: () => withEnv
@@ -61995,10 +61995,18 @@ function isSupportedUserSearch(query) {
61995
61995
  { op: "equal" /* EQUAL */, key: "_id" },
61996
61996
  { op: "oneOf" /* ONE_OF */, key: "_id" }
61997
61997
  ];
61998
- for (let [key, operation] of Object.entries(query)) {
61998
+ for (const [key, operation] of Object.entries(query)) {
61999
61999
  if (typeof operation !== "object") {
62000
62000
  return false;
62001
62001
  }
62002
+ if (isLogicalSearchOperator(key)) {
62003
+ for (const condition of query[key].conditions) {
62004
+ if (!isSupportedUserSearch(condition)) {
62005
+ return false;
62006
+ }
62007
+ }
62008
+ return true;
62009
+ }
62002
62010
  const fields = Object.keys(operation || {});
62003
62011
  if (fields.length === 0) {
62004
62012
  continue;
@@ -62281,6 +62289,7 @@ var views_exports = {};
62281
62289
  __export(views_exports, {
62282
62290
  basicFields: () => basicFields,
62283
62291
  calculationFields: () => calculationFields,
62292
+ hasCalculationFields: () => hasCalculationFields,
62284
62293
  isBasicViewField: () => isBasicViewField,
62285
62294
  isCalculationField: () => isCalculationField,
62286
62295
  isCalculationView: () => isCalculationView
@@ -62293,6 +62302,9 @@ function isBasicViewField(field) {
62293
62302
  return !isCalculationField(field);
62294
62303
  }
62295
62304
  function isCalculationView(view) {
62305
+ return view.type === "calculation" /* CALCULATION */;
62306
+ }
62307
+ function hasCalculationFields(view) {
62296
62308
  return Object.values(view.schema || {}).some(isCalculationField);
62297
62309
  }
62298
62310
  function calculationFields(view) {
@@ -62726,7 +62738,7 @@ function search(docs, query) {
62726
62738
  if (query.sort) {
62727
62739
  result = sort(result, query.sort, query.sortOrder || "ascending" /* ASCENDING */);
62728
62740
  }
62729
- let totalRows = result.length;
62741
+ const totalRows = result.length;
62730
62742
  if (query.limit) {
62731
62743
  result = limit(result, query.limit.toString());
62732
62744
  }
@@ -67116,8 +67128,12 @@ __export(features_exports, {
67116
67128
  FlagSet: () => FlagSet,
67117
67129
  flags: () => flags,
67118
67130
  init: () => init4,
67119
- shutdown: () => shutdown2
67131
+ parseEnvFlags: () => parseEnvFlags,
67132
+ shutdown: () => shutdown2,
67133
+ testutils: () => utils_exports5
67120
67134
  });
67135
+
67136
+ // src/features/features.ts
67121
67137
  var import_posthog_node = require("posthog-node");
67122
67138
  var import_dd_trace3 = __toESM(require("dd-trace"));
67123
67139
 
@@ -67498,7 +67514,7 @@ var Duration = class _Duration {
67498
67514
  }
67499
67515
  };
67500
67516
 
67501
- // src/features/index.ts
67517
+ // src/features/features.ts
67502
67518
  var posthog;
67503
67519
  function init4(opts) {
67504
67520
  if (environment_default.POSTHOG_TOKEN && environment_default.POSTHOG_API_HOST && !environment_default.SELF_HOSTED && environment_default.POSTHOG_FEATURE_FLAGS_ENABLED) {
@@ -67563,6 +67579,21 @@ var NumberFlag = class extends Flag {
67563
67579
  throw new Error(`could not parse value "${value}" as number`);
67564
67580
  }
67565
67581
  };
67582
+ function parseEnvFlags(flags2) {
67583
+ const split = flags2.split(",").map((x) => x.split(":"));
67584
+ const result = [];
67585
+ for (const [tenantId, ...features] of split) {
67586
+ for (let feature of features) {
67587
+ let value = true;
67588
+ if (feature.startsWith("!")) {
67589
+ feature = feature.slice(1);
67590
+ value = false;
67591
+ }
67592
+ result.push({ tenantId, key: feature, value });
67593
+ }
67594
+ }
67595
+ return result;
67596
+ }
67566
67597
  var FlagSet = class {
67567
67598
  constructor(flagSchema) {
67568
67599
  this.flagSchema = flagSchema;
@@ -67597,28 +67628,24 @@ var FlagSet = class {
67597
67628
  const flagValues = this.defaults();
67598
67629
  const currentTenantId = getTenantId();
67599
67630
  const specificallySetFalse = /* @__PURE__ */ new Set();
67600
- const split = (environment_default.TENANT_FEATURE_FLAGS || "").split(",").map((x) => x.split(":"));
67601
- for (const [tenantId, ...features] of split) {
67631
+ for (const { tenantId, key, value } of parseEnvFlags(
67632
+ environment_default.TENANT_FEATURE_FLAGS || ""
67633
+ )) {
67602
67634
  if (!tenantId || tenantId !== "*" && tenantId !== currentTenantId) {
67603
67635
  continue;
67604
67636
  }
67605
67637
  tags[`readFromEnvironmentVars`] = true;
67606
- for (let feature of features) {
67607
- let value = true;
67608
- if (feature.startsWith("!")) {
67609
- feature = feature.slice(1);
67610
- value = false;
67611
- specificallySetFalse.add(feature);
67612
- }
67613
- if (!this.isFlagName(feature)) {
67614
- continue;
67615
- }
67616
- if (typeof flagValues[feature] !== "boolean") {
67617
- throw new Error(`Feature: ${feature} is not a boolean`);
67618
- }
67619
- flagValues[feature] = value;
67620
- tags[`flags.${feature}.source`] = "environment";
67638
+ if (value === false) {
67639
+ specificallySetFalse.add(key);
67640
+ }
67641
+ if (!this.isFlagName(key)) {
67642
+ continue;
67643
+ }
67644
+ if (typeof flagValues[key] !== "boolean") {
67645
+ throw new Error(`Feature: ${key} is not a boolean`);
67621
67646
  }
67647
+ flagValues[key] = value;
67648
+ tags[`flags.${key}.source`] = "environment";
67622
67649
  }
67623
67650
  const license = ctx?.user?.license;
67624
67651
  if (license) {
@@ -67685,6 +67712,60 @@ var flags = new FlagSet({
67685
67712
  ["ENRICHED_RELATIONSHIPS" /* ENRICHED_RELATIONSHIPS */]: Flag.boolean(environment_default.isDev())
67686
67713
  });
67687
67714
 
67715
+ // src/features/tests/utils.ts
67716
+ var utils_exports5 = {};
67717
+ __export(utils_exports5, {
67718
+ setFeatureFlags: () => setFeatureFlags2,
67719
+ withFeatureFlags: () => withFeatureFlags
67720
+ });
67721
+ function getCurrentFlags() {
67722
+ const result = {};
67723
+ for (const { tenantId, key, value } of parseEnvFlags(
67724
+ process.env.TENANT_FEATURE_FLAGS || ""
67725
+ )) {
67726
+ const tenantFlags = result[tenantId] || {};
67727
+ if (tenantFlags[key] === false) {
67728
+ continue;
67729
+ }
67730
+ tenantFlags[key] = value;
67731
+ result[tenantId] = tenantFlags;
67732
+ }
67733
+ return result;
67734
+ }
67735
+ function buildFlagString(flags2) {
67736
+ const parts = [];
67737
+ for (const [tenantId, tenantFlags] of Object.entries(flags2)) {
67738
+ for (const [key, value] of Object.entries(tenantFlags)) {
67739
+ if (value === false) {
67740
+ parts.push(`${tenantId}:!${key}`);
67741
+ } else {
67742
+ parts.push(`${tenantId}:${key}`);
67743
+ }
67744
+ }
67745
+ }
67746
+ return parts.join(",");
67747
+ }
67748
+ function setFeatureFlags2(tenantId, flags2) {
67749
+ const current = getCurrentFlags();
67750
+ for (const [key, value] of Object.entries(flags2)) {
67751
+ const tenantFlags = current[tenantId] || {};
67752
+ tenantFlags[key] = value;
67753
+ current[tenantId] = tenantFlags;
67754
+ }
67755
+ const flagString = buildFlagString(current);
67756
+ return setEnv({ TENANT_FEATURE_FLAGS: flagString });
67757
+ }
67758
+ function withFeatureFlags(tenantId, flags2, f) {
67759
+ const cleanup3 = setFeatureFlags2(tenantId, flags2);
67760
+ const result = f();
67761
+ if (result instanceof Promise) {
67762
+ return result.finally(cleanup3);
67763
+ } else {
67764
+ cleanup3();
67765
+ return result;
67766
+ }
67767
+ }
67768
+
67688
67769
  // src/db/couch/DatabaseImpl.ts
67689
67770
  var DATABASE_NOT_FOUND = "Database does not exist.";
67690
67771
  function buildNano(couchInfo) {
@@ -67815,12 +67896,13 @@ var DatabaseImpl = class _DatabaseImpl {
67815
67896
  }
67816
67897
  async getMultiple(ids, opts) {
67817
67898
  ids = [...new Set(ids)];
67899
+ const includeDocs = !opts?.excludeDocs;
67818
67900
  const response = await this.allDocs({
67819
67901
  keys: ids,
67820
- include_docs: true
67902
+ include_docs: includeDocs
67821
67903
  });
67822
67904
  const rowUnavailable = (row) => {
67823
- if (row.doc == null || "deleted" in row.value && row.value.deleted) {
67905
+ if (includeDocs && row.doc == null || row.value && "deleted" in row.value && row.value.deleted) {
67824
67906
  return true;
67825
67907
  }
67826
67908
  return row.error === "not_found";
@@ -67832,7 +67914,7 @@ var DatabaseImpl = class _DatabaseImpl {
67832
67914
  const missingIds = missing.map((row) => row.key).join(", ");
67833
67915
  throw new Error(`Unable to get documents: ${missingIds}`);
67834
67916
  }
67835
- return rows.map((row) => row.doc);
67917
+ return rows.map((row) => includeDocs ? row.doc : row.value);
67836
67918
  }
67837
67919
  async remove(idOrDoc, rev) {
67838
67920
  return this.performCall((db) => {
@@ -67948,11 +68030,21 @@ var DatabaseImpl = class _DatabaseImpl {
67948
68030
  return this.performCall(() => {
67949
68031
  return async () => {
67950
68032
  const response = await directCouchUrlCall(args);
67951
- const json = await response.json();
68033
+ const text = await response.text();
67952
68034
  if (response.status > 300) {
68035
+ let json;
68036
+ try {
68037
+ json = JSON.parse(text);
68038
+ } catch (err) {
68039
+ console.error(`SQS error: ${text}`);
68040
+ throw new CouchDBError(
68041
+ "error while running SQS query, please try again later",
68042
+ { name: "sqs_error", status: response.status }
68043
+ );
68044
+ }
67953
68045
  throw json;
67954
68046
  }
67955
- return json;
68047
+ return JSON.parse(text);
67956
68048
  };
67957
68049
  });
67958
68050
  }
@@ -68965,8 +69057,8 @@ __export(users_exports3, {
68965
69057
  });
68966
69058
 
68967
69059
  // src/users/utils.ts
68968
- var utils_exports5 = {};
68969
- __export(utils_exports5, {
69060
+ var utils_exports6 = {};
69061
+ __export(utils_exports6, {
68970
69062
  getAccountHolderFromUserIds: () => getAccountHolderFromUserIds,
68971
69063
  hasAdminPermissions: () => hasAdminPermissions2,
68972
69064
  hasAppBuilderPermissions: () => hasAppBuilderPermissions2,
@@ -72991,6 +73083,12 @@ async function paginatedUsers({
72991
73083
  userList = await bulkGetGlobalUsersById(query?.oneOf?._id, {
72992
73084
  cleanup: true
72993
73085
  });
73086
+ } else if (query) {
73087
+ const response = await db.allDocs(
73088
+ getGlobalUserParams(null, { ...opts, limit: void 0 })
73089
+ );
73090
+ userList = response.rows.map((row) => row.doc);
73091
+ userList = filters_exports.search(userList, { query, limit: opts.limit }).rows;
72994
73092
  } else {
72995
73093
  const response = await db.allDocs(getGlobalUserParams(null, opts));
72996
73094
  userList = response.rows.map((row) => row.doc);
@@ -73808,7 +73906,6 @@ __export(google_exports, {
73808
73906
  });
73809
73907
 
73810
73908
  // src/middleware/passport/sso/sso.ts
73811
- var import_node_fetch5 = __toESM(require("node-fetch"));
73812
73909
  var ssoSaveUserNoOp = (user) => Promise.resolve(user);
73813
73910
  async function authenticate2(details, requireLocalAccount = true, done, saveUserFn) {
73814
73911
  if (!saveUserFn) {
@@ -73863,24 +73960,10 @@ async function authenticate2(details, requireLocalAccount = true, done, saveUser
73863
73960
  }
73864
73961
  return done(null, ssoUser);
73865
73962
  }
73866
- async function getProfilePictureUrl(user, details) {
73867
- const pictureUrl = details.profile?._json.picture;
73868
- if (pictureUrl) {
73869
- const response = await (0, import_node_fetch5.default)(pictureUrl);
73870
- if (response.status === 200) {
73871
- const type = response.headers.get("content-type");
73872
- if (type.startsWith("image/")) {
73873
- return pictureUrl;
73874
- }
73875
- }
73876
- }
73877
- }
73878
73963
  async function syncUser(user, details) {
73879
73964
  let firstName;
73880
73965
  let lastName;
73881
- let pictureUrl;
73882
73966
  let oauth2;
73883
- let thirdPartyProfile;
73884
73967
  if (details.profile) {
73885
73968
  const profile = details.profile;
73886
73969
  if (profile.name) {
@@ -73892,10 +73975,6 @@ async function syncUser(user, details) {
73892
73975
  lastName = name.familyName;
73893
73976
  }
73894
73977
  }
73895
- pictureUrl = await getProfilePictureUrl(user, details);
73896
- thirdPartyProfile = {
73897
- ...profile._json
73898
- };
73899
73978
  }
73900
73979
  if (details.oauth2) {
73901
73980
  oauth2 = {
@@ -73908,8 +73987,6 @@ async function syncUser(user, details) {
73908
73987
  providerType: details.providerType,
73909
73988
  firstName,
73910
73989
  lastName,
73911
- thirdPartyProfile,
73912
- pictureUrl,
73913
73990
  oauth2
73914
73991
  };
73915
73992
  }
@@ -73971,7 +74048,7 @@ __export(oidc_exports, {
73971
74048
  getCallbackUrl: () => getCallbackUrl2,
73972
74049
  strategyFactory: () => strategyFactory2
73973
74050
  });
73974
- var import_node_fetch6 = __toESM(require("node-fetch"));
74051
+ var import_node_fetch5 = __toESM(require("node-fetch"));
73975
74052
  var OIDCStrategy = require_lib9().Strategy;
73976
74053
  function buildVerifyFn2(saveUserFn) {
73977
74054
  return async (issuer, sub, profile, jwtClaims, accessToken, refreshToken, idToken, params2, done) => {
@@ -74031,7 +74108,7 @@ async function fetchStrategyConfig(oidcConfig, callbackUrl) {
74031
74108
  "Configuration invalid. Must contain clientID, clientSecret, callbackUrl and configUrl"
74032
74109
  );
74033
74110
  }
74034
- const response = await (0, import_node_fetch6.default)(configUrl);
74111
+ const response = await (0, import_node_fetch5.default)(configUrl);
74035
74112
  if (!response.ok) {
74036
74113
  throw new Error(
74037
74114
  `Unexpected response when fetching openid-configuration: ${response.statusText}`