@budibase/shared-core 2.32.11 → 2.32.13

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/filters.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Datasource, BBReferenceFieldSubType, FieldType, FormulaType, SearchFilter, SearchFilters, SearchQueryFields, SortType, FieldConstraints, SortOrder, RowSearchParams, SearchResponse, Table } from "@budibase/types";
1
+ import { Datasource, BBReferenceFieldSubType, FieldType, FormulaType, LegacyFilter, SearchFilters, SearchQueryFields, SortType, FieldConstraints, SortOrder, RowSearchParams, SearchResponse, Table, SearchFilterGroup } from "@budibase/types";
2
2
  /**
3
3
  * Returns the valid operator options for a certain data type
4
4
  */
@@ -60,13 +60,21 @@ export declare class ColumnSplitter {
60
60
  column: string;
61
61
  };
62
62
  }
63
+ export declare const buildQueryLegacy: (filter?: LegacyFilter[] | SearchFilters) => SearchFilters | undefined;
63
64
  /**
64
- * Builds a JSON query from the filter structure generated in the builder
65
- * @param filter the builder filter structure
65
+ * Converts a **SearchFilterGroup** filter definition into a grouped
66
+ * search query of type **SearchFilters**
67
+ *
68
+ * Legacy support remains for the old **SearchFilter[]** format.
69
+ * These will be migrated to an appropriate **SearchFilters** object, if encountered
70
+ *
71
+ * @param filter
72
+ *
73
+ * @returns {SearchFilters}
66
74
  */
67
- export declare const buildQuery: (filter: SearchFilter[]) => SearchFilters;
75
+ export declare const buildQuery: (filter?: SearchFilterGroup | LegacyFilter[]) => SearchFilters | undefined;
68
76
  export declare function fixupFilterArrays(filters: SearchFilters): SearchFilters;
69
- export declare function search<T>(docs: Record<string, T>[], query: RowSearchParams): SearchResponse<Record<string, T>>;
77
+ export declare function search<T extends Record<string, any>>(docs: T[], query: Omit<RowSearchParams, "tableId">): SearchResponse<T>;
70
78
  /**
71
79
  * Performs a client-side search on an array of data
72
80
  * @param docs the data
@@ -3,6 +3,7 @@ export declare function isCalculationField(field: ViewFieldMetadata): field is V
3
3
  export declare function isBasicViewField(field: ViewFieldMetadata): field is BasicViewFieldMetadata;
4
4
  type UnsavedViewV2 = Omit<ViewV2, "id" | "version">;
5
5
  export declare function isCalculationView(view: UnsavedViewV2): boolean;
6
+ export declare function hasCalculationFields(view: UnsavedViewV2): boolean;
6
7
  export declare function calculationFields(view: UnsavedViewV2): import("lodash").Dictionary<ViewCalculationFieldMetadata>;
7
8
  export declare function basicFields(view: UnsavedViewV2): import("lodash").Dictionary<ViewFieldMetadata>;
8
9
  export {};
package/dist/index.js CHANGED
@@ -17470,6 +17470,7 @@ __export(filters_exports, {
17470
17470
  ColumnSplitter: () => ColumnSplitter,
17471
17471
  NoEmptyFilterStrings: () => NoEmptyFilterStrings,
17472
17472
  buildQuery: () => buildQuery,
17473
+ buildQueryLegacy: () => buildQueryLegacy,
17473
17474
  cleanupQuery: () => cleanupQuery,
17474
17475
  fixupFilterArrays: () => fixupFilterArrays,
17475
17476
  getKeyNumbering: () => getKeyNumbering,
@@ -17485,6 +17486,164 @@ __export(filters_exports, {
17485
17486
  });
17486
17487
  var import_dayjs = __toESM(require_dayjs_min());
17487
17488
 
17489
+ // src/utils.ts
17490
+ var utils_exports = {};
17491
+ __export(utils_exports, {
17492
+ filterValueToLabel: () => filterValueToLabel,
17493
+ hasSchema: () => hasSchema,
17494
+ isSupportedUserSearch: () => isSupportedUserSearch,
17495
+ parallelForeach: () => parallelForeach,
17496
+ processSearchFilters: () => processSearchFilters,
17497
+ trimOtherProps: () => trimOtherProps,
17498
+ unreachable: () => unreachable
17499
+ });
17500
+ function unreachable(value, message = `No such case in exhaustive switch: ${value}`) {
17501
+ throw new Error(message);
17502
+ }
17503
+ async function parallelForeach(items, task, maxConcurrency) {
17504
+ const promises = [];
17505
+ let index = 0;
17506
+ const processItem = async (item) => {
17507
+ try {
17508
+ await task(item);
17509
+ } finally {
17510
+ processNext();
17511
+ }
17512
+ };
17513
+ const processNext = () => {
17514
+ if (index >= items.length) {
17515
+ return;
17516
+ }
17517
+ const item = items[index];
17518
+ index++;
17519
+ const promise = processItem(item);
17520
+ promises.push(promise);
17521
+ if (promises.length >= maxConcurrency) {
17522
+ Promise.race(promises).then(processNext);
17523
+ } else {
17524
+ processNext();
17525
+ }
17526
+ };
17527
+ processNext();
17528
+ await Promise.all(promises);
17529
+ }
17530
+ function filterValueToLabel() {
17531
+ return Object.keys(OperatorOptions).reduce(
17532
+ (acc, key) => {
17533
+ const ops = OperatorOptions;
17534
+ const op = ops[key];
17535
+ acc[op["value"]] = op.label;
17536
+ return acc;
17537
+ },
17538
+ {}
17539
+ );
17540
+ }
17541
+ function hasSchema(test) {
17542
+ return typeof test === "object" && !Array.isArray(test) && test !== null && !(test instanceof Date) && Object.keys(test).length > 0;
17543
+ }
17544
+ function trimOtherProps(object, allowedProps) {
17545
+ const result = Object.keys(object).filter((key) => allowedProps.includes(key)).reduce(
17546
+ (acc, key) => ({ ...acc, [key]: object[key] }),
17547
+ {}
17548
+ );
17549
+ return result;
17550
+ }
17551
+ function isSupportedUserSearch(query) {
17552
+ const allowed = [
17553
+ { op: "string" /* STRING */, key: "email" },
17554
+ { op: "equal" /* EQUAL */, key: "_id" },
17555
+ { op: "oneOf" /* ONE_OF */, key: "_id" }
17556
+ ];
17557
+ for (const [key, operation] of Object.entries(query)) {
17558
+ if (typeof operation !== "object") {
17559
+ return false;
17560
+ }
17561
+ if (isLogicalSearchOperator(key)) {
17562
+ for (const condition of query[key].conditions) {
17563
+ if (!isSupportedUserSearch(condition)) {
17564
+ return false;
17565
+ }
17566
+ }
17567
+ return true;
17568
+ }
17569
+ const fields = Object.keys(operation || {});
17570
+ if (fields.length === 0) {
17571
+ continue;
17572
+ }
17573
+ const allowedOperation = allowed.find(
17574
+ (allow) => allow.op === key && fields.length === 1 && fields[0] === allow.key
17575
+ );
17576
+ if (!allowedOperation) {
17577
+ return false;
17578
+ }
17579
+ }
17580
+ return true;
17581
+ }
17582
+ var processSearchFilters = (filters) => {
17583
+ if (!filters) {
17584
+ return;
17585
+ }
17586
+ const defaultCfg = {
17587
+ logicalOperator: "all" /* ALL */,
17588
+ groups: []
17589
+ };
17590
+ const filterAllowedKeys = [
17591
+ "field",
17592
+ "operator",
17593
+ "value",
17594
+ "type",
17595
+ "externalType",
17596
+ "valueType",
17597
+ "noValue",
17598
+ "formulaType"
17599
+ ];
17600
+ if (Array.isArray(filters)) {
17601
+ let baseGroup = {
17602
+ filters: [],
17603
+ logicalOperator: "all" /* ALL */
17604
+ };
17605
+ return filters.reduce((acc, filter) => {
17606
+ const filterPropertyKeys = Object.keys(filter).sort((a, b) => {
17607
+ return a.localeCompare(b);
17608
+ }).filter((key) => key in filter);
17609
+ if (filterPropertyKeys.length == 1) {
17610
+ const key = filterPropertyKeys[0], value = filter[key];
17611
+ if (key === "onEmptyFilter") {
17612
+ acc.onEmptyFilter = value;
17613
+ } else if (key === "operator" && value === "allOr") {
17614
+ baseGroup.logicalOperator = "any" /* ANY */;
17615
+ }
17616
+ return acc;
17617
+ }
17618
+ const allowedFilterSettings = filterPropertyKeys.reduce(
17619
+ (acc2, key) => {
17620
+ const value = filter[key];
17621
+ if (filterAllowedKeys.includes(key)) {
17622
+ if (key === "field") {
17623
+ acc2.push([key, removeKeyNumbering(value)]);
17624
+ } else {
17625
+ acc2.push([key, value]);
17626
+ }
17627
+ }
17628
+ return acc2;
17629
+ },
17630
+ []
17631
+ );
17632
+ const migratedFilter = Object.fromEntries(
17633
+ allowedFilterSettings
17634
+ );
17635
+ baseGroup.filters.push(migratedFilter);
17636
+ if (!acc.groups || !acc.groups.length) {
17637
+ acc.groups = [baseGroup];
17638
+ }
17639
+ return acc;
17640
+ }, defaultCfg);
17641
+ } else if (!filters?.groups) {
17642
+ return;
17643
+ }
17644
+ return filters;
17645
+ };
17646
+
17488
17647
  // src/helpers/index.ts
17489
17648
  var helpers_exports = {};
17490
17649
  __export(helpers_exports, {
@@ -17689,6 +17848,7 @@ var views_exports = {};
17689
17848
  __export(views_exports, {
17690
17849
  basicFields: () => basicFields,
17691
17850
  calculationFields: () => calculationFields,
17851
+ hasCalculationFields: () => hasCalculationFields,
17692
17852
  isBasicViewField: () => isBasicViewField,
17693
17853
  isCalculationField: () => isCalculationField,
17694
17854
  isCalculationView: () => isCalculationView
@@ -17701,6 +17861,9 @@ function isBasicViewField(field) {
17701
17861
  return !isCalculationField(field);
17702
17862
  }
17703
17863
  function isCalculationView(view) {
17864
+ return view.type === "calculation" /* CALCULATION */;
17865
+ }
17866
+ function hasCalculationFields(view) {
17704
17867
  return Object.values(view.schema || {}).some(isCalculationField);
17705
17868
  }
17706
17869
  function calculationFields(view) {
@@ -17780,7 +17943,7 @@ var NoEmptyFilterStrings = [
17780
17943
  ];
17781
17944
  function recurseLogicalOperators(filters, fn) {
17782
17945
  for (const logical of LOGICAL_OPERATORS) {
17783
- if (filters?.[logical]) {
17946
+ if (filters[logical]) {
17784
17947
  filters[logical].conditions = filters[logical].conditions.map(
17785
17948
  (condition) => fn(condition)
17786
17949
  );
@@ -17800,9 +17963,6 @@ function recurseSearchFilters(filters, processFn) {
17800
17963
  return filters;
17801
17964
  }
17802
17965
  var cleanupQuery = (query) => {
17803
- if (!query) {
17804
- return query;
17805
- }
17806
17966
  for (let filterField of NoEmptyFilterStrings) {
17807
17967
  if (!query[filterField]) {
17808
17968
  continue;
@@ -17893,7 +18053,106 @@ var ColumnSplitter = class {
17893
18053
  };
17894
18054
  }
17895
18055
  };
17896
- var buildQuery = (filter) => {
18056
+ var buildCondition = (expression) => {
18057
+ let query = {
18058
+ string: {},
18059
+ fuzzy: {},
18060
+ range: {},
18061
+ equal: {},
18062
+ notEqual: {},
18063
+ empty: {},
18064
+ notEmpty: {},
18065
+ contains: {},
18066
+ notContains: {},
18067
+ oneOf: {},
18068
+ containsAny: {}
18069
+ };
18070
+ let { operator, field, type, value, externalType, onEmptyFilter } = expression;
18071
+ if (!operator || !field) {
18072
+ return;
18073
+ }
18074
+ const queryOperator = operator;
18075
+ const isHbs = typeof value === "string" && (value.match(HBS_REGEX) || []).length > 0;
18076
+ if (operator === "allOr") {
18077
+ query.allOr = true;
18078
+ return;
18079
+ }
18080
+ if (onEmptyFilter) {
18081
+ query.onEmptyFilter = onEmptyFilter;
18082
+ return;
18083
+ }
18084
+ if (queryOperator === "empty" || queryOperator === "notEmpty") {
18085
+ value = null;
18086
+ }
18087
+ if (type === "datetime" && !isHbs && queryOperator !== "empty" && queryOperator !== "notEmpty") {
18088
+ if (!value) {
18089
+ return;
18090
+ }
18091
+ try {
18092
+ value = new Date(value).toISOString();
18093
+ } catch (error) {
18094
+ return;
18095
+ }
18096
+ }
18097
+ if (type === "number" && typeof value === "string" && !isHbs) {
18098
+ if (queryOperator === "oneOf") {
18099
+ value = value.split(",").map((item) => parseFloat(item));
18100
+ } else {
18101
+ value = parseFloat(value);
18102
+ }
18103
+ }
18104
+ if (type === "boolean") {
18105
+ value = `${value}`?.toLowerCase() === "true";
18106
+ }
18107
+ if (["contains", "notContains", "containsAny"].includes(
18108
+ operator.toLocaleString()
18109
+ ) && type === "array" && typeof value === "string") {
18110
+ value = value.split(",");
18111
+ }
18112
+ if (operator.toLocaleString().startsWith("range") && query.range) {
18113
+ const minint = SqlNumberTypeRangeMap[externalType]?.min || Number.MIN_SAFE_INTEGER;
18114
+ const maxint = SqlNumberTypeRangeMap[externalType]?.max || Number.MAX_SAFE_INTEGER;
18115
+ if (!query.range[field]) {
18116
+ query.range[field] = {
18117
+ low: type === "number" ? minint : "0000-00-00T00:00:00.000Z",
18118
+ high: type === "number" ? maxint : "9999-00-00T00:00:00.000Z"
18119
+ };
18120
+ }
18121
+ if (operator === "rangeLow" && value != null && value !== "") {
18122
+ query.range[field] = {
18123
+ ...query.range[field],
18124
+ low: value
18125
+ };
18126
+ } else if (operator === "rangeHigh" && value != null && value !== "") {
18127
+ query.range[field] = {
18128
+ ...query.range[field],
18129
+ high: value
18130
+ };
18131
+ }
18132
+ } else if (isLogicalSearchOperator(queryOperator)) {
18133
+ } else if (query[queryOperator] && operator !== "onEmptyFilter") {
18134
+ if (type === "boolean") {
18135
+ if (queryOperator === "equal" && value === false) {
18136
+ query.notEqual = query.notEqual || {};
18137
+ query.notEqual[field] = true;
18138
+ } else if (queryOperator === "notEqual" && value === false) {
18139
+ query.equal = query.equal || {};
18140
+ query.equal[field] = true;
18141
+ } else {
18142
+ query[queryOperator] ??= {};
18143
+ query[queryOperator][field] = value;
18144
+ }
18145
+ } else {
18146
+ query[queryOperator] ??= {};
18147
+ query[queryOperator][field] = value;
18148
+ }
18149
+ }
18150
+ return query;
18151
+ };
18152
+ var buildQueryLegacy = (filter) => {
18153
+ if (!Array.isArray(filter)) {
18154
+ return filter;
18155
+ }
17897
18156
  let query = {
17898
18157
  string: {},
17899
18158
  fuzzy: {},
@@ -17942,10 +18201,12 @@ var buildQuery = (filter) => {
17942
18201
  if (type === "boolean") {
17943
18202
  value = `${value}`?.toLowerCase() === "true";
17944
18203
  }
17945
- if (["contains", "notContains", "containsAny"].includes(operator) && type === "array" && typeof value === "string") {
18204
+ if (["contains", "notContains", "containsAny"].includes(
18205
+ operator.toLocaleString()
18206
+ ) && type === "array" && typeof value === "string") {
17946
18207
  value = value.split(",");
17947
18208
  }
17948
- if (operator.startsWith("range") && query.range) {
18209
+ if (operator.toLocaleString().startsWith("range") && query.range) {
17949
18210
  const minint = SqlNumberTypeRangeMap[externalType]?.min || Number.MIN_SAFE_INTEGER;
17950
18211
  const maxint = SqlNumberTypeRangeMap[externalType]?.max || Number.MAX_SAFE_INTEGER;
17951
18212
  if (!query.range[field]) {
@@ -17986,6 +18247,30 @@ var buildQuery = (filter) => {
17986
18247
  });
17987
18248
  return query;
17988
18249
  };
18250
+ var buildQuery = (filter) => {
18251
+ const parsedFilter = processSearchFilters(filter);
18252
+ if (!parsedFilter) {
18253
+ return;
18254
+ }
18255
+ const operatorMap = {
18256
+ ["all" /* ALL */]: "$and" /* AND */,
18257
+ ["any" /* ANY */]: "$or" /* OR */
18258
+ };
18259
+ const globalOnEmpty = parsedFilter.onEmptyFilter ? parsedFilter.onEmptyFilter : null;
18260
+ const globalOperator = operatorMap[parsedFilter.logicalOperator];
18261
+ return {
18262
+ ...globalOnEmpty ? { onEmptyFilter: globalOnEmpty } : {},
18263
+ [globalOperator]: {
18264
+ conditions: parsedFilter.groups?.map((group) => {
18265
+ return {
18266
+ [operatorMap[group.logicalOperator]]: {
18267
+ conditions: group.filters?.map((x) => buildCondition(x)).filter((filter2) => filter2)
18268
+ }
18269
+ };
18270
+ })
18271
+ }
18272
+ };
18273
+ };
17989
18274
  function fixupFilterArrays(filters) {
17990
18275
  for (const searchField of Object.values(ArrayOperator)) {
17991
18276
  const field = filters[searchField];
@@ -18012,7 +18297,7 @@ function search(docs, query) {
18012
18297
  if (query.sort) {
18013
18298
  result = sort(result, query.sort, query.sortOrder || "ascending" /* ASCENDING */);
18014
18299
  }
18015
- let totalRows = result.length;
18300
+ const totalRows = result.length;
18016
18301
  if (query.limit) {
18017
18302
  result = limit(result, query.limit.toString());
18018
18303
  }
@@ -18326,91 +18611,6 @@ var hasFilters = (query) => {
18326
18611
  return check(query);
18327
18612
  };
18328
18613
 
18329
- // src/utils.ts
18330
- var utils_exports = {};
18331
- __export(utils_exports, {
18332
- filterValueToLabel: () => filterValueToLabel,
18333
- hasSchema: () => hasSchema,
18334
- isSupportedUserSearch: () => isSupportedUserSearch,
18335
- parallelForeach: () => parallelForeach,
18336
- trimOtherProps: () => trimOtherProps,
18337
- unreachable: () => unreachable
18338
- });
18339
- function unreachable(value, message = `No such case in exhaustive switch: ${value}`) {
18340
- throw new Error(message);
18341
- }
18342
- async function parallelForeach(items, task, maxConcurrency) {
18343
- const promises = [];
18344
- let index = 0;
18345
- const processItem = async (item) => {
18346
- try {
18347
- await task(item);
18348
- } finally {
18349
- processNext();
18350
- }
18351
- };
18352
- const processNext = () => {
18353
- if (index >= items.length) {
18354
- return;
18355
- }
18356
- const item = items[index];
18357
- index++;
18358
- const promise = processItem(item);
18359
- promises.push(promise);
18360
- if (promises.length >= maxConcurrency) {
18361
- Promise.race(promises).then(processNext);
18362
- } else {
18363
- processNext();
18364
- }
18365
- };
18366
- processNext();
18367
- await Promise.all(promises);
18368
- }
18369
- function filterValueToLabel() {
18370
- return Object.keys(OperatorOptions).reduce(
18371
- (acc, key) => {
18372
- const ops = OperatorOptions;
18373
- const op = ops[key];
18374
- acc[op["value"]] = op.label;
18375
- return acc;
18376
- },
18377
- {}
18378
- );
18379
- }
18380
- function hasSchema(test) {
18381
- return typeof test === "object" && !Array.isArray(test) && test !== null && !(test instanceof Date) && Object.keys(test).length > 0;
18382
- }
18383
- function trimOtherProps(object, allowedProps) {
18384
- const result = Object.keys(object).filter((key) => allowedProps.includes(key)).reduce(
18385
- (acc, key) => ({ ...acc, [key]: object[key] }),
18386
- {}
18387
- );
18388
- return result;
18389
- }
18390
- function isSupportedUserSearch(query) {
18391
- const allowed = [
18392
- { op: "string" /* STRING */, key: "email" },
18393
- { op: "equal" /* EQUAL */, key: "_id" },
18394
- { op: "oneOf" /* ONE_OF */, key: "_id" }
18395
- ];
18396
- for (let [key, operation] of Object.entries(query)) {
18397
- if (typeof operation !== "object") {
18398
- return false;
18399
- }
18400
- const fields = Object.keys(operation || {});
18401
- if (fields.length === 0) {
18402
- continue;
18403
- }
18404
- const allowedOperation = allowed.find(
18405
- (allow) => allow.op === key && fields.length === 1 && fields[0] === allow.key
18406
- );
18407
- if (!allowedOperation) {
18408
- return false;
18409
- }
18410
- }
18411
- return true;
18412
- }
18413
-
18414
18614
  // src/sdk/index.ts
18415
18615
  var sdk_exports = {};
18416
18616
  __export(sdk_exports, {