@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.
- package/CHANGELOG.md +20 -0
- package/README.md +23 -0
- package/lib/CXPinsight.d.ts +33 -3
- package/lib/CXPinsight.d.ts.map +1 -1
- package/lib/CXPinsight.js +57 -3
- package/lib/CXPinsight.js.map +1 -1
- package/lib/CXPinsightProvider.d.ts.map +1 -1
- package/lib/CXPinsightProvider.js +13 -1
- package/lib/CXPinsightProvider.js.map +1 -1
- package/lib/consent/ConsentBanner.d.ts +18 -0
- package/lib/consent/ConsentBanner.d.ts.map +1 -0
- package/lib/consent/ConsentBanner.js +90 -0
- package/lib/consent/ConsentBanner.js.map +1 -0
- package/lib/core/consent.d.ts +16 -1
- package/lib/core/consent.d.ts.map +1 -1
- package/lib/core/consent.js +79 -32
- package/lib/core/consent.js.map +1 -1
- package/lib/index.d.ts +4 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +5 -1
- package/lib/index.js.map +1 -1
- package/lib/survey/pagesFromV1.d.ts.map +1 -1
- package/lib/survey/pagesFromV1.js +30 -76
- package/lib/survey/pagesFromV1.js.map +1 -1
- package/lib/survey/validate.d.ts.map +1 -1
- package/lib/survey/validate.js +13 -163
- package/lib/survey/validate.js.map +1 -1
- package/lib/survey/voiceParsers.d.ts +12 -42
- package/lib/survey/voiceParsers.d.ts.map +1 -1
- package/lib/survey/voiceParsers.js +60 -284
- package/lib/survey/voiceParsers.js.map +1 -1
- package/lib/types.d.ts +17 -0
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js.map +1 -1
- package/package.json +3 -3
package/lib/core/consent.d.ts
CHANGED
|
@@ -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
|
-
|
|
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":"
|
|
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"}
|
package/lib/core/consent.js
CHANGED
|
@@ -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
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 &&
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return;
|
|
109
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
175
|
+
categories = null;
|
|
129
176
|
await storage.remove(types_1.STORAGE_KEYS.CONSENT);
|
|
130
177
|
}
|
|
131
178
|
//# sourceMappingURL=consent.js.map
|
package/lib/core/consent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/core/consent.ts"],"names":[],"mappings":"
|
|
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';
|
package/lib/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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":"
|
|
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.
|
|
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
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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":";AAAA,
|
|
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":"
|
|
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"}
|
package/lib/survey/validate.js
CHANGED
|
@@ -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
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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":"
|
|
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"}
|