@lowdefy/operators 5.5.1 → 6.0.0

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.
@@ -45,7 +45,14 @@ function hasDynamicMarker(value) {
45
45
  if ((type.isArray(value) || type.isObject(value)) && value['~dyn'] === true) return true;
46
46
  return hasDynChild(value);
47
47
  }
48
- function evaluateOperators({ input, operators, operatorPrefix = '_', env, dynamicIdentifiers, typeNames, args }) {
48
+ // Build-time prefixes are '_build.' and its escaped forms ('__build.', '___build.', …)
49
+ // produced when _function re-parses its body with `_${operatorPrefix}`. Runtime
50
+ // prefixes ('_', '__', '___', …) never match. Escape depth does not change the
51
+ // evaluation mode: any build prefix means the author asked for build-time evaluation.
52
+ function isBuildPrefix(operatorPrefix) {
53
+ return /^_+build\.$/.test(operatorPrefix);
54
+ }
55
+ function evaluateOperators({ input, operators, operatorPrefix = '_', env, lowdefyApp, dynamicIdentifiers, typeNames, args }) {
49
56
  if (type.isUndefined(input)) {
50
57
  return {
51
58
  output: input,
@@ -57,6 +64,7 @@ function evaluateOperators({ input, operators, operatorPrefix = '_', env, dynami
57
64
  }
58
65
  const resolvedDynamicIdentifiers = dynamicIdentifiers ?? new Set();
59
66
  const resolvedTypeNames = typeNames ?? new Set();
67
+ const isBuildTimePrefix = isBuildPrefix(operatorPrefix);
60
68
  const errors = [];
61
69
  const parser = {
62
70
  parse: ({ args: callArgs, input: callInput, operatorPrefix: callPrefix })=>evaluateOperators({
@@ -64,6 +72,7 @@ function evaluateOperators({ input, operators, operatorPrefix = '_', env, dynami
64
72
  operators,
65
73
  operatorPrefix: callPrefix ?? operatorPrefix,
66
74
  env,
75
+ lowdefyApp,
67
76
  dynamicIdentifiers: resolvedDynamicIdentifiers,
68
77
  typeNames: resolvedTypeNames,
69
78
  args: callArgs
@@ -100,7 +109,7 @@ function evaluateOperators({ input, operators, operatorPrefix = '_', env, dynami
100
109
  }
101
110
  // Bubble up ~dyn from children (but not at type boundaries).
102
111
  // _build.* operators always evaluate even with dynamic params, so skip bubble-up for them.
103
- const isBuildOperator = isOperatorObject && operatorPrefix === '_build.';
112
+ const isBuildOperator = isOperatorObject && isBuildTimePrefix;
104
113
  if (!isTypeBoundary && !isBuildOperator && hasDynamicMarker(node)) {
105
114
  return setDynamicMarker(node);
106
115
  }
@@ -111,17 +120,32 @@ function evaluateOperators({ input, operators, operatorPrefix = '_', env, dynami
111
120
  const [op, methodName] = `_${key.substring(operatorPrefix.length)}`.split('.');
112
121
  // Dynamic identifier check — skip for _build.* operators
113
122
  const fullIdentifier = methodName ? `${op}.${methodName}` : op;
114
- if (operatorPrefix !== '_build.') {
123
+ if (!isBuildTimePrefix) {
115
124
  if (resolvedDynamicIdentifiers.has(fullIdentifier) || resolvedDynamicIdentifiers.has(op)) {
116
125
  return setDynamicMarker(node);
117
126
  }
118
127
  }
119
- // Unknown operator — mark as dynamic
128
+ // Unknown operator. Under a runtime prefix an operator may be runtime-only,
129
+ // so it is deferred by marking it dynamic. Under a build prefix (at any escape
130
+ // depth) the author asked for build-time evaluation, so an unknown operator
131
+ // (typo, or a runtime-only operator misused at build time) is a config error.
120
132
  if (type.isUndefined(operators[op])) {
133
+ if (isBuildTimePrefix) {
134
+ const error = new ConfigError(`Operator "${key}" is not a valid _build operator.`, {
135
+ configKey: node['~k'],
136
+ received: {
137
+ [key]: node[key]
138
+ }
139
+ });
140
+ error.lineNumber = node['~l'];
141
+ error.refId = node['~r'];
142
+ errors.push(error);
143
+ return null;
144
+ }
121
145
  return setDynamicMarker(node);
122
146
  }
123
147
  // Dynamic params check — skip for _build.* operators (they always evaluate)
124
- if (operatorPrefix !== '_build.') {
148
+ if (!isBuildTimePrefix) {
125
149
  if (hasDynamicMarker(node[key])) {
126
150
  return setDynamicMarker(node);
127
151
  }
@@ -135,6 +159,7 @@ function evaluateOperators({ input, operators, operatorPrefix = '_', env, dynami
135
159
  args,
136
160
  arrayIndices: [],
137
161
  env,
162
+ lowdefyApp,
138
163
  methodName,
139
164
  operators,
140
165
  params,
@@ -12,7 +12,7 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import { applyArrayIndices, get, serializer, type } from '@lowdefy/helpers';
15
+ */ import { ReservedKeyError, applyArrayIndices, get, serializer, type } from '@lowdefy/helpers';
16
16
  function getFromObject({ params, object, arrayIndices, operator, location }) {
17
17
  if (params === true) params = {
18
18
  all: true
@@ -23,20 +23,26 @@ function getFromObject({ params, object, arrayIndices, operator, location }) {
23
23
  if (!type.isObject(params)) {
24
24
  throw new Error(`${operator} params must be of type string, integer, boolean or object.`);
25
25
  }
26
- if (params.key === null) return get(params, 'default', {
26
+ const defaultValue = get(params, 'default', {
27
27
  default: null,
28
28
  copy: true
29
29
  });
30
+ if (params.key === null) return defaultValue;
30
31
  if (params.all === true) return serializer.copy(object);
31
32
  if (!type.isString(params.key) && !type.isInt(params.key)) {
32
33
  throw new Error(`${operator}.key must be of type string or integer.`);
33
34
  }
34
- return get(object, applyArrayIndices(arrayIndices, params.key), {
35
- default: get(params, 'default', {
36
- default: null,
35
+ try {
36
+ return get(object, applyArrayIndices(arrayIndices, params.key), {
37
+ default: defaultValue,
37
38
  copy: true
38
- }),
39
- copy: true
40
- });
39
+ });
40
+ } catch (error) {
41
+ // Tier 2: a runtime read of an app-developer keypath. There is no loud failure that helps here —
42
+ // a thrown error in the browser is worse than the default, and the reserved rule's job (refusing
43
+ // the read) is already done. Author-written identifiers fail loudly at build instead.
44
+ if (error instanceof ReservedKeyError) return defaultValue;
45
+ throw error;
46
+ }
41
47
  }
42
48
  export default getFromObject;
package/dist/webParser.js CHANGED
@@ -71,7 +71,8 @@ let WebParser = class WebParser {
71
71
  runtime: 'browser',
72
72
  state: this.context.state,
73
73
  theme,
74
- user
74
+ user,
75
+ websockets: this.context.websockets
75
76
  });
76
77
  return res;
77
78
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/operators",
3
- "version": "5.5.1",
3
+ "version": "6.0.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "",
6
6
  "homepage": "https://lowdefy.com",
@@ -34,13 +34,13 @@
34
34
  "dist/*"
35
35
  ],
36
36
  "dependencies": {
37
- "@lowdefy/errors": "5.5.1",
38
- "@lowdefy/helpers": "5.5.1"
37
+ "@lowdefy/errors": "6.0.0",
38
+ "@lowdefy/helpers": "6.0.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@jest/globals": "28.1.3",
42
- "@swc/cli": "0.8.0",
43
- "@swc/core": "1.15.18",
42
+ "@swc/cli": "0.8.1",
43
+ "@swc/core": "1.15.32",
44
44
  "@swc/jest": "0.2.39",
45
45
  "jest": "28.1.3",
46
46
  "jest-environment-jsdom": "28.1.3"