@openforis/arena-core 2.18.2 → 2.18.4

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.
@@ -57,11 +57,16 @@ export class CallEvaluator extends ExpressionNodeEvaluator {
57
57
  evaluateMember(expressionNode, callee) {
58
58
  return __awaiter(this, void 0, void 0, function* () {
59
59
  const { arguments: exprArgs } = expressionNode;
60
- // global function (e.g. Math.round(...))
61
- const fn = yield this.evaluator.evaluateNode(callee, this.context);
60
+ // evaluate the callee object only once: evaluating it again to find the function would take exponential time
61
+ // with chained calls (e.g. text.trim().toUpperCase().toLowerCase())
62
+ const fnObject = yield this.evaluator.evaluateNode(callee.object, this.context);
63
+ if (fnObject === null || fnObject === undefined)
64
+ return null;
65
+ // find the function (e.g. Math.round or text.toUpperCase) among the properties of the already evaluated object
66
+ const fnObjectLiteral = { type: ExpressionNodeType.Literal, value: fnObject, raw: '' };
67
+ const calleeWithObjectEvaluated = Object.assign(Object.assign({}, callee), { object: fnObjectLiteral });
68
+ const fn = yield this.evaluator.evaluateNode(calleeWithObjectEvaluated, this.context);
62
69
  if (fn) {
63
- const { object: calleeObj } = callee;
64
- const fnObject = yield this.evaluator.evaluateNode(calleeObj, this.context);
65
70
  const argsEvaluated = yield Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, this.context)));
66
71
  const args = argsEvaluated.flat();
67
72
  const regExpStringPattern = isRegExpStringPatternCall(callee, args);
@@ -1 +1 @@
1
- {"version":3,"file":"call.js","sourceRoot":"","sources":["../../../../../src/expression/javascript/node/call.ts"],"names":[],"mappings":"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","sourcesContent":["import { SystemError, SystemErrorKeys } from '../../../error'\nimport { ExpressionContext } from '../../context'\nimport { ExpressionFunction } from '../../function'\nimport {\n CallExpression,\n ExpressionNodeEvaluator,\n ExpressionNodeType,\n IdentifierExpression,\n MemberExpression,\n} from '../../node'\nimport { getGlobalObjectProperty } from '../global'\nimport { hasNestedQuantifiers, REG_EXP_INPUT_MAX_LENGTH } from '../regExpSafety'\n\n// String functions converting a string argument into a regular expression (replace, replaceAll and split don't)\nconst stringFunctionsWithRegExpArgument = new Set(['match', 'matchAll', 'search'])\n\n// e.g. text.match('(a+)+$'): the string pattern is checked like a regular expression literal\nconst isRegExpStringPatternCall = (callee: MemberExpression, args: any[]): boolean => {\n const { computed, property } = callee\n return (\n !computed &&\n property.type === ExpressionNodeType.Identifier &&\n stringFunctionsWithRegExpArgument.has((property as IdentifierExpression).name) &&\n typeof args[0] === 'string'\n )\n}\n\nconst checkRegExpStringPattern = (pattern: string): void => {\n if (hasNestedQuantifiers(pattern)) {\n throw new SystemError(SystemErrorKeys.expression.unsafeRegularExpression, { pattern })\n }\n}\n\n// e.g. /.../.test(text) or text.match(/.../): the time taken by a regular expression grows with the input length\nconst checkRegExpInputLength = (fnObject: any, args: any[], regExpStringPattern: boolean): void => {\n const values = [fnObject, ...args]\n if (!regExpStringPattern && !values.some((value) => value instanceof RegExp)) return\n const inputTooLong = values.find((value) => typeof value === 'string' && value.length > REG_EXP_INPUT_MAX_LENGTH)\n if (inputTooLong !== undefined) {\n throw new SystemError(SystemErrorKeys.expression.regularExpressionInputTooLong, {\n maxLength: REG_EXP_INPUT_MAX_LENGTH,\n length: inputTooLong.length,\n })\n }\n}\n\nexport class CallEvaluator<C extends ExpressionContext> extends ExpressionNodeEvaluator<C, CallExpression> {\n async evaluate(expressionNode: CallExpression): Promise<any> {\n const { callee } = expressionNode\n\n if (callee.type === ExpressionNodeType.Member) {\n return this.evaluateMember(expressionNode, callee)\n } else if (callee.type === ExpressionNodeType.Identifier) {\n return this.evaluateIdentifier(expressionNode, callee)\n }\n\n // No complex expressions may be put in place of a function body.\n // Only a plain identifier is allowed.\n throw new SystemError(SystemErrorKeys.expression.invalidCalleeType, { type: callee.type })\n }\n\n private async evaluateMember(expressionNode: CallExpression, callee: MemberExpression): Promise<any> {\n const { arguments: exprArgs } = expressionNode\n\n // global function (e.g. Math.round(...))\n const fn = await this.evaluator.evaluateNode(callee, this.context)\n if (fn) {\n const { object: calleeObj } = callee\n const fnObject = await this.evaluator.evaluateNode(calleeObj, this.context)\n const argsEvaluated = await Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, this.context)))\n const args = argsEvaluated.flat()\n const regExpStringPattern = isRegExpStringPatternCall(callee, args)\n if (regExpStringPattern) {\n checkRegExpStringPattern(args[0])\n }\n checkRegExpInputLength(fnObject, args, regExpStringPattern)\n return fn.call(fnObject, ...args)\n }\n return null\n }\n\n private async evaluateIdentifier(expressionNode: CallExpression, callee: IdentifierExpression): Promise<any> {\n // Arguments is a reserved word in strict mode\n const { arguments: exprArgs } = expressionNode\n const { object: contextObject } = this.context\n\n const { name: fnName } = callee\n\n const functionInfo = this.evaluator.functions[fnName]\n if (functionInfo) {\n // The function is among the custom functions.\n return this.evaluateCustomIdentifier(expressionNode, fnName)\n }\n // identifier is a global object\n const globalFn = getGlobalObjectProperty(fnName, contextObject)\n if (globalFn !== null) {\n // global functions like String, Number, must be invoked passing node value as argument, not the node itself\n const contextArg = { ...this.context, evaluateToNode: false }\n const args = await Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, contextArg)))\n return globalFn(...args)\n }\n\n throw new SystemError(SystemErrorKeys.expression.undefinedFunction, { name: fnName })\n }\n\n private async evaluateCustomIdentifier(expressionNode: CallExpression, fnName: string): Promise<any> {\n // Arguments is a reserved word in strict mode\n const { arguments: exprArgs } = expressionNode\n const numArgs = exprArgs.length\n\n const expressionFunction: ExpressionFunction<C> = this.evaluator.functions[fnName]\n\n const { minArity, maxArity, evaluateArgsToNodes, executor } = expressionFunction\n\n if (numArgs < minArity)\n throw new SystemError(SystemErrorKeys.expression.functionHasTooFewArguments, { fnName, minArity, numArgs })\n if (maxArity !== undefined && maxArity >= 0 && numArgs > maxArity)\n throw new SystemError(SystemErrorKeys.expression.functionHasTooManyArguments, { fnName, maxArity, numArgs })\n\n // evaluate arguments\n const argsContext = { ...this.context, evaluateToNode: evaluateArgsToNodes }\n const args = await Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, argsContext)))\n\n // Currently there are no side effects from function evaluation so it's\n // safe to call the function even when we're just parsing the expression\n // to find all identifiers being used.\n return executor(this.context)(...args)\n }\n}\n"]}
1
+ {"version":3,"file":"call.js","sourceRoot":"","sources":["../../../../../src/expression/javascript/node/call.ts"],"names":[],"mappings":"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","sourcesContent":["import { SystemError, SystemErrorKeys } from '../../../error'\nimport { ExpressionContext } from '../../context'\nimport { ExpressionFunction } from '../../function'\nimport {\n CallExpression,\n ExpressionNodeEvaluator,\n ExpressionNodeType,\n IdentifierExpression,\n LiteralExpression,\n MemberExpression,\n} from '../../node'\nimport { getGlobalObjectProperty } from '../global'\nimport { hasNestedQuantifiers, REG_EXP_INPUT_MAX_LENGTH } from '../regExpSafety'\n\n// String functions converting a string argument into a regular expression (replace, replaceAll and split don't)\nconst stringFunctionsWithRegExpArgument = new Set(['match', 'matchAll', 'search'])\n\n// e.g. text.match('(a+)+$'): the string pattern is checked like a regular expression literal\nconst isRegExpStringPatternCall = (callee: MemberExpression, args: any[]): boolean => {\n const { computed, property } = callee\n return (\n !computed &&\n property.type === ExpressionNodeType.Identifier &&\n stringFunctionsWithRegExpArgument.has((property as IdentifierExpression).name) &&\n typeof args[0] === 'string'\n )\n}\n\nconst checkRegExpStringPattern = (pattern: string): void => {\n if (hasNestedQuantifiers(pattern)) {\n throw new SystemError(SystemErrorKeys.expression.unsafeRegularExpression, { pattern })\n }\n}\n\n// e.g. /.../.test(text) or text.match(/.../): the time taken by a regular expression grows with the input length\nconst checkRegExpInputLength = (fnObject: any, args: any[], regExpStringPattern: boolean): void => {\n const values = [fnObject, ...args]\n if (!regExpStringPattern && !values.some((value) => value instanceof RegExp)) return\n const inputTooLong = values.find((value) => typeof value === 'string' && value.length > REG_EXP_INPUT_MAX_LENGTH)\n if (inputTooLong !== undefined) {\n throw new SystemError(SystemErrorKeys.expression.regularExpressionInputTooLong, {\n maxLength: REG_EXP_INPUT_MAX_LENGTH,\n length: inputTooLong.length,\n })\n }\n}\n\nexport class CallEvaluator<C extends ExpressionContext> extends ExpressionNodeEvaluator<C, CallExpression> {\n async evaluate(expressionNode: CallExpression): Promise<any> {\n const { callee } = expressionNode\n\n if (callee.type === ExpressionNodeType.Member) {\n return this.evaluateMember(expressionNode, callee)\n } else if (callee.type === ExpressionNodeType.Identifier) {\n return this.evaluateIdentifier(expressionNode, callee)\n }\n\n // No complex expressions may be put in place of a function body.\n // Only a plain identifier is allowed.\n throw new SystemError(SystemErrorKeys.expression.invalidCalleeType, { type: callee.type })\n }\n\n private async evaluateMember(expressionNode: CallExpression, callee: MemberExpression): Promise<any> {\n const { arguments: exprArgs } = expressionNode\n\n // evaluate the callee object only once: evaluating it again to find the function would take exponential time\n // with chained calls (e.g. text.trim().toUpperCase().toLowerCase())\n const fnObject = await this.evaluator.evaluateNode(callee.object, this.context)\n if (fnObject === null || fnObject === undefined) return null\n\n // find the function (e.g. Math.round or text.toUpperCase) among the properties of the already evaluated object\n const fnObjectLiteral: LiteralExpression = { type: ExpressionNodeType.Literal, value: fnObject, raw: '' }\n const calleeWithObjectEvaluated: MemberExpression = { ...callee, object: fnObjectLiteral }\n const fn = await this.evaluator.evaluateNode(calleeWithObjectEvaluated, this.context)\n if (fn) {\n const argsEvaluated = await Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, this.context)))\n const args = argsEvaluated.flat()\n const regExpStringPattern = isRegExpStringPatternCall(callee, args)\n if (regExpStringPattern) {\n checkRegExpStringPattern(args[0])\n }\n checkRegExpInputLength(fnObject, args, regExpStringPattern)\n return fn.call(fnObject, ...args)\n }\n return null\n }\n\n private async evaluateIdentifier(expressionNode: CallExpression, callee: IdentifierExpression): Promise<any> {\n // Arguments is a reserved word in strict mode\n const { arguments: exprArgs } = expressionNode\n const { object: contextObject } = this.context\n\n const { name: fnName } = callee\n\n const functionInfo = this.evaluator.functions[fnName]\n if (functionInfo) {\n // The function is among the custom functions.\n return this.evaluateCustomIdentifier(expressionNode, fnName)\n }\n // identifier is a global object\n const globalFn = getGlobalObjectProperty(fnName, contextObject)\n if (globalFn !== null) {\n // global functions like String, Number, must be invoked passing node value as argument, not the node itself\n const contextArg = { ...this.context, evaluateToNode: false }\n const args = await Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, contextArg)))\n return globalFn(...args)\n }\n\n throw new SystemError(SystemErrorKeys.expression.undefinedFunction, { name: fnName })\n }\n\n private async evaluateCustomIdentifier(expressionNode: CallExpression, fnName: string): Promise<any> {\n // Arguments is a reserved word in strict mode\n const { arguments: exprArgs } = expressionNode\n const numArgs = exprArgs.length\n\n const expressionFunction: ExpressionFunction<C> = this.evaluator.functions[fnName]\n\n const { minArity, maxArity, evaluateArgsToNodes, executor } = expressionFunction\n\n if (numArgs < minArity)\n throw new SystemError(SystemErrorKeys.expression.functionHasTooFewArguments, { fnName, minArity, numArgs })\n if (maxArity !== undefined && maxArity >= 0 && numArgs > maxArity)\n throw new SystemError(SystemErrorKeys.expression.functionHasTooManyArguments, { fnName, maxArity, numArgs })\n\n // evaluate arguments\n const argsContext = { ...this.context, evaluateToNode: evaluateArgsToNodes }\n const args = await Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, argsContext)))\n\n // Currently there are no side effects from function evaluation so it's\n // safe to call the function even when we're just parsing the expression\n // to find all identifiers being used.\n return executor(this.context)(...args)\n }\n}\n"]}
@@ -39,7 +39,8 @@ export class MemberEvaluator extends ExpressionNodeEvaluator {
39
39
  return __awaiter(this, void 0, void 0, function* () {
40
40
  const { object, property, computed } = expressionNode;
41
41
  const objectEval = yield this.evaluator.evaluateNode(object, Object.assign(Object.assign({}, this.context), { evaluateToNode: true }));
42
- if (!objectEval)
42
+ // null or undefined object (falsy values like 0 or '' have properties too)
43
+ if (objectEval === null || objectEval === undefined)
43
44
  return null;
44
45
  if (computed) {
45
46
  return this.evaluateComputedProperty(property, objectEval);
@@ -1 +1 @@
1
- {"version":3,"file":"member.js","sourceRoot":"","sources":["../../../../../src/expression/javascript/node/member.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAkB,uBAAuB,EAAoB,MAAM,YAAY,CAAA;AAEtF,MAAM,OAAO,eAA6C,SAAQ,uBAA4C;IACtG,wBAAwB,CAAC,QAA6B,EAAE,UAAe;;YAC3E,iDAAiD;YAEjD,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,kCAC1D,IAAI,CAAC,OAAO,KACf,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,IAC3B,CAAA;YAEF,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;gBACrC,2BAA2B;gBAC3B,OAAO,UAAU,CAAC,YAAY,CAAC,CAAA;YACjC,CAAC;YAED,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC9B,uFAAuF;gBAEvF,MAAM,QAAQ,GAAG,EAAE,CAAA;gBACnB,KAAK,MAAM,cAAc,IAAI,UAAU,EAAE,CAAC;oBACxC,MAAM,WAAW,mCAAQ,IAAI,CAAC,OAAO,KAAE,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,GAAE,CAAA;oBACtF,oGAAoG;oBACpG,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAA,CAAC,UAAU;oBACvF,IAAI,WAAW,EAAE,CAAC;wBAChB,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;oBAC/B,CAAC;gBACH,CAAC;gBACD,OAAO,QAAQ,CAAA;YACjB,CAAC;iBAAM,CAAC;gBACN,OAAO,YAAY,CAAA;YACrB,CAAC;QACH,CAAC;KAAA;IAEK,QAAQ,CAAC,cAAgC;;YAC7C,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAA;YAErD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM,kCAAO,IAAI,CAAC,OAAO,KAAE,cAAc,EAAE,IAAI,IAAG,CAAA;YACvG,IAAI,CAAC,UAAU;gBAAE,OAAO,IAAI,CAAA;YAE5B,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,IAAI,CAAC,wBAAwB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;YAC5D,CAAC;YAED,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,kCAC1D,IAAI,CAAC,OAAO,KACf,MAAM,EAAE,UAAU,IAClB,CAAA;YAEF,OAAO,YAAY,CAAA;QACrB,CAAC;KAAA;CACF","sourcesContent":["import { ExpressionContext } from '../../context'\nimport { ExpressionNode, ExpressionNodeEvaluator, MemberExpression } from '../../node'\n\nexport class MemberEvaluator<C extends ExpressionContext> extends ExpressionNodeEvaluator<C, MemberExpression> {\n async evaluateComputedProperty(property: ExpressionNode<any>, objectEval: any): Promise<any> {\n // expressions like plot[1] or plot[plot_id == 1]\n\n const propertyEval = await this.evaluator.evaluateNode(property, {\n ...this.context,\n object: this.context.object,\n })\n\n if (typeof propertyEval === 'number') {\n // expressions like plot[1]\n return objectEval[propertyEval]\n }\n\n if (Array.isArray(objectEval)) {\n // expressions where the member property is a filter function (e.g. plot[plot_id == 1])\n\n const filtered = []\n for (const objectEvalItem of objectEval) {\n const itemContext = { ...this.context, object: objectEvalItem, evaluateToNode: false }\n // evaluate items sequentially, to avoid concurrent category item / taxon lookups on large node sets\n const itemMatches = await this.evaluator.evaluateNode(property, itemContext) // NOSONAR\n if (itemMatches) {\n filtered.push(objectEvalItem)\n }\n }\n return filtered\n } else {\n return propertyEval\n }\n }\n\n async evaluate(expressionNode: MemberExpression): Promise<any> {\n const { object, property, computed } = expressionNode\n\n const objectEval = await this.evaluator.evaluateNode(object, { ...this.context, evaluateToNode: true })\n if (!objectEval) return null\n\n if (computed) {\n return this.evaluateComputedProperty(property, objectEval)\n }\n\n const propertyEval = await this.evaluator.evaluateNode(property, {\n ...this.context,\n object: objectEval,\n })\n\n return propertyEval\n }\n}\n"]}
1
+ {"version":3,"file":"member.js","sourceRoot":"","sources":["../../../../../src/expression/javascript/node/member.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAkB,uBAAuB,EAAoB,MAAM,YAAY,CAAA;AAEtF,MAAM,OAAO,eAA6C,SAAQ,uBAA4C;IACtG,wBAAwB,CAAC,QAA6B,EAAE,UAAe;;YAC3E,iDAAiD;YAEjD,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,kCAC1D,IAAI,CAAC,OAAO,KACf,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,IAC3B,CAAA;YAEF,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;gBACrC,2BAA2B;gBAC3B,OAAO,UAAU,CAAC,YAAY,CAAC,CAAA;YACjC,CAAC;YAED,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC9B,uFAAuF;gBAEvF,MAAM,QAAQ,GAAG,EAAE,CAAA;gBACnB,KAAK,MAAM,cAAc,IAAI,UAAU,EAAE,CAAC;oBACxC,MAAM,WAAW,mCAAQ,IAAI,CAAC,OAAO,KAAE,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,GAAE,CAAA;oBACtF,oGAAoG;oBACpG,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAA,CAAC,UAAU;oBACvF,IAAI,WAAW,EAAE,CAAC;wBAChB,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;oBAC/B,CAAC;gBACH,CAAC;gBACD,OAAO,QAAQ,CAAA;YACjB,CAAC;iBAAM,CAAC;gBACN,OAAO,YAAY,CAAA;YACrB,CAAC;QACH,CAAC;KAAA;IAEK,QAAQ,CAAC,cAAgC;;YAC7C,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAA;YAErD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM,kCAAO,IAAI,CAAC,OAAO,KAAE,cAAc,EAAE,IAAI,IAAG,CAAA;YACvG,2EAA2E;YAC3E,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,KAAK,SAAS;gBAAE,OAAO,IAAI,CAAA;YAEhE,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,IAAI,CAAC,wBAAwB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;YAC5D,CAAC;YAED,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,kCAC1D,IAAI,CAAC,OAAO,KACf,MAAM,EAAE,UAAU,IAClB,CAAA;YAEF,OAAO,YAAY,CAAA;QACrB,CAAC;KAAA;CACF","sourcesContent":["import { ExpressionContext } from '../../context'\nimport { ExpressionNode, ExpressionNodeEvaluator, MemberExpression } from '../../node'\n\nexport class MemberEvaluator<C extends ExpressionContext> extends ExpressionNodeEvaluator<C, MemberExpression> {\n async evaluateComputedProperty(property: ExpressionNode<any>, objectEval: any): Promise<any> {\n // expressions like plot[1] or plot[plot_id == 1]\n\n const propertyEval = await this.evaluator.evaluateNode(property, {\n ...this.context,\n object: this.context.object,\n })\n\n if (typeof propertyEval === 'number') {\n // expressions like plot[1]\n return objectEval[propertyEval]\n }\n\n if (Array.isArray(objectEval)) {\n // expressions where the member property is a filter function (e.g. plot[plot_id == 1])\n\n const filtered = []\n for (const objectEvalItem of objectEval) {\n const itemContext = { ...this.context, object: objectEvalItem, evaluateToNode: false }\n // evaluate items sequentially, to avoid concurrent category item / taxon lookups on large node sets\n const itemMatches = await this.evaluator.evaluateNode(property, itemContext) // NOSONAR\n if (itemMatches) {\n filtered.push(objectEvalItem)\n }\n }\n return filtered\n } else {\n return propertyEval\n }\n }\n\n async evaluate(expressionNode: MemberExpression): Promise<any> {\n const { object, property, computed } = expressionNode\n\n const objectEval = await this.evaluator.evaluateNode(object, { ...this.context, evaluateToNode: true })\n // null or undefined object (falsy values like 0 or '' have properties too)\n if (objectEval === null || objectEval === undefined) return null\n\n if (computed) {\n return this.evaluateComputedProperty(property, objectEval)\n }\n\n const propertyEval = await this.evaluator.evaluateNode(property, {\n ...this.context,\n object: objectEval,\n })\n\n return propertyEval\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { ExpressionVariable } from '../../../../expression';
2
- import { isForbiddenPropertyName } from '../../../../expression/javascript/global';
2
+ import { getGlobalObjectProperty, isForbiddenPropertyName } from '../../../../expression/javascript/global';
3
3
  import { IdentifierEvaluator } from '../../../../expression/javascript/node/identifier';
4
4
  import { NodeDefs, NodeDefType } from '../../../../nodeDef';
5
5
  import { Records } from '../../../records';
@@ -81,6 +81,16 @@ export class RecordIdentifierEvaluator extends IdentifierEvaluator {
81
81
  if (isForbiddenPropertyName(propName)) {
82
82
  throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName });
83
83
  }
84
+ const { survey, record, evaluateToNode, object: contextObject, item } = this.context;
85
+ if (contextObject === null || contextObject === void 0 ? void 0 : contextObject.nodeDefUuid) {
86
+ // context object is a record node: its own properties (id, uuid, value...) are not accessible,
87
+ // so that node defs named like them are always found (as in NodeDefIdentifierEvaluator)
88
+ const globalProp = getGlobalObjectProperty(propName, contextObject);
89
+ if (globalProp !== null) {
90
+ return globalProp;
91
+ }
92
+ return evaluateIdentifierOnNode({ survey, record, nodeObject: contextObject, evaluateToNode, propName });
93
+ }
84
94
  // try to find identifier among global or native properties
85
95
  // (not-found is signaled without throwing; the try/catch only handles the rare case of an unknown member of a global object)
86
96
  try {
@@ -94,7 +104,6 @@ export class RecordIdentifierEvaluator extends IdentifierEvaluator {
94
104
  }
95
105
  // identifier not found among global or native properties
96
106
  // identifier should be a node or a node value property
97
- const { survey, record, evaluateToNode, object: contextObject, item } = this.context;
98
107
  if (item && item === contextObject) {
99
108
  // evaluating category or taxon item prop or extra prop
100
109
  return (_d = (_a = item.props) === null || _a === void 0 ? void 0 : _a[propName]) !== null && _d !== void 0 ? _d : (_c = (_b = item.props) === null || _b === void 0 ? void 0 : _b.extra) === null || _c === void 0 ? void 0 : _c[propName];
@@ -1 +1 @@
1
- {"version":3,"file":"identifier.js","sourceRoot":"","sources":["../../../../../../src/record/recordExpressionEvaluator/node/identifier/identifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { ExpressionVariable, IdentifierExpression } from '../../../../expression'\nimport { isForbiddenPropertyName } from '../../../../expression/javascript/global'\nimport { IdentifierEvaluator } from '../../../../expression/javascript/node/identifier'\nimport { ArenaRecordNode } from '../../../../node'\nimport { NodeDef, NodeDefs, NodeDefType } from '../../../../nodeDef'\nimport { Records } from '../../../records'\nimport { NodeValues } from '../../../../node/nodeValues'\nimport { Survey, Surveys } from '../../../../survey'\nimport { RecordExpressionContext } from '../../context'\nimport { NodesFinder } from './nodesFinder'\nimport { SystemError, SystemErrorKeys } from '../../../../error'\nimport { NodeValueExtractor } from '../../nodeValueExtractor'\nimport { ArenaRecord } from '../../../record'\n\nconst getNodesOrValues = (params: {\n survey: Survey\n referencedNodes: ArenaRecordNode[]\n nodeDefReferenced: NodeDef<any>\n propName: string\n evaluateToNode?: boolean\n}): any[] => {\n const { survey, nodeDefReferenced, referencedNodes, propName, evaluateToNode } = params\n const single = !nodeDefReferenced.props.multiple\n if (single) {\n if (referencedNodes.length === 0) {\n // do not throw error if node is missing, consider its value as null\n // throw new SystemError(SystemErrorKeys.expression.nodeNotFoundForNodeDef, { nodeDefName: propName })\n return []\n }\n if (referencedNodes.length > 1)\n throw new SystemError(SystemErrorKeys.expression.multipleNodesFoundForNodeDef, { nodeDefName: propName })\n }\n if (nodeDefReferenced.type === NodeDefType.entity || evaluateToNode) {\n // return nodes\n return referencedNodes\n }\n // return node values\n return referencedNodes.map((referencedNode) =>\n NodeValueExtractor.getNodeValue({ survey, node: referencedNode, nodeDef: nodeDefReferenced })\n )\n}\n\nconst evaluateIdentifierOnNode = (params: {\n survey: Survey\n record: ArenaRecord\n nodeObject: ArenaRecordNode\n evaluateToNode?: boolean\n propName: string\n}) => {\n const { survey, record, nodeObject, evaluateToNode = false, propName } = params\n const { nodeDefUuid: nodeDefObjectUuid, value } = nodeObject\n if (!nodeDefObjectUuid) {\n throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName })\n }\n\n const nodeDefObject = Surveys.getNodeDefByUuid({ survey, uuid: nodeDefObjectUuid })\n\n // node value prop (native)\n const valueProp = value?.[propName]\n if (valueProp !== undefined) {\n return valueProp\n }\n if (NodeDefs.isAttribute(nodeDefObject)) {\n // node value prop (Arena specific value property)\n if (NodeValues.isValueProp({ nodeDef: nodeDefObject, prop: propName })) {\n return NodeValues.getNodeValueProp({ nodeDef: nodeDefObject, node: nodeObject, prop: propName })\n } else {\n throw new SystemError(SystemErrorKeys.expression.invalidAttributeValuePropertyName, {\n attributeName: NodeDefs.getName(nodeDefObject),\n propName,\n })\n }\n }\n\n const nodeDefReferenced = Surveys.findNodeDefByName({ survey, name: propName })\n\n if (!nodeDefReferenced) {\n throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName })\n }\n\n if (nodeObject.nodeDefUuid === nodeDefReferenced.uuid) {\n // the referenced node is the current node itself\n return nodeObject\n }\n if (Surveys.isNodeDefAncestor({ nodeDefAncestor: nodeDefReferenced, nodeDefDescendant: nodeDefObject })) {\n return Records.getAncestor(nodeObject, nodeDefReferenced.uuid)(record)\n }\n // the referenced nodes can be siblings of the current node\n const referencedNodes = NodesFinder.findDescendants({ survey, record, nodeContext: nodeObject, nodeDefReferenced })\n const nodesOrValues = getNodesOrValues({ survey, referencedNodes, nodeDefReferenced, propName, evaluateToNode })\n return nodeDefReferenced.props.multiple ? nodesOrValues : nodesOrValues[0]\n}\n\nexport class RecordIdentifierEvaluator extends IdentifierEvaluator<RecordExpressionContext> {\n evaluate(expressionNode: IdentifierExpression): Promise<any> {\n return Promise.resolve(this.evaluateValue(expressionNode))\n }\n\n private evaluateValue(expressionNode: IdentifierExpression): any {\n const { name: propName } = expressionNode\n\n if (propName === ExpressionVariable.CONTEXT) {\n return this.context.nodeContext\n }\n if (isForbiddenPropertyName(propName)) {\n throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName })\n }\n\n // try to find identifier among global or native properties\n // (not-found is signaled without throwing; the try/catch only handles the rare case of an unknown member of a global object)\n try {\n const property = this.findGlobalOrNativeProperty(expressionNode)\n if (property) {\n return property.value\n }\n } catch {\n // ignore it\n }\n\n // identifier not found among global or native properties\n // identifier should be a node or a node value property\n const { survey, record, evaluateToNode, object: contextObject, item } = this.context\n\n if (item && item === contextObject) {\n // evaluating category or taxon item prop or extra prop\n return item.props?.[propName] ?? item.props?.extra?.[propName]\n }\n\n if (Array.isArray(contextObject)) {\n return contextObject.flatMap((contextNode) => {\n const evaluationResult = evaluateIdentifierOnNode({\n survey,\n record,\n nodeObject: contextNode,\n evaluateToNode,\n propName,\n })\n return Array.isArray(evaluationResult) ? evaluationResult : [evaluationResult]\n })\n }\n return evaluateIdentifierOnNode({ survey, record, nodeObject: contextObject, evaluateToNode, propName })\n }\n}\n"]}
1
+ {"version":3,"file":"identifier.js","sourceRoot":"","sources":["../../../../../../src/record/recordExpressionEvaluator/node/identifier/identifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { ExpressionVariable, IdentifierExpression } from '../../../../expression'\nimport { getGlobalObjectProperty, isForbiddenPropertyName } from '../../../../expression/javascript/global'\nimport { IdentifierEvaluator } from '../../../../expression/javascript/node/identifier'\nimport { ArenaRecordNode } from '../../../../node'\nimport { NodeDef, NodeDefs, NodeDefType } from '../../../../nodeDef'\nimport { Records } from '../../../records'\nimport { NodeValues } from '../../../../node/nodeValues'\nimport { Survey, Surveys } from '../../../../survey'\nimport { RecordExpressionContext } from '../../context'\nimport { NodesFinder } from './nodesFinder'\nimport { SystemError, SystemErrorKeys } from '../../../../error'\nimport { NodeValueExtractor } from '../../nodeValueExtractor'\nimport { ArenaRecord } from '../../../record'\n\nconst getNodesOrValues = (params: {\n survey: Survey\n referencedNodes: ArenaRecordNode[]\n nodeDefReferenced: NodeDef<any>\n propName: string\n evaluateToNode?: boolean\n}): any[] => {\n const { survey, nodeDefReferenced, referencedNodes, propName, evaluateToNode } = params\n const single = !nodeDefReferenced.props.multiple\n if (single) {\n if (referencedNodes.length === 0) {\n // do not throw error if node is missing, consider its value as null\n // throw new SystemError(SystemErrorKeys.expression.nodeNotFoundForNodeDef, { nodeDefName: propName })\n return []\n }\n if (referencedNodes.length > 1)\n throw new SystemError(SystemErrorKeys.expression.multipleNodesFoundForNodeDef, { nodeDefName: propName })\n }\n if (nodeDefReferenced.type === NodeDefType.entity || evaluateToNode) {\n // return nodes\n return referencedNodes\n }\n // return node values\n return referencedNodes.map((referencedNode) =>\n NodeValueExtractor.getNodeValue({ survey, node: referencedNode, nodeDef: nodeDefReferenced })\n )\n}\n\nconst evaluateIdentifierOnNode = (params: {\n survey: Survey\n record: ArenaRecord\n nodeObject: ArenaRecordNode\n evaluateToNode?: boolean\n propName: string\n}) => {\n const { survey, record, nodeObject, evaluateToNode = false, propName } = params\n const { nodeDefUuid: nodeDefObjectUuid, value } = nodeObject\n if (!nodeDefObjectUuid) {\n throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName })\n }\n\n const nodeDefObject = Surveys.getNodeDefByUuid({ survey, uuid: nodeDefObjectUuid })\n\n // node value prop (native)\n const valueProp = value?.[propName]\n if (valueProp !== undefined) {\n return valueProp\n }\n if (NodeDefs.isAttribute(nodeDefObject)) {\n // node value prop (Arena specific value property)\n if (NodeValues.isValueProp({ nodeDef: nodeDefObject, prop: propName })) {\n return NodeValues.getNodeValueProp({ nodeDef: nodeDefObject, node: nodeObject, prop: propName })\n } else {\n throw new SystemError(SystemErrorKeys.expression.invalidAttributeValuePropertyName, {\n attributeName: NodeDefs.getName(nodeDefObject),\n propName,\n })\n }\n }\n\n const nodeDefReferenced = Surveys.findNodeDefByName({ survey, name: propName })\n\n if (!nodeDefReferenced) {\n throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName })\n }\n\n if (nodeObject.nodeDefUuid === nodeDefReferenced.uuid) {\n // the referenced node is the current node itself\n return nodeObject\n }\n if (Surveys.isNodeDefAncestor({ nodeDefAncestor: nodeDefReferenced, nodeDefDescendant: nodeDefObject })) {\n return Records.getAncestor(nodeObject, nodeDefReferenced.uuid)(record)\n }\n // the referenced nodes can be siblings of the current node\n const referencedNodes = NodesFinder.findDescendants({ survey, record, nodeContext: nodeObject, nodeDefReferenced })\n const nodesOrValues = getNodesOrValues({ survey, referencedNodes, nodeDefReferenced, propName, evaluateToNode })\n return nodeDefReferenced.props.multiple ? nodesOrValues : nodesOrValues[0]\n}\n\nexport class RecordIdentifierEvaluator extends IdentifierEvaluator<RecordExpressionContext> {\n evaluate(expressionNode: IdentifierExpression): Promise<any> {\n return Promise.resolve(this.evaluateValue(expressionNode))\n }\n\n private evaluateValue(expressionNode: IdentifierExpression): any {\n const { name: propName } = expressionNode\n\n if (propName === ExpressionVariable.CONTEXT) {\n return this.context.nodeContext\n }\n if (isForbiddenPropertyName(propName)) {\n throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName })\n }\n\n const { survey, record, evaluateToNode, object: contextObject, item } = this.context\n\n if (contextObject?.nodeDefUuid) {\n // context object is a record node: its own properties (id, uuid, value...) are not accessible,\n // so that node defs named like them are always found (as in NodeDefIdentifierEvaluator)\n const globalProp = getGlobalObjectProperty(propName, contextObject)\n if (globalProp !== null) {\n return globalProp\n }\n return evaluateIdentifierOnNode({ survey, record, nodeObject: contextObject, evaluateToNode, propName })\n }\n\n // try to find identifier among global or native properties\n // (not-found is signaled without throwing; the try/catch only handles the rare case of an unknown member of a global object)\n try {\n const property = this.findGlobalOrNativeProperty(expressionNode)\n if (property) {\n return property.value\n }\n } catch {\n // ignore it\n }\n\n // identifier not found among global or native properties\n // identifier should be a node or a node value property\n\n if (item && item === contextObject) {\n // evaluating category or taxon item prop or extra prop\n return item.props?.[propName] ?? item.props?.extra?.[propName]\n }\n\n if (Array.isArray(contextObject)) {\n return contextObject.flatMap((contextNode) => {\n const evaluationResult = evaluateIdentifierOnNode({\n survey,\n record,\n nodeObject: contextNode,\n evaluateToNode,\n propName,\n })\n return Array.isArray(evaluationResult) ? evaluationResult : [evaluationResult]\n })\n }\n return evaluateIdentifierOnNode({ survey, record, nodeObject: contextObject, evaluateToNode, propName })\n }\n}\n"]}
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { NodeValues, Nodes } from '../../node';
11
11
  import { NodeDefs } from '../../nodeDef';
12
12
  import { Surveys } from '../../survey';
13
- import { Dates } from '../../utils';
13
+ import { Arrays, Dates } from '../../utils';
14
14
  import { Records } from '../records';
15
15
  import { RecordUpdateResult } from './recordUpdateResult';
16
16
  // A dependent code attribute's stored item is still valid as long as its category item's parent is the
@@ -48,8 +48,9 @@ export const updateDependentCodeAttributes = (params) => __awaiter(void 0, void
48
48
  const { survey, record, node, categoryItemProvider, sideEffect = false } = params;
49
49
  const updateResult = new RecordUpdateResult({ record });
50
50
  const recordUpdateOptions = { sideEffect };
51
- // 1. get dependent code attributes
52
- const dependentCodeAttributes = Records.getDependentCodeAttributes(node)(record);
51
+ // 1. get dependent code attributes: only the direct ones (the index registers every ancestor code attribute);
52
+ // deeper levels are checked when the cleared direct dependents are visited
53
+ const dependentCodeAttributes = Records.getDependentCodeAttributes(node)(record).filter((dependentCodeAttribute) => Arrays.last(Nodes.getHierarchyCode(dependentCodeAttribute)) === node.uuid);
53
54
  if (dependentCodeAttributes.length === 0)
54
55
  return updateResult;
55
56
  const parentItemUuid = NodeValues.getItemUuid(node);
@@ -1 +1 @@
1
- {"version":3,"file":"recordNodeDependentsCodeAttributesUpdater.js","sourceRoot":"","sources":["../../../../src/record/recordNodesUpdater/recordNodeDependentsCodeAttributesUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodeValues, Nodes } from '../../node'\nimport { NodeDefCode, NodeDefs } from '../../nodeDef'\nimport { Surveys } from '../../survey'\nimport { Dates } from '../../utils'\nimport { Records } from '../records'\nimport type { ArenaRecord } from '../record'\nimport { RecordNodeDependentsUpdateParams } from './recordNodeDependentsUpdateParams'\nimport { RecordUpdateResult } from './recordUpdateResult'\n\n// A dependent code attribute's stored item is still valid as long as its category item's parent is the\n// (possibly unchanged) item currently selected on the parent code attribute node - so this single check\n// covers both \"did the parent's value change\" and \"is the dependent's value still valid\": if the parent's\n// item didn't change, the dependent's item still has the same parent and is left alone.\n//\n// The dependent's item may belong to a \"big\" category whose items aren't loaded into the survey's\n// in-memory ref data index (e.g. an externally-referenced category) - Surveys.getCategoryItemByUuid only\n// ever looks there, so it falls back to the async categoryItemProvider (same pattern as attribute value\n// validation) before concluding the item doesn't exist.\nconst isDependentCodeAttributeValueStillValid = async (\n params: Pick<RecordNodeDependentsUpdateParams, 'survey' | 'categoryItemProvider'> & {\n parentItemUuid?: string\n dependentCodeAttribute: ArenaRecordNode\n dependentCodeAttributeDef: NodeDefCode\n record: ArenaRecord\n }\n): Promise<boolean> => {\n const { survey, parentItemUuid, dependentCodeAttribute, dependentCodeAttributeDef, record, categoryItemProvider } =\n params\n if (!parentItemUuid) return false\n\n const dependentItemUuid = NodeValues.getItemUuid(dependentCodeAttribute)\n if (!dependentItemUuid) return false\n\n let dependentItem = Surveys.getCategoryItemByUuid({ survey, itemUuid: dependentItemUuid })\n if (!dependentItem && categoryItemProvider) {\n const categoryUuid = NodeDefs.getCategoryUuid(dependentCodeAttributeDef)\n if (categoryUuid) {\n const draft = !!record.preview\n dependentItem = await categoryItemProvider.getItemByUuid({\n survey,\n categoryUuid,\n itemUuid: dependentItemUuid,\n draft,\n })\n }\n }\n return dependentItem?.parentUuid === parentItemUuid\n}\n\nexport const updateDependentCodeAttributes = async (params: RecordNodeDependentsUpdateParams) => {\n const { survey, record, node, categoryItemProvider, sideEffect = false } = params\n\n const updateResult = new RecordUpdateResult({ record })\n\n const recordUpdateOptions = { sideEffect }\n\n // 1. get dependent code attributes\n const dependentCodeAttributes = Records.getDependentCodeAttributes(node)(record)\n if (dependentCodeAttributes.length === 0) return updateResult\n\n const parentItemUuid = NodeValues.getItemUuid(node)\n\n // 2. clear dependent code attributes' values that are no longer valid children of the parent's current item\n for (const dependentCodeAttribute of dependentCodeAttributes) {\n const dependentCodeAttributeDef = Surveys.getNodeDefByUuid({\n survey,\n uuid: dependentCodeAttribute.nodeDefUuid,\n }) as NodeDefCode\n if (NodeDefs.isReadOnly(dependentCodeAttributeDef) || !Nodes.hasUserInputValue(dependentCodeAttribute)) continue\n\n const validityCheckParams = {\n survey,\n parentItemUuid,\n dependentCodeAttribute,\n dependentCodeAttributeDef,\n record,\n categoryItemProvider,\n }\n // check attributes sequentially: invalid values are cleared in the record at every iteration\n const stillValid = await isDependentCodeAttributeValueStillValid(validityCheckParams) // NOSONAR\n if (stillValid) continue\n\n const nodeUpdated: ArenaRecordNode = {\n ...dependentCodeAttribute,\n value: null,\n updated: true,\n dateModified: Dates.nowFormattedForStorage(),\n }\n updateResult.addNode(nodeUpdated, recordUpdateOptions)\n updateResult.addClearedDefUuid(nodeUpdated.nodeDefUuid)\n }\n\n return updateResult\n}\n"]}
1
+ {"version":3,"file":"recordNodeDependentsCodeAttributesUpdater.js","sourceRoot":"","sources":["../../../../src/record/recordNodesUpdater/recordNodeDependentsCodeAttributesUpdater.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAmB,UAAU,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAC/D,OAAO,EAAe,QAAQ,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAGpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAEzD,uGAAuG;AACvG,wGAAwG;AACxG,0GAA0G;AAC1G,wFAAwF;AACxF,EAAE;AACF,kGAAkG;AAClG,yGAAyG;AACzG,wGAAwG;AACxG,wDAAwD;AACxD,MAAM,uCAAuC,GAAG,CAC9C,MAKC,EACiB,EAAE;IACpB,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,EAAE,oBAAoB,EAAE,GAC/G,MAAM,CAAA;IACR,IAAI,CAAC,cAAc;QAAE,OAAO,KAAK,CAAA;IAEjC,MAAM,iBAAiB,GAAG,UAAU,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAA;IACxE,IAAI,CAAC,iBAAiB;QAAE,OAAO,KAAK,CAAA;IAEpC,IAAI,aAAa,GAAG,OAAO,CAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC,CAAA;IAC1F,IAAI,CAAC,aAAa,IAAI,oBAAoB,EAAE,CAAC;QAC3C,MAAM,YAAY,GAAG,QAAQ,CAAC,eAAe,CAAC,yBAAyB,CAAC,CAAA;QACxE,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAA;YAC9B,aAAa,GAAG,MAAM,oBAAoB,CAAC,aAAa,CAAC;gBACvD,MAAM;gBACN,YAAY;gBACZ,QAAQ,EAAE,iBAAiB;gBAC3B,KAAK;aACN,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IACD,OAAO,CAAA,aAAa,aAAb,aAAa,uBAAb,aAAa,CAAE,UAAU,MAAK,cAAc,CAAA;AACrD,CAAC,CAAA,CAAA;AAED,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAO,MAAwC,EAAE,EAAE;IAC9F,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,EAAE,UAAU,GAAG,KAAK,EAAE,GAAG,MAAM,CAAA;IAEjF,MAAM,YAAY,GAAG,IAAI,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAA;IAEvD,MAAM,mBAAmB,GAAG,EAAE,UAAU,EAAE,CAAA;IAE1C,8GAA8G;IAC9G,2EAA2E;IAC3E,MAAM,uBAAuB,GAAG,OAAO,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CACrF,CAAC,sBAAsB,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,sBAAsB,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,CACtG,CAAA;IACD,IAAI,uBAAuB,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,YAAY,CAAA;IAE7D,MAAM,cAAc,GAAG,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;IAEnD,4GAA4G;IAC5G,KAAK,MAAM,sBAAsB,IAAI,uBAAuB,EAAE,CAAC;QAC7D,MAAM,yBAAyB,GAAG,OAAO,CAAC,gBAAgB,CAAC;YACzD,MAAM;YACN,IAAI,EAAE,sBAAsB,CAAC,WAAW;SACzC,CAAgB,CAAA;QACjB,IAAI,QAAQ,CAAC,UAAU,CAAC,yBAAyB,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,sBAAsB,CAAC;YAAE,SAAQ;QAEhH,MAAM,mBAAmB,GAAG;YAC1B,MAAM;YACN,cAAc;YACd,sBAAsB;YACtB,yBAAyB;YACzB,MAAM;YACN,oBAAoB;SACrB,CAAA;QACD,6FAA6F;QAC7F,MAAM,UAAU,GAAG,MAAM,uCAAuC,CAAC,mBAAmB,CAAC,CAAA,CAAC,UAAU;QAChG,IAAI,UAAU;YAAE,SAAQ;QAExB,MAAM,WAAW,mCACZ,sBAAsB,KACzB,KAAK,EAAE,IAAI,EACX,OAAO,EAAE,IAAI,EACb,YAAY,EAAE,KAAK,CAAC,sBAAsB,EAAE,GAC7C,CAAA;QACD,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAA;QACtD,YAAY,CAAC,iBAAiB,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;IACzD,CAAC;IAED,OAAO,YAAY,CAAA;AACrB,CAAC,CAAA,CAAA","sourcesContent":["import { ArenaRecordNode, NodeValues, Nodes } from '../../node'\nimport { NodeDefCode, NodeDefs } from '../../nodeDef'\nimport { Surveys } from '../../survey'\nimport { Arrays, Dates } from '../../utils'\nimport { Records } from '../records'\nimport type { ArenaRecord } from '../record'\nimport { RecordNodeDependentsUpdateParams } from './recordNodeDependentsUpdateParams'\nimport { RecordUpdateResult } from './recordUpdateResult'\n\n// A dependent code attribute's stored item is still valid as long as its category item's parent is the\n// (possibly unchanged) item currently selected on the parent code attribute node - so this single check\n// covers both \"did the parent's value change\" and \"is the dependent's value still valid\": if the parent's\n// item didn't change, the dependent's item still has the same parent and is left alone.\n//\n// The dependent's item may belong to a \"big\" category whose items aren't loaded into the survey's\n// in-memory ref data index (e.g. an externally-referenced category) - Surveys.getCategoryItemByUuid only\n// ever looks there, so it falls back to the async categoryItemProvider (same pattern as attribute value\n// validation) before concluding the item doesn't exist.\nconst isDependentCodeAttributeValueStillValid = async (\n params: Pick<RecordNodeDependentsUpdateParams, 'survey' | 'categoryItemProvider'> & {\n parentItemUuid?: string\n dependentCodeAttribute: ArenaRecordNode\n dependentCodeAttributeDef: NodeDefCode\n record: ArenaRecord\n }\n): Promise<boolean> => {\n const { survey, parentItemUuid, dependentCodeAttribute, dependentCodeAttributeDef, record, categoryItemProvider } =\n params\n if (!parentItemUuid) return false\n\n const dependentItemUuid = NodeValues.getItemUuid(dependentCodeAttribute)\n if (!dependentItemUuid) return false\n\n let dependentItem = Surveys.getCategoryItemByUuid({ survey, itemUuid: dependentItemUuid })\n if (!dependentItem && categoryItemProvider) {\n const categoryUuid = NodeDefs.getCategoryUuid(dependentCodeAttributeDef)\n if (categoryUuid) {\n const draft = !!record.preview\n dependentItem = await categoryItemProvider.getItemByUuid({\n survey,\n categoryUuid,\n itemUuid: dependentItemUuid,\n draft,\n })\n }\n }\n return dependentItem?.parentUuid === parentItemUuid\n}\n\nexport const updateDependentCodeAttributes = async (params: RecordNodeDependentsUpdateParams) => {\n const { survey, record, node, categoryItemProvider, sideEffect = false } = params\n\n const updateResult = new RecordUpdateResult({ record })\n\n const recordUpdateOptions = { sideEffect }\n\n // 1. get dependent code attributes: only the direct ones (the index registers every ancestor code attribute);\n // deeper levels are checked when the cleared direct dependents are visited\n const dependentCodeAttributes = Records.getDependentCodeAttributes(node)(record).filter(\n (dependentCodeAttribute) => Arrays.last(Nodes.getHierarchyCode(dependentCodeAttribute)) === node.uuid\n )\n if (dependentCodeAttributes.length === 0) return updateResult\n\n const parentItemUuid = NodeValues.getItemUuid(node)\n\n // 2. clear dependent code attributes' values that are no longer valid children of the parent's current item\n for (const dependentCodeAttribute of dependentCodeAttributes) {\n const dependentCodeAttributeDef = Surveys.getNodeDefByUuid({\n survey,\n uuid: dependentCodeAttribute.nodeDefUuid,\n }) as NodeDefCode\n if (NodeDefs.isReadOnly(dependentCodeAttributeDef) || !Nodes.hasUserInputValue(dependentCodeAttribute)) continue\n\n const validityCheckParams = {\n survey,\n parentItemUuid,\n dependentCodeAttribute,\n dependentCodeAttributeDef,\n record,\n categoryItemProvider,\n }\n // check attributes sequentially: invalid values are cleared in the record at every iteration\n const stillValid = await isDependentCodeAttributeValueStillValid(validityCheckParams) // NOSONAR\n if (stillValid) continue\n\n const nodeUpdated: ArenaRecordNode = {\n ...dependentCodeAttribute,\n value: null,\n updated: true,\n dateModified: Dates.nowFormattedForStorage(),\n }\n updateResult.addNode(nodeUpdated, recordUpdateOptions)\n updateResult.addClearedDefUuid(nodeUpdated.nodeDefUuid)\n }\n\n return updateResult\n}\n"]}
@@ -60,11 +60,16 @@ class CallEvaluator extends node_1.ExpressionNodeEvaluator {
60
60
  evaluateMember(expressionNode, callee) {
61
61
  return __awaiter(this, void 0, void 0, function* () {
62
62
  const { arguments: exprArgs } = expressionNode;
63
- // global function (e.g. Math.round(...))
64
- const fn = yield this.evaluator.evaluateNode(callee, this.context);
63
+ // evaluate the callee object only once: evaluating it again to find the function would take exponential time
64
+ // with chained calls (e.g. text.trim().toUpperCase().toLowerCase())
65
+ const fnObject = yield this.evaluator.evaluateNode(callee.object, this.context);
66
+ if (fnObject === null || fnObject === undefined)
67
+ return null;
68
+ // find the function (e.g. Math.round or text.toUpperCase) among the properties of the already evaluated object
69
+ const fnObjectLiteral = { type: node_1.ExpressionNodeType.Literal, value: fnObject, raw: '' };
70
+ const calleeWithObjectEvaluated = Object.assign(Object.assign({}, callee), { object: fnObjectLiteral });
71
+ const fn = yield this.evaluator.evaluateNode(calleeWithObjectEvaluated, this.context);
65
72
  if (fn) {
66
- const { object: calleeObj } = callee;
67
- const fnObject = yield this.evaluator.evaluateNode(calleeObj, this.context);
68
73
  const argsEvaluated = yield Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, this.context)));
69
74
  const args = argsEvaluated.flat();
70
75
  const regExpStringPattern = isRegExpStringPatternCall(callee, args);
@@ -1 +1 @@
1
- {"version":3,"file":"call.js","sourceRoot":"","sources":["../../../../src/expression/javascript/node/call.ts"],"names":[],"mappings":"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","sourcesContent":["import { SystemError, SystemErrorKeys } from '../../../error'\nimport { ExpressionContext } from '../../context'\nimport { ExpressionFunction } from '../../function'\nimport {\n CallExpression,\n ExpressionNodeEvaluator,\n ExpressionNodeType,\n IdentifierExpression,\n MemberExpression,\n} from '../../node'\nimport { getGlobalObjectProperty } from '../global'\nimport { hasNestedQuantifiers, REG_EXP_INPUT_MAX_LENGTH } from '../regExpSafety'\n\n// String functions converting a string argument into a regular expression (replace, replaceAll and split don't)\nconst stringFunctionsWithRegExpArgument = new Set(['match', 'matchAll', 'search'])\n\n// e.g. text.match('(a+)+$'): the string pattern is checked like a regular expression literal\nconst isRegExpStringPatternCall = (callee: MemberExpression, args: any[]): boolean => {\n const { computed, property } = callee\n return (\n !computed &&\n property.type === ExpressionNodeType.Identifier &&\n stringFunctionsWithRegExpArgument.has((property as IdentifierExpression).name) &&\n typeof args[0] === 'string'\n )\n}\n\nconst checkRegExpStringPattern = (pattern: string): void => {\n if (hasNestedQuantifiers(pattern)) {\n throw new SystemError(SystemErrorKeys.expression.unsafeRegularExpression, { pattern })\n }\n}\n\n// e.g. /.../.test(text) or text.match(/.../): the time taken by a regular expression grows with the input length\nconst checkRegExpInputLength = (fnObject: any, args: any[], regExpStringPattern: boolean): void => {\n const values = [fnObject, ...args]\n if (!regExpStringPattern && !values.some((value) => value instanceof RegExp)) return\n const inputTooLong = values.find((value) => typeof value === 'string' && value.length > REG_EXP_INPUT_MAX_LENGTH)\n if (inputTooLong !== undefined) {\n throw new SystemError(SystemErrorKeys.expression.regularExpressionInputTooLong, {\n maxLength: REG_EXP_INPUT_MAX_LENGTH,\n length: inputTooLong.length,\n })\n }\n}\n\nexport class CallEvaluator<C extends ExpressionContext> extends ExpressionNodeEvaluator<C, CallExpression> {\n async evaluate(expressionNode: CallExpression): Promise<any> {\n const { callee } = expressionNode\n\n if (callee.type === ExpressionNodeType.Member) {\n return this.evaluateMember(expressionNode, callee)\n } else if (callee.type === ExpressionNodeType.Identifier) {\n return this.evaluateIdentifier(expressionNode, callee)\n }\n\n // No complex expressions may be put in place of a function body.\n // Only a plain identifier is allowed.\n throw new SystemError(SystemErrorKeys.expression.invalidCalleeType, { type: callee.type })\n }\n\n private async evaluateMember(expressionNode: CallExpression, callee: MemberExpression): Promise<any> {\n const { arguments: exprArgs } = expressionNode\n\n // global function (e.g. Math.round(...))\n const fn = await this.evaluator.evaluateNode(callee, this.context)\n if (fn) {\n const { object: calleeObj } = callee\n const fnObject = await this.evaluator.evaluateNode(calleeObj, this.context)\n const argsEvaluated = await Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, this.context)))\n const args = argsEvaluated.flat()\n const regExpStringPattern = isRegExpStringPatternCall(callee, args)\n if (regExpStringPattern) {\n checkRegExpStringPattern(args[0])\n }\n checkRegExpInputLength(fnObject, args, regExpStringPattern)\n return fn.call(fnObject, ...args)\n }\n return null\n }\n\n private async evaluateIdentifier(expressionNode: CallExpression, callee: IdentifierExpression): Promise<any> {\n // Arguments is a reserved word in strict mode\n const { arguments: exprArgs } = expressionNode\n const { object: contextObject } = this.context\n\n const { name: fnName } = callee\n\n const functionInfo = this.evaluator.functions[fnName]\n if (functionInfo) {\n // The function is among the custom functions.\n return this.evaluateCustomIdentifier(expressionNode, fnName)\n }\n // identifier is a global object\n const globalFn = getGlobalObjectProperty(fnName, contextObject)\n if (globalFn !== null) {\n // global functions like String, Number, must be invoked passing node value as argument, not the node itself\n const contextArg = { ...this.context, evaluateToNode: false }\n const args = await Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, contextArg)))\n return globalFn(...args)\n }\n\n throw new SystemError(SystemErrorKeys.expression.undefinedFunction, { name: fnName })\n }\n\n private async evaluateCustomIdentifier(expressionNode: CallExpression, fnName: string): Promise<any> {\n // Arguments is a reserved word in strict mode\n const { arguments: exprArgs } = expressionNode\n const numArgs = exprArgs.length\n\n const expressionFunction: ExpressionFunction<C> = this.evaluator.functions[fnName]\n\n const { minArity, maxArity, evaluateArgsToNodes, executor } = expressionFunction\n\n if (numArgs < minArity)\n throw new SystemError(SystemErrorKeys.expression.functionHasTooFewArguments, { fnName, minArity, numArgs })\n if (maxArity !== undefined && maxArity >= 0 && numArgs > maxArity)\n throw new SystemError(SystemErrorKeys.expression.functionHasTooManyArguments, { fnName, maxArity, numArgs })\n\n // evaluate arguments\n const argsContext = { ...this.context, evaluateToNode: evaluateArgsToNodes }\n const args = await Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, argsContext)))\n\n // Currently there are no side effects from function evaluation so it's\n // safe to call the function even when we're just parsing the expression\n // to find all identifiers being used.\n return executor(this.context)(...args)\n }\n}\n"]}
1
+ {"version":3,"file":"call.js","sourceRoot":"","sources":["../../../../src/expression/javascript/node/call.ts"],"names":[],"mappings":"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","sourcesContent":["import { SystemError, SystemErrorKeys } from '../../../error'\nimport { ExpressionContext } from '../../context'\nimport { ExpressionFunction } from '../../function'\nimport {\n CallExpression,\n ExpressionNodeEvaluator,\n ExpressionNodeType,\n IdentifierExpression,\n LiteralExpression,\n MemberExpression,\n} from '../../node'\nimport { getGlobalObjectProperty } from '../global'\nimport { hasNestedQuantifiers, REG_EXP_INPUT_MAX_LENGTH } from '../regExpSafety'\n\n// String functions converting a string argument into a regular expression (replace, replaceAll and split don't)\nconst stringFunctionsWithRegExpArgument = new Set(['match', 'matchAll', 'search'])\n\n// e.g. text.match('(a+)+$'): the string pattern is checked like a regular expression literal\nconst isRegExpStringPatternCall = (callee: MemberExpression, args: any[]): boolean => {\n const { computed, property } = callee\n return (\n !computed &&\n property.type === ExpressionNodeType.Identifier &&\n stringFunctionsWithRegExpArgument.has((property as IdentifierExpression).name) &&\n typeof args[0] === 'string'\n )\n}\n\nconst checkRegExpStringPattern = (pattern: string): void => {\n if (hasNestedQuantifiers(pattern)) {\n throw new SystemError(SystemErrorKeys.expression.unsafeRegularExpression, { pattern })\n }\n}\n\n// e.g. /.../.test(text) or text.match(/.../): the time taken by a regular expression grows with the input length\nconst checkRegExpInputLength = (fnObject: any, args: any[], regExpStringPattern: boolean): void => {\n const values = [fnObject, ...args]\n if (!regExpStringPattern && !values.some((value) => value instanceof RegExp)) return\n const inputTooLong = values.find((value) => typeof value === 'string' && value.length > REG_EXP_INPUT_MAX_LENGTH)\n if (inputTooLong !== undefined) {\n throw new SystemError(SystemErrorKeys.expression.regularExpressionInputTooLong, {\n maxLength: REG_EXP_INPUT_MAX_LENGTH,\n length: inputTooLong.length,\n })\n }\n}\n\nexport class CallEvaluator<C extends ExpressionContext> extends ExpressionNodeEvaluator<C, CallExpression> {\n async evaluate(expressionNode: CallExpression): Promise<any> {\n const { callee } = expressionNode\n\n if (callee.type === ExpressionNodeType.Member) {\n return this.evaluateMember(expressionNode, callee)\n } else if (callee.type === ExpressionNodeType.Identifier) {\n return this.evaluateIdentifier(expressionNode, callee)\n }\n\n // No complex expressions may be put in place of a function body.\n // Only a plain identifier is allowed.\n throw new SystemError(SystemErrorKeys.expression.invalidCalleeType, { type: callee.type })\n }\n\n private async evaluateMember(expressionNode: CallExpression, callee: MemberExpression): Promise<any> {\n const { arguments: exprArgs } = expressionNode\n\n // evaluate the callee object only once: evaluating it again to find the function would take exponential time\n // with chained calls (e.g. text.trim().toUpperCase().toLowerCase())\n const fnObject = await this.evaluator.evaluateNode(callee.object, this.context)\n if (fnObject === null || fnObject === undefined) return null\n\n // find the function (e.g. Math.round or text.toUpperCase) among the properties of the already evaluated object\n const fnObjectLiteral: LiteralExpression = { type: ExpressionNodeType.Literal, value: fnObject, raw: '' }\n const calleeWithObjectEvaluated: MemberExpression = { ...callee, object: fnObjectLiteral }\n const fn = await this.evaluator.evaluateNode(calleeWithObjectEvaluated, this.context)\n if (fn) {\n const argsEvaluated = await Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, this.context)))\n const args = argsEvaluated.flat()\n const regExpStringPattern = isRegExpStringPatternCall(callee, args)\n if (regExpStringPattern) {\n checkRegExpStringPattern(args[0])\n }\n checkRegExpInputLength(fnObject, args, regExpStringPattern)\n return fn.call(fnObject, ...args)\n }\n return null\n }\n\n private async evaluateIdentifier(expressionNode: CallExpression, callee: IdentifierExpression): Promise<any> {\n // Arguments is a reserved word in strict mode\n const { arguments: exprArgs } = expressionNode\n const { object: contextObject } = this.context\n\n const { name: fnName } = callee\n\n const functionInfo = this.evaluator.functions[fnName]\n if (functionInfo) {\n // The function is among the custom functions.\n return this.evaluateCustomIdentifier(expressionNode, fnName)\n }\n // identifier is a global object\n const globalFn = getGlobalObjectProperty(fnName, contextObject)\n if (globalFn !== null) {\n // global functions like String, Number, must be invoked passing node value as argument, not the node itself\n const contextArg = { ...this.context, evaluateToNode: false }\n const args = await Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, contextArg)))\n return globalFn(...args)\n }\n\n throw new SystemError(SystemErrorKeys.expression.undefinedFunction, { name: fnName })\n }\n\n private async evaluateCustomIdentifier(expressionNode: CallExpression, fnName: string): Promise<any> {\n // Arguments is a reserved word in strict mode\n const { arguments: exprArgs } = expressionNode\n const numArgs = exprArgs.length\n\n const expressionFunction: ExpressionFunction<C> = this.evaluator.functions[fnName]\n\n const { minArity, maxArity, evaluateArgsToNodes, executor } = expressionFunction\n\n if (numArgs < minArity)\n throw new SystemError(SystemErrorKeys.expression.functionHasTooFewArguments, { fnName, minArity, numArgs })\n if (maxArity !== undefined && maxArity >= 0 && numArgs > maxArity)\n throw new SystemError(SystemErrorKeys.expression.functionHasTooManyArguments, { fnName, maxArity, numArgs })\n\n // evaluate arguments\n const argsContext = { ...this.context, evaluateToNode: evaluateArgsToNodes }\n const args = await Promise.all(exprArgs.map((arg) => this.evaluator.evaluateNode(arg, argsContext)))\n\n // Currently there are no side effects from function evaluation so it's\n // safe to call the function even when we're just parsing the expression\n // to find all identifiers being used.\n return executor(this.context)(...args)\n }\n}\n"]}
@@ -42,7 +42,8 @@ class MemberEvaluator extends node_1.ExpressionNodeEvaluator {
42
42
  return __awaiter(this, void 0, void 0, function* () {
43
43
  const { object, property, computed } = expressionNode;
44
44
  const objectEval = yield this.evaluator.evaluateNode(object, Object.assign(Object.assign({}, this.context), { evaluateToNode: true }));
45
- if (!objectEval)
45
+ // null or undefined object (falsy values like 0 or '' have properties too)
46
+ if (objectEval === null || objectEval === undefined)
46
47
  return null;
47
48
  if (computed) {
48
49
  return this.evaluateComputedProperty(property, objectEval);
@@ -1 +1 @@
1
- {"version":3,"file":"member.js","sourceRoot":"","sources":["../../../../src/expression/javascript/node/member.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,qCAAsF;AAEtF,qBAA0D,SAAQ,8BAA4C;IACtG,wBAAwB,CAAC,QAA6B,EAAE,UAAe;;YAC3E,iDAAiD;YAEjD,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,kCAC1D,IAAI,CAAC,OAAO,KACf,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,IAC3B,CAAA;YAEF,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;gBACrC,2BAA2B;gBAC3B,OAAO,UAAU,CAAC,YAAY,CAAC,CAAA;YACjC,CAAC;YAED,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC9B,uFAAuF;gBAEvF,MAAM,QAAQ,GAAG,EAAE,CAAA;gBACnB,KAAK,MAAM,cAAc,IAAI,UAAU,EAAE,CAAC;oBACxC,MAAM,WAAW,mCAAQ,IAAI,CAAC,OAAO,KAAE,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,GAAE,CAAA;oBACtF,oGAAoG;oBACpG,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAA,CAAC,UAAU;oBACvF,IAAI,WAAW,EAAE,CAAC;wBAChB,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;oBAC/B,CAAC;gBACH,CAAC;gBACD,OAAO,QAAQ,CAAA;YACjB,CAAC;iBAAM,CAAC;gBACN,OAAO,YAAY,CAAA;YACrB,CAAC;QACH,CAAC;KAAA;IAEK,QAAQ,CAAC,cAAgC;;YAC7C,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAA;YAErD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM,kCAAO,IAAI,CAAC,OAAO,KAAE,cAAc,EAAE,IAAI,IAAG,CAAA;YACvG,IAAI,CAAC,UAAU;gBAAE,OAAO,IAAI,CAAA;YAE5B,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,IAAI,CAAC,wBAAwB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;YAC5D,CAAC;YAED,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,kCAC1D,IAAI,CAAC,OAAO,KACf,MAAM,EAAE,UAAU,IAClB,CAAA;YAEF,OAAO,YAAY,CAAA;QACrB,CAAC;KAAA;CACF","sourcesContent":["import { ExpressionContext } from '../../context'\nimport { ExpressionNode, ExpressionNodeEvaluator, MemberExpression } from '../../node'\n\nexport class MemberEvaluator<C extends ExpressionContext> extends ExpressionNodeEvaluator<C, MemberExpression> {\n async evaluateComputedProperty(property: ExpressionNode<any>, objectEval: any): Promise<any> {\n // expressions like plot[1] or plot[plot_id == 1]\n\n const propertyEval = await this.evaluator.evaluateNode(property, {\n ...this.context,\n object: this.context.object,\n })\n\n if (typeof propertyEval === 'number') {\n // expressions like plot[1]\n return objectEval[propertyEval]\n }\n\n if (Array.isArray(objectEval)) {\n // expressions where the member property is a filter function (e.g. plot[plot_id == 1])\n\n const filtered = []\n for (const objectEvalItem of objectEval) {\n const itemContext = { ...this.context, object: objectEvalItem, evaluateToNode: false }\n // evaluate items sequentially, to avoid concurrent category item / taxon lookups on large node sets\n const itemMatches = await this.evaluator.evaluateNode(property, itemContext) // NOSONAR\n if (itemMatches) {\n filtered.push(objectEvalItem)\n }\n }\n return filtered\n } else {\n return propertyEval\n }\n }\n\n async evaluate(expressionNode: MemberExpression): Promise<any> {\n const { object, property, computed } = expressionNode\n\n const objectEval = await this.evaluator.evaluateNode(object, { ...this.context, evaluateToNode: true })\n if (!objectEval) return null\n\n if (computed) {\n return this.evaluateComputedProperty(property, objectEval)\n }\n\n const propertyEval = await this.evaluator.evaluateNode(property, {\n ...this.context,\n object: objectEval,\n })\n\n return propertyEval\n }\n}\n"]}
1
+ {"version":3,"file":"member.js","sourceRoot":"","sources":["../../../../src/expression/javascript/node/member.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,qCAAsF;AAEtF,qBAA0D,SAAQ,8BAA4C;IACtG,wBAAwB,CAAC,QAA6B,EAAE,UAAe;;YAC3E,iDAAiD;YAEjD,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,kCAC1D,IAAI,CAAC,OAAO,KACf,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,IAC3B,CAAA;YAEF,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;gBACrC,2BAA2B;gBAC3B,OAAO,UAAU,CAAC,YAAY,CAAC,CAAA;YACjC,CAAC;YAED,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC9B,uFAAuF;gBAEvF,MAAM,QAAQ,GAAG,EAAE,CAAA;gBACnB,KAAK,MAAM,cAAc,IAAI,UAAU,EAAE,CAAC;oBACxC,MAAM,WAAW,mCAAQ,IAAI,CAAC,OAAO,KAAE,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,GAAE,CAAA;oBACtF,oGAAoG;oBACpG,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAA,CAAC,UAAU;oBACvF,IAAI,WAAW,EAAE,CAAC;wBAChB,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;oBAC/B,CAAC;gBACH,CAAC;gBACD,OAAO,QAAQ,CAAA;YACjB,CAAC;iBAAM,CAAC;gBACN,OAAO,YAAY,CAAA;YACrB,CAAC;QACH,CAAC;KAAA;IAEK,QAAQ,CAAC,cAAgC;;YAC7C,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAA;YAErD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM,kCAAO,IAAI,CAAC,OAAO,KAAE,cAAc,EAAE,IAAI,IAAG,CAAA;YACvG,2EAA2E;YAC3E,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,KAAK,SAAS;gBAAE,OAAO,IAAI,CAAA;YAEhE,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,IAAI,CAAC,wBAAwB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;YAC5D,CAAC;YAED,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,kCAC1D,IAAI,CAAC,OAAO,KACf,MAAM,EAAE,UAAU,IAClB,CAAA;YAEF,OAAO,YAAY,CAAA;QACrB,CAAC;KAAA;CACF","sourcesContent":["import { ExpressionContext } from '../../context'\nimport { ExpressionNode, ExpressionNodeEvaluator, MemberExpression } from '../../node'\n\nexport class MemberEvaluator<C extends ExpressionContext> extends ExpressionNodeEvaluator<C, MemberExpression> {\n async evaluateComputedProperty(property: ExpressionNode<any>, objectEval: any): Promise<any> {\n // expressions like plot[1] or plot[plot_id == 1]\n\n const propertyEval = await this.evaluator.evaluateNode(property, {\n ...this.context,\n object: this.context.object,\n })\n\n if (typeof propertyEval === 'number') {\n // expressions like plot[1]\n return objectEval[propertyEval]\n }\n\n if (Array.isArray(objectEval)) {\n // expressions where the member property is a filter function (e.g. plot[plot_id == 1])\n\n const filtered = []\n for (const objectEvalItem of objectEval) {\n const itemContext = { ...this.context, object: objectEvalItem, evaluateToNode: false }\n // evaluate items sequentially, to avoid concurrent category item / taxon lookups on large node sets\n const itemMatches = await this.evaluator.evaluateNode(property, itemContext) // NOSONAR\n if (itemMatches) {\n filtered.push(objectEvalItem)\n }\n }\n return filtered\n } else {\n return propertyEval\n }\n }\n\n async evaluate(expressionNode: MemberExpression): Promise<any> {\n const { object, property, computed } = expressionNode\n\n const objectEval = await this.evaluator.evaluateNode(object, { ...this.context, evaluateToNode: true })\n // null or undefined object (falsy values like 0 or '' have properties too)\n if (objectEval === null || objectEval === undefined) return null\n\n if (computed) {\n return this.evaluateComputedProperty(property, objectEval)\n }\n\n const propertyEval = await this.evaluator.evaluateNode(property, {\n ...this.context,\n object: objectEval,\n })\n\n return propertyEval\n }\n}\n"]}
@@ -84,6 +84,16 @@ class RecordIdentifierEvaluator extends identifier_1.IdentifierEvaluator {
84
84
  if ((0, global_1.isForbiddenPropertyName)(propName)) {
85
85
  throw new error_1.SystemError(error_1.SystemErrorKeys.expression.identifierNotFound, { name: propName });
86
86
  }
87
+ const { survey, record, evaluateToNode, object: contextObject, item } = this.context;
88
+ if (contextObject === null || contextObject === void 0 ? void 0 : contextObject.nodeDefUuid) {
89
+ // context object is a record node: its own properties (id, uuid, value...) are not accessible,
90
+ // so that node defs named like them are always found (as in NodeDefIdentifierEvaluator)
91
+ const globalProp = (0, global_1.getGlobalObjectProperty)(propName, contextObject);
92
+ if (globalProp !== null) {
93
+ return globalProp;
94
+ }
95
+ return evaluateIdentifierOnNode({ survey, record, nodeObject: contextObject, evaluateToNode, propName });
96
+ }
87
97
  // try to find identifier among global or native properties
88
98
  // (not-found is signaled without throwing; the try/catch only handles the rare case of an unknown member of a global object)
89
99
  try {
@@ -97,7 +107,6 @@ class RecordIdentifierEvaluator extends identifier_1.IdentifierEvaluator {
97
107
  }
98
108
  // identifier not found among global or native properties
99
109
  // identifier should be a node or a node value property
100
- const { survey, record, evaluateToNode, object: contextObject, item } = this.context;
101
110
  if (item && item === contextObject) {
102
111
  // evaluating category or taxon item prop or extra prop
103
112
  return (_d = (_a = item.props) === null || _a === void 0 ? void 0 : _a[propName]) !== null && _d !== void 0 ? _d : (_c = (_b = item.props) === null || _b === void 0 ? void 0 : _b.extra) === null || _c === void 0 ? void 0 : _c[propName];
@@ -1 +1 @@
1
- {"version":3,"file":"identifier.js","sourceRoot":"","sources":["../../../../../src/record/recordExpressionEvaluator/node/identifier/identifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { ExpressionVariable, IdentifierExpression } from '../../../../expression'\nimport { isForbiddenPropertyName } from '../../../../expression/javascript/global'\nimport { IdentifierEvaluator } from '../../../../expression/javascript/node/identifier'\nimport { ArenaRecordNode } from '../../../../node'\nimport { NodeDef, NodeDefs, NodeDefType } from '../../../../nodeDef'\nimport { Records } from '../../../records'\nimport { NodeValues } from '../../../../node/nodeValues'\nimport { Survey, Surveys } from '../../../../survey'\nimport { RecordExpressionContext } from '../../context'\nimport { NodesFinder } from './nodesFinder'\nimport { SystemError, SystemErrorKeys } from '../../../../error'\nimport { NodeValueExtractor } from '../../nodeValueExtractor'\nimport { ArenaRecord } from '../../../record'\n\nconst getNodesOrValues = (params: {\n survey: Survey\n referencedNodes: ArenaRecordNode[]\n nodeDefReferenced: NodeDef<any>\n propName: string\n evaluateToNode?: boolean\n}): any[] => {\n const { survey, nodeDefReferenced, referencedNodes, propName, evaluateToNode } = params\n const single = !nodeDefReferenced.props.multiple\n if (single) {\n if (referencedNodes.length === 0) {\n // do not throw error if node is missing, consider its value as null\n // throw new SystemError(SystemErrorKeys.expression.nodeNotFoundForNodeDef, { nodeDefName: propName })\n return []\n }\n if (referencedNodes.length > 1)\n throw new SystemError(SystemErrorKeys.expression.multipleNodesFoundForNodeDef, { nodeDefName: propName })\n }\n if (nodeDefReferenced.type === NodeDefType.entity || evaluateToNode) {\n // return nodes\n return referencedNodes\n }\n // return node values\n return referencedNodes.map((referencedNode) =>\n NodeValueExtractor.getNodeValue({ survey, node: referencedNode, nodeDef: nodeDefReferenced })\n )\n}\n\nconst evaluateIdentifierOnNode = (params: {\n survey: Survey\n record: ArenaRecord\n nodeObject: ArenaRecordNode\n evaluateToNode?: boolean\n propName: string\n}) => {\n const { survey, record, nodeObject, evaluateToNode = false, propName } = params\n const { nodeDefUuid: nodeDefObjectUuid, value } = nodeObject\n if (!nodeDefObjectUuid) {\n throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName })\n }\n\n const nodeDefObject = Surveys.getNodeDefByUuid({ survey, uuid: nodeDefObjectUuid })\n\n // node value prop (native)\n const valueProp = value?.[propName]\n if (valueProp !== undefined) {\n return valueProp\n }\n if (NodeDefs.isAttribute(nodeDefObject)) {\n // node value prop (Arena specific value property)\n if (NodeValues.isValueProp({ nodeDef: nodeDefObject, prop: propName })) {\n return NodeValues.getNodeValueProp({ nodeDef: nodeDefObject, node: nodeObject, prop: propName })\n } else {\n throw new SystemError(SystemErrorKeys.expression.invalidAttributeValuePropertyName, {\n attributeName: NodeDefs.getName(nodeDefObject),\n propName,\n })\n }\n }\n\n const nodeDefReferenced = Surveys.findNodeDefByName({ survey, name: propName })\n\n if (!nodeDefReferenced) {\n throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName })\n }\n\n if (nodeObject.nodeDefUuid === nodeDefReferenced.uuid) {\n // the referenced node is the current node itself\n return nodeObject\n }\n if (Surveys.isNodeDefAncestor({ nodeDefAncestor: nodeDefReferenced, nodeDefDescendant: nodeDefObject })) {\n return Records.getAncestor(nodeObject, nodeDefReferenced.uuid)(record)\n }\n // the referenced nodes can be siblings of the current node\n const referencedNodes = NodesFinder.findDescendants({ survey, record, nodeContext: nodeObject, nodeDefReferenced })\n const nodesOrValues = getNodesOrValues({ survey, referencedNodes, nodeDefReferenced, propName, evaluateToNode })\n return nodeDefReferenced.props.multiple ? nodesOrValues : nodesOrValues[0]\n}\n\nexport class RecordIdentifierEvaluator extends IdentifierEvaluator<RecordExpressionContext> {\n evaluate(expressionNode: IdentifierExpression): Promise<any> {\n return Promise.resolve(this.evaluateValue(expressionNode))\n }\n\n private evaluateValue(expressionNode: IdentifierExpression): any {\n const { name: propName } = expressionNode\n\n if (propName === ExpressionVariable.CONTEXT) {\n return this.context.nodeContext\n }\n if (isForbiddenPropertyName(propName)) {\n throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName })\n }\n\n // try to find identifier among global or native properties\n // (not-found is signaled without throwing; the try/catch only handles the rare case of an unknown member of a global object)\n try {\n const property = this.findGlobalOrNativeProperty(expressionNode)\n if (property) {\n return property.value\n }\n } catch {\n // ignore it\n }\n\n // identifier not found among global or native properties\n // identifier should be a node or a node value property\n const { survey, record, evaluateToNode, object: contextObject, item } = this.context\n\n if (item && item === contextObject) {\n // evaluating category or taxon item prop or extra prop\n return item.props?.[propName] ?? item.props?.extra?.[propName]\n }\n\n if (Array.isArray(contextObject)) {\n return contextObject.flatMap((contextNode) => {\n const evaluationResult = evaluateIdentifierOnNode({\n survey,\n record,\n nodeObject: contextNode,\n evaluateToNode,\n propName,\n })\n return Array.isArray(evaluationResult) ? evaluationResult : [evaluationResult]\n })\n }\n return evaluateIdentifierOnNode({ survey, record, nodeObject: contextObject, evaluateToNode, propName })\n }\n}\n"]}
1
+ {"version":3,"file":"identifier.js","sourceRoot":"","sources":["../../../../../src/record/recordExpressionEvaluator/node/identifier/identifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { ExpressionVariable, IdentifierExpression } from '../../../../expression'\nimport { getGlobalObjectProperty, isForbiddenPropertyName } from '../../../../expression/javascript/global'\nimport { IdentifierEvaluator } from '../../../../expression/javascript/node/identifier'\nimport { ArenaRecordNode } from '../../../../node'\nimport { NodeDef, NodeDefs, NodeDefType } from '../../../../nodeDef'\nimport { Records } from '../../../records'\nimport { NodeValues } from '../../../../node/nodeValues'\nimport { Survey, Surveys } from '../../../../survey'\nimport { RecordExpressionContext } from '../../context'\nimport { NodesFinder } from './nodesFinder'\nimport { SystemError, SystemErrorKeys } from '../../../../error'\nimport { NodeValueExtractor } from '../../nodeValueExtractor'\nimport { ArenaRecord } from '../../../record'\n\nconst getNodesOrValues = (params: {\n survey: Survey\n referencedNodes: ArenaRecordNode[]\n nodeDefReferenced: NodeDef<any>\n propName: string\n evaluateToNode?: boolean\n}): any[] => {\n const { survey, nodeDefReferenced, referencedNodes, propName, evaluateToNode } = params\n const single = !nodeDefReferenced.props.multiple\n if (single) {\n if (referencedNodes.length === 0) {\n // do not throw error if node is missing, consider its value as null\n // throw new SystemError(SystemErrorKeys.expression.nodeNotFoundForNodeDef, { nodeDefName: propName })\n return []\n }\n if (referencedNodes.length > 1)\n throw new SystemError(SystemErrorKeys.expression.multipleNodesFoundForNodeDef, { nodeDefName: propName })\n }\n if (nodeDefReferenced.type === NodeDefType.entity || evaluateToNode) {\n // return nodes\n return referencedNodes\n }\n // return node values\n return referencedNodes.map((referencedNode) =>\n NodeValueExtractor.getNodeValue({ survey, node: referencedNode, nodeDef: nodeDefReferenced })\n )\n}\n\nconst evaluateIdentifierOnNode = (params: {\n survey: Survey\n record: ArenaRecord\n nodeObject: ArenaRecordNode\n evaluateToNode?: boolean\n propName: string\n}) => {\n const { survey, record, nodeObject, evaluateToNode = false, propName } = params\n const { nodeDefUuid: nodeDefObjectUuid, value } = nodeObject\n if (!nodeDefObjectUuid) {\n throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName })\n }\n\n const nodeDefObject = Surveys.getNodeDefByUuid({ survey, uuid: nodeDefObjectUuid })\n\n // node value prop (native)\n const valueProp = value?.[propName]\n if (valueProp !== undefined) {\n return valueProp\n }\n if (NodeDefs.isAttribute(nodeDefObject)) {\n // node value prop (Arena specific value property)\n if (NodeValues.isValueProp({ nodeDef: nodeDefObject, prop: propName })) {\n return NodeValues.getNodeValueProp({ nodeDef: nodeDefObject, node: nodeObject, prop: propName })\n } else {\n throw new SystemError(SystemErrorKeys.expression.invalidAttributeValuePropertyName, {\n attributeName: NodeDefs.getName(nodeDefObject),\n propName,\n })\n }\n }\n\n const nodeDefReferenced = Surveys.findNodeDefByName({ survey, name: propName })\n\n if (!nodeDefReferenced) {\n throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName })\n }\n\n if (nodeObject.nodeDefUuid === nodeDefReferenced.uuid) {\n // the referenced node is the current node itself\n return nodeObject\n }\n if (Surveys.isNodeDefAncestor({ nodeDefAncestor: nodeDefReferenced, nodeDefDescendant: nodeDefObject })) {\n return Records.getAncestor(nodeObject, nodeDefReferenced.uuid)(record)\n }\n // the referenced nodes can be siblings of the current node\n const referencedNodes = NodesFinder.findDescendants({ survey, record, nodeContext: nodeObject, nodeDefReferenced })\n const nodesOrValues = getNodesOrValues({ survey, referencedNodes, nodeDefReferenced, propName, evaluateToNode })\n return nodeDefReferenced.props.multiple ? nodesOrValues : nodesOrValues[0]\n}\n\nexport class RecordIdentifierEvaluator extends IdentifierEvaluator<RecordExpressionContext> {\n evaluate(expressionNode: IdentifierExpression): Promise<any> {\n return Promise.resolve(this.evaluateValue(expressionNode))\n }\n\n private evaluateValue(expressionNode: IdentifierExpression): any {\n const { name: propName } = expressionNode\n\n if (propName === ExpressionVariable.CONTEXT) {\n return this.context.nodeContext\n }\n if (isForbiddenPropertyName(propName)) {\n throw new SystemError(SystemErrorKeys.expression.identifierNotFound, { name: propName })\n }\n\n const { survey, record, evaluateToNode, object: contextObject, item } = this.context\n\n if (contextObject?.nodeDefUuid) {\n // context object is a record node: its own properties (id, uuid, value...) are not accessible,\n // so that node defs named like them are always found (as in NodeDefIdentifierEvaluator)\n const globalProp = getGlobalObjectProperty(propName, contextObject)\n if (globalProp !== null) {\n return globalProp\n }\n return evaluateIdentifierOnNode({ survey, record, nodeObject: contextObject, evaluateToNode, propName })\n }\n\n // try to find identifier among global or native properties\n // (not-found is signaled without throwing; the try/catch only handles the rare case of an unknown member of a global object)\n try {\n const property = this.findGlobalOrNativeProperty(expressionNode)\n if (property) {\n return property.value\n }\n } catch {\n // ignore it\n }\n\n // identifier not found among global or native properties\n // identifier should be a node or a node value property\n\n if (item && item === contextObject) {\n // evaluating category or taxon item prop or extra prop\n return item.props?.[propName] ?? item.props?.extra?.[propName]\n }\n\n if (Array.isArray(contextObject)) {\n return contextObject.flatMap((contextNode) => {\n const evaluationResult = evaluateIdentifierOnNode({\n survey,\n record,\n nodeObject: contextNode,\n evaluateToNode,\n propName,\n })\n return Array.isArray(evaluationResult) ? evaluationResult : [evaluationResult]\n })\n }\n return evaluateIdentifierOnNode({ survey, record, nodeObject: contextObject, evaluateToNode, propName })\n }\n}\n"]}
@@ -51,8 +51,9 @@ const updateDependentCodeAttributes = (params) => __awaiter(void 0, void 0, void
51
51
  const { survey, record, node, categoryItemProvider, sideEffect = false } = params;
52
52
  const updateResult = new recordUpdateResult_1.RecordUpdateResult({ record });
53
53
  const recordUpdateOptions = { sideEffect };
54
- // 1. get dependent code attributes
55
- const dependentCodeAttributes = records_1.Records.getDependentCodeAttributes(node)(record);
54
+ // 1. get dependent code attributes: only the direct ones (the index registers every ancestor code attribute);
55
+ // deeper levels are checked when the cleared direct dependents are visited
56
+ const dependentCodeAttributes = records_1.Records.getDependentCodeAttributes(node)(record).filter((dependentCodeAttribute) => utils_1.Arrays.last(node_1.Nodes.getHierarchyCode(dependentCodeAttribute)) === node.uuid);
56
57
  if (dependentCodeAttributes.length === 0)
57
58
  return updateResult;
58
59
  const parentItemUuid = node_1.NodeValues.getItemUuid(node);
@@ -1 +1 @@
1
- {"version":3,"file":"recordNodeDependentsCodeAttributesUpdater.js","sourceRoot":"","sources":["../../../src/record/recordNodesUpdater/recordNodeDependentsCodeAttributesUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodeValues, Nodes } from '../../node'\nimport { NodeDefCode, NodeDefs } from '../../nodeDef'\nimport { Surveys } from '../../survey'\nimport { Dates } from '../../utils'\nimport { Records } from '../records'\nimport type { ArenaRecord } from '../record'\nimport { RecordNodeDependentsUpdateParams } from './recordNodeDependentsUpdateParams'\nimport { RecordUpdateResult } from './recordUpdateResult'\n\n// A dependent code attribute's stored item is still valid as long as its category item's parent is the\n// (possibly unchanged) item currently selected on the parent code attribute node - so this single check\n// covers both \"did the parent's value change\" and \"is the dependent's value still valid\": if the parent's\n// item didn't change, the dependent's item still has the same parent and is left alone.\n//\n// The dependent's item may belong to a \"big\" category whose items aren't loaded into the survey's\n// in-memory ref data index (e.g. an externally-referenced category) - Surveys.getCategoryItemByUuid only\n// ever looks there, so it falls back to the async categoryItemProvider (same pattern as attribute value\n// validation) before concluding the item doesn't exist.\nconst isDependentCodeAttributeValueStillValid = async (\n params: Pick<RecordNodeDependentsUpdateParams, 'survey' | 'categoryItemProvider'> & {\n parentItemUuid?: string\n dependentCodeAttribute: ArenaRecordNode\n dependentCodeAttributeDef: NodeDefCode\n record: ArenaRecord\n }\n): Promise<boolean> => {\n const { survey, parentItemUuid, dependentCodeAttribute, dependentCodeAttributeDef, record, categoryItemProvider } =\n params\n if (!parentItemUuid) return false\n\n const dependentItemUuid = NodeValues.getItemUuid(dependentCodeAttribute)\n if (!dependentItemUuid) return false\n\n let dependentItem = Surveys.getCategoryItemByUuid({ survey, itemUuid: dependentItemUuid })\n if (!dependentItem && categoryItemProvider) {\n const categoryUuid = NodeDefs.getCategoryUuid(dependentCodeAttributeDef)\n if (categoryUuid) {\n const draft = !!record.preview\n dependentItem = await categoryItemProvider.getItemByUuid({\n survey,\n categoryUuid,\n itemUuid: dependentItemUuid,\n draft,\n })\n }\n }\n return dependentItem?.parentUuid === parentItemUuid\n}\n\nexport const updateDependentCodeAttributes = async (params: RecordNodeDependentsUpdateParams) => {\n const { survey, record, node, categoryItemProvider, sideEffect = false } = params\n\n const updateResult = new RecordUpdateResult({ record })\n\n const recordUpdateOptions = { sideEffect }\n\n // 1. get dependent code attributes\n const dependentCodeAttributes = Records.getDependentCodeAttributes(node)(record)\n if (dependentCodeAttributes.length === 0) return updateResult\n\n const parentItemUuid = NodeValues.getItemUuid(node)\n\n // 2. clear dependent code attributes' values that are no longer valid children of the parent's current item\n for (const dependentCodeAttribute of dependentCodeAttributes) {\n const dependentCodeAttributeDef = Surveys.getNodeDefByUuid({\n survey,\n uuid: dependentCodeAttribute.nodeDefUuid,\n }) as NodeDefCode\n if (NodeDefs.isReadOnly(dependentCodeAttributeDef) || !Nodes.hasUserInputValue(dependentCodeAttribute)) continue\n\n const validityCheckParams = {\n survey,\n parentItemUuid,\n dependentCodeAttribute,\n dependentCodeAttributeDef,\n record,\n categoryItemProvider,\n }\n // check attributes sequentially: invalid values are cleared in the record at every iteration\n const stillValid = await isDependentCodeAttributeValueStillValid(validityCheckParams) // NOSONAR\n if (stillValid) continue\n\n const nodeUpdated: ArenaRecordNode = {\n ...dependentCodeAttribute,\n value: null,\n updated: true,\n dateModified: Dates.nowFormattedForStorage(),\n }\n updateResult.addNode(nodeUpdated, recordUpdateOptions)\n updateResult.addClearedDefUuid(nodeUpdated.nodeDefUuid)\n }\n\n return updateResult\n}\n"]}
1
+ {"version":3,"file":"recordNodeDependentsCodeAttributesUpdater.js","sourceRoot":"","sources":["../../../src/record/recordNodesUpdater/recordNodeDependentsCodeAttributesUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodeValues, Nodes } from '../../node'\nimport { NodeDefCode, NodeDefs } from '../../nodeDef'\nimport { Surveys } from '../../survey'\nimport { Arrays, Dates } from '../../utils'\nimport { Records } from '../records'\nimport type { ArenaRecord } from '../record'\nimport { RecordNodeDependentsUpdateParams } from './recordNodeDependentsUpdateParams'\nimport { RecordUpdateResult } from './recordUpdateResult'\n\n// A dependent code attribute's stored item is still valid as long as its category item's parent is the\n// (possibly unchanged) item currently selected on the parent code attribute node - so this single check\n// covers both \"did the parent's value change\" and \"is the dependent's value still valid\": if the parent's\n// item didn't change, the dependent's item still has the same parent and is left alone.\n//\n// The dependent's item may belong to a \"big\" category whose items aren't loaded into the survey's\n// in-memory ref data index (e.g. an externally-referenced category) - Surveys.getCategoryItemByUuid only\n// ever looks there, so it falls back to the async categoryItemProvider (same pattern as attribute value\n// validation) before concluding the item doesn't exist.\nconst isDependentCodeAttributeValueStillValid = async (\n params: Pick<RecordNodeDependentsUpdateParams, 'survey' | 'categoryItemProvider'> & {\n parentItemUuid?: string\n dependentCodeAttribute: ArenaRecordNode\n dependentCodeAttributeDef: NodeDefCode\n record: ArenaRecord\n }\n): Promise<boolean> => {\n const { survey, parentItemUuid, dependentCodeAttribute, dependentCodeAttributeDef, record, categoryItemProvider } =\n params\n if (!parentItemUuid) return false\n\n const dependentItemUuid = NodeValues.getItemUuid(dependentCodeAttribute)\n if (!dependentItemUuid) return false\n\n let dependentItem = Surveys.getCategoryItemByUuid({ survey, itemUuid: dependentItemUuid })\n if (!dependentItem && categoryItemProvider) {\n const categoryUuid = NodeDefs.getCategoryUuid(dependentCodeAttributeDef)\n if (categoryUuid) {\n const draft = !!record.preview\n dependentItem = await categoryItemProvider.getItemByUuid({\n survey,\n categoryUuid,\n itemUuid: dependentItemUuid,\n draft,\n })\n }\n }\n return dependentItem?.parentUuid === parentItemUuid\n}\n\nexport const updateDependentCodeAttributes = async (params: RecordNodeDependentsUpdateParams) => {\n const { survey, record, node, categoryItemProvider, sideEffect = false } = params\n\n const updateResult = new RecordUpdateResult({ record })\n\n const recordUpdateOptions = { sideEffect }\n\n // 1. get dependent code attributes: only the direct ones (the index registers every ancestor code attribute);\n // deeper levels are checked when the cleared direct dependents are visited\n const dependentCodeAttributes = Records.getDependentCodeAttributes(node)(record).filter(\n (dependentCodeAttribute) => Arrays.last(Nodes.getHierarchyCode(dependentCodeAttribute)) === node.uuid\n )\n if (dependentCodeAttributes.length === 0) return updateResult\n\n const parentItemUuid = NodeValues.getItemUuid(node)\n\n // 2. clear dependent code attributes' values that are no longer valid children of the parent's current item\n for (const dependentCodeAttribute of dependentCodeAttributes) {\n const dependentCodeAttributeDef = Surveys.getNodeDefByUuid({\n survey,\n uuid: dependentCodeAttribute.nodeDefUuid,\n }) as NodeDefCode\n if (NodeDefs.isReadOnly(dependentCodeAttributeDef) || !Nodes.hasUserInputValue(dependentCodeAttribute)) continue\n\n const validityCheckParams = {\n survey,\n parentItemUuid,\n dependentCodeAttribute,\n dependentCodeAttributeDef,\n record,\n categoryItemProvider,\n }\n // check attributes sequentially: invalid values are cleared in the record at every iteration\n const stillValid = await isDependentCodeAttributeValueStillValid(validityCheckParams) // NOSONAR\n if (stillValid) continue\n\n const nodeUpdated: ArenaRecordNode = {\n ...dependentCodeAttribute,\n value: null,\n updated: true,\n dateModified: Dates.nowFormattedForStorage(),\n }\n updateResult.addNode(nodeUpdated, recordUpdateOptions)\n updateResult.addClearedDefUuid(nodeUpdated.nodeDefUuid)\n }\n\n return updateResult\n}\n"]}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "name": "OpenForis",
5
5
  "email": "openforis.arena@gmail.com"
6
6
  },
7
- "version": "2.18.2",
7
+ "version": "2.18.4",
8
8
  "description": "Arena core module",
9
9
  "repository": {
10
10
  "type": "git",