@cxpinsight/survey-spec 0.7.0 → 0.9.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/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 { 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';
18
+ export { complianceGate, questionForValidation, validateAnswer, impliedChoices, impliedActions, impliedStepActions, impliedEqualProminence, safeActionHref, safeImageSrc, isDeferChoice, canSwipeToDismiss, showsClose, autoDismissMs, deadlineCountdown, formatCountdown, outcomeFor, voiceUsable, rewardToShow, rewardTerms, showsConfirmation, confirmationText, showsQuestionLabel, collectsAnswer, answerShape, resolveBranding, resolveTheme, resolveThemeDecision, cardClickTarget, cardIsClickable, cardHoverEffect, showActionButton, imageLayout, imageScrim, imageDesign, triggerVariables, resolveAction } from './validate';
19
+ export type { ComplianceState, EscapeHatch, ComplianceInput, ComplianceVerdict, ValidatableQuestion, OutcomeKind, OutcomeInput, ActionKind, RawAction, ResolvedAction, ImageLayout, ImageDesign, TriggerContext } 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';
@@ -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,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"}
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,UAAU,EAAE,aAAa,EAAE,iBAAiB,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzmB,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACpO,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,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
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;
3
+ exports.matchBoolean = exports.normalizeTranscript = exports.MESSAGE_CODES = exports.EN_MESSAGES = exports.wordMessage = exports.validateAnswerCode = exports.resolveAction = exports.triggerVariables = exports.imageDesign = exports.imageScrim = exports.imageLayout = exports.showActionButton = exports.cardHoverEffect = exports.cardIsClickable = exports.cardClickTarget = exports.resolveThemeDecision = exports.resolveTheme = exports.resolveBranding = exports.answerShape = exports.collectsAnswer = exports.showsQuestionLabel = exports.confirmationText = exports.showsConfirmation = exports.rewardTerms = exports.rewardToShow = exports.voiceUsable = exports.outcomeFor = exports.formatCountdown = exports.deadlineCountdown = exports.autoDismissMs = exports.showsClose = 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 = 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 = void 0;
5
5
  /**
6
6
  * @cxpinsight/survey-spec
7
7
  *
@@ -38,14 +38,30 @@ Object.defineProperty(exports, "safeActionHref", { enumerable: true, get: functi
38
38
  Object.defineProperty(exports, "safeImageSrc", { enumerable: true, get: function () { return validate_1.safeImageSrc; } });
39
39
  Object.defineProperty(exports, "isDeferChoice", { enumerable: true, get: function () { return validate_1.isDeferChoice; } });
40
40
  Object.defineProperty(exports, "canSwipeToDismiss", { enumerable: true, get: function () { return validate_1.canSwipeToDismiss; } });
41
+ Object.defineProperty(exports, "showsClose", { enumerable: true, get: function () { return validate_1.showsClose; } });
41
42
  Object.defineProperty(exports, "autoDismissMs", { enumerable: true, get: function () { return validate_1.autoDismissMs; } });
42
43
  Object.defineProperty(exports, "deadlineCountdown", { enumerable: true, get: function () { return validate_1.deadlineCountdown; } });
43
44
  Object.defineProperty(exports, "formatCountdown", { enumerable: true, get: function () { return validate_1.formatCountdown; } });
44
45
  Object.defineProperty(exports, "outcomeFor", { enumerable: true, get: function () { return validate_1.outcomeFor; } });
45
46
  Object.defineProperty(exports, "voiceUsable", { enumerable: true, get: function () { return validate_1.voiceUsable; } });
46
47
  Object.defineProperty(exports, "rewardToShow", { enumerable: true, get: function () { return validate_1.rewardToShow; } });
48
+ Object.defineProperty(exports, "rewardTerms", { enumerable: true, get: function () { return validate_1.rewardTerms; } });
49
+ Object.defineProperty(exports, "showsConfirmation", { enumerable: true, get: function () { return validate_1.showsConfirmation; } });
50
+ Object.defineProperty(exports, "confirmationText", { enumerable: true, get: function () { return validate_1.confirmationText; } });
51
+ Object.defineProperty(exports, "showsQuestionLabel", { enumerable: true, get: function () { return validate_1.showsQuestionLabel; } });
52
+ Object.defineProperty(exports, "collectsAnswer", { enumerable: true, get: function () { return validate_1.collectsAnswer; } });
53
+ Object.defineProperty(exports, "answerShape", { enumerable: true, get: function () { return validate_1.answerShape; } });
54
+ Object.defineProperty(exports, "resolveBranding", { enumerable: true, get: function () { return validate_1.resolveBranding; } });
55
+ Object.defineProperty(exports, "resolveTheme", { enumerable: true, get: function () { return validate_1.resolveTheme; } });
56
+ Object.defineProperty(exports, "resolveThemeDecision", { enumerable: true, get: function () { return validate_1.resolveThemeDecision; } });
47
57
  Object.defineProperty(exports, "cardClickTarget", { enumerable: true, get: function () { return validate_1.cardClickTarget; } });
58
+ Object.defineProperty(exports, "cardIsClickable", { enumerable: true, get: function () { return validate_1.cardIsClickable; } });
48
59
  Object.defineProperty(exports, "cardHoverEffect", { enumerable: true, get: function () { return validate_1.cardHoverEffect; } });
60
+ Object.defineProperty(exports, "showActionButton", { enumerable: true, get: function () { return validate_1.showActionButton; } });
61
+ Object.defineProperty(exports, "imageLayout", { enumerable: true, get: function () { return validate_1.imageLayout; } });
62
+ Object.defineProperty(exports, "imageScrim", { enumerable: true, get: function () { return validate_1.imageScrim; } });
63
+ Object.defineProperty(exports, "imageDesign", { enumerable: true, get: function () { return validate_1.imageDesign; } });
64
+ Object.defineProperty(exports, "triggerVariables", { enumerable: true, get: function () { return validate_1.triggerVariables; } });
49
65
  Object.defineProperty(exports, "resolveAction", { enumerable: true, get: function () { return validate_1.resolveAction; } });
50
66
  var validate_2 = require("./validate");
51
67
  Object.defineProperty(exports, "validateAnswerCode", { enumerable: true, get: function () { return validate_2.validateAnswerCode; } });
@@ -177,6 +177,34 @@ export declare function canSwipeToDismiss(display: {
177
177
  } | null | undefined, delivery?: {
178
178
  mustShow?: unknown;
179
179
  } | null): boolean;
180
+ /**
181
+ * Is the ✕ drawn?
182
+ *
183
+ * TWO RENDERERS, TWO ANSWERS, from one authored survey. Web resolved an unset
184
+ * `showClose` to "hidden on a notification, shown everywhere else" — a
185
+ * deliberate rule, written down: a banner is swiped away, and canSwipeToDismiss
186
+ * turns the gesture on precisely when the ✕ is off. The phone read
187
+ * `showClose !== false`, so an unset notification kept its ✕ AND, because its
188
+ * display is a plain merge that leaves the value undefined, never enabled the
189
+ * swipe. The same notification was dismissable two different ways depending on
190
+ * which screen you were looking at, and a panel describing it could only be
191
+ * right about one of them.
192
+ *
193
+ * ONLY WHERE THE GESTURE REPLACES IT. Dropping the ✕ from a notification that
194
+ * cannot be swiped — a modal, a fullscreen takeover, a must-show — would leave
195
+ * no way out at all. That trap was already live on web, which hid the mark on
196
+ * every notification regardless of mode; asking canSwipeToDismiss here closes
197
+ * it on both.
198
+ *
199
+ * An explicit value always wins: '' / null / undefined mean "inherit", the same
200
+ * convention the rest of `display` uses.
201
+ */
202
+ export declare function showsClose(display: {
203
+ mode?: unknown;
204
+ showClose?: unknown;
205
+ } | null | undefined, interactionType?: unknown, delivery?: {
206
+ mustShow?: unknown;
207
+ } | null): boolean;
180
208
  /**
181
209
  * How long before this surface dismisses ITSELF, in ms. 0 = never.
182
210
  *
@@ -367,6 +395,116 @@ export declare function complianceGate(input: ComplianceInput): ComplianceVerdic
367
395
  * regardless of what the survey or the application says.
368
396
  */
369
397
  export declare function voiceUsable(questions: ValidatableQuestion[] | null | undefined): boolean;
398
+ export declare function collectsAnswer(question: {
399
+ type?: unknown;
400
+ } | null | undefined): boolean;
401
+ /**
402
+ * The part of a question set that responses are keyed by.
403
+ *
404
+ * Two sets with the same shape can be swapped without orphaning a single
405
+ * answer — which is the only thing the lock has to protect. Order is included
406
+ * because a reordered set changes nothing about the keys but IS how a
407
+ * different survey can be smuggled in with matching ids.
408
+ */
409
+ export declare function answerShape(questions: Array<{
410
+ id?: unknown;
411
+ name?: unknown;
412
+ type?: unknown;
413
+ }> | null | undefined): string;
414
+ export declare function resolveBranding(survey: {
415
+ branding?: Record<string, unknown> | null;
416
+ applicationBranding?: Record<string, unknown> | null;
417
+ settings?: {
418
+ themeDetached?: unknown;
419
+ } | null;
420
+ } | null | undefined): Record<string, unknown>;
421
+ /**
422
+ * LIGHT OR DARK, AND WHO GETS TO SAY.
423
+ *
424
+ * Five answers can arrive at once and they were ranked differently on each
425
+ * renderer, so the same card came out white on a phone and dark in a browser.
426
+ * The order, highest first:
427
+ *
428
+ * pinned a theme set on THIS interaction. An author who picked dark
429
+ * meant it; 'auto' or absent steps aside.
430
+ * user identify({ theme }) — the host telling us about this person.
431
+ * declared init({ theme }), or on the web a live <html data-theme>/dark
432
+ * class. The host telling us about the surface.
433
+ * appDefault the APPLICATION's theme. A default, with exactly the standing
434
+ * `defaultLanguage` has always had.
435
+ * device Appearance on a phone, prefers-color-scheme in a browser.
436
+ *
437
+ * `device` is LAST, which is the part that is easy to get wrong — it was
438
+ * ranked above `appDefault` for a day and broke both renderers in opposite
439
+ * directions. It describes the hardware, not the surface the card is drawn
440
+ * into: an app shipping `userInterfaceStyle: "dark"` is dark on a light
441
+ * handset, and a light website is light to a visitor whose desktop is dark.
442
+ * A card that trusts the device paints itself against the surface holding it.
443
+ * A host whose theme genuinely tracks the device says so through `declared`,
444
+ * which is a statement rather than a guess.
445
+ */
446
+ export type ThemeSource = 'pinned' | 'user' | 'declared' | 'appDefault' | 'device' | 'fallback';
447
+ /**
448
+ * The same decision, with WHICH CANDIDATE WON attached.
449
+ *
450
+ * Diagnostics, and worth having: the question "why is this card light?" was
451
+ * asked four times across two renderers and answered wrongly twice, because
452
+ * from the outside every tier produces the same two words. A host running in
453
+ * debug can now read the reason instead of inferring it from a screenshot.
454
+ */
455
+ export declare function resolveThemeDecision(input: {
456
+ pinned?: unknown;
457
+ user?: unknown;
458
+ declared?: unknown;
459
+ appDefault?: unknown;
460
+ device?: unknown;
461
+ } | null | undefined): {
462
+ theme: 'light' | 'dark';
463
+ from: ThemeSource;
464
+ };
465
+ export declare function resolveTheme(input: {
466
+ pinned?: unknown;
467
+ user?: unknown;
468
+ declared?: unknown;
469
+ appDefault?: unknown;
470
+ device?: unknown;
471
+ } | null | undefined): 'light' | 'dark';
472
+ /**
473
+ * DOES THE CARD SHOW A CONFIRMATION AFTER IT IS ACCEPTED?
474
+ *
475
+ * An offer's ending is hardcoded: "It's yours", "Use this at checkout." Two
476
+ * more sentences about somebody else's business, written by us, in a screen an
477
+ * author could neither reword nor turn off — the same shape as the small print
478
+ * that shipped inside a template.
479
+ *
480
+ * Off is a real answer: an offer whose reward is applied by the host app, or
481
+ * one that navigates somewhere it is already applied, has nothing to confirm —
482
+ * the card dwelling on a thank-you is a step between the customer and the thing
483
+ * they just asked for.
484
+ *
485
+ * WITH ONE REFUSAL. A code is handed over ON that screen and nowhere else, so
486
+ * hiding it would take the discount away at the moment of giving it. That is
487
+ * not a preference, so it is not offered as one: the flag is ignored and the
488
+ * screen shows.
489
+ */
490
+ export declare function showsConfirmation(survey: {
491
+ settings?: {
492
+ showConfirmation?: unknown;
493
+ } | null;
494
+ reward?: {
495
+ type?: unknown;
496
+ value?: unknown;
497
+ } | null;
498
+ } | null | undefined): boolean;
499
+ /**
500
+ * The words on that screen, the author's where they wrote any.
501
+ *
502
+ * `thankYouMessage` is the field every other interaction already uses for
503
+ * this, so an offer reaching for a second one would be a second place to look.
504
+ */
505
+ export declare function confirmationText(survey: {
506
+ thankYouMessage?: unknown;
507
+ } | null | undefined, fallback: string): string;
370
508
  /** The reward, ready to show — or null when there is nothing to hand over. */
371
509
  export declare function rewardToShow(survey: {
372
510
  reward?: {
@@ -381,6 +519,63 @@ export declare function rewardToShow(survey: {
381
519
  whereValid: string;
382
520
  expiresAt: string | null;
383
521
  } | null;
522
+ /**
523
+ * Does this question draw a label above itself?
524
+ *
525
+ * Every renderer emitted one unconditionally, so a question whose whole
526
+ * content IS its content — a picture, a block of copy — drew an EMPTY label
527
+ * and its margin above itself. The way round that was to compose such things
528
+ * outside the question flow, which puts things on a card that the survey
529
+ * schema does not describe.
530
+ *
531
+ * `hideLabel` is a declared question option now, and this is the one rule all
532
+ * three renderers ask:
533
+ *
534
+ * hideLabel === true → never, even with text
535
+ * hideLabel === false → always, even empty (an author who wants the space)
536
+ * unset → only if there is something to say
537
+ */
538
+ export declare function showsQuestionLabel(question: {
539
+ type?: unknown;
540
+ hideLabel?: unknown;
541
+ text?: unknown;
542
+ title?: unknown;
543
+ label?: unknown;
544
+ } | null | undefined): boolean;
545
+ /**
546
+ * THE SMALL PRINT ON AN OFFER — the author's words, not ours.
547
+ *
548
+ * "Valid on your first order only. Cannot be combined with other offers."
549
+ * shipped as a `content` question in the first-purchase template, which made it
550
+ * three separate problems:
551
+ *
552
+ * • It is a claim about somebody else's business, stated by us.
553
+ * • A `content` question lives in `questions`, which is SCHEMA — so the one
554
+ * kind of text most likely to need correcting could not be corrected on a
555
+ * LIVE offer, which is the only time correcting it matters.
556
+ * • The template translated its title, description and choices into Arabic
557
+ * and not this, so an Arabic offer carried English legal text.
558
+ *
559
+ * It belongs to the reward, beside what the customer gets and where it is
560
+ * valid. Empty means no small print, which is the right default: an offer with
561
+ * no conditions should not be made to invent some.
562
+ *
563
+ * FALLS BACK TO THE LEGACY QUESTION so offers created before this keep showing
564
+ * what they already show. Tags are stripped rather than rendered — the text is
565
+ * drawn as text by all three renderers, and `<small>` was only ever standing in
566
+ * for the styling the card now does itself.
567
+ */
568
+ export declare function rewardTerms(survey: {
569
+ reward?: {
570
+ terms?: unknown;
571
+ } | null;
572
+ questions?: Array<{
573
+ id?: unknown;
574
+ type?: unknown;
575
+ html?: unknown;
576
+ text?: unknown;
577
+ }> | null;
578
+ } | null | undefined): string;
384
579
  /**
385
580
  * Where clicking the CARD ITSELF should take someone, or null.
386
581
  *
@@ -400,6 +595,148 @@ export declare function cardClickTarget(display: {
400
595
  } | null | undefined, actions: Array<{
401
596
  href?: string;
402
597
  }> | null | undefined): string | null;
598
+ /**
599
+ * May the whole card be pressed?
600
+ *
601
+ * SEPARATE FROM WHERE IT GOES, and that separation is the fix. cardClickTarget
602
+ * answers "what destination does the card carry", and returns null when there
603
+ * is none — but a notification whose action is "tell your app" or "call an
604
+ * endpoint" has no destination and is still entirely pressable: pressing it
605
+ * records the answer and runs the action, which is the whole point of those
606
+ * two kinds.
607
+ *
608
+ * Reading the target as the permission meant that on any non-URL action the
609
+ * card lost its pointer cursor, its hover treatment and its click handler at
610
+ * once — so "What the card does under the cursor" did nothing, with no clue
611
+ * why.
612
+ *
613
+ * The safety argument is unchanged and lives here: exactly ONE action. That is
614
+ * the notification shape, and it excludes every consent choice — on a
615
+ * two-action compliance takeover a stray click anywhere would otherwise record
616
+ * agreement, which a consent record must never manufacture.
617
+ */
618
+ export declare function cardIsClickable(display: {
619
+ cardClickable?: unknown;
620
+ } | null | undefined, actions: Array<{
621
+ href?: string;
622
+ }> | null | undefined): boolean;
623
+ /**
624
+ * Is the action BUTTON drawn?
625
+ *
626
+ * A card whose whole surface is the action does not always need a button
627
+ * repeating it — "Resume" written twice on a banner two lines tall is the
628
+ * button competing with the card for the same press. So an author may hide it
629
+ * and let the card carry the action alone.
630
+ *
631
+ * ONLY WHEN THE CARD CAN ACTUALLY TAKE THE PRESS. Hiding the button on a card
632
+ * that is not clickable leaves a surface with something to say and no way to
633
+ * say yes to it — the respondent's only exit is the dismissal, which records
634
+ * the opposite of what the author was asking for. So the hide is honoured
635
+ * strictly when cardIsClickable agrees, and ignored otherwise.
636
+ *
637
+ * That also inherits the one-action guard: a two-action consent takeover can
638
+ * never hide its buttons, because a card that silently picks one of two
639
+ * answers is the thing consent must never do.
640
+ */
641
+ export declare function showActionButton(display: {
642
+ cardClickable?: unknown;
643
+ showAction?: unknown;
644
+ } | null | undefined, actions: Array<{
645
+ href?: string;
646
+ }> | null | undefined): boolean;
647
+ export interface TriggerContext {
648
+ definitionId?: unknown;
649
+ eventName?: unknown;
650
+ eventType?: unknown;
651
+ eventData?: unknown;
652
+ pathname?: unknown;
653
+ }
654
+ /**
655
+ * The variables a response carries about what made the interaction appear.
656
+ *
657
+ * `eventData` is spread FIRST so a caller's own values (URL params, identify()
658
+ * traits) still win when merged on top — the triggering event describes the
659
+ * occasion, not the respondent.
660
+ */
661
+ export declare function triggerVariables(tc: TriggerContext | null | undefined): Record<string, unknown>;
662
+ export type ImageLayout = 'beside' | 'above' | 'below' | 'background' | 'half' | 'none';
663
+ export declare function imageLayout(display: {
664
+ imageLayout?: unknown;
665
+ } | null | undefined, interactionType: unknown, hasImage: boolean): ImageLayout;
666
+ /**
667
+ * EVERYTHING ABOUT THE PICTURE, RESOLVED ONCE.
668
+ *
669
+ * Layout alone was enough while the answer was "a square, a band, or the whole
670
+ * card". An offer wants the picture to CARRY the card — half of it, cropped to
671
+ * the part that matters, with the seam between image and words softened — and
672
+ * that is five more decisions. Asked one at a time they drift: the preview
673
+ * centres a band the phone left-aligns, and nobody notices until an author
674
+ * says the two do not match.
675
+ *
676
+ * WHAT IS DELIBERATELY ABSENT: blur. The React Native SDK declares no
677
+ * dependencies beyond AsyncStorage, and React Native has no blur primitive —
678
+ * `react-native-svg` is an OPTIONAL peer, so it cannot be relied on either. A
679
+ * Blur control would work in the preview and do nothing on a phone, which is
680
+ * the exact class of control this codebase has spent a long time removing.
681
+ * Everything below works natively on both.
682
+ */
683
+ export interface ImageDesign {
684
+ layout: ImageLayout;
685
+ /** For 'half': which half the picture takes. Mirrored in a right-to-left
686
+ * card, because the whole card is. */
687
+ side: 'left' | 'right' | 'top';
688
+ /** For 'half': the share of the card the picture takes, 20-80. */
689
+ split: number;
690
+ /** How wide a band is, as a share of the card, 20-100. Alignment only means
691
+ * something once this is less than the whole width. */
692
+ width: number;
693
+ /** Where the picture sits in the space it is given. Vertical for a square
694
+ * beside the text, horizontal for a band narrower than the card.
695
+ *
696
+ * BOX POSITION, NOT FOCAL POINT. The obvious reading — which part of a
697
+ * cropped photograph you see — is object-position, and React Native has no
698
+ * equivalent: it would work in the preview and do nothing on a phone. This
699
+ * moves the picture's box, which both runtimes do natively. */
700
+ align: 'start' | 'center' | 'end';
701
+ /** How it fills that space. 'contain' shows all of it and may letterbox. */
702
+ fit: 'cover' | 'contain';
703
+ /** WHICH PART STAYS VISIBLE when 'cover' crops, 0-100 on each axis, 50 being
704
+ * the centre crop every renderer does by default.
705
+ *
706
+ * The thing that makes one picture work at three different shapes: a face
707
+ * near the top survives a wide band, a product on the right survives a left
708
+ * half. Without it an author has to re-crop the file for each layout.
709
+ *
710
+ * Meaningless under 'contain', which shows all of it — the editor offers it
711
+ * only where it bites. */
712
+ focusX: number;
713
+ focusY: number;
714
+ /** Card colour painted over the picture, 0-100. Only where words sit ON it. */
715
+ scrim: number;
716
+ /** How far the picture dissolves into the card at the seam, 0-100. A hard
717
+ * edge between a photograph and a block of text is the main thing that
718
+ * makes a split card look unfinished. */
719
+ fade: number;
720
+ }
721
+ export declare function imageDesign(display: {
722
+ imageLayout?: unknown;
723
+ imageSide?: unknown;
724
+ imageSplit?: unknown;
725
+ imageWidth?: unknown;
726
+ imageAlign?: unknown;
727
+ imageFit?: unknown;
728
+ imageScrim?: unknown;
729
+ imageFade?: unknown;
730
+ imageFocusX?: unknown;
731
+ imageFocusY?: unknown;
732
+ } | null | undefined, interactionType: unknown, hasImage: boolean): ImageDesign;
733
+ /** How much of the card colour is painted over the picture, 0-100.
734
+ *
735
+ * Only where the words sit ON the picture: behind the whole card, or across
736
+ * a half. The layouts that keep image and text in separate boxes need none. */
737
+ export declare function imageScrim(display: {
738
+ imageScrim?: unknown;
739
+ } | null | undefined): number;
403
740
  /** Hover treatment for a clickable card. Pointer devices only. */
404
741
  export declare function cardHoverEffect(display: {
405
742
  hoverEffect?: unknown;
@@ -407,7 +744,11 @@ export declare function cardHoverEffect(display: {
407
744
  export type ActionKind = 'url' | 'event' | 'webhook';
408
745
  export interface RawAction {
409
746
  type?: unknown;
747
+ /** The endpoint for a webhook, or the legacy destination for type 'url'. */
410
748
  value?: unknown;
749
+ /** Where the press navigates, independent of kind — so an action can both
750
+ * take someone somewhere and report that it happened. */
751
+ href?: unknown;
411
752
  /** For 'event': the name the host application listens for. */
412
753
  eventName?: unknown;
413
754
  /** Template payload — values may contain {{variable}} references. */
@@ -415,8 +756,12 @@ export interface RawAction {
415
756
  }
416
757
  export interface ResolvedAction {
417
758
  kind: ActionKind;
418
- /** Navigable target for 'url', endpoint for 'webhook'. Null when unusable. */
759
+ /** Navigable target for 'url', endpoint for 'webhook'. Null when unusable.
760
+ * Unchanged in meaning — hosts read it — see `destination` below. */
419
761
  href: string | null;
762
+ /** Where the press NAVIGATES, whatever the kind. Set only from action.href,
763
+ * so it is never confused with an endpoint. Null when there is none. */
764
+ destination: string | null;
420
765
  eventName: string | null;
421
766
  /** Payload with every {{reference}} substituted. */
422
767
  payload: Record<string, string>;
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
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,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,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;AAsQD,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAetE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,mBAAmB;AAC7B;;;;;;;;;;GAUG;AACH,WAAW,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAiB,GAC5C,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAwDzE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAIvF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CA0BlE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAajD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,mBAAmB,EAAE,GAAG,IAAI,GAAG,SAAS,GAClD,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAUzC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,mBAAmB,EAAE,GAAG,IAAI,GAAG,SAAS,GAClD,OAAO,CAKT;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACnE,QAAQ,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GACvC,OAAO,CAMT;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACnE,eAAe,CAAC,EAAE,OAAO,EACzB,QAAQ,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GACvC,OAAO,CAKT;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACrE,QAAQ,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GACvC,MAAM,CAUR;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EAChD,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,EAC3C,GAAG,CAAC,EAAE,MAAM,GACX;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CASrC;AAED,yFAAyF;AACzF,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAUlD;AAcD,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,UAAU,GACV,SAAS,GACT,QAAQ,GACR,SAAS,GACT,MAAM,CAAC;AAEX,MAAM,WAAW,YAAY;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,yEAAyE;IACzE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1E,UAAU,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,CAuB3D;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS;IAC9C,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CAClE,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,CAAC,CAQtD;AAID,iDAAiD;AACjD,MAAM,MAAM,eAAe,GACvB,MAAM,GACN,SAAS,GACT,cAAc,GACd,SAAS,CAAC;AAEd,oEAAoE;AACpE,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,SAAS,GACT,OAAO,CAAC;AAEZ,MAAM,WAAW,eAAe;IAC9B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE;QACX,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,GAAG,IAAI,CAAC;CACV;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,eAAe,CAAC;IACvB,4CAA4C;IAC5C,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;IACpB,wEAAwE;IACxE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,4DAA4D;IAC5D,WAAW,EAAE,oBAAoB,GAAG,yBAAyB,GAAG,IAAI,CAAC;IACrE,8EAA8E;IAC9E,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,yEAAyE;IACzE,UAAU,EAAE,OAAO,CAAC;CACrB;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,iBAAiB,CAmDxE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,mBAAmB,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAQxF;AAuBD,wBAAgB,cAAc,CAC5B,QAAQ,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAC9C,OAAO,CAGT;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,KAAK,CAAC;IAAE,EAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACpF,MAAM,CAMR;AAuBD,wBAAgB,eAAe,CAC7B,MAAM,EAAE;IACN,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACrD,QAAQ,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CAC/C,GAAG,IAAI,GAAG,SAAS,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4CzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,WAAW,GACrB,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,YAAY,GAAG,QAAQ,GAAG,UAAU,CAAC;AAExE;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,IAAI,GAAG,SAAS,GAAG;IAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE,CAWpE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAEtC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE;IACN,QAAQ,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACrD,GAAG,IAAI,GAAG,SAAS,GACnB,OAAO,CAMT;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACxD,QAAQ,EAAE,MAAM,GACf,MAAM,CAIR;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAC1B,MAAM,EAAE;IAAE,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAC1H;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAiBtF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/F,IAAI,GAAG,SAAS,GACnB,OAAO,CAeT;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE;IACN,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACpC,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,IAAI,CAAC;CAC5F,GAAG,IAAI,GAAG,SAAS,GACnB,MAAM,CAYR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EAC3E,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACnD,MAAM,GAAG,IAAI,CASf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACvD,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAeT;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EAC7E,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GACnD,OAAO,CAGT;AAoBD,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,GACpC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAezB;AAiBD,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAQxF,wBAAgB,WAAW,CACzB,OAAO,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,EACrD,eAAe,EAAE,OAAO,EACxB,QAAQ,EAAE,OAAO,GAChB,WAAW,CAWb;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,WAAW,CAAC;IACpB;2CACuC;IACvC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;IAC/B,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd;4DACwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;oEAMgE;IAChE,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IAClC,4EAA4E;IAC5E,GAAG,EAAE,OAAO,GAAG,SAAS,CAAC;IACzB;;;;;;;;+BAQ2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd;;8CAE0C;IAC1C,IAAI,EAAE,MAAM,CAAC;CACd;AAYD,wBAAgB,WAAW,CACzB,OAAO,EAAE;IACP,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IACvF,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpF,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAC9C,GAAG,IAAI,GAAG,SAAS,EACpB,eAAe,EAAE,OAAO,EACxB,QAAQ,EAAE,OAAO,GAChB,WAAW,CA2Bb;AAED;;;gFAGgF;AAChF,wBAAgB,UAAU,CACxB,OAAO,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GACnD,MAAM,CAIR;AAED,kEAAkE;AAClE,wBAAgB,eAAe,CAC7B,OAAO,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GACpD,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAGnC;AAeD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,OAAO,GAAG,SAAS,CAAC;AAErD,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,4EAA4E;IAC5E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;8DAC0D;IAC1D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,8DAA8D;IAC9D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB;0EACsE;IACtE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB;6EACyE;IACzE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,EACpC,WAAW,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAiB,GAC5C,cAAc,GAAG,IAAI,CAmDvB"}
package/dist/validate.js CHANGED
@@ -10,6 +10,7 @@ exports.safeImageSrc = safeImageSrc;
10
10
  exports.impliedStepActions = impliedStepActions;
11
11
  exports.impliedEqualProminence = impliedEqualProminence;
12
12
  exports.canSwipeToDismiss = canSwipeToDismiss;
13
+ exports.showsClose = showsClose;
13
14
  exports.autoDismissMs = autoDismissMs;
14
15
  exports.deadlineCountdown = deadlineCountdown;
15
16
  exports.formatCountdown = formatCountdown;
@@ -17,8 +18,23 @@ exports.outcomeFor = outcomeFor;
17
18
  exports.questionForValidation = questionForValidation;
18
19
  exports.complianceGate = complianceGate;
19
20
  exports.voiceUsable = voiceUsable;
21
+ exports.collectsAnswer = collectsAnswer;
22
+ exports.answerShape = answerShape;
23
+ exports.resolveBranding = resolveBranding;
24
+ exports.resolveThemeDecision = resolveThemeDecision;
25
+ exports.resolveTheme = resolveTheme;
26
+ exports.showsConfirmation = showsConfirmation;
27
+ exports.confirmationText = confirmationText;
20
28
  exports.rewardToShow = rewardToShow;
29
+ exports.showsQuestionLabel = showsQuestionLabel;
30
+ exports.rewardTerms = rewardTerms;
21
31
  exports.cardClickTarget = cardClickTarget;
32
+ exports.cardIsClickable = cardIsClickable;
33
+ exports.showActionButton = showActionButton;
34
+ exports.triggerVariables = triggerVariables;
35
+ exports.imageLayout = imageLayout;
36
+ exports.imageDesign = imageDesign;
37
+ exports.imageScrim = imageScrim;
22
38
  exports.cardHoverEffect = cardHoverEffect;
23
39
  exports.resolveAction = resolveAction;
24
40
  const messages_1 = require("./messages");
@@ -414,9 +430,15 @@ interpolate = (s) => s) {
414
430
  // Interpolated BEFORE the scheme check, never after: a template that
415
431
  // resolves to `javascript:` has to be caught on what it BECAME, not on
416
432
  // the harmless-looking string it started as.
417
- const rawHref = (o.action && typeof o.action === 'object')
418
- ? o.action.value
433
+ const act = (o.action && typeof o.action === 'object')
434
+ ? o.action
419
435
  : undefined;
436
+ // href first — see safeActionHref. Interpolated the same way, because a
437
+ // destination is a template wherever it is stored.
438
+ const rawHref = act
439
+ ? (typeof act.href === 'string' && act.href ? act.href : act.value)
440
+ : undefined;
441
+ const viaHref = !!(act && typeof act.href === 'string' && act.href);
420
442
  const resolved = typeof rawHref === 'string' && rawHref
421
443
  ? interpolate(rawHref)
422
444
  : rawHref;
@@ -427,7 +449,9 @@ interpolate = (s) => s) {
427
449
  const usable = typeof resolved === 'string' && /\{\{.*\}\}/.test(resolved)
428
450
  ? undefined
429
451
  : resolved;
430
- const href = safeActionHref(usable === undefined ? undefined : { ...o.action, value: usable });
452
+ const href = safeActionHref(usable === undefined ? undefined
453
+ : viaHref ? { ...o.action, href: usable }
454
+ : { ...o.action, value: usable });
431
455
  if (value !== '' || label !== '') {
432
456
  const entry = href ? { value, label: label || value, href } : { value, label: label || value };
433
457
  // "Remind me later" — see isDeferChoice. Carried through because a
@@ -485,9 +509,21 @@ function safeActionHref(action) {
485
509
  if (!action || typeof action !== 'object')
486
510
  return undefined;
487
511
  const a = action;
488
- if (String(a.type || '').toLowerCase() !== 'url')
489
- return undefined;
490
- const v = typeof a.value === 'string' ? a.value.trim() : '';
512
+ // A DESTINATION IS NOT A KIND OF ACTION, IT IS A PROPERTY OF ONE.
513
+ //
514
+ // Reading the destination out of `value` meant `value` had to be a URL, which
515
+ // meant type had to be 'url', which made "take them somewhere" and "report
516
+ // this press" mutually exclusive. They are not: a button on an abandoned-cart
517
+ // nudge that opens the cart AND tells your backend is the ordinary case, and
518
+ // an author asked why it had to be one or the other.
519
+ //
520
+ // `href` carries the destination on its own, whatever the action's kind. The
521
+ // legacy pair (type 'url' + value) still resolves, so nothing already
522
+ // authored changes.
523
+ const explicit = typeof a.href === 'string' ? a.href.trim() : '';
524
+ const legacy = String(a.type || '').toLowerCase() === 'url'
525
+ && typeof a.value === 'string' ? a.value.trim() : '';
526
+ const v = explicit || legacy;
491
527
  if (!v)
492
528
  return undefined; // '' = not configured yet
493
529
  // Reject anything whose scheme could execute in the host document.
@@ -627,6 +663,36 @@ function canSwipeToDismiss(display, delivery) {
627
663
  return false; // fullscreen / modal never swipe
628
664
  return display.showClose === false; // the gesture REPLACES the ✕
629
665
  }
666
+ /**
667
+ * Is the ✕ drawn?
668
+ *
669
+ * TWO RENDERERS, TWO ANSWERS, from one authored survey. Web resolved an unset
670
+ * `showClose` to "hidden on a notification, shown everywhere else" — a
671
+ * deliberate rule, written down: a banner is swiped away, and canSwipeToDismiss
672
+ * turns the gesture on precisely when the ✕ is off. The phone read
673
+ * `showClose !== false`, so an unset notification kept its ✕ AND, because its
674
+ * display is a plain merge that leaves the value undefined, never enabled the
675
+ * swipe. The same notification was dismissable two different ways depending on
676
+ * which screen you were looking at, and a panel describing it could only be
677
+ * right about one of them.
678
+ *
679
+ * ONLY WHERE THE GESTURE REPLACES IT. Dropping the ✕ from a notification that
680
+ * cannot be swiped — a modal, a fullscreen takeover, a must-show — would leave
681
+ * no way out at all. That trap was already live on web, which hid the mark on
682
+ * every notification regardless of mode; asking canSwipeToDismiss here closes
683
+ * it on both.
684
+ *
685
+ * An explicit value always wins: '' / null / undefined mean "inherit", the same
686
+ * convention the rest of `display` uses.
687
+ */
688
+ function showsClose(display, interactionType, delivery) {
689
+ const raw = display?.showClose;
690
+ if (raw !== undefined && raw !== null && raw !== '')
691
+ return raw !== false;
692
+ if (String(interactionType || '').toLowerCase() !== 'notify')
693
+ return true;
694
+ return !canSwipeToDismiss({ ...(display || {}), showClose: false }, delivery);
695
+ }
630
696
  /**
631
697
  * How long before this surface dismisses ITSELF, in ms. 0 = never.
632
698
  *
@@ -882,6 +948,178 @@ function voiceUsable(questions) {
882
948
  && t !== 'image' && t !== 'expression' && t !== 'pagebreak';
883
949
  });
884
950
  }
951
+ /**
952
+ * Does this question record an answer?
953
+ *
954
+ * WHAT THE QUESTION LOCK IS ACTUALLY FOR. A survey that has started collecting
955
+ * refuses edits to `questions`, because responses are keyed by question id and
956
+ * a moved key orphans the data already gathered. That is a real rule — and it
957
+ * was applied to the WHOLE array, including questions that record nothing.
958
+ *
959
+ * A `content` block, a picture, a page break: none of them has an answer, so
960
+ * changing their words cannot orphan anything. Locking them made the offer's
961
+ * small print — the text most likely to need correcting on a running offer —
962
+ * uneditable for the same reason a rating scale is, which is not the same
963
+ * reason at all. The headline and the description sit right beside it and were
964
+ * never locked, because they are not in `questions`.
965
+ *
966
+ * NOT voiceUsable's list. That one also excludes `implied`, because a button
967
+ * cannot be spoken — but an implied question very much records an answer, and
968
+ * for this question it must count.
969
+ */
970
+ const RECORDS_NOTHING = ['html', 'content', 'image', 'expression', 'pagebreak', 'page_break'];
971
+ function collectsAnswer(question) {
972
+ if (!question)
973
+ return false;
974
+ return RECORDS_NOTHING.indexOf(String(question.type || '').toLowerCase()) < 0;
975
+ }
976
+ /**
977
+ * The part of a question set that responses are keyed by.
978
+ *
979
+ * Two sets with the same shape can be swapped without orphaning a single
980
+ * answer — which is the only thing the lock has to protect. Order is included
981
+ * because a reordered set changes nothing about the keys but IS how a
982
+ * different survey can be smuggled in with matching ids.
983
+ */
984
+ function answerShape(questions) {
985
+ if (!Array.isArray(questions))
986
+ return '';
987
+ return questions
988
+ .filter((q) => collectsAnswer(q))
989
+ .map((q) => `${String(q?.id ?? q?.name ?? '')}:${String(q?.type ?? '').toLowerCase()}`)
990
+ .join('|');
991
+ }
992
+ /**
993
+ * THE BRANDING CASCADE: survey.branding ▶ applicationBranding ▶ built-in.
994
+ *
995
+ * A survey stores only what its author changed, so everything else has to
996
+ * resolve up to the application at render time. The web SDK learned this once
997
+ * already — it read `surveyData.branding` alone and an application palette
998
+ * stopped there — and the React Native SDK never learned it at all: it took
999
+ * `raw.branding` and nothing else, so an interaction with no palette of its own
1000
+ * drew the SDK's built-in lime on a phone no matter what the application said,
1001
+ * and an application-level button padding never arrived either.
1002
+ *
1003
+ * Reported as both, separately, which is what an unresolved layer looks like
1004
+ * from the outside: a list of unrelated settings that "do not apply on mobile".
1005
+ *
1006
+ * Nested groups merge per KEY, and `palette` one level deeper again because it
1007
+ * is keyed by theme — a survey overriding one surface must not drop the
1008
+ * application's others. '' / null / undefined mean INHERIT, the same convention
1009
+ * `display` uses.
1010
+ */
1011
+ const BRANDING_GROUPS = ['palette', 'style', 'typography', 'layout'];
1012
+ function resolveBranding(survey) {
1013
+ const own = (survey?.branding || {});
1014
+ // A detached survey carries its own theme and stops inheriting entirely.
1015
+ if (survey?.settings?.themeDetached)
1016
+ return own;
1017
+ const app = (survey?.applicationBranding || {});
1018
+ const keep = (v) => v !== undefined && v !== null && v !== '';
1019
+ const out = { ...app };
1020
+ for (const [k, v] of Object.entries(own))
1021
+ if (keep(v))
1022
+ out[k] = v;
1023
+ // THEME IS THE ONE FIELD THAT DOES NOT MERGE, because it is the one field a
1024
+ // respondent has an opinion about. Every other branding key is the brand's
1025
+ // to decide — a colour, a radius, a font. Light-vs-dark is the reader's, and
1026
+ // an application-level value folded into `theme` here became indistinguish-
1027
+ // able from an author pinning this one card, so it outranked the respondent's
1028
+ // own device on every surface. It is carried separately instead, and the
1029
+ // renderers place it where a default belongs: under everything the SDK can
1030
+ // tell us about the person, over nothing but the built-in. Exactly the
1031
+ // standing `defaultLanguage` has always had, which is why language was right
1032
+ // and theme was wrong.
1033
+ if (keep(app.theme))
1034
+ out.themeDefault = app.theme;
1035
+ if (!keep(own.theme))
1036
+ delete out.theme;
1037
+ for (const g of BRANDING_GROUPS) {
1038
+ const a = app[g];
1039
+ const o = own[g];
1040
+ if (!a || !o || typeof a !== 'object' || typeof o !== 'object')
1041
+ continue;
1042
+ const merged = { ...a };
1043
+ for (const [kk, ov] of Object.entries(o)) {
1044
+ const av = merged[kk];
1045
+ if (av && ov && typeof av === 'object' && typeof ov === 'object'
1046
+ && !Array.isArray(av) && !Array.isArray(ov)) {
1047
+ const deep = { ...av };
1048
+ for (const [k3, v3] of Object.entries(ov)) {
1049
+ if (keep(v3))
1050
+ deep[k3] = v3;
1051
+ }
1052
+ merged[kk] = deep;
1053
+ }
1054
+ else if (keep(ov)) {
1055
+ merged[kk] = ov;
1056
+ }
1057
+ }
1058
+ out[g] = merged;
1059
+ }
1060
+ return out;
1061
+ }
1062
+ /**
1063
+ * The same decision, with WHICH CANDIDATE WON attached.
1064
+ *
1065
+ * Diagnostics, and worth having: the question "why is this card light?" was
1066
+ * asked four times across two renderers and answered wrongly twice, because
1067
+ * from the outside every tier produces the same two words. A host running in
1068
+ * debug can now read the reason instead of inferring it from a screenshot.
1069
+ */
1070
+ function resolveThemeDecision(input) {
1071
+ const pick = (v) => {
1072
+ const t = typeof v === 'string' ? v.toLowerCase() : '';
1073
+ return t === 'light' || t === 'dark' ? t : undefined; // 'auto' steps aside
1074
+ };
1075
+ const order = ['pinned', 'user', 'declared', 'appDefault', 'device'];
1076
+ for (const from of order) {
1077
+ const theme = pick(input?.[from]);
1078
+ if (theme)
1079
+ return { theme, from };
1080
+ }
1081
+ return { theme: 'dark', from: 'fallback' };
1082
+ }
1083
+ function resolveTheme(input) {
1084
+ return resolveThemeDecision(input).theme;
1085
+ }
1086
+ /**
1087
+ * DOES THE CARD SHOW A CONFIRMATION AFTER IT IS ACCEPTED?
1088
+ *
1089
+ * An offer's ending is hardcoded: "It's yours", "Use this at checkout." Two
1090
+ * more sentences about somebody else's business, written by us, in a screen an
1091
+ * author could neither reword nor turn off — the same shape as the small print
1092
+ * that shipped inside a template.
1093
+ *
1094
+ * Off is a real answer: an offer whose reward is applied by the host app, or
1095
+ * one that navigates somewhere it is already applied, has nothing to confirm —
1096
+ * the card dwelling on a thank-you is a step between the customer and the thing
1097
+ * they just asked for.
1098
+ *
1099
+ * WITH ONE REFUSAL. A code is handed over ON that screen and nowhere else, so
1100
+ * hiding it would take the discount away at the moment of giving it. That is
1101
+ * not a preference, so it is not offered as one: the flag is ignored and the
1102
+ * screen shows.
1103
+ */
1104
+ function showsConfirmation(survey) {
1105
+ if (survey?.settings?.showConfirmation !== false)
1106
+ return true;
1107
+ // The only thing the customer cannot get any other way.
1108
+ const r = survey?.reward;
1109
+ const isCode = String(r?.type || '') === 'code' && !!String(r?.value ?? '').trim();
1110
+ return isCode;
1111
+ }
1112
+ /**
1113
+ * The words on that screen, the author's where they wrote any.
1114
+ *
1115
+ * `thankYouMessage` is the field every other interaction already uses for
1116
+ * this, so an offer reaching for a second one would be a second place to look.
1117
+ */
1118
+ function confirmationText(survey, fallback) {
1119
+ const own = String(survey?.thankYouMessage ?? '')
1120
+ .replace(/<[^>]*>/g, '').replace(/&nbsp;/gi, ' ').trim();
1121
+ return own || fallback;
1122
+ }
885
1123
  /** The reward, ready to show — or null when there is nothing to hand over. */
886
1124
  function rewardToShow(survey) {
887
1125
  const r = survey?.reward;
@@ -904,6 +1142,82 @@ function rewardToShow(survey) {
904
1142
  expiresAt: survey?.endDate ? new Date(String(survey.endDate)).toISOString() : null,
905
1143
  };
906
1144
  }
1145
+ /**
1146
+ * Does this question draw a label above itself?
1147
+ *
1148
+ * Every renderer emitted one unconditionally, so a question whose whole
1149
+ * content IS its content — a picture, a block of copy — drew an EMPTY label
1150
+ * and its margin above itself. The way round that was to compose such things
1151
+ * outside the question flow, which puts things on a card that the survey
1152
+ * schema does not describe.
1153
+ *
1154
+ * `hideLabel` is a declared question option now, and this is the one rule all
1155
+ * three renderers ask:
1156
+ *
1157
+ * hideLabel === true → never, even with text
1158
+ * hideLabel === false → always, even empty (an author who wants the space)
1159
+ * unset → only if there is something to say
1160
+ */
1161
+ function showsQuestionLabel(question) {
1162
+ if (!question)
1163
+ return false;
1164
+ // AN IMPLIED QUESTION NEVER SHOWS ONE, whatever it carries.
1165
+ //
1166
+ // Its `text` — "Offer accepted?" — is the analytics prompt, what the recorded
1167
+ // answer MEANS, not copy for the customer. Its choices are the buttons. Both
1168
+ // renderers had that as a special case beside this one, which is precisely
1169
+ // how a rule ends up applied in one place and not the other: the first thing
1170
+ // that happened when the phone started asking this function instead was the
1171
+ // stray authoring text coming back.
1172
+ if (String(question.type || '').toLowerCase() === 'implied')
1173
+ return false;
1174
+ if (question.hideLabel === true)
1175
+ return false;
1176
+ if (question.hideLabel === false)
1177
+ return true;
1178
+ const text = question.title ?? question.text ?? question.label ?? '';
1179
+ return String(text).trim().length > 0;
1180
+ }
1181
+ /**
1182
+ * THE SMALL PRINT ON AN OFFER — the author's words, not ours.
1183
+ *
1184
+ * "Valid on your first order only. Cannot be combined with other offers."
1185
+ * shipped as a `content` question in the first-purchase template, which made it
1186
+ * three separate problems:
1187
+ *
1188
+ * • It is a claim about somebody else's business, stated by us.
1189
+ * • A `content` question lives in `questions`, which is SCHEMA — so the one
1190
+ * kind of text most likely to need correcting could not be corrected on a
1191
+ * LIVE offer, which is the only time correcting it matters.
1192
+ * • The template translated its title, description and choices into Arabic
1193
+ * and not this, so an Arabic offer carried English legal text.
1194
+ *
1195
+ * It belongs to the reward, beside what the customer gets and where it is
1196
+ * valid. Empty means no small print, which is the right default: an offer with
1197
+ * no conditions should not be made to invent some.
1198
+ *
1199
+ * FALLS BACK TO THE LEGACY QUESTION so offers created before this keep showing
1200
+ * what they already show. Tags are stripped rather than rendered — the text is
1201
+ * drawn as text by all three renderers, and `<small>` was only ever standing in
1202
+ * for the styling the card now does itself.
1203
+ */
1204
+ function rewardTerms(survey) {
1205
+ const own = survey?.reward?.terms;
1206
+ if (own != null && String(own).trim())
1207
+ return String(own).trim();
1208
+ const qs = Array.isArray(survey?.questions) ? survey.questions : [];
1209
+ for (const q of qs) {
1210
+ if (String(q?.id || '') !== 'offer_terms')
1211
+ continue;
1212
+ if (String(q?.type || '').toLowerCase() !== 'content')
1213
+ continue;
1214
+ const raw = String(q?.html ?? q?.text ?? '');
1215
+ const text = raw.replace(/<[^>]*>/g, '').replace(/&nbsp;/gi, ' ').trim();
1216
+ if (text)
1217
+ return text;
1218
+ }
1219
+ return '';
1220
+ }
907
1221
  /**
908
1222
  * Where clicking the CARD ITSELF should take someone, or null.
909
1223
  *
@@ -928,6 +1242,160 @@ function cardClickTarget(display, actions) {
928
1242
  const own = safeActionHref({ type: 'url', value: display?.cardHref });
929
1243
  return own || acts[0].href || null;
930
1244
  }
1245
+ /**
1246
+ * May the whole card be pressed?
1247
+ *
1248
+ * SEPARATE FROM WHERE IT GOES, and that separation is the fix. cardClickTarget
1249
+ * answers "what destination does the card carry", and returns null when there
1250
+ * is none — but a notification whose action is "tell your app" or "call an
1251
+ * endpoint" has no destination and is still entirely pressable: pressing it
1252
+ * records the answer and runs the action, which is the whole point of those
1253
+ * two kinds.
1254
+ *
1255
+ * Reading the target as the permission meant that on any non-URL action the
1256
+ * card lost its pointer cursor, its hover treatment and its click handler at
1257
+ * once — so "What the card does under the cursor" did nothing, with no clue
1258
+ * why.
1259
+ *
1260
+ * The safety argument is unchanged and lives here: exactly ONE action. That is
1261
+ * the notification shape, and it excludes every consent choice — on a
1262
+ * two-action compliance takeover a stray click anywhere would otherwise record
1263
+ * agreement, which a consent record must never manufacture.
1264
+ */
1265
+ function cardIsClickable(display, actions) {
1266
+ const acts = Array.isArray(actions) ? actions : [];
1267
+ if (acts.length !== 1)
1268
+ return false;
1269
+ // OPT IN, NOT OPT OUT.
1270
+ //
1271
+ // This read `!== false`, so a card with one action was pressable anywhere on
1272
+ // its surface unless someone had turned that off. A whole card that navigates
1273
+ // is a real behaviour to choose, not a default to discover: on a notification
1274
+ // it means the message and the dismissal target overlap, and the author who
1275
+ // never opened this panel never agreed to it.
1276
+ //
1277
+ // The guard above still holds either way — two actions are never
1278
+ // card-clickable, because a card that silently picks one of two answers is
1279
+ // the thing consent must never do.
1280
+ return display?.cardClickable === true;
1281
+ }
1282
+ /**
1283
+ * Is the action BUTTON drawn?
1284
+ *
1285
+ * A card whose whole surface is the action does not always need a button
1286
+ * repeating it — "Resume" written twice on a banner two lines tall is the
1287
+ * button competing with the card for the same press. So an author may hide it
1288
+ * and let the card carry the action alone.
1289
+ *
1290
+ * ONLY WHEN THE CARD CAN ACTUALLY TAKE THE PRESS. Hiding the button on a card
1291
+ * that is not clickable leaves a surface with something to say and no way to
1292
+ * say yes to it — the respondent's only exit is the dismissal, which records
1293
+ * the opposite of what the author was asking for. So the hide is honoured
1294
+ * strictly when cardIsClickable agrees, and ignored otherwise.
1295
+ *
1296
+ * That also inherits the one-action guard: a two-action consent takeover can
1297
+ * never hide its buttons, because a card that silently picks one of two
1298
+ * answers is the thing consent must never do.
1299
+ */
1300
+ function showActionButton(display, actions) {
1301
+ if (display?.showAction !== false)
1302
+ return true; // shown unless hidden
1303
+ return !cardIsClickable(display, actions); // hide only if the card takes it
1304
+ }
1305
+ /**
1306
+ * The variables a response carries about what made the interaction appear.
1307
+ *
1308
+ * `eventData` is spread FIRST so a caller's own values (URL params, identify()
1309
+ * traits) still win when merged on top — the triggering event describes the
1310
+ * occasion, not the respondent.
1311
+ */
1312
+ function triggerVariables(tc) {
1313
+ const out = {};
1314
+ if (!tc || typeof tc !== 'object')
1315
+ return out;
1316
+ const data = tc.eventData;
1317
+ if (data && typeof data === 'object') {
1318
+ for (const [k, v] of Object.entries(data)) {
1319
+ if (v != null && v !== '')
1320
+ out[k] = v;
1321
+ }
1322
+ }
1323
+ // Underscore-prefixed so they cannot collide with an author's own event
1324
+ // field — `eventName` is a plausible thing to put in eventData.
1325
+ if (tc.definitionId)
1326
+ out._triggerDefinition = String(tc.definitionId);
1327
+ if (tc.eventName)
1328
+ out._triggerEvent = String(tc.eventName);
1329
+ if (tc.pathname)
1330
+ out._triggerPath = String(tc.pathname);
1331
+ return out;
1332
+ }
1333
+ const IMAGE_LAYOUTS = ['beside', 'above', 'below', 'background', 'half'];
1334
+ /** Which interactions compose their own picture. Everything else leaves the
1335
+ * `image` question where it is. */
1336
+ const COMPOSES_IMAGE = ['notify', 'offer'];
1337
+ function imageLayout(display, interactionType, hasImage) {
1338
+ if (!hasImage)
1339
+ return 'none';
1340
+ const kind = String(interactionType || '').toLowerCase();
1341
+ if (COMPOSES_IMAGE.indexOf(kind) < 0)
1342
+ return 'none';
1343
+ const raw = String(display?.imageLayout || '').toLowerCase();
1344
+ if (IMAGE_LAYOUTS.indexOf(raw) >= 0)
1345
+ return raw;
1346
+ // UNSET IS THE SHAPE EACH ONE ALREADY HAD. A notification has always drawn
1347
+ // the square beside its text, and changing that under every existing
1348
+ // notification would be a redesign nobody asked for. An offer has no
1349
+ // incumbent layout to preserve and the picture is the offer, so it leads.
1350
+ return kind === 'notify' ? 'beside' : 'above';
1351
+ }
1352
+ const SIDES = ['left', 'right', 'top'];
1353
+ const ALIGNS = ['start', 'center', 'end'];
1354
+ /** Clamped to a range with a stated default — never NaN, never out of bounds. */
1355
+ function pct(v, dflt, lo = 0, hi = 100) {
1356
+ const n = Number(v);
1357
+ if (!isFinite(n))
1358
+ return dflt;
1359
+ return Math.max(lo, Math.min(hi, Math.round(n)));
1360
+ }
1361
+ function imageDesign(display, interactionType, hasImage) {
1362
+ const layout = imageLayout(display, interactionType, hasImage);
1363
+ const rawSide = String(display?.imageSide || '').toLowerCase();
1364
+ const rawAlign = String(display?.imageAlign || '').toLowerCase();
1365
+ return {
1366
+ layout,
1367
+ side: (SIDES.indexOf(rawSide) >= 0 ? rawSide : 'left'),
1368
+ // 20-80 rather than 0-100: a 5% sliver is not a half, and a 95% one leaves
1369
+ // the words a gutter. A control should refuse what the layout cannot be.
1370
+ split: pct(display?.imageSplit, 50, 20, 80),
1371
+ width: pct(display?.imageWidth, 100, 20, 100),
1372
+ // Centre is right for a band and wrong for a square beside text, which
1373
+ // should sit level with the headline it belongs to.
1374
+ align: (ALIGNS.indexOf(rawAlign) >= 0 ? rawAlign
1375
+ : (layout === 'beside' ? 'start' : 'center')),
1376
+ fit: String(display?.imageFit || '').toLowerCase() === 'contain' ? 'contain' : 'cover',
1377
+ focusX: pct(display?.imageFocusX, 50),
1378
+ focusY: pct(display?.imageFocusY, 50),
1379
+ // 40 BEHIND THE CARD, 0 EVERYWHERE ELSE.
1380
+ //
1381
+ // The scrim exists because words sit ON the picture, which is true by
1382
+ // construction of a backdrop and not of a half — there the text has its own
1383
+ // side. Carrying the backdrop's default across would have dimmed every
1384
+ // split image by 40% that nobody asked to dim.
1385
+ scrim: layout === 'background' ? imageScrim(display) : pct(display?.imageScrim, 0),
1386
+ fade: pct(display?.imageFade, 0),
1387
+ };
1388
+ }
1389
+ /** How much of the card colour is painted over the picture, 0-100.
1390
+ *
1391
+ * Only where the words sit ON the picture: behind the whole card, or across
1392
+ * a half. The layouts that keep image and text in separate boxes need none. */
1393
+ function imageScrim(display) {
1394
+ const n = Number(display?.imageScrim);
1395
+ if (!isFinite(n))
1396
+ return 40;
1397
+ return Math.max(0, Math.min(100, Math.round(n)));
1398
+ }
931
1399
  /** Hover treatment for a clickable card. Pointer devices only. */
932
1400
  function cardHoverEffect(display) {
933
1401
  const v = String(display?.hoverEffect || '').toLowerCase();
@@ -954,6 +1422,19 @@ function resolveAction(action, interpolate = (s) => s) {
954
1422
  const template = typeof action.value === 'string' ? action.value : '';
955
1423
  const substituted = template ? interpolate(template) : '';
956
1424
  const href = substituted ? safeActionHref({ type: 'url', value: substituted }) || null : null;
1425
+ // WHERE THE PRESS NAVIGATES, kept separate from `href` on purpose.
1426
+ //
1427
+ // `href` has always meant two different things depending on kind — the
1428
+ // destination for a 'url' action, the endpoint to call for a 'webhook' one —
1429
+ // and hosts read it that way, so it is left exactly as it was. Now that one
1430
+ // action can BOTH navigate and report, the navigation target needs a name of
1431
+ // its own: without it, a button with an endpoint in `value` and a destination
1432
+ // in `href` would send the customer to the customer's own backend.
1433
+ const destTemplate = typeof action.href === 'string' ? action.href : '';
1434
+ const destSubstituted = destTemplate ? interpolate(destTemplate) : '';
1435
+ const destination = destSubstituted
1436
+ ? safeActionHref({ href: destSubstituted }) || null
1437
+ : null;
957
1438
  const payload = {};
958
1439
  const p = action.payload;
959
1440
  if (p && typeof p === 'object' && !Array.isArray(p)) {
@@ -981,7 +1462,7 @@ function resolveAction(action, interpolate = (s) => s) {
981
1462
  const eventName = typeof action.eventName === 'string' && action.eventName.trim()
982
1463
  ? action.eventName.trim() : null;
983
1464
  // Nothing to do: no destination, no event name, no payload.
984
- if (!href && !eventName && Object.keys(payload).length === 0)
1465
+ if (!href && !destination && !eventName && Object.keys(payload).length === 0)
985
1466
  return null;
986
- return { kind, href, eventName, payload };
1467
+ return { kind, href, destination, eventName, payload };
987
1468
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cxpinsight/survey-spec",
3
- "version": "0.7.0",
3
+ "version": "0.9.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",