@fibery/expression-utils 9.1.5 → 9.2.1

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.
@@ -127,7 +127,11 @@ const parseRelativeDatePlaceholder = placeholder => {
127
127
  let unit = relativeDatePlaceholderMatch[2];
128
128
  return {
129
129
  isStartOfInterval,
130
- unitForStart: fullMatch.startsWith("$start-of-day") || fullMatch.startsWith("$end-of-day") ? "day" : isStartOfInterval !== undefined ? unit : undefined,
130
+ unitForStart:
131
+ // Usually the "start" unit is the same as the unit for relative date calculation.
132
+ // e.g. when we calculate the date "1 week ago", we want the beginning of the previous week.
133
+ // But sometimes they are different. We have hardcoded "day" for now, but this can be extended to any unit.
134
+ fullMatch.startsWith("$start-of-day") || fullMatch.startsWith("$end-of-day") ? "day" : isStartOfInterval !== undefined ? unit : undefined,
131
135
  amount: Number.parseInt(relativeDatePlaceholderMatch[1]),
132
136
  unit,
133
137
  isBeforeNow: relativeDatePlaceholderMatch[3] === "before-now",
@@ -249,6 +253,11 @@ const assertIsValidExpression = expression => {
249
253
  });
250
254
  trace.assert(expression.length > 0, "empty expression does not make any sense");
251
255
  };
256
+ const textTypes = ["fibery/text", "fibery/email", "fibery/url", "fibery/emoji"];
257
+ const dateTypes = ["fibery/date", "fibery/date-time"];
258
+ const dateRangeTypes = ["fibery/date-range", "fibery/date-time-range"];
259
+ const convertableDateTypes = ["fibery/date", "fibery/date-range"];
260
+ const numberTypes = ["fibery/int", "fibery/decimal"];
252
261
  const dateRangeFunctions = new Set(["q/start", "q/end"]);
253
262
  const firstLastFunctions = new Set(["q/first", "q/last"]);
254
263
  const collectionOps = new Set(["q/count", "q/count-distinct", "q/sum", "q/min", "q/max", "q/avg", "q/join", "q/first", "q/last"]);
@@ -260,9 +269,6 @@ const binaryOperations = new Set(["=", "!=", "<", ">", "<=", ">=", "in",
260
269
  "q/contains", "q/not-contains", "+", "-", "q/+", "q/-", "*", "/", "and", "or",
261
270
  //asc: obsolete. use q/and, q/or
262
271
  "q/and", "q/or", "q/in", "q/not-in"]);
263
-
264
- // TODO: get rid of this. Use visitors everywhere
265
- const naryOperations = new Set(["and", "or", "q/and", "q/or"]);
266
272
  const logicalOperators = new Set(["and", "or", "q/and", "q/or"]);
267
273
  const relationalOperators = new Set(["=", "!=", "<", ">", "<=", ">="]);
268
274
  const mathOperators = new Set(["+", "-", "*", "/", "q/+", "q/-", "q/concat"]);
@@ -275,7 +281,7 @@ const isCollectionFunctionExpression = expression =>
275
281
  (expression.length === 2 || expression.length === 3) && collectionOps.has(expression[0]);
276
282
  const isAccessFunctionExpression = expresion => expresion.length === 2 && expresion[0] === "q/access?" && isFieldExpression(expresion[1]);
277
283
  const isBinaryExpression = expression => expression.length === 3 && binaryOperations.has(expression[0]);
278
- const isNaryExpression = expression => expression.length > 1 && naryOperations.has(expression[0]);
284
+ const isNaryExpression = expression => expression.length > 1 && logicalOperators.has(expression[0]);
279
285
  const isVariableExpression = expression => ___default["default"].isString(expression) && expression.startsWith("$");
280
286
  const isMultiFieldAccess = expression => ___default["default"].isArray(expression) && expression.length === 2 && !isFunctionCallExpression(expression) && expression.every(x => ___default["default"].isString(x));
281
287
  const isMultiFieldExpression = expression => Array.isArray(expression) && !isFunctionCallExpression(expression) && expression.some(x => isMultiFieldAccess(x)) && expression.every(x => !isVariableExpression(x) && !binaryOperations.has(x) && (___default["default"].isString(x) || isMultiFieldAccess(x)));
@@ -313,6 +319,38 @@ const collectFieldExpressions = (memo, expression) => {
313
319
  throw new NotImplementedError(expression, "expression");
314
320
  }
315
321
  };
322
+ const getFieldObjectsByFieldExpression = ({
323
+ typeObject,
324
+ expression
325
+ }) => {
326
+ assertIsValidExpression(expression);
327
+ const fieldObjects = [];
328
+ let currentTypeObject = typeObject;
329
+ for (const fieldOrMultiFieldAccess of expression) {
330
+ if (isMultiFieldAccess(fieldOrMultiFieldAccess)) {
331
+ const [multiField, type] = fieldOrMultiFieldAccess;
332
+ const fieldObject = currentTypeObject.fieldObjectsByName[multiField];
333
+ fieldObjects.push(fieldObject);
334
+ const foundField = fieldObject.multiRelatedFieldObjects.find(f => f.holderType === type);
335
+ trace.assert(foundField, "incorrect expression for multi-field");
336
+ currentTypeObject = foundField.holderTypeObject;
337
+ } else {
338
+ const fieldObject = currentTypeObject.fieldObjectsByName[fieldOrMultiFieldAccess];
339
+ fieldObjects.push(fieldObject);
340
+ currentTypeObject = fieldObject.typeObject;
341
+ }
342
+ }
343
+ return fieldObjects;
344
+ };
345
+ const getFieldObjectByFieldExpression = (x // TODO: review types. using type assertion now to avoid rewriting half of app
346
+ ) => ___default["default"].last(getFieldObjectsByFieldExpression(x));
347
+ const getFieldObjects = (expression, typeObject) => {
348
+ const fieldExpression = extractFieldExpressions(expression)[0];
349
+ return getFieldObjectsByFieldExpression({
350
+ typeObject,
351
+ expression: fieldExpression
352
+ });
353
+ };
316
354
 
317
355
  /** @deprecated
318
356
  * This method checks few expression forms, that we do not generate on frontend anymore(field-access shortcut, direct value as parameter).
@@ -387,6 +425,11 @@ const createExpressionVisitor = visitor => {
387
425
  var utils = {
388
426
  __proto__: null,
389
427
  assertIsValidExpression: assertIsValidExpression,
428
+ textTypes: textTypes,
429
+ dateTypes: dateTypes,
430
+ dateRangeTypes: dateRangeTypes,
431
+ convertableDateTypes: convertableDateTypes,
432
+ numberTypes: numberTypes,
390
433
  dateRangeFunctions: dateRangeFunctions,
391
434
  firstLastFunctions: firstLastFunctions,
392
435
  logicalOperators: logicalOperators,
@@ -405,6 +448,9 @@ var utils = {
405
448
  isMultiFieldExpression: isMultiFieldExpression,
406
449
  isFieldExpression: isFieldExpression,
407
450
  isQueryExpression: isQueryExpression,
451
+ getFieldObjectsByFieldExpression: getFieldObjectsByFieldExpression,
452
+ getFieldObjectByFieldExpression: getFieldObjectByFieldExpression,
453
+ getFieldObjects: getFieldObjects,
408
454
  extractFieldExpressions: extractFieldExpressions,
409
455
  createExpressionVisitor: createExpressionVisitor
410
456
  };
@@ -126,7 +126,11 @@ const parseRelativeDatePlaceholder = placeholder => {
126
126
  let unit = relativeDatePlaceholderMatch[2];
127
127
  return {
128
128
  isStartOfInterval,
129
- unitForStart: fullMatch.startsWith("$start-of-day") || fullMatch.startsWith("$end-of-day") ? "day" : isStartOfInterval !== undefined ? unit : undefined,
129
+ unitForStart:
130
+ // Usually the "start" unit is the same as the unit for relative date calculation.
131
+ // e.g. when we calculate the date "1 week ago", we want the beginning of the previous week.
132
+ // But sometimes they are different. We have hardcoded "day" for now, but this can be extended to any unit.
133
+ fullMatch.startsWith("$start-of-day") || fullMatch.startsWith("$end-of-day") ? "day" : isStartOfInterval !== undefined ? unit : undefined,
130
134
  amount: Number.parseInt(relativeDatePlaceholderMatch[1]),
131
135
  unit,
132
136
  isBeforeNow: relativeDatePlaceholderMatch[3] === "before-now",
package/lib/utils.js CHANGED
@@ -23,6 +23,11 @@ const assertIsValidExpression = expression => {
23
23
  });
24
24
  trace.assert(expression.length > 0, "empty expression does not make any sense");
25
25
  };
26
+ const textTypes = ["fibery/text", "fibery/email", "fibery/url", "fibery/emoji"];
27
+ const dateTypes = ["fibery/date", "fibery/date-time"];
28
+ const dateRangeTypes = ["fibery/date-range", "fibery/date-time-range"];
29
+ const convertableDateTypes = ["fibery/date", "fibery/date-range"];
30
+ const numberTypes = ["fibery/int", "fibery/decimal"];
26
31
  const dateRangeFunctions = new Set(["q/start", "q/end"]);
27
32
  const firstLastFunctions = new Set(["q/first", "q/last"]);
28
33
  const collectionOps = new Set(["q/count", "q/count-distinct", "q/sum", "q/min", "q/max", "q/avg", "q/join", "q/first", "q/last"]);
@@ -34,9 +39,6 @@ const binaryOperations = new Set(["=", "!=", "<", ">", "<=", ">=", "in",
34
39
  "q/contains", "q/not-contains", "+", "-", "q/+", "q/-", "*", "/", "and", "or",
35
40
  //asc: obsolete. use q/and, q/or
36
41
  "q/and", "q/or", "q/in", "q/not-in"]);
37
-
38
- // TODO: get rid of this. Use visitors everywhere
39
- const naryOperations = new Set(["and", "or", "q/and", "q/or"]);
40
42
  const logicalOperators = new Set(["and", "or", "q/and", "q/or"]);
41
43
  const relationalOperators = new Set(["=", "!=", "<", ">", "<=", ">="]);
42
44
  const mathOperators = new Set(["+", "-", "*", "/", "q/+", "q/-", "q/concat"]);
@@ -49,7 +51,7 @@ const isCollectionFunctionExpression = expression =>
49
51
  (expression.length === 2 || expression.length === 3) && collectionOps.has(expression[0]);
50
52
  const isAccessFunctionExpression = expresion => expresion.length === 2 && expresion[0] === "q/access?" && isFieldExpression(expresion[1]);
51
53
  const isBinaryExpression = expression => expression.length === 3 && binaryOperations.has(expression[0]);
52
- const isNaryExpression = expression => expression.length > 1 && naryOperations.has(expression[0]);
54
+ const isNaryExpression = expression => expression.length > 1 && logicalOperators.has(expression[0]);
53
55
  const isVariableExpression = expression => ___default["default"].isString(expression) && expression.startsWith("$");
54
56
  const isMultiFieldAccess = expression => ___default["default"].isArray(expression) && expression.length === 2 && !isFunctionCallExpression(expression) && expression.every(x => ___default["default"].isString(x));
55
57
  const isMultiFieldExpression = expression => Array.isArray(expression) && !isFunctionCallExpression(expression) && expression.some(x => isMultiFieldAccess(x)) && expression.every(x => !isVariableExpression(x) && !binaryOperations.has(x) && (___default["default"].isString(x) || isMultiFieldAccess(x)));
@@ -87,6 +89,38 @@ const collectFieldExpressions = (memo, expression) => {
87
89
  throw new NotImplementedError(expression, "expression");
88
90
  }
89
91
  };
92
+ const getFieldObjectsByFieldExpression = ({
93
+ typeObject,
94
+ expression
95
+ }) => {
96
+ assertIsValidExpression(expression);
97
+ const fieldObjects = [];
98
+ let currentTypeObject = typeObject;
99
+ for (const fieldOrMultiFieldAccess of expression) {
100
+ if (isMultiFieldAccess(fieldOrMultiFieldAccess)) {
101
+ const [multiField, type] = fieldOrMultiFieldAccess;
102
+ const fieldObject = currentTypeObject.fieldObjectsByName[multiField];
103
+ fieldObjects.push(fieldObject);
104
+ const foundField = fieldObject.multiRelatedFieldObjects.find(f => f.holderType === type);
105
+ trace.assert(foundField, "incorrect expression for multi-field");
106
+ currentTypeObject = foundField.holderTypeObject;
107
+ } else {
108
+ const fieldObject = currentTypeObject.fieldObjectsByName[fieldOrMultiFieldAccess];
109
+ fieldObjects.push(fieldObject);
110
+ currentTypeObject = fieldObject.typeObject;
111
+ }
112
+ }
113
+ return fieldObjects;
114
+ };
115
+ const getFieldObjectByFieldExpression = (x // TODO: review types. using type assertion now to avoid rewriting half of app
116
+ ) => ___default["default"].last(getFieldObjectsByFieldExpression(x));
117
+ const getFieldObjects = (expression, typeObject) => {
118
+ const fieldExpression = extractFieldExpressions(expression)[0];
119
+ return getFieldObjectsByFieldExpression({
120
+ typeObject,
121
+ expression: fieldExpression
122
+ });
123
+ };
90
124
 
91
125
  /** @deprecated
92
126
  * This method checks few expression forms, that we do not generate on frontend anymore(field-access shortcut, direct value as parameter).
@@ -159,11 +193,17 @@ const createExpressionVisitor = visitor => {
159
193
  };
160
194
 
161
195
  exports.assertIsValidExpression = assertIsValidExpression;
196
+ exports.convertableDateTypes = convertableDateTypes;
162
197
  exports.createExpressionVisitor = createExpressionVisitor;
163
198
  exports.dateRangeFunctions = dateRangeFunctions;
199
+ exports.dateRangeTypes = dateRangeTypes;
200
+ exports.dateTypes = dateTypes;
164
201
  exports.extractFieldExpressions = extractFieldExpressions;
165
202
  exports.firstLastFunctions = firstLastFunctions;
166
203
  exports.fromRootKeyword = fromRootKeyword;
204
+ exports.getFieldObjectByFieldExpression = getFieldObjectByFieldExpression;
205
+ exports.getFieldObjects = getFieldObjects;
206
+ exports.getFieldObjectsByFieldExpression = getFieldObjectsByFieldExpression;
167
207
  exports.isAccessFunctionExpression = isAccessFunctionExpression;
168
208
  exports.isBinaryExpression = isBinaryExpression;
169
209
  exports.isCollectionFunctionExpression = isCollectionFunctionExpression;
@@ -178,4 +218,6 @@ exports.isQueryExpression = isQueryExpression;
178
218
  exports.isVariableExpression = isVariableExpression;
179
219
  exports.logicalOperators = logicalOperators;
180
220
  exports.mathOperators = mathOperators;
221
+ exports.numberTypes = numberTypes;
181
222
  exports.relationalOperators = relationalOperators;
223
+ exports.textTypes = textTypes;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fibery/expression-utils",
3
- "version": "9.1.5",
3
+ "version": "9.2.1",
4
4
  "description": "utils for working with fibery api expressions",
5
5
  "exports": {
6
6
  ".": "./lib/expression-utils.js",
@@ -26,7 +26,7 @@
26
26
  "dependencies": {
27
27
  "lodash": "4.17.21",
28
28
  "moment": "2.29.4",
29
- "@fibery/helpers": "1.3.1"
29
+ "@fibery/helpers": "1.3.3"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@babel/core": "7.23.9",
@@ -38,7 +38,7 @@
38
38
  "@fibery/eslint-config": "8.6.1"
39
39
  },
40
40
  "peerDependencies": {
41
- "@fibery/schema": "10.2.6"
41
+ "@fibery/schema": "10.2.8"
42
42
  },
43
43
  "jest": {
44
44
  "testEnvironment": "node",
package/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // eslint-disable-next-line import/no-extraneous-dependencies
2
- import {Schema, TypeObject, FieldObject} from "@fibery/schema";
2
+ import {FieldObject, Schema, TypeObject} from "@fibery/schema";
3
3
 
4
4
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
5
  export type $TSFixMe = any;
@@ -61,6 +61,14 @@ declare module "@fibery/expression-utils" {
61
61
  firstLastFunctions: Set<string>;
62
62
  isQueryExpression: (expression: $TSFixMe) => boolean;
63
63
  isVariableExpression: (expression: $TSFixMe) => boolean;
64
+ isFunctionCallExpression: (expression: $TSFixMe) => boolean;
65
+ getFieldObjectByFieldExpression: (x: {typeObject: TypeObject; expression: Expression}) => FieldObject | undefined;
66
+ isFieldExpression: (expression: $TSFixMe) => boolean;
67
+ isBinaryExpression: (expression: $TSFixMe) => boolean;
68
+ isMultiFieldAccess: (expression: $TSFixMe) => boolean;
69
+ logicalOperators: Set<string>;
70
+ relationalOperators: Set<string>;
71
+ mathOperators: Set<string>;
64
72
  };
65
73
  export const contextVariables: {
66
74
  getEntityQueryVariables: (
@@ -69,6 +77,7 @@ declare module "@fibery/expression-utils" {
69
77
  };
70
78
  export const paramsPlaceholders: {
71
79
  formulaTodayDateParamPlaceholder: string;
80
+ formulaNowDateTimeParamPlaceholder: string;
72
81
  dynamicFilterParamPrefix: string;
73
82
 
74
83
  isDynamicFilterParam(paramValue: string): boolean;