@cxpinsight/survey-spec 0.5.1 → 0.7.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/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +5 -0
- package/dist/validate.d.ts +97 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +164 -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/index.d.ts
CHANGED
|
@@ -15,8 +15,8 @@ export { evaluateTree, compareValues, MAX_TREE_DEPTH } from './evaluateTree';
|
|
|
15
15
|
export type { ConditionNode, Operand, TreeContext } from './evaluateTree';
|
|
16
16
|
export { resolveVariable } from './resolveVariable';
|
|
17
17
|
export { splitIntoPages } from './pageFlow';
|
|
18
|
-
export { validateAnswer, impliedChoices, impliedActions, impliedStepActions, impliedEqualProminence, safeActionHref, safeImageSrc, isDeferChoice, canSwipeToDismiss, autoDismissMs, deadlineCountdown, formatCountdown, outcomeFor, voiceUsable, rewardToShow, cardClickTarget, cardHoverEffect, resolveAction } from './validate';
|
|
19
|
-
export type { ValidatableQuestion, OutcomeKind, OutcomeInput, ActionKind, RawAction, ResolvedAction } from './validate';
|
|
18
|
+
export { complianceGate, questionForValidation, validateAnswer, impliedChoices, impliedActions, impliedStepActions, impliedEqualProminence, safeActionHref, safeImageSrc, isDeferChoice, canSwipeToDismiss, autoDismissMs, deadlineCountdown, formatCountdown, outcomeFor, voiceUsable, rewardToShow, cardClickTarget, cardHoverEffect, resolveAction } from './validate';
|
|
19
|
+
export type { ComplianceState, EscapeHatch, ComplianceInput, ComplianceVerdict, ValidatableQuestion, OutcomeKind, OutcomeInput, ActionKind, RawAction, ResolvedAction } from './validate';
|
|
20
20
|
export { validateAnswerCode } from './validate';
|
|
21
21
|
export { wordMessage, EN_MESSAGES, MESSAGE_CODES } from './messages';
|
|
22
22
|
export type { ValidationMessage } from './messages';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC7E,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC7E,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1W,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC1L,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACrE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC7F,OAAO,EACL,mBAAmB,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EACzE,kBAAkB,EAAE,SAAS,EAAE,iBAAiB,EAChD,YAAY,EAAE,cAAc,EAAE,mBAAmB,EACjD,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EACtE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,GAC7D,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACvF,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.optionValueOf = exports.optionLabelOf = exports.shortCode = exports.buildPromptText = exports.scaffoldFor = exports.skipNoteText = exports.voiceDisclaimer = exports.auditVoiceQuestions = exports.voiceCanHandle = exports.voiceSupport = exports.distinctiveLabels = exports.matchDate = exports.matchNumberInRange = exports.rankOptions = exports.matchOptions = exports.matchOption = exports.matchBoolean = exports.normalizeTranscript = exports.MESSAGE_CODES = exports.EN_MESSAGES = exports.wordMessage = exports.validateAnswerCode = exports.resolveAction = exports.cardHoverEffect = exports.cardClickTarget = exports.rewardToShow = exports.voiceUsable = exports.outcomeFor = exports.formatCountdown = exports.deadlineCountdown = exports.autoDismissMs = exports.canSwipeToDismiss = exports.isDeferChoice = exports.safeImageSrc = exports.safeActionHref = exports.impliedEqualProminence = exports.impliedStepActions = exports.impliedActions = exports.impliedChoices = exports.validateAnswer = exports.questionForValidation = exports.complianceGate = exports.splitIntoPages = exports.resolveVariable = exports.MAX_TREE_DEPTH = exports.compareValues = exports.evaluateTree = exports.isParseable = exports.evaluateCondition = exports.evaluateExpression = void 0;
|
|
4
|
+
exports.optionLabelsOf = exports.voiceOptionsOf = void 0;
|
|
4
5
|
/**
|
|
5
6
|
* @cxpinsight/survey-spec
|
|
6
7
|
*
|
|
@@ -26,6 +27,8 @@ Object.defineProperty(exports, "resolveVariable", { enumerable: true, get: funct
|
|
|
26
27
|
var pageFlow_1 = require("./pageFlow");
|
|
27
28
|
Object.defineProperty(exports, "splitIntoPages", { enumerable: true, get: function () { return pageFlow_1.splitIntoPages; } });
|
|
28
29
|
var validate_1 = require("./validate");
|
|
30
|
+
Object.defineProperty(exports, "complianceGate", { enumerable: true, get: function () { return validate_1.complianceGate; } });
|
|
31
|
+
Object.defineProperty(exports, "questionForValidation", { enumerable: true, get: function () { return validate_1.questionForValidation; } });
|
|
29
32
|
Object.defineProperty(exports, "validateAnswer", { enumerable: true, get: function () { return validate_1.validateAnswer; } });
|
|
30
33
|
Object.defineProperty(exports, "impliedChoices", { enumerable: true, get: function () { return validate_1.impliedChoices; } });
|
|
31
34
|
Object.defineProperty(exports, "impliedActions", { enumerable: true, get: function () { return validate_1.impliedActions; } });
|
package/dist/messages.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,4EAA4E;AAC5E,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,4EAA4E;AAC5E,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAiC9E,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAI5E;AAED,0EAA0E;AAC1E,eAAO,MAAM,aAAa,UAA2B,CAAC"}
|
package/dist/messages.js
CHANGED
|
@@ -25,6 +25,11 @@ exports.wordMessage = wordMessage;
|
|
|
25
25
|
/** English wording, one entry per code. The only prose in the validator. */
|
|
26
26
|
exports.EN_MESSAGES = {
|
|
27
27
|
required: () => 'This field is required',
|
|
28
|
+
// Compliance. Written for someone who is being held, not for a developer:
|
|
29
|
+
// say what is happening and what happens next, never just "blocked".
|
|
30
|
+
compliance_under_review: () => 'Thanks — someone is reviewing this now. '
|
|
31
|
+
+ 'You do not need to do anything else, and this will update when the review is done.',
|
|
32
|
+
compliance_blocked: () => 'This needs to be accepted before you can continue.',
|
|
28
33
|
choose_option: () => 'Please choose an option',
|
|
29
34
|
format_email: () => 'Please enter a valid email address (e.g., you@example.com)',
|
|
30
35
|
format_url: () => 'Please enter a full URL, including https://',
|
package/dist/validate.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface ValidatableQuestion {
|
|
|
8
8
|
maxLength?: number;
|
|
9
9
|
minValue?: number;
|
|
10
10
|
maxValue?: number;
|
|
11
|
+
/** spec §8's other spelling of a pattern rule — see validateRules. */
|
|
12
|
+
regex?: string;
|
|
11
13
|
minCount?: number;
|
|
12
14
|
maxCount?: number;
|
|
13
15
|
rules?: unknown;
|
|
@@ -257,6 +259,101 @@ export interface OutcomeInput {
|
|
|
257
259
|
* surface you get past by saying no is not a compliance surface.
|
|
258
260
|
*/
|
|
259
261
|
export declare function outcomeFor(input: OutcomeInput): OutcomeKind;
|
|
262
|
+
/**
|
|
263
|
+
* The question as it should be validated RIGHT NOW.
|
|
264
|
+
*
|
|
265
|
+
* `requiredWhen` — "required only if they said yes earlier" — was compiled by
|
|
266
|
+
* the server and read by no renderer at all. An author who set it got a question
|
|
267
|
+
* that was never required, on every channel, with nothing reporting it. The last
|
|
268
|
+
* of the four conditions to be inert everywhere rather than somewhere.
|
|
269
|
+
*
|
|
270
|
+
* ADDITIVE, deliberately: a question already marked required stays required. The
|
|
271
|
+
* alternative reading — that requiredWhen REPLACES required — would let a
|
|
272
|
+
* condition make an existing question optional, so a survey that collects an
|
|
273
|
+
* answer today could quietly stop collecting it. This direction can only ever
|
|
274
|
+
* ask for more, never less, which is the safe way to turn on a rule that has
|
|
275
|
+
* never run.
|
|
276
|
+
*
|
|
277
|
+
* The caller supplies the evaluator because each renderer has its own, already
|
|
278
|
+
* wired to its answers and its compiled trees. What lives here is the decision,
|
|
279
|
+
* which is the part that must not differ between them.
|
|
280
|
+
*
|
|
281
|
+
* @param evaluate must FAIL OPEN by returning false for a condition it cannot
|
|
282
|
+
* read — an unreadable requiredWhen leaves the question optional rather than
|
|
283
|
+
* blocking a respondent behind a rule nobody can see.
|
|
284
|
+
*/
|
|
285
|
+
export declare function questionForValidation<T extends {
|
|
286
|
+
required?: unknown;
|
|
287
|
+
isRequired?: unknown;
|
|
288
|
+
requiredWhen?: unknown;
|
|
289
|
+
}>(question: T, evaluate: (expr: string) => boolean): T;
|
|
290
|
+
/** Where a compliance interaction has got to. */
|
|
291
|
+
export type ComplianceState = 'open' | 'blocked' | 'under_review' | 'cleared';
|
|
292
|
+
/** What a respondent is offered when they cannot simply proceed. */
|
|
293
|
+
export type EscapeHatch = 'none' | 'contact' | 'defer';
|
|
294
|
+
export interface ComplianceInput {
|
|
295
|
+
interactionType?: string | null;
|
|
296
|
+
/** The implied choice taken, or null before they have answered. */
|
|
297
|
+
action?: string | null;
|
|
298
|
+
/** True when the chosen action is the affirmative one. */
|
|
299
|
+
affirmative?: boolean;
|
|
300
|
+
compliance?: {
|
|
301
|
+
reviewRequired?: unknown;
|
|
302
|
+
onDecline?: unknown;
|
|
303
|
+
blockedMessage?: unknown;
|
|
304
|
+
reviewMessage?: unknown;
|
|
305
|
+
escapeHatch?: unknown;
|
|
306
|
+
contactHref?: unknown;
|
|
307
|
+
} | null;
|
|
308
|
+
}
|
|
309
|
+
export interface ComplianceVerdict {
|
|
310
|
+
state: ComplianceState;
|
|
311
|
+
/** May the respondent close the surface? */
|
|
312
|
+
dismissible: boolean;
|
|
313
|
+
escape: EscapeHatch;
|
|
314
|
+
/** Where `escape: 'contact'` sends them. Null when there is nowhere. */
|
|
315
|
+
contactHref: string | null;
|
|
316
|
+
/** A wording code, or null when there is nothing to say. */
|
|
317
|
+
messageCode: 'compliance_blocked' | 'compliance_under_review' | null;
|
|
318
|
+
/** The author's own prose, which travels verbatim and is never translated. */
|
|
319
|
+
authorMessage: string | null;
|
|
320
|
+
/** True when this state is worth recording — see the monitoring note. */
|
|
321
|
+
reportable: boolean;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Decide whether a compliance interaction lets the respondent go.
|
|
325
|
+
*
|
|
326
|
+
* This is the half of compliance that was never built. `outcomeFor` already
|
|
327
|
+
* returns `hold`, meaning "do not let them through yet", and all three renderers
|
|
328
|
+
* read it — to choose a closing message. Nothing anywhere connected it to
|
|
329
|
+
* whether the surface could be CLOSED, so a compliance check was dismissed
|
|
330
|
+
* exactly like a rating prompt. A compliance surface you get past by ignoring it
|
|
331
|
+
* is not a compliance surface.
|
|
332
|
+
*
|
|
333
|
+
* Three states hold, and they are not the same thing to the person looking at
|
|
334
|
+
* them:
|
|
335
|
+
*
|
|
336
|
+
* open they have not answered yet
|
|
337
|
+
* blocked they declined, and the author chose to hold them
|
|
338
|
+
* under_review they accepted, and a person has to look at it
|
|
339
|
+
*
|
|
340
|
+
* `under_review` matters most and is the easiest to get wrong. Letting someone
|
|
341
|
+
* through on submit tells them they are finished when they are not, and defeats
|
|
342
|
+
* the check; saying nothing leaves them staring at a surface that looks stuck.
|
|
343
|
+
* So it holds AND it says so.
|
|
344
|
+
*
|
|
345
|
+
* ── The escape hatch ──
|
|
346
|
+
* A surface that cannot be closed can trap someone whose answer will never be
|
|
347
|
+
* accepted — a failed document check at 2am with nobody to ask. The author
|
|
348
|
+
* chooses which way out exists, and the default is `contact` rather than `none`,
|
|
349
|
+
* because a route to a human costs the check nothing while a dead end costs a
|
|
350
|
+
* customer everything. `defer` is the only one that actually releases them; it
|
|
351
|
+
* is a postponement, and the interaction comes back.
|
|
352
|
+
*
|
|
353
|
+
* Anything unrecognised falls back to `contact` rather than to `none`: a
|
|
354
|
+
* mis-typed setting must not silently become a trap.
|
|
355
|
+
*/
|
|
356
|
+
export declare function complianceGate(input: ComplianceInput): ComplianceVerdict;
|
|
260
357
|
/**
|
|
261
358
|
* Can voice mode do anything on this step?
|
|
262
359
|
*
|
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
|
@@ -14,6 +14,8 @@ exports.autoDismissMs = autoDismissMs;
|
|
|
14
14
|
exports.deadlineCountdown = deadlineCountdown;
|
|
15
15
|
exports.formatCountdown = formatCountdown;
|
|
16
16
|
exports.outcomeFor = outcomeFor;
|
|
17
|
+
exports.questionForValidation = questionForValidation;
|
|
18
|
+
exports.complianceGate = complianceGate;
|
|
17
19
|
exports.voiceUsable = voiceUsable;
|
|
18
20
|
exports.rewardToShow = rewardToShow;
|
|
19
21
|
exports.cardClickTarget = cardClickTarget;
|
|
@@ -194,6 +196,34 @@ function validateTextish(question, value) {
|
|
|
194
196
|
if (typeof question.minLength === 'number' && trimmed.length < question.minLength) {
|
|
195
197
|
return { code: 'min_length', params: { min: question.minLength } };
|
|
196
198
|
}
|
|
199
|
+
// minValue / maxValue on a NUMERIC text input.
|
|
200
|
+
//
|
|
201
|
+
// These used to do nothing here. validateNumericRange is reached only from
|
|
202
|
+
// rating/csat/ces/nps, which carry real numbers; a question with
|
|
203
|
+
// inputType 'number' submits the STRING '5' and arrived at this function
|
|
204
|
+
// instead, which checked lengths and formats and never a range. An author
|
|
205
|
+
// setting "between 1 and 10" on a number question got a control that read as
|
|
206
|
+
// configured and enforced nothing, on every channel at once.
|
|
207
|
+
//
|
|
208
|
+
// The coercion is not new behaviour invented here: applyRule already does
|
|
209
|
+
// exactly this for a rules[] entry of type min/max. So the same author intent
|
|
210
|
+
// was enforced when written one way and ignored when written the other. This
|
|
211
|
+
// makes the two agree.
|
|
212
|
+
//
|
|
213
|
+
// Guarded on inputType rather than applied to all text, because a range on a
|
|
214
|
+
// free-text comment is meaningless and coercing it would reject prose for
|
|
215
|
+
// being NaN.
|
|
216
|
+
if (inputType === 'number' && trimmed !== '') {
|
|
217
|
+
const n = Number(trimmed);
|
|
218
|
+
if (!Number.isNaN(n)) {
|
|
219
|
+
if (typeof question.minValue === 'number' && n < question.minValue) {
|
|
220
|
+
return { code: 'min_value', params: { min: question.minValue } };
|
|
221
|
+
}
|
|
222
|
+
if (typeof question.maxValue === 'number' && n > question.maxValue) {
|
|
223
|
+
return { code: 'max_value', params: { max: question.maxValue } };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
197
227
|
return null;
|
|
198
228
|
}
|
|
199
229
|
function validateCheckbox(question, value) {
|
|
@@ -224,7 +254,20 @@ function validateNumericRange(question, value) {
|
|
|
224
254
|
return null;
|
|
225
255
|
}
|
|
226
256
|
function validateRules(question, value) {
|
|
227
|
-
const
|
|
257
|
+
const declared = Array.isArray(question.rules) ? question.rules : [];
|
|
258
|
+
// A top-level `regex` is the other spelling of a pattern rule, and it was read
|
|
259
|
+
// by nothing: `rules: [{ type: 'pattern' }]` rejected a bad value while
|
|
260
|
+
// `regex: '…'` accepted it, from the same author meaning the same thing. The
|
|
261
|
+
// setting did not fail — it disappeared, on every channel, with no warning.
|
|
262
|
+
//
|
|
263
|
+
// Treated as a pattern rule so one intent has one behaviour. This is the same
|
|
264
|
+
// client-side exposure the existing spelling already carries: a catastrophic
|
|
265
|
+
// pattern costs the respondent their own tab, which is why the SERVER still
|
|
266
|
+
// refuses to evaluate either spelling — there the cost is shared, and
|
|
267
|
+
// `^(a+)+$` against 24 characters takes 116ms and doubles every two.
|
|
268
|
+
const rules = typeof question.regex === 'string' && question.regex !== ''
|
|
269
|
+
? declared.concat([{ type: 'pattern', value: question.regex }])
|
|
270
|
+
: declared;
|
|
228
271
|
if (rules.length === 0)
|
|
229
272
|
return null;
|
|
230
273
|
const str = typeof value === 'string' ? value : String(value ?? '');
|
|
@@ -697,6 +740,126 @@ function outcomeFor(input) {
|
|
|
697
740
|
// for something we do not recognise: it acknowledges without promising.
|
|
698
741
|
return 'thanks';
|
|
699
742
|
}
|
|
743
|
+
/**
|
|
744
|
+
* The question as it should be validated RIGHT NOW.
|
|
745
|
+
*
|
|
746
|
+
* `requiredWhen` — "required only if they said yes earlier" — was compiled by
|
|
747
|
+
* the server and read by no renderer at all. An author who set it got a question
|
|
748
|
+
* that was never required, on every channel, with nothing reporting it. The last
|
|
749
|
+
* of the four conditions to be inert everywhere rather than somewhere.
|
|
750
|
+
*
|
|
751
|
+
* ADDITIVE, deliberately: a question already marked required stays required. The
|
|
752
|
+
* alternative reading — that requiredWhen REPLACES required — would let a
|
|
753
|
+
* condition make an existing question optional, so a survey that collects an
|
|
754
|
+
* answer today could quietly stop collecting it. This direction can only ever
|
|
755
|
+
* ask for more, never less, which is the safe way to turn on a rule that has
|
|
756
|
+
* never run.
|
|
757
|
+
*
|
|
758
|
+
* The caller supplies the evaluator because each renderer has its own, already
|
|
759
|
+
* wired to its answers and its compiled trees. What lives here is the decision,
|
|
760
|
+
* which is the part that must not differ between them.
|
|
761
|
+
*
|
|
762
|
+
* @param evaluate must FAIL OPEN by returning false for a condition it cannot
|
|
763
|
+
* read — an unreadable requiredWhen leaves the question optional rather than
|
|
764
|
+
* blocking a respondent behind a rule nobody can see.
|
|
765
|
+
*/
|
|
766
|
+
function questionForValidation(question, evaluate) {
|
|
767
|
+
if (!question)
|
|
768
|
+
return question;
|
|
769
|
+
if (question.required === true || question.isRequired === true)
|
|
770
|
+
return question;
|
|
771
|
+
const expr = question.requiredWhen;
|
|
772
|
+
if (typeof expr !== 'string' || !expr.trim())
|
|
773
|
+
return question;
|
|
774
|
+
let now = false;
|
|
775
|
+
try {
|
|
776
|
+
now = evaluate(expr) === true;
|
|
777
|
+
}
|
|
778
|
+
catch {
|
|
779
|
+
now = false;
|
|
780
|
+
}
|
|
781
|
+
return now ? { ...question, required: true } : question;
|
|
782
|
+
}
|
|
783
|
+
const ESCAPES = ['none', 'contact', 'defer'];
|
|
784
|
+
/**
|
|
785
|
+
* Decide whether a compliance interaction lets the respondent go.
|
|
786
|
+
*
|
|
787
|
+
* This is the half of compliance that was never built. `outcomeFor` already
|
|
788
|
+
* returns `hold`, meaning "do not let them through yet", and all three renderers
|
|
789
|
+
* read it — to choose a closing message. Nothing anywhere connected it to
|
|
790
|
+
* whether the surface could be CLOSED, so a compliance check was dismissed
|
|
791
|
+
* exactly like a rating prompt. A compliance surface you get past by ignoring it
|
|
792
|
+
* is not a compliance surface.
|
|
793
|
+
*
|
|
794
|
+
* Three states hold, and they are not the same thing to the person looking at
|
|
795
|
+
* them:
|
|
796
|
+
*
|
|
797
|
+
* open they have not answered yet
|
|
798
|
+
* blocked they declined, and the author chose to hold them
|
|
799
|
+
* under_review they accepted, and a person has to look at it
|
|
800
|
+
*
|
|
801
|
+
* `under_review` matters most and is the easiest to get wrong. Letting someone
|
|
802
|
+
* through on submit tells them they are finished when they are not, and defeats
|
|
803
|
+
* the check; saying nothing leaves them staring at a surface that looks stuck.
|
|
804
|
+
* So it holds AND it says so.
|
|
805
|
+
*
|
|
806
|
+
* ── The escape hatch ──
|
|
807
|
+
* A surface that cannot be closed can trap someone whose answer will never be
|
|
808
|
+
* accepted — a failed document check at 2am with nobody to ask. The author
|
|
809
|
+
* chooses which way out exists, and the default is `contact` rather than `none`,
|
|
810
|
+
* because a route to a human costs the check nothing while a dead end costs a
|
|
811
|
+
* customer everything. `defer` is the only one that actually releases them; it
|
|
812
|
+
* is a postponement, and the interaction comes back.
|
|
813
|
+
*
|
|
814
|
+
* Anything unrecognised falls back to `contact` rather than to `none`: a
|
|
815
|
+
* mis-typed setting must not silently become a trap.
|
|
816
|
+
*/
|
|
817
|
+
function complianceGate(input) {
|
|
818
|
+
const free = {
|
|
819
|
+
state: 'cleared', dismissible: true, escape: 'none',
|
|
820
|
+
contactHref: null, messageCode: null, authorMessage: null, reportable: false,
|
|
821
|
+
};
|
|
822
|
+
if (String(input?.interactionType || '').toLowerCase() !== 'comply')
|
|
823
|
+
return free;
|
|
824
|
+
const c = input.compliance || {};
|
|
825
|
+
const raw = String(c.escapeHatch ?? '').toLowerCase();
|
|
826
|
+
const escape = ESCAPES.includes(raw) ? raw : 'contact';
|
|
827
|
+
// Through safeActionHref, not raw: this is a destination the host page is told
|
|
828
|
+
// to follow, so `javascript:` and friends are refused the same way an action
|
|
829
|
+
// button's destination is. `mailto:` passes, which is the common case for a
|
|
830
|
+
// route to a human.
|
|
831
|
+
const contactHref = (escape === 'contact' ? safeActionHref({ type: 'url', value: c.contactHref }) : null) ?? null;
|
|
832
|
+
const authorPose = (v) => (typeof v === 'string' && v.trim() ? v : null);
|
|
833
|
+
// `defer` is the only hatch that releases them — closing IS the postponement.
|
|
834
|
+
const canLeave = escape === 'defer';
|
|
835
|
+
const answered = input.action !== null && input.action !== undefined && input.action !== '';
|
|
836
|
+
if (!answered) {
|
|
837
|
+
return {
|
|
838
|
+
state: 'open', dismissible: canLeave, escape, contactHref,
|
|
839
|
+
messageCode: null, authorMessage: null, reportable: false,
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
if (input.affirmative === false) {
|
|
843
|
+
// A decline the author chose to accept is simply the end of it.
|
|
844
|
+
if (String(c.onDecline || 'block') !== 'block')
|
|
845
|
+
return free;
|
|
846
|
+
return {
|
|
847
|
+
state: 'blocked', dismissible: canLeave, escape, contactHref,
|
|
848
|
+
messageCode: 'compliance_blocked',
|
|
849
|
+
authorMessage: authorPose(c.blockedMessage),
|
|
850
|
+
reportable: true,
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
if (c.reviewRequired === true) {
|
|
854
|
+
return {
|
|
855
|
+
state: 'under_review', dismissible: canLeave, escape, contactHref,
|
|
856
|
+
messageCode: 'compliance_under_review',
|
|
857
|
+
authorMessage: authorPose(c.reviewMessage),
|
|
858
|
+
reportable: true,
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
return free;
|
|
862
|
+
}
|
|
700
863
|
/**
|
|
701
864
|
* Can voice mode do anything on this step?
|
|
702
865
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cxpinsight/survey-spec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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",
|