@cxpinsight/survey-spec 0.5.1 → 0.6.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.
- package/dist/expressionEval.js +29 -1
- package/dist/validate.d.ts +2 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +42 -1
- package/package.json +1 -1
package/dist/expressionEval.js
CHANGED
|
@@ -198,8 +198,10 @@ function tokenize(src) {
|
|
|
198
198
|
tokens.push({ kind: 'op', value: '&&' });
|
|
199
199
|
else if (lower === 'or')
|
|
200
200
|
tokens.push({ kind: 'op', value: '||' });
|
|
201
|
+
// Kept DISTINCT from `!` on purpose — the two do not bind the same way.
|
|
202
|
+
// See parseUnary: the word binds below comparison, the symbol above it.
|
|
201
203
|
else if (lower === 'not')
|
|
202
|
-
tokens.push({ kind: 'op', value: '
|
|
204
|
+
tokens.push({ kind: 'op', value: 'not' });
|
|
203
205
|
else if (lower === 'contains')
|
|
204
206
|
tokens.push({ kind: 'op', value: 'contains' });
|
|
205
207
|
else
|
|
@@ -243,10 +245,36 @@ function parseExpression(tokens, pos, ctx, minPrec = 1) {
|
|
|
243
245
|
}
|
|
244
246
|
return [lhs, next];
|
|
245
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Precedence of the WORD `not`, which is deliberately not the precedence of `!`.
|
|
250
|
+
*
|
|
251
|
+
* `not {a} = 2` used to parse as `(not {a}) = 2` — false for every value of `a`,
|
|
252
|
+
* so an author writing "a is not 2" got a condition that could never be true and
|
|
253
|
+
* a question that never appeared. Conventional precedence, and SurveyJS whose
|
|
254
|
+
* dialect this emulates, put `not` BELOW comparison.
|
|
255
|
+
*
|
|
256
|
+
* It became urgent rather than latent when the server started shipping compiled
|
|
257
|
+
* trees: the compiler's `notExpr` already wrapped the whole comparison, so with
|
|
258
|
+
* a = 1 the same condition returned false here and true from the tree. One
|
|
259
|
+
* survey, two answers, decided by which path happened to run.
|
|
260
|
+
*
|
|
261
|
+
* 3 is the comparison tier in PREC, so `not` swallows a comparison and its
|
|
262
|
+
* arithmetic and stops at `and`/`or` — matching the compiler exactly:
|
|
263
|
+
* not {a} = 2 and {b} = 1 → (not ({a} = 2)) and ({b} = 1)
|
|
264
|
+
*
|
|
265
|
+
* `!` is left alone. The compiler rejects it outright, so no tree exists to
|
|
266
|
+
* disagree with, and it binds tightly in every C-family language including the
|
|
267
|
+
* JavaScript authors are borrowing from.
|
|
268
|
+
*/
|
|
269
|
+
const NOT_WORD_PREC = 3;
|
|
246
270
|
function parseUnary(tokens, pos, ctx) {
|
|
247
271
|
const t = tokens[pos];
|
|
248
272
|
if (!t)
|
|
249
273
|
throw new Error('unexpected end of expression');
|
|
274
|
+
if (t.kind === 'op' && t.value === 'not') {
|
|
275
|
+
const [inner, after] = parseExpression(tokens, pos + 1, ctx, NOT_WORD_PREC);
|
|
276
|
+
return [!truthy(inner), after];
|
|
277
|
+
}
|
|
250
278
|
if (t.kind === 'op' && t.value === '!') {
|
|
251
279
|
const [inner, after] = parseUnary(tokens, pos + 1, ctx);
|
|
252
280
|
return [!truthy(inner), after];
|
package/dist/validate.d.ts
CHANGED
package/dist/validate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAkDjE,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,mBAAmB,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAE3F;AAED,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,KAAK,EAAE,OAAO,GAAG,iBAAiB,GAAG,IAAI,CAgB1G;
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
|
package/dist/validate.js
CHANGED
|
@@ -194,6 +194,34 @@ function validateTextish(question, value) {
|
|
|
194
194
|
if (typeof question.minLength === 'number' && trimmed.length < question.minLength) {
|
|
195
195
|
return { code: 'min_length', params: { min: question.minLength } };
|
|
196
196
|
}
|
|
197
|
+
// minValue / maxValue on a NUMERIC text input.
|
|
198
|
+
//
|
|
199
|
+
// These used to do nothing here. validateNumericRange is reached only from
|
|
200
|
+
// rating/csat/ces/nps, which carry real numbers; a question with
|
|
201
|
+
// inputType 'number' submits the STRING '5' and arrived at this function
|
|
202
|
+
// instead, which checked lengths and formats and never a range. An author
|
|
203
|
+
// setting "between 1 and 10" on a number question got a control that read as
|
|
204
|
+
// configured and enforced nothing, on every channel at once.
|
|
205
|
+
//
|
|
206
|
+
// The coercion is not new behaviour invented here: applyRule already does
|
|
207
|
+
// exactly this for a rules[] entry of type min/max. So the same author intent
|
|
208
|
+
// was enforced when written one way and ignored when written the other. This
|
|
209
|
+
// makes the two agree.
|
|
210
|
+
//
|
|
211
|
+
// Guarded on inputType rather than applied to all text, because a range on a
|
|
212
|
+
// free-text comment is meaningless and coercing it would reject prose for
|
|
213
|
+
// being NaN.
|
|
214
|
+
if (inputType === 'number' && trimmed !== '') {
|
|
215
|
+
const n = Number(trimmed);
|
|
216
|
+
if (!Number.isNaN(n)) {
|
|
217
|
+
if (typeof question.minValue === 'number' && n < question.minValue) {
|
|
218
|
+
return { code: 'min_value', params: { min: question.minValue } };
|
|
219
|
+
}
|
|
220
|
+
if (typeof question.maxValue === 'number' && n > question.maxValue) {
|
|
221
|
+
return { code: 'max_value', params: { max: question.maxValue } };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
197
225
|
return null;
|
|
198
226
|
}
|
|
199
227
|
function validateCheckbox(question, value) {
|
|
@@ -224,7 +252,20 @@ function validateNumericRange(question, value) {
|
|
|
224
252
|
return null;
|
|
225
253
|
}
|
|
226
254
|
function validateRules(question, value) {
|
|
227
|
-
const
|
|
255
|
+
const declared = Array.isArray(question.rules) ? question.rules : [];
|
|
256
|
+
// A top-level `regex` is the other spelling of a pattern rule, and it was read
|
|
257
|
+
// by nothing: `rules: [{ type: 'pattern' }]` rejected a bad value while
|
|
258
|
+
// `regex: '…'` accepted it, from the same author meaning the same thing. The
|
|
259
|
+
// setting did not fail — it disappeared, on every channel, with no warning.
|
|
260
|
+
//
|
|
261
|
+
// Treated as a pattern rule so one intent has one behaviour. This is the same
|
|
262
|
+
// client-side exposure the existing spelling already carries: a catastrophic
|
|
263
|
+
// pattern costs the respondent their own tab, which is why the SERVER still
|
|
264
|
+
// refuses to evaluate either spelling — there the cost is shared, and
|
|
265
|
+
// `^(a+)+$` against 24 characters takes 116ms and doubles every two.
|
|
266
|
+
const rules = typeof question.regex === 'string' && question.regex !== ''
|
|
267
|
+
? declared.concat([{ type: 'pattern', value: question.regex }])
|
|
268
|
+
: declared;
|
|
228
269
|
if (rules.length === 0)
|
|
229
270
|
return null;
|
|
230
271
|
const str = typeof value === 'string' ? value : String(value ?? '');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cxpinsight/survey-spec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "The survey specification the renderers share — expression evaluation first. Source of truth for behaviour that must be identical on web, links and mobile.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|