@cxpinsight/react-native 0.5.0 → 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.
@@ -1,4 +1,8 @@
1
1
  export type ConsentState = 'granted' | 'denied' | null;
2
+ /** Only what this SDK can actually do. Same list as the web SDK. */
3
+ export declare const CONSENT_CATEGORIES: readonly ['analytics', 'recording', 'network', 'surveys'];
4
+ export type ConsentCategory = (typeof CONSENT_CATEGORIES)[number];
5
+ export type ConsentMap = Record<ConsentCategory, boolean>;
2
6
  /** Restore any prior decision. Call once during init, before capture starts. */
3
7
  export declare function initConsent(requireConsent?: boolean): Promise<void>;
4
8
  /** Notified when consent flips, so the SDK can start or stop capture. */
@@ -9,11 +13,22 @@ export declare function setConsentHandler(cb: ((granted: boolean) => void) | nul
9
13
  * An explicit denial always wins, even when requireConsent is off — a host that
10
14
  * calls optOut() means it, regardless of how the SDK was configured.
11
15
  */
16
+ export declare function mayUse(category: ConsentCategory): boolean;
17
+ /** Kept for call sites that predate categories; analytics is the broadest. */
12
18
  export declare function mayCapture(): boolean;
13
19
  /** True when events should be held rather than sent (awaiting a decision). */
14
20
  export declare function isPending(): boolean;
15
21
  export declare function consentRequired(): boolean;
16
- export declare function getConsent(): ConsentState;
22
+ /**
23
+ * The per-category decision, or null when the visitor has not decided.
24
+ *
25
+ * Null rather than an all-false map: "declined" and "not asked yet" need
26
+ * different UI, and a banner that cannot tell them apart re-prompts someone who
27
+ * already said no.
28
+ */
29
+ export declare function getConsent(): ConsentMap | null;
30
+ /** Set some categories, leaving the rest as they are. Unknown keys are ignored. */
31
+ export declare function setConsent(partial: Partial<ConsentMap>): void;
17
32
  export declare function grantConsent(): void;
18
33
  export declare function revokeConsent(): void;
19
34
  /** Reset to "no decision yet" — used by CXPinsight.reset(). */
@@ -1 +1 @@
1
- {"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../../src/core/consent.ts"],"names":[],"mappings":"AA4BA,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,IAAI,CAAC;AAMvD,gFAAgF;AAChF,wBAAsB,WAAW,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzE;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAE/E;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAGpC;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,wBAAgB,UAAU,IAAI,YAAY,CAEzC;AAED,wBAAgB,YAAY,IAAI,IAAI,CAKnC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED,+DAA+D;AAC/D,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAGlD"}
1
+ {"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../../src/core/consent.ts"],"names":[],"mappings":"AAsCA,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,IAAI,CAAC;AAEvD,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,YAAI,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,CAAU,CAAC;AAC5F,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAClE,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;AAS1D,gFAAgF;AAChF,wBAAsB,WAAW,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAczE;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAE/E;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAIzD;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,UAAU,GAAG,IAAI,CAM9C;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAiB7D;AAED,wBAAgB,YAAY,IAAI,IAAI,CAAqC;AACzE,wBAAgB,aAAa,IAAI,IAAI,CAAsC;AAE3E,+DAA+D;AAC/D,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAGlD"}
@@ -33,12 +33,15 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.CONSENT_CATEGORIES = void 0;
36
37
  exports.initConsent = initConsent;
37
38
  exports.setConsentHandler = setConsentHandler;
39
+ exports.mayUse = mayUse;
38
40
  exports.mayCapture = mayCapture;
39
41
  exports.isPending = isPending;
40
42
  exports.consentRequired = consentRequired;
41
43
  exports.getConsent = getConsent;
44
+ exports.setConsent = setConsent;
42
45
  exports.grantConsent = grantConsent;
43
46
  exports.revokeConsent = revokeConsent;
44
47
  exports.clearConsent = clearConsent;
@@ -60,23 +63,48 @@ exports.clearConsent = clearConsent;
60
63
  * `requireConsent` is opt-in and defaults to false, so nothing changes for an
61
64
  * app that does not set it. With it on, capture stays OFF until the host grants.
62
65
  *
63
- * One deliberate difference from web, in the stricter direction. Web's gate
64
- * covers session recording and network capture but lets ordinary analytics
65
- * events through. Here, requireConsent also holds ANALYTICS events they are
66
- * buffered in the event queue rather than dropped, so a visitor who accepts the
67
- * banner a few seconds in keeps their whole session rather than losing its
68
- * opening. Revoking clears that buffer instead of sending it.
66
+ * Consent is PER CATEGORY, on both channels. "Do you accept tracking?" is not
67
+ * one question: someone happy to be counted may not be happy to be recorded,
68
+ * and a single yes/no makes them refuse everything to refuse one thing.
69
+ *
70
+ * analytics screens, taps, scroll depth, custom events, lifecycle
71
+ * recording session replay (the mobile timeline recorder)
72
+ * network the shape of outbound requests: method, host, path, status
73
+ * surveys showing a survey and collecting the response
74
+ *
75
+ * There is deliberately no "push notifications" category — neither SDK has that
76
+ * capability, and a toggle for something that does nothing promises what the
77
+ * product cannot deliver.
78
+ *
79
+ * Analytics events are still BUFFERED rather than dropped while a decision is
80
+ * pending, so a visitor who accepts a few seconds in keeps the opening of their
81
+ * session. Revoking clears that buffer instead of sending it.
69
82
  */
70
83
  const types_1 = require("../types");
71
84
  const storage = __importStar(require("./storage"));
85
+ /** Only what this SDK can actually do. Same list as the web SDK. */
86
+ exports.CONSENT_CATEGORIES = ['analytics', 'recording', 'network', 'surveys'];
72
87
  let required = false;
73
- let state = null;
88
+ let categories = null;
74
89
  let onChange = null;
90
+ const allCategories = (granted) => exports.CONSENT_CATEGORIES.reduce((m, c) => { m[c] = granted; return m; }, {});
75
91
  /** Restore any prior decision. Call once during init, before capture starts. */
76
92
  async function initConsent(requireConsent) {
77
93
  required = requireConsent === true;
78
94
  const saved = await storage.get(types_1.STORAGE_KEYS.CONSENT);
79
- state = saved === 'granted' || saved === 'denied' ? saved : null;
95
+ // Migrate the old scalar. A device storing 'granted' had consented to
96
+ // everything the SDK then did; reading it as undecided would re-prompt
97
+ // someone who has already answered, and as denied would stop collection they
98
+ // agreed to.
99
+ if (saved === 'granted' || saved === 'denied') {
100
+ categories = allCategories(saved === 'granted');
101
+ }
102
+ else if (saved && typeof saved === 'object') {
103
+ categories = saved;
104
+ }
105
+ else {
106
+ categories = null;
107
+ }
80
108
  }
81
109
  /** Notified when consent flips, so the SDK can start or stop capture. */
82
110
  function setConsentHandler(cb) {
@@ -88,44 +116,63 @@ function setConsentHandler(cb) {
88
116
  * An explicit denial always wins, even when requireConsent is off — a host that
89
117
  * calls optOut() means it, regardless of how the SDK was configured.
90
118
  */
119
+ function mayUse(category) {
120
+ const decided = categories?.[category];
121
+ if (typeof decided === 'boolean')
122
+ return decided;
123
+ return !required;
124
+ }
125
+ /** Kept for call sites that predate categories; analytics is the broadest. */
91
126
  function mayCapture() {
92
- if (state === 'denied')
93
- return false;
94
- return !required || state === 'granted';
127
+ return mayUse('analytics');
95
128
  }
96
129
  /** True when events should be held rather than sent (awaiting a decision). */
97
130
  function isPending() {
98
- return required && state === null;
131
+ return required && categories === null;
99
132
  }
100
133
  function consentRequired() {
101
134
  return required;
102
135
  }
136
+ /**
137
+ * The per-category decision, or null when the visitor has not decided.
138
+ *
139
+ * Null rather than an all-false map: "declined" and "not asked yet" need
140
+ * different UI, and a banner that cannot tell them apart re-prompts someone who
141
+ * already said no.
142
+ */
103
143
  function getConsent() {
104
- return state;
105
- }
106
- function grantConsent() {
107
- if (state === 'granted')
108
- return;
109
- state = 'granted';
110
- void storage.set(types_1.STORAGE_KEYS.CONSENT, state);
111
- try {
112
- onChange?.(true);
113
- }
114
- catch { /* host callback must not break the gate */ }
144
+ if (!categories)
145
+ return null;
146
+ return exports.CONSENT_CATEGORIES.reduce((m, c) => {
147
+ m[c] = categories?.[c] === true;
148
+ return m;
149
+ }, {});
115
150
  }
116
- function revokeConsent() {
117
- if (state === 'denied')
118
- return;
119
- state = 'denied';
120
- void storage.set(types_1.STORAGE_KEYS.CONSENT, state);
121
- try {
122
- onChange?.(false);
151
+ /** Set some categories, leaving the rest as they are. Unknown keys are ignored. */
152
+ function setConsent(partial) {
153
+ const next = exports.CONSENT_CATEGORIES.reduce((m, c) => {
154
+ m[c] = typeof partial[c] === 'boolean' ? partial[c] : categories?.[c] === true;
155
+ return m;
156
+ }, {});
157
+ // Notify when the MAP changed, not when a summary boolean did. Comparing a
158
+ // single boolean would miss turning recording off while analytics stays on —
159
+ // the exact case categories exist for — while comparing nothing at all would
160
+ // fire on a repeated grant that changed nothing.
161
+ const changed = !categories || exports.CONSENT_CATEGORIES.some((c) => (categories?.[c] === true) !== next[c]);
162
+ categories = next;
163
+ void storage.set(types_1.STORAGE_KEYS.CONSENT, next);
164
+ if (changed) {
165
+ try {
166
+ onChange?.(exports.CONSENT_CATEGORIES.some((c) => next[c]));
167
+ }
168
+ catch { /* a host callback must not break the gate */ }
123
169
  }
124
- catch { /* best-effort */ }
125
170
  }
171
+ function grantConsent() { setConsent(allCategories(true)); }
172
+ function revokeConsent() { setConsent(allCategories(false)); }
126
173
  /** Reset to "no decision yet" — used by CXPinsight.reset(). */
127
174
  async function clearConsent() {
128
- state = null;
175
+ categories = null;
129
176
  await storage.remove(types_1.STORAGE_KEYS.CONSENT);
130
177
  }
131
178
  //# sourceMappingURL=consent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/core/consent.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,oCAAwC;AACxC,MAAY,OAAO,sCAAkB;AAIrC,IAAI,QAAQ,GAAG,KAAK,CAAC;AACrB,IAAI,KAAK,GAAiB,IAAI,CAAC;AAC/B,IAAI,QAAQ,GAAwC,IAAI,CAAC;AAEzD,gFAAgF;AACzE,KAAK,sBAAsB,cAAwB;IACxD,QAAQ,GAAG,cAAc,KAAK,IAAI,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAe,oBAAY,CAAC,OAAO,CAAC,CAAC;IACpE,KAAK,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACnE,CAAC;AAED,yEAAyE;AACzE,2BAAkC,EAAuC;IACvE,QAAQ,GAAG,EAAE,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH;IACE,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACrC,OAAO,CAAC,QAAQ,IAAI,KAAK,KAAK,SAAS,CAAC;AAC1C,CAAC;AAED,8EAA8E;AAC9E;IACE,OAAO,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACpC,CAAC;AAED;IACE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;IACE,OAAO,KAAK,CAAC;AACf,CAAC;AAED;IACE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO;IAChC,KAAK,GAAG,SAAS,CAAC;IAClB,KAAK,OAAO,CAAC,GAAG,CAAC,oBAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC9C,IAAI,CAAC;QAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,2CAA2C,CAAC,CAAC;AACjF,CAAC;AAED;IACE,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO;IAC/B,KAAK,GAAG,QAAQ,CAAC;IACjB,KAAK,OAAO,CAAC,GAAG,CAAC,oBAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC9C,IAAI,CAAC;QAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;AACxD,CAAC;AAED,+DAA+D;AACxD,KAAK;IACV,KAAK,GAAG,IAAI,CAAC;IACb,MAAM,OAAO,CAAC,MAAM,CAAC,oBAAY,CAAC,OAAO,CAAC,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/core/consent.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  export { CXPinsight } from './CXPinsight';
2
2
  export { CXPinsightProvider } from './CXPinsightProvider';
3
+ export { ConsentBanner } from './consent/ConsentBanner';
4
+ export type { ConsentBannerProps, ConsentBannerText } from './consent/ConsentBanner';
5
+ export { CONSENT_CATEGORIES } from './core/consent';
6
+ export type { ConsentCategory, ConsentMap } from './core/consent';
3
7
  export type { CXPinsightConfig, UserIdentity, CXPinsightCallbacks, FunnelStepOptions, ExperimentOptions, ConversionOptions, } from './types';
4
8
  export { useCXPElement } from './setup/useCXPElement';
5
9
  export type { ElementInfo, StoredElementIdentifier } from './setup/ElementIdentifier';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACtF,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAClE,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACtF,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC"}
package/lib/index.js CHANGED
@@ -1,10 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useCXPElement = exports.CXPinsightProvider = exports.CXPinsight = void 0;
3
+ exports.useCXPElement = exports.CONSENT_CATEGORIES = exports.ConsentBanner = exports.CXPinsightProvider = exports.CXPinsight = void 0;
4
4
  var CXPinsight_1 = require("./CXPinsight");
5
5
  Object.defineProperty(exports, "CXPinsight", { enumerable: true, get: function () { return CXPinsight_1.CXPinsight; } });
6
6
  var CXPinsightProvider_1 = require("./CXPinsightProvider");
7
7
  Object.defineProperty(exports, "CXPinsightProvider", { enumerable: true, get: function () { return CXPinsightProvider_1.CXPinsightProvider; } });
8
+ var ConsentBanner_1 = require("./consent/ConsentBanner");
9
+ Object.defineProperty(exports, "ConsentBanner", { enumerable: true, get: function () { return ConsentBanner_1.ConsentBanner; } });
10
+ var consent_1 = require("./core/consent");
11
+ Object.defineProperty(exports, "CONSENT_CATEGORIES", { enumerable: true, get: function () { return consent_1.CONSENT_CATEGORIES; } });
8
12
  // Setup mode exports
9
13
  var useCXPElement_1 = require("./setup/useCXPElement");
10
14
  Object.defineProperty(exports, "useCXPElement", { enumerable: true, get: function () { return useCXPElement_1.useCXPElement; } });
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAU3B,qBAAqB;AACrB,uDAAsD;AAA7C,8GAAA,aAAa,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAC3B,yDAAwD;AAA/C,8GAAA,aAAa,OAAA;AAEtB,0CAAoD;AAA3C,6GAAA,kBAAkB,OAAA;AAW3B,qBAAqB;AACrB,uDAAsD;AAA7C,8GAAA,aAAa,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"pagesFromV1.d.ts","sourceRoot":"","sources":["../../src/survey/pagesFromV1.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEhF,wBAAgB,WAAW,CAAC,EAAE,EAAE;IAC9B,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;CAC5B,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CA8DzF"}
1
+ {"version":3,"file":"pagesFromV1.d.ts","sourceRoot":"","sources":["../../src/survey/pagesFromV1.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEhF,wBAAgB,WAAW,CAAC,EAAE,EAAE;IAC9B,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;CAC5B,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAUzF"}
@@ -1,83 +1,37 @@
1
1
  "use strict";
2
- // v1.1 → in-modal pagination. Splits the flat questions[] into pages
3
- // using sections[].questionIds[] as the overlay, then falls back to
4
- // type `pageBreak` markers, then to a single page. Renderer-level
5
- // rules (spec §22 §26): a section with pageBreak:true begins a new
6
- // page; pageBreak type questions are omitted from the rendered list.
2
+ // v1.1 → in-modal pagination.
7
3
  //
8
- // Multi-flow (spec §28): questions carry an optional `flow` string.
9
- // undefined = root flow (implicit "A"). When the caller supplies a
10
- // non-empty `flow` filter, ONLY questions where `question.flow ===
11
- // flow` are considered. When flow is undefined, only questions with
12
- // `question.flow` undefined/empty are considered — the root-flow
13
- // respondent never sees fork-flow questions inline. Passing null
14
- // disables the filter (used for cross-flow lookups like resolving
15
- // a rejoinAt target).
4
+ // The rules now live in @cxpinsight/survey-spec. They used to live here AND in
5
+ // the React renderer, and the two disagreed on 6 of 17 fixtures — including
6
+ // PAGE ORDER, so a survey with a loose question before a section was asked in a
7
+ // different order on each channel while the answers were recorded as though it
8
+ // were the same instrument.
9
+ //
10
+ // Two behaviours change on mobile as a result, both toward the shared rule:
11
+ //
12
+ // • Page order follows QUESTION order. This file walked sections[] and
13
+ // appended unclaimed questions at the end, so section order in the
14
+ // definition — which the builder does not treat as meaningful — could
15
+ // reorder the survey.
16
+ //
17
+ // • Section showWhen / hideWhen are evaluated. This file checked only
18
+ // `hidden`, so a section conditioned away still split pages here.
19
+ //
20
+ // Typeform/conversation is unchanged here: one page per question, which is what
21
+ // this file always did. It was briefly a parameter while that was an open
22
+ // question between the channels; it is now the single behaviour, and React
23
+ // moved onto it.
24
+ //
25
+ // This file is now a shape adapter: the spec returns `{ items, questions }`,
26
+ // the modal wants `{ elements }`.
16
27
  Object.defineProperty(exports, "__esModule", { value: true });
17
28
  exports.pagesFromV1 = pagesFromV1;
29
+ const survey_spec_1 = require("@cxpinsight/survey-spec");
18
30
  function pagesFromV1(v1, flow, presentationMode) {
19
- const rawQuestions = Array.isArray(v1.questions) ? v1.questions : [];
20
- if (rawQuestions.length === 0)
21
- return [];
22
- // Multi-flow filter (spec §28). null bypasses; undefined = root flow;
23
- // any string = named flow. Applied BEFORE section overlay so a
24
- // section that mixes flows only shows the current flow's questions.
25
- const questions = flow === null
26
- ? rawQuestions
27
- : rawQuestions.filter((q) => (q.flow || undefined) === (flow || undefined));
28
- if (questions.length === 0)
29
- return [];
30
- // Typeform / conversation presentation modes: one question per page.
31
- // The web widget renders this way by default (its question-at-a-time
32
- // navigation loops over the flat list) but mobile paginates via the
33
- // sections/pageBreak structure below — without this short-circuit the
34
- // whole survey collapses into a single scrollable page. Author's
35
- // section grouping is ignored in these modes because the builder itself
36
- // treats each question as its own page (add-page button is hidden and
37
- // section headers become intro pages).
38
- const isTypeform = presentationMode === 'typeform' || presentationMode === 'conversation';
39
- if (isTypeform) {
40
- return questions
41
- .filter((q) => q.type !== 'pageBreak')
42
- .map((q) => ({ elements: [q] }));
43
- }
44
- const sections = Array.isArray(v1.sections) ? v1.sections.filter((s) => !s.hidden) : [];
45
- if (sections.length > 0) {
46
- const byId = new Map(questions.map((q) => [q.id, q]));
47
- const claimed = new Set();
48
- const pages = [];
49
- for (const s of sections) {
50
- const ids = Array.isArray(s.questionIds) ? s.questionIds : [];
51
- const els = [];
52
- for (const id of ids) {
53
- const q = byId.get(id);
54
- if (!q || q.type === 'pageBreak')
55
- continue;
56
- claimed.add(id);
57
- els.push(q);
58
- }
59
- if (els.length > 0)
60
- pages.push({ elements: els });
61
- }
62
- const leftover = questions.filter((q) => !claimed.has(q.id) && q.type !== 'pageBreak');
63
- if (leftover.length > 0)
64
- pages.push({ elements: leftover });
65
- return pages;
66
- }
67
- const pages = [];
68
- let current = [];
69
- for (const q of questions) {
70
- if (q.type === 'pageBreak') {
71
- if (current.length > 0) {
72
- pages.push({ elements: current });
73
- current = [];
74
- }
75
- continue;
76
- }
77
- current.push(q);
78
- }
79
- if (current.length > 0)
80
- pages.push({ elements: current });
81
- return pages;
31
+ // No evaluation context here: the modal computes pages before any answer
32
+ // exists, so a section condition referencing an answer resolves false — the
33
+ // same as it did on React, which is what parity requires.
34
+ const pages = (0, survey_spec_1.splitIntoPages)(v1, { answers: {}, variables: {} }, { flow, presentationMode });
35
+ return pages.map((p) => ({ elements: p.questions }));
82
36
  }
83
37
  //# sourceMappingURL=pagesFromV1.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pagesFromV1.js","sourceRoot":"","sources":["../../src/survey/pagesFromV1.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pagesFromV1.js","sourceRoot":"","sources":["../../src/survey/pagesFromV1.ts"],"names":[],"mappings":";AAAA,8BAA8B;AAC9B,EAAE;AACF,+EAA+E;AAC/E,4EAA4E;AAC5E,gFAAgF;AAChF,+EAA+E;AAC/E,4BAA4B;AAC5B,EAAE;AACF,4EAA4E;AAC5E,EAAE;AACF,yEAAyE;AACzE,uEAAuE;AACvE,0EAA0E;AAC1E,0BAA0B;AAC1B,EAAE;AACF,wEAAwE;AACxE,sEAAsE;AACtE,EAAE;AACF,gFAAgF;AAChF,0EAA0E;AAC1E,2EAA2E;AAC3E,iBAAiB;AACjB,EAAE;AACF,6EAA6E;AAC7E,kCAAkC;;;AAElC,yDAAyD;AAGzD,qBAA4B,EAG3B,EAAE,IAAgC,EAAE,gBAAyB;IAC5D,yEAAyE;IACzE,4EAA4E;IAC5E,0DAA0D;IAC1D,MAAM,KAAK,GAAG,IAAA,4BAAc,EAC1B,EAAE,EACF,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,EAC9B,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAC3B,CAAC;IACF,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;AACvD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/survey/validate.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAK/C,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAWtF"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/survey/validate.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAEtF"}
@@ -1,169 +1,19 @@
1
1
  "use strict";
2
- // Answer validation — spec §8 + type-specific rules. Kept in lockstep
3
- // with apps/frontend/src/components/survey/renderer/lib/validate.ts,
4
- // with two mobile simplifications:
5
- //
6
- // - Mobile question components emit primitive values, not the
7
- // compound {text,attachments} / {choice,otherText} shapes that
8
- // the web renderer uses for allowAttachments + "Other". Whenever
9
- // the value shape simplifies, the mobile helper's per-type path
10
- // shrinks too.
11
- // - inputType regexes stay identical so a text field flagged as
12
- // invalid on web reads as invalid on mobile and vice versa.
13
- //
14
- // Order per spec §8: required → type-specific format → rules[] array.
15
- // Empty non-required answers are valid — a blank optional email
16
- // shouldn't complain about missing '@'.
17
2
  Object.defineProperty(exports, "__esModule", { value: true });
18
3
  exports.validateAnswer = validateAnswer;
19
- const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
20
- const URL_RE = /^https?:\/\/[\w.\-]+(?:\.[\w.\-]+)+(?:[/?#][^\s]*)?$/i;
4
+ // Answer validation now lives in @cxpinsight/survey-spec.
5
+ //
6
+ // This file was the superset in type routing (email / number / csat / ces) and
7
+ // the subset in emptiness: its isEmpty only understood primitives, so a
8
+ // compound value like `{ choice: undefined }` counted as answered. The shared
9
+ // version takes the dispatcher from here and the type-aware isEmpty from the
10
+ // React renderer.
11
+ //
12
+ // One behaviour changes on mobile: the required message is now "This field is
13
+ // required" rather than "This question requires an answer", so the wording is
14
+ // the same wherever a respondent meets it.
15
+ const survey_spec_1 = require("@cxpinsight/survey-spec");
21
16
  function validateAnswer(question, value) {
22
- const empty = isEmpty(value);
23
- if ((question.required || question.isRequired) && empty) {
24
- return 'This question requires an answer';
25
- }
26
- if (empty)
27
- return null; // optional + blank → OK
28
- const typeErr = validateByType(question, value);
29
- if (typeErr)
30
- return typeErr;
31
- return validateRules(question, value);
32
- }
33
- function isEmpty(v) {
34
- if (v === undefined || v === null)
35
- return true;
36
- if (typeof v === 'string')
37
- return v.trim().length === 0;
38
- if (Array.isArray(v))
39
- return v.length === 0;
40
- if (typeof v === 'number')
41
- return Number.isNaN(v);
42
- return false; // 0, false, non-empty obj → not empty
43
- }
44
- function validateByType(question, value) {
45
- switch (question.type) {
46
- case 'text':
47
- case 'comment':
48
- case 'email':
49
- case 'number':
50
- return validateTextish(question, value);
51
- case 'checkbox':
52
- return validateCheckbox(question, value);
53
- case 'rating':
54
- case 'csat':
55
- case 'ces':
56
- case 'nps':
57
- return validateNumericRange(question, value);
58
- default:
59
- return null;
60
- }
61
- }
62
- function validateTextish(question, value) {
63
- const trimmed = String(value ?? '').trim();
64
- const inputType = question.inputType || (question.type === 'email' ? 'email' : question.type === 'number' ? 'number' : undefined);
65
- if (inputType === 'email' && !EMAIL_RE.test(trimmed)) {
66
- return 'Please enter a valid email address (e.g., you@example.com)';
67
- }
68
- if (inputType === 'url' && !URL_RE.test(trimmed)) {
69
- return 'Please enter a full URL, including https://';
70
- }
71
- if (inputType === 'number' && Number.isNaN(Number(trimmed))) {
72
- return 'Please enter a number';
73
- }
74
- if (inputType === 'tel' && !/^[\d\s+\-()]{3,}$/.test(trimmed)) {
75
- return 'Please enter a valid phone number';
76
- }
77
- const maxLength = typeof question.maxLength === 'number' ? question.maxLength : undefined;
78
- if (maxLength !== undefined && trimmed.length > maxLength) {
79
- return `Please keep this under ${maxLength} characters`;
80
- }
81
- // minLength was only honoured through the rules[] array, never as a direct
82
- // question property — so a question with minLength set in the builder was
83
- // enforced on links and silently ignored here. Same survey, different rules,
84
- // depending on how the respondent arrived.
85
- const minLength = typeof question.minLength === 'number'
86
- ? question.minLength
87
- : undefined;
88
- if (minLength !== undefined && trimmed.length < minLength) {
89
- return `Please enter at least ${minLength} characters`;
90
- }
91
- return null;
92
- }
93
- function validateCheckbox(question, value) {
94
- const arr = Array.isArray(value) ? value : [];
95
- const count = arr.length;
96
- const minCount = typeof question.minCount === 'number' ? question.minCount : undefined;
97
- const maxCount = typeof question.maxCount === 'number' ? question.maxCount : undefined;
98
- if (minCount !== undefined && count < minCount)
99
- return `Please select at least ${minCount}`;
100
- if (maxCount !== undefined && count > maxCount)
101
- return `Please select at most ${maxCount}`;
102
- return null;
103
- }
104
- function validateNumericRange(question, value) {
105
- if (typeof value !== 'number')
106
- return null;
107
- const min = typeof question.minValue === 'number' ? question.minValue : undefined;
108
- const max = typeof question.maxValue === 'number' ? question.maxValue : undefined;
109
- if (min !== undefined && value < min)
110
- return `Must be at least ${min}`;
111
- if (max !== undefined && value > max)
112
- return `Must be at most ${max}`;
113
- return null;
114
- }
115
- function validateRules(question, value) {
116
- const rules = Array.isArray(question.rules)
117
- ? question.rules
118
- : [];
119
- if (rules.length === 0)
120
- return null;
121
- const str = typeof value === 'string' ? value : String(value ?? '');
122
- for (const r of rules) {
123
- const err = applyRule(r, str);
124
- if (err)
125
- return r.message || err;
126
- }
127
- return null;
128
- }
129
- function applyRule(rule, str) {
130
- switch (rule.type) {
131
- case 'minLength':
132
- if (typeof rule.value === 'number' && str.length < rule.value)
133
- return `At least ${rule.value} characters`;
134
- return null;
135
- case 'maxLength':
136
- if (typeof rule.value === 'number' && str.length > rule.value)
137
- return `At most ${rule.value} characters`;
138
- return null;
139
- case 'min':
140
- if (typeof rule.value === 'number' && Number(str) < rule.value)
141
- return `Must be at least ${rule.value}`;
142
- return null;
143
- case 'max':
144
- if (typeof rule.value === 'number' && Number(str) > rule.value)
145
- return `Must be at most ${rule.value}`;
146
- return null;
147
- case 'email':
148
- if (!EMAIL_RE.test(str))
149
- return 'Please enter a valid email address';
150
- return null;
151
- case 'url':
152
- if (!URL_RE.test(str))
153
- return 'Please enter a valid URL';
154
- return null;
155
- case 'pattern': {
156
- if (typeof rule.value !== 'string')
157
- return null;
158
- try {
159
- const re = new RegExp(rule.value);
160
- return re.test(str) ? null : 'Format doesn\'t match';
161
- }
162
- catch {
163
- return null;
164
- }
165
- }
166
- default: return null;
167
- }
17
+ return (0, survey_spec_1.validateAnswer)(question, value);
168
18
  }
169
19
  //# sourceMappingURL=validate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/survey/validate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/survey/validate.ts"],"names":[],"mappings":";;;AAAA,0DAA0D;AAC1D,EAAE;AACF,+EAA+E;AAC/E,wEAAwE;AACxE,8EAA8E;AAC9E,6EAA6E;AAC7E,kBAAkB;AAClB,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,2CAA2C;AAC3C,yDAA+E;AAG/E,wBAA+B,QAAwB,EAAE,KAAc;IACrE,OAAO,IAAA,4BAAkB,EAAC,QAAoD,EAAE,KAAK,CAAC,CAAC;AACzF,CAAC"}