@cxpinsight/react-native 0.2.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.
Files changed (253) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/babel.js +101 -0
  4. package/instrumented/Pressable.d.ts +2 -0
  5. package/instrumented/Pressable.js +2 -0
  6. package/instrumented/ScrollView.d.ts +2 -0
  7. package/instrumented/ScrollView.js +2 -0
  8. package/instrumented/TextInput.d.ts +2 -0
  9. package/instrumented/TextInput.js +2 -0
  10. package/instrumented/TouchableOpacity.d.ts +2 -0
  11. package/instrumented/TouchableOpacity.js +2 -0
  12. package/lib/CXPinsight.d.ts +113 -0
  13. package/lib/CXPinsight.d.ts.map +1 -0
  14. package/lib/CXPinsight.js +831 -0
  15. package/lib/CXPinsight.js.map +1 -0
  16. package/lib/CXPinsightProvider.d.ts +19 -0
  17. package/lib/CXPinsightProvider.d.ts.map +1 -0
  18. package/lib/CXPinsightProvider.js +382 -0
  19. package/lib/CXPinsightProvider.js.map +1 -0
  20. package/lib/core/autoScreenTracker.d.ts +20 -0
  21. package/lib/core/autoScreenTracker.d.ts.map +1 -0
  22. package/lib/core/autoScreenTracker.js +66 -0
  23. package/lib/core/autoScreenTracker.js.map +1 -0
  24. package/lib/core/deepLinkTracker.d.ts +17 -0
  25. package/lib/core/deepLinkTracker.d.ts.map +1 -0
  26. package/lib/core/deepLinkTracker.js +81 -0
  27. package/lib/core/deepLinkTracker.js.map +1 -0
  28. package/lib/core/deviceInfo.d.ts +14 -0
  29. package/lib/core/deviceInfo.d.ts.map +1 -0
  30. package/lib/core/deviceInfo.js +166 -0
  31. package/lib/core/deviceInfo.js.map +1 -0
  32. package/lib/core/eventQueue.d.ts +23 -0
  33. package/lib/core/eventQueue.d.ts.map +1 -0
  34. package/lib/core/eventQueue.js +240 -0
  35. package/lib/core/eventQueue.js.map +1 -0
  36. package/lib/core/lifecycle.d.ts +18 -0
  37. package/lib/core/lifecycle.d.ts.map +1 -0
  38. package/lib/core/lifecycle.js +127 -0
  39. package/lib/core/lifecycle.js.map +1 -0
  40. package/lib/core/mobileRecorder.d.ts +15 -0
  41. package/lib/core/mobileRecorder.d.ts.map +1 -0
  42. package/lib/core/mobileRecorder.js +231 -0
  43. package/lib/core/mobileRecorder.js.map +1 -0
  44. package/lib/core/network.d.ts +10 -0
  45. package/lib/core/network.d.ts.map +1 -0
  46. package/lib/core/network.js +74 -0
  47. package/lib/core/network.js.map +1 -0
  48. package/lib/core/networkCapture.d.ts +22 -0
  49. package/lib/core/networkCapture.d.ts.map +1 -0
  50. package/lib/core/networkCapture.js +100 -0
  51. package/lib/core/networkCapture.js.map +1 -0
  52. package/lib/core/scrollCapture.d.ts +48 -0
  53. package/lib/core/scrollCapture.d.ts.map +1 -0
  54. package/lib/core/scrollCapture.js +221 -0
  55. package/lib/core/scrollCapture.js.map +1 -0
  56. package/lib/core/session.d.ts +15 -0
  57. package/lib/core/session.d.ts.map +1 -0
  58. package/lib/core/session.js +157 -0
  59. package/lib/core/session.js.map +1 -0
  60. package/lib/core/storage.d.ts +11 -0
  61. package/lib/core/storage.d.ts.map +1 -0
  62. package/lib/core/storage.js +59 -0
  63. package/lib/core/storage.js.map +1 -0
  64. package/lib/core/triggerEngine.d.ts +56 -0
  65. package/lib/core/triggerEngine.d.ts.map +1 -0
  66. package/lib/core/triggerEngine.js +432 -0
  67. package/lib/core/triggerEngine.js.map +1 -0
  68. package/lib/index.d.ts +7 -0
  69. package/lib/index.d.ts.map +1 -0
  70. package/lib/index.js +11 -0
  71. package/lib/index.js.map +1 -0
  72. package/lib/instrumented/Pressable.d.ts +9 -0
  73. package/lib/instrumented/Pressable.d.ts.map +1 -0
  74. package/lib/instrumented/Pressable.js +96 -0
  75. package/lib/instrumented/Pressable.js.map +1 -0
  76. package/lib/instrumented/ScrollView.d.ts +6 -0
  77. package/lib/instrumented/ScrollView.d.ts.map +1 -0
  78. package/lib/instrumented/ScrollView.js +101 -0
  79. package/lib/instrumented/ScrollView.js.map +1 -0
  80. package/lib/instrumented/TextInput.d.ts +9 -0
  81. package/lib/instrumented/TextInput.d.ts.map +1 -0
  82. package/lib/instrumented/TextInput.js +75 -0
  83. package/lib/instrumented/TextInput.js.map +1 -0
  84. package/lib/instrumented/TouchableOpacity.d.ts +11 -0
  85. package/lib/instrumented/TouchableOpacity.d.ts.map +1 -0
  86. package/lib/instrumented/TouchableOpacity.js +96 -0
  87. package/lib/instrumented/TouchableOpacity.js.map +1 -0
  88. package/lib/instrumented/index.d.ts +19 -0
  89. package/lib/instrumented/index.d.ts.map +1 -0
  90. package/lib/instrumented/index.js +28 -0
  91. package/lib/instrumented/index.js.map +1 -0
  92. package/lib/instrumented/interactionObserver.d.ts +29 -0
  93. package/lib/instrumented/interactionObserver.d.ts.map +1 -0
  94. package/lib/instrumented/interactionObserver.js +38 -0
  95. package/lib/instrumented/interactionObserver.js.map +1 -0
  96. package/lib/setup/AccessibilityScanner.d.ts +32 -0
  97. package/lib/setup/AccessibilityScanner.d.ts.map +1 -0
  98. package/lib/setup/AccessibilityScanner.js +127 -0
  99. package/lib/setup/AccessibilityScanner.js.map +1 -0
  100. package/lib/setup/ElementIdentifier.d.ts +65 -0
  101. package/lib/setup/ElementIdentifier.d.ts.map +1 -0
  102. package/lib/setup/ElementIdentifier.js +114 -0
  103. package/lib/setup/ElementIdentifier.js.map +1 -0
  104. package/lib/setup/ElementRegistry.d.ts +22 -0
  105. package/lib/setup/ElementRegistry.d.ts.map +1 -0
  106. package/lib/setup/ElementRegistry.js +47 -0
  107. package/lib/setup/ElementRegistry.js.map +1 -0
  108. package/lib/setup/SetupCodeEntry.d.ts +15 -0
  109. package/lib/setup/SetupCodeEntry.d.ts.map +1 -0
  110. package/lib/setup/SetupCodeEntry.js +198 -0
  111. package/lib/setup/SetupCodeEntry.js.map +1 -0
  112. package/lib/setup/SetupController.d.ts +29 -0
  113. package/lib/setup/SetupController.d.ts.map +1 -0
  114. package/lib/setup/SetupController.js +189 -0
  115. package/lib/setup/SetupController.js.map +1 -0
  116. package/lib/setup/SetupOverlay.d.ts +10 -0
  117. package/lib/setup/SetupOverlay.d.ts.map +1 -0
  118. package/lib/setup/SetupOverlay.js +408 -0
  119. package/lib/setup/SetupOverlay.js.map +1 -0
  120. package/lib/setup/TapInterceptor.d.ts +19 -0
  121. package/lib/setup/TapInterceptor.d.ts.map +1 -0
  122. package/lib/setup/TapInterceptor.js +98 -0
  123. package/lib/setup/TapInterceptor.js.map +1 -0
  124. package/lib/setup/TouchObserver.d.ts +25 -0
  125. package/lib/setup/TouchObserver.d.ts.map +1 -0
  126. package/lib/setup/TouchObserver.js +88 -0
  127. package/lib/setup/TouchObserver.js.map +1 -0
  128. package/lib/setup/TouchPatcher.d.ts +27 -0
  129. package/lib/setup/TouchPatcher.d.ts.map +1 -0
  130. package/lib/setup/TouchPatcher.js +182 -0
  131. package/lib/setup/TouchPatcher.js.map +1 -0
  132. package/lib/setup/index.d.ts +13 -0
  133. package/lib/setup/index.d.ts.map +1 -0
  134. package/lib/setup/index.js +45 -0
  135. package/lib/setup/index.js.map +1 -0
  136. package/lib/setup/useCXPElement.d.ts +25 -0
  137. package/lib/setup/useCXPElement.d.ts.map +1 -0
  138. package/lib/setup/useCXPElement.js +33 -0
  139. package/lib/setup/useCXPElement.js.map +1 -0
  140. package/lib/survey/SurveyModal.d.ts +10 -0
  141. package/lib/survey/SurveyModal.d.ts.map +1 -0
  142. package/lib/survey/SurveyModal.js +1650 -0
  143. package/lib/survey/SurveyModal.js.map +1 -0
  144. package/lib/survey/expressionEval.d.ts +10 -0
  145. package/lib/survey/expressionEval.d.ts.map +1 -0
  146. package/lib/survey/expressionEval.js +287 -0
  147. package/lib/survey/expressionEval.js.map +1 -0
  148. package/lib/survey/frequencyCap.d.ts +15 -0
  149. package/lib/survey/frequencyCap.d.ts.map +1 -0
  150. package/lib/survey/frequencyCap.js +116 -0
  151. package/lib/survey/frequencyCap.js.map +1 -0
  152. package/lib/survey/handsFreeVoice.d.ts +30 -0
  153. package/lib/survey/handsFreeVoice.d.ts.map +1 -0
  154. package/lib/survey/handsFreeVoice.js +312 -0
  155. package/lib/survey/handsFreeVoice.js.map +1 -0
  156. package/lib/survey/interpolate.d.ts +6 -0
  157. package/lib/survey/interpolate.d.ts.map +1 -0
  158. package/lib/survey/interpolate.js +61 -0
  159. package/lib/survey/interpolate.js.map +1 -0
  160. package/lib/survey/pagesFromV1.d.ts +6 -0
  161. package/lib/survey/pagesFromV1.d.ts.map +1 -0
  162. package/lib/survey/pagesFromV1.js +83 -0
  163. package/lib/survey/pagesFromV1.js.map +1 -0
  164. package/lib/survey/questions/CSATQuestion.d.ts +16 -0
  165. package/lib/survey/questions/CSATQuestion.d.ts.map +1 -0
  166. package/lib/survey/questions/CSATQuestion.js +37 -0
  167. package/lib/survey/questions/CSATQuestion.js.map +1 -0
  168. package/lib/survey/questions/DateQuestion.d.ts +20 -0
  169. package/lib/survey/questions/DateQuestion.d.ts.map +1 -0
  170. package/lib/survey/questions/DateQuestion.js +103 -0
  171. package/lib/survey/questions/DateQuestion.js.map +1 -0
  172. package/lib/survey/questions/ImagePickerQuestion.d.ts +33 -0
  173. package/lib/survey/questions/ImagePickerQuestion.d.ts.map +1 -0
  174. package/lib/survey/questions/ImagePickerQuestion.js +68 -0
  175. package/lib/survey/questions/ImagePickerQuestion.js.map +1 -0
  176. package/lib/survey/questions/ImageQuestion.d.ts +26 -0
  177. package/lib/survey/questions/ImageQuestion.d.ts.map +1 -0
  178. package/lib/survey/questions/ImageQuestion.js +24 -0
  179. package/lib/survey/questions/ImageQuestion.js.map +1 -0
  180. package/lib/survey/questions/MatrixCell.d.ts +45 -0
  181. package/lib/survey/questions/MatrixCell.d.ts.map +1 -0
  182. package/lib/survey/questions/MatrixCell.js +54 -0
  183. package/lib/survey/questions/MatrixCell.js.map +1 -0
  184. package/lib/survey/questions/MatrixDropdownQuestion.d.ts +29 -0
  185. package/lib/survey/questions/MatrixDropdownQuestion.d.ts.map +1 -0
  186. package/lib/survey/questions/MatrixDropdownQuestion.js +42 -0
  187. package/lib/survey/questions/MatrixDropdownQuestion.js.map +1 -0
  188. package/lib/survey/questions/MatrixDynamicQuestion.d.ts +26 -0
  189. package/lib/survey/questions/MatrixDynamicQuestion.d.ts.map +1 -0
  190. package/lib/survey/questions/MatrixDynamicQuestion.js +52 -0
  191. package/lib/survey/questions/MatrixDynamicQuestion.js.map +1 -0
  192. package/lib/survey/questions/MatrixQuestion.d.ts +38 -0
  193. package/lib/survey/questions/MatrixQuestion.d.ts.map +1 -0
  194. package/lib/survey/questions/MatrixQuestion.js +64 -0
  195. package/lib/survey/questions/MatrixQuestion.js.map +1 -0
  196. package/lib/survey/questions/MultiTextQuestion.d.ts +19 -0
  197. package/lib/survey/questions/MultiTextQuestion.d.ts.map +1 -0
  198. package/lib/survey/questions/MultiTextQuestion.js +64 -0
  199. package/lib/survey/questions/MultiTextQuestion.js.map +1 -0
  200. package/lib/survey/questions/MultipleChoiceQuestion.d.ts +25 -0
  201. package/lib/survey/questions/MultipleChoiceQuestion.d.ts.map +1 -0
  202. package/lib/survey/questions/MultipleChoiceQuestion.js +111 -0
  203. package/lib/survey/questions/MultipleChoiceQuestion.js.map +1 -0
  204. package/lib/survey/questions/NPSQuestion.d.ts +18 -0
  205. package/lib/survey/questions/NPSQuestion.d.ts.map +1 -0
  206. package/lib/survey/questions/NPSQuestion.js +62 -0
  207. package/lib/survey/questions/NPSQuestion.js.map +1 -0
  208. package/lib/survey/questions/PanelDynamicQuestion.d.ts +50 -0
  209. package/lib/survey/questions/PanelDynamicQuestion.d.ts.map +1 -0
  210. package/lib/survey/questions/PanelDynamicQuestion.js +62 -0
  211. package/lib/survey/questions/PanelDynamicQuestion.js.map +1 -0
  212. package/lib/survey/questions/RankingQuestion.d.ts +34 -0
  213. package/lib/survey/questions/RankingQuestion.d.ts.map +1 -0
  214. package/lib/survey/questions/RankingQuestion.js +85 -0
  215. package/lib/survey/questions/RankingQuestion.js.map +1 -0
  216. package/lib/survey/questions/RatingQuestion.d.ts +20 -0
  217. package/lib/survey/questions/RatingQuestion.d.ts.map +1 -0
  218. package/lib/survey/questions/RatingQuestion.js +53 -0
  219. package/lib/survey/questions/RatingQuestion.js.map +1 -0
  220. package/lib/survey/questions/TextQuestion.d.ts +17 -0
  221. package/lib/survey/questions/TextQuestion.d.ts.map +1 -0
  222. package/lib/survey/questions/TextQuestion.js +46 -0
  223. package/lib/survey/questions/TextQuestion.js.map +1 -0
  224. package/lib/survey/styleContract.d.ts +230 -0
  225. package/lib/survey/styleContract.d.ts.map +1 -0
  226. package/lib/survey/styleContract.js +365 -0
  227. package/lib/survey/styleContract.js.map +1 -0
  228. package/lib/survey/themePalette.d.ts +56 -0
  229. package/lib/survey/themePalette.d.ts.map +1 -0
  230. package/lib/survey/themePalette.js +63 -0
  231. package/lib/survey/themePalette.js.map +1 -0
  232. package/lib/survey/translationLookup.d.ts +4 -0
  233. package/lib/survey/translationLookup.d.ts.map +1 -0
  234. package/lib/survey/translationLookup.js +62 -0
  235. package/lib/survey/translationLookup.js.map +1 -0
  236. package/lib/survey/validate.d.ts +3 -0
  237. package/lib/survey/validate.d.ts.map +1 -0
  238. package/lib/survey/validate.js +159 -0
  239. package/lib/survey/validate.js.map +1 -0
  240. package/lib/survey/variablesResolver.d.ts +10 -0
  241. package/lib/survey/variablesResolver.d.ts.map +1 -0
  242. package/lib/survey/variablesResolver.js +107 -0
  243. package/lib/survey/variablesResolver.js.map +1 -0
  244. package/lib/survey/voiceParsers.d.ts +47 -0
  245. package/lib/survey/voiceParsers.d.ts.map +1 -0
  246. package/lib/survey/voiceParsers.js +310 -0
  247. package/lib/survey/voiceParsers.js.map +1 -0
  248. package/lib/types.d.ts +349 -0
  249. package/lib/types.d.ts.map +1 -0
  250. package/lib/types.js +20 -0
  251. package/lib/types.js.map +1 -0
  252. package/metro.js +55 -0
  253. package/package.json +78 -0
@@ -0,0 +1,159 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.validateAnswer = validateAnswer;
19
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
20
+ const URL_RE = /^https?:\/\/[\w.\-]+(?:\.[\w.\-]+)+(?:[/?#][^\s]*)?$/i;
21
+ 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
+ return null;
82
+ }
83
+ function validateCheckbox(question, value) {
84
+ const arr = Array.isArray(value) ? value : [];
85
+ const count = arr.length;
86
+ const minCount = typeof question.minCount === 'number' ? question.minCount : undefined;
87
+ const maxCount = typeof question.maxCount === 'number' ? question.maxCount : undefined;
88
+ if (minCount !== undefined && count < minCount)
89
+ return `Please select at least ${minCount}`;
90
+ if (maxCount !== undefined && count > maxCount)
91
+ return `Please select at most ${maxCount}`;
92
+ return null;
93
+ }
94
+ function validateNumericRange(question, value) {
95
+ if (typeof value !== 'number')
96
+ return null;
97
+ const min = typeof question.minValue === 'number' ? question.minValue : undefined;
98
+ const max = typeof question.maxValue === 'number' ? question.maxValue : undefined;
99
+ if (min !== undefined && value < min)
100
+ return `Must be at least ${min}`;
101
+ if (max !== undefined && value > max)
102
+ return `Must be at most ${max}`;
103
+ return null;
104
+ }
105
+ function validateRules(question, value) {
106
+ const rules = Array.isArray(question.rules)
107
+ ? question.rules
108
+ : [];
109
+ if (rules.length === 0)
110
+ return null;
111
+ const str = typeof value === 'string' ? value : String(value ?? '');
112
+ for (const r of rules) {
113
+ const err = applyRule(r, str);
114
+ if (err)
115
+ return r.message || err;
116
+ }
117
+ return null;
118
+ }
119
+ function applyRule(rule, str) {
120
+ switch (rule.type) {
121
+ case 'minLength':
122
+ if (typeof rule.value === 'number' && str.length < rule.value)
123
+ return `At least ${rule.value} characters`;
124
+ return null;
125
+ case 'maxLength':
126
+ if (typeof rule.value === 'number' && str.length > rule.value)
127
+ return `At most ${rule.value} characters`;
128
+ return null;
129
+ case 'min':
130
+ if (typeof rule.value === 'number' && Number(str) < rule.value)
131
+ return `Must be at least ${rule.value}`;
132
+ return null;
133
+ case 'max':
134
+ if (typeof rule.value === 'number' && Number(str) > rule.value)
135
+ return `Must be at most ${rule.value}`;
136
+ return null;
137
+ case 'email':
138
+ if (!EMAIL_RE.test(str))
139
+ return 'Please enter a valid email address';
140
+ return null;
141
+ case 'url':
142
+ if (!URL_RE.test(str))
143
+ return 'Please enter a valid URL';
144
+ return null;
145
+ case 'pattern': {
146
+ if (typeof rule.value !== 'string')
147
+ return null;
148
+ try {
149
+ const re = new RegExp(rule.value);
150
+ return re.test(str) ? null : 'Format doesn\'t match';
151
+ }
152
+ catch {
153
+ return null;
154
+ }
155
+ }
156
+ default: return null;
157
+ }
158
+ }
159
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/survey/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { SurveyVariable } from '../types';
2
+ export interface VariableSources {
3
+ sdkIdentifyData?: Record<string, unknown> | null;
4
+ linkTrackingData?: Record<string, unknown>;
5
+ }
6
+ export declare function resolveVariables(variables: SurveyVariable[] | undefined, sources: VariableSources, answers: Record<string, unknown>, contextDefaults?: Record<string, unknown>): Record<string, unknown>;
7
+ declare const ATTR_NAMESPACES: readonly ['event', 'device', 'user', 'session', 'geo', 'location'];
8
+ export declare function buildAttributeContext(signals: Partial<Record<(typeof ATTR_NAMESPACES)[number], Record<string, unknown>>> | undefined): Record<string, unknown>;
9
+ export {};
10
+ //# sourceMappingURL=variablesResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variablesResolver.d.ts","sourceRoot":"","sources":["../../src/survey/variablesResolver.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG/C,MAAM,WAAW,eAAe;IAC9B,eAAe,CAAC,EAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAClD,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAED,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,cAAc,EAAE,GAAG,SAAS,EACvC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAIhC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACxC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA8BzB;AAQD,QAAA,MAAM,eAAe,YAAI,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,CAAU,CAAC;AAC3F,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,GAC9F,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazB"}
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ // Mobile variables resolver — spec §22.2, mobile-flavored.
3
+ //
4
+ // Web renderer sources variables from URL params, SDK identify(),
5
+ // personalized-link tracking, static, and calculated. Mobile doesn't
6
+ // have a URL bar, so `url` / `query` sources fall back to the same
7
+ // pool as `sdk` — the identify() blob is the only user-supplied data
8
+ // path on mobile. Static and calculated are unchanged.
9
+ //
10
+ // Merged pool priority (later overrides earlier):
11
+ // 1. SDK identify (via sources.sdkIdentifyData)
12
+ // 2. Personalized-link tracking data
13
+ // 3. Static (design-time literal in variable.defaultValue)
14
+ // 4. Calculated (evaluates last, may reference all others + answers)
15
+ //
16
+ // Unresolved refs fall back to defaultValue; if still absent, they
17
+ // interpolate to empty string (per spec §22.2, done in
18
+ // interpolate.ts). NEVER emit the literal `{{name}}` — that reads
19
+ // as broken to the respondent.
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.resolveVariables = resolveVariables;
22
+ exports.buildAttributeContext = buildAttributeContext;
23
+ const expressionEval_1 = require("./expressionEval");
24
+ function resolveVariables(variables, sources, answers,
25
+ // P4 — Attribute Context floor. The SDK's captured signals ({{user.plan}},
26
+ // {{device.os}}, …) auto-seed as default variables; anything a survey author
27
+ // declares below (static/link/calculated) overrides on name collision.
28
+ contextDefaults) {
29
+ const out = { ...(contextDefaults || {}) };
30
+ if (!variables || variables.length === 0)
31
+ return out;
32
+ // First pass: non-calculated variables. Priority per spec §22.2.
33
+ for (const v of variables) {
34
+ if (v.source === 'calculated')
35
+ continue;
36
+ const val = resolveNonCalculated(v, sources);
37
+ if (val !== undefined)
38
+ out[v.name] = val;
39
+ else if (v.defaultValue !== undefined)
40
+ out[v.name] = v.defaultValue;
41
+ }
42
+ // Second pass: calculated. Runs against the already-populated pool
43
+ // + the current answers map. evaluateExpression reads both.
44
+ for (const v of variables) {
45
+ if (v.source !== 'calculated')
46
+ continue;
47
+ if (!v.expression)
48
+ continue;
49
+ try {
50
+ const val = (0, expressionEval_1.evaluateExpression)(v.expression, { variables: out, answers });
51
+ if (val !== undefined && val !== null)
52
+ out[v.name] = val;
53
+ else if (v.defaultValue !== undefined)
54
+ out[v.name] = v.defaultValue;
55
+ }
56
+ catch {
57
+ // Broken expression shouldn't crash the survey. Fall back to
58
+ // defaultValue if any, otherwise leave unset — empty-string
59
+ // fallback happens at interpolation time.
60
+ if (v.defaultValue !== undefined)
61
+ out[v.name] = v.defaultValue;
62
+ }
63
+ }
64
+ return out;
65
+ }
66
+ // Lockstep TS port of shared/models/settingsSchema.js `buildAttributeContext`
67
+ // (the SDK is a standalone package and can't import shared/). Flattens the SDK's
68
+ // known signal groups into the namespaced { 'ns.key': value } bag used to
69
+ // auto-seed variables here and, later, to evaluate on-device targeting
70
+ // conditions. Scalars only — objects/arrays are dropped so the bag stays flat
71
+ // and matchable. Keep in sync with the shared JS version.
72
+ const ATTR_NAMESPACES = ['event', 'device', 'user', 'session', 'geo', 'location'];
73
+ function buildAttributeContext(signals) {
74
+ const out = {};
75
+ if (!signals)
76
+ return out;
77
+ for (const ns of ATTR_NAMESPACES) {
78
+ const group = signals[ns];
79
+ if (!group || typeof group !== 'object' || Array.isArray(group))
80
+ continue;
81
+ for (const k of Object.keys(group)) {
82
+ const val = group[k];
83
+ if (val == null || typeof val === 'object')
84
+ continue;
85
+ out[`${ns}.${k}`] = val;
86
+ }
87
+ }
88
+ return out;
89
+ }
90
+ function resolveNonCalculated(v, sources) {
91
+ switch (v.source) {
92
+ // No URL bar on mobile — fall URL-family sources through to SDK
93
+ // identity data so the same variable can be sourced by both web
94
+ // (URL param) and mobile (identify() blob) authors.
95
+ case 'url':
96
+ case 'query':
97
+ case 'sdk':
98
+ return sources.sdkIdentifyData?.[v.name];
99
+ case 'link':
100
+ return sources.linkTrackingData?.[v.name];
101
+ case 'static':
102
+ return v.defaultValue;
103
+ default:
104
+ return undefined;
105
+ }
106
+ }
107
+ //# sourceMappingURL=variablesResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variablesResolver.js","sourceRoot":"","sources":["../../src/survey/variablesResolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ export type VoiceSupport = 'full' | 'matrix' | 'read' | 'partial';
2
+ interface VQuestion {
3
+ type?: string;
4
+ title?: string;
5
+ text?: string;
6
+ label?: string;
7
+ options?: unknown;
8
+ choices?: unknown;
9
+ yesLabel?: string;
10
+ noLabel?: string;
11
+ minValue?: number;
12
+ maxValue?: number;
13
+ minLabel?: string;
14
+ maxLabel?: string;
15
+ rows?: unknown;
16
+ columns?: unknown;
17
+ }
18
+ export declare function parseAnswer(q: VQuestion, transcript: string): {
19
+ value: unknown;
20
+ parsed: boolean;
21
+ };
22
+ export declare function voiceSupport(type: string): VoiceSupport;
23
+ export declare function voiceCanHandle(type: string): boolean;
24
+ interface AuditQ {
25
+ type?: string;
26
+ isRequired?: boolean;
27
+ required?: boolean;
28
+ }
29
+ export declare function auditVoiceQuestions(questions: AuditQ[]): {
30
+ blocking: AuditQ[];
31
+ skippable: AuditQ[];
32
+ };
33
+ export declare function voiceDisclaimer(n: number, lang?: string): string;
34
+ export declare function skipNoteText(lang?: string): string;
35
+ export declare function scaffoldFor(lang: string): {
36
+ scale: (min: number, max: number, minL?: string, maxL?: string) => string;
37
+ binary: (y: string, n: string) => string;
38
+ options: (o: string[]) => string;
39
+ multi: (o: string[]) => string;
40
+ ranking: (o: string[]) => string;
41
+ date: () => string;
42
+ matrixStem: (stem: string) => string;
43
+ matrixRow: (row: string, cols: string[]) => string;
44
+ };
45
+ export declare function buildPromptText(q: VQuestion, lang: string): string;
46
+ export {};
47
+ //# sourceMappingURL=voiceParsers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voiceParsers.d.ts","sourceRoot":"","sources":["../../src/survey/voiceParsers.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAGlE,UAAU,SAAS;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3E,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnC;AAiHD,wBAAgB,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAajG;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAYvD;AAGD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAA6C;AAElG,UAAU,MAAM;IAAG,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE;AAE5E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAQpG;AACD,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAGhE;AACD,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAGlD;AAkCD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM;WA9B/B,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,MAAM;YACjE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM;aAC/B,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,MAAM;WACzB,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,MAAM;aACrB,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,MAAM;UAC1B,MAAM,MAAM;gBACN,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM;eACzB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM;EAuB6E;AAIjI,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAYlE"}
@@ -0,0 +1,310 @@
1
+ "use strict";
2
+ // Voice answer parsers + prompt scaffolds for the mobile hands-free session.
3
+ //
4
+ // Pure logic ported from the web SDK's voice.js / the React handsFreeVoice.tsx
5
+ // so all three surfaces (web SDK, link renderer, mobile) parse spoken answers
6
+ // identically. No native dependencies here — only string → value mapping.
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.parseAnswer = parseAnswer;
9
+ exports.voiceSupport = voiceSupport;
10
+ exports.voiceCanHandle = voiceCanHandle;
11
+ exports.auditVoiceQuestions = auditVoiceQuestions;
12
+ exports.voiceDisclaimer = voiceDisclaimer;
13
+ exports.skipNoteText = skipNoteText;
14
+ exports.scaffoldFor = scaffoldFor;
15
+ exports.buildPromptText = buildPromptText;
16
+ function normalize(s) {
17
+ try {
18
+ return String(s).toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, ' ').replace(/\s+/g, ' ').trim();
19
+ }
20
+ catch {
21
+ return String(s).toLowerCase().replace(/[^a-z0-9\s]/gi, ' ').replace(/\s+/g, ' ').trim();
22
+ }
23
+ }
24
+ const YES_WORDS = ['yes', 'yeah', 'yep', 'sure', 'ok', 'okay', 'true', 'correct', 'right', 'affirmative', 'نعم', 'أجل', 'صحيح', 'أيوه', 'sí', 'si', 'oui', 'ja', 'sì'];
25
+ const NO_WORDS = ['no', 'nope', 'nah', 'false', 'incorrect', 'negative', 'لا', 'كلا', 'non', 'nein'];
26
+ // Explicit ordinals first (first/second/third) so "the second one" → index 1,
27
+ // not the cardinal "one" (index 0) that also appears in the phrase. Whole-token.
28
+ const ORDINAL_LIST = [
29
+ ['first', 0], ['second', 1], ['third', 2], ['fourth', 3], ['fifth', 4],
30
+ ['one', 0], ['two', 1], ['three', 2], ['four', 3], ['five', 4],
31
+ ['1', 0], ['2', 1], ['3', 2], ['4', 3], ['5', 4],
32
+ ];
33
+ function ordinalIndex(tokens, count) {
34
+ for (const [word, idx] of ORDINAL_LIST)
35
+ if (tokens.indexOf(word) !== -1 && idx < count)
36
+ return idx;
37
+ return -1;
38
+ }
39
+ const NUMWORDS = {
40
+ zero: 0, one: 1, two: 2, three: 3, four: 4, five: 5, six: 6, seven: 7, eight: 8, nine: 9, ten: 10,
41
+ first: 1, second: 2, third: 3, fourth: 4, fifth: 5, sixth: 6, seventh: 7, eighth: 8, ninth: 9, tenth: 10,
42
+ 'صفر': 0, 'واحد': 1, 'واحدة': 1, 'اثنان': 2, 'اثنين': 2, 'اتنين': 2, 'ثلاثة': 3, 'ثلاث': 3, 'أربعة': 4, 'اربعة': 4,
43
+ 'خمسة': 5, 'خمس': 5, 'ستة': 6, 'سبعة': 7, 'سبع': 7, 'ثمانية': 8, 'ثماني': 8, 'تسعة': 9, 'عشرة': 10, 'عشر': 10,
44
+ };
45
+ function optLabel(o) { return String((o && (o.label != null ? o.label : (o.text != null ? o.text : (o.value != null ? o.value : o.alt)))) || ''); }
46
+ function optVal(o) { return (o && o.value != null) ? o.value : optLabel(o); }
47
+ function getOptions(q) { return Array.isArray(q.options) ? q.options : (Array.isArray(q.choices) ? q.choices : []); }
48
+ function parseBinary(q, t) {
49
+ const tokens = normalize(t).split(' ').filter(Boolean);
50
+ const yes = typeof q.yesLabel === 'string' ? normalize(q.yesLabel) : '';
51
+ const no = typeof q.noLabel === 'string' ? normalize(q.noLabel) : '';
52
+ for (const tok of tokens) {
53
+ if (YES_WORDS.indexOf(tok) !== -1)
54
+ return true;
55
+ if (NO_WORDS.indexOf(tok) !== -1)
56
+ return false;
57
+ if (yes && tok === yes)
58
+ return true;
59
+ if (no && tok === no)
60
+ return false;
61
+ }
62
+ const norm = normalize(t);
63
+ if (yes && norm.indexOf(yes) !== -1)
64
+ return true;
65
+ if (no && norm.indexOf(no) !== -1)
66
+ return false;
67
+ return null;
68
+ }
69
+ function parseChoice(q, t) {
70
+ const options = getOptions(q);
71
+ if (!options.length)
72
+ return null;
73
+ const norm = normalize(t);
74
+ let best = null;
75
+ for (const o of options) {
76
+ const nl = normalize(optLabel(o));
77
+ if (nl && norm.indexOf(nl) !== -1 && (!best || nl.length > best.length))
78
+ best = { value: optVal(o), length: nl.length };
79
+ }
80
+ if (!best) {
81
+ const oi = ordinalIndex(norm.split(' ').filter(Boolean), options.length);
82
+ if (oi >= 0)
83
+ best = { value: optVal(options[oi]), length: 0 };
84
+ }
85
+ return best ? best.value : null;
86
+ }
87
+ function parseCheckbox(q, t) {
88
+ const options = getOptions(q);
89
+ if (!options.length)
90
+ return null;
91
+ const norm = normalize(t);
92
+ const picked = [];
93
+ for (const o of options) {
94
+ const nl = normalize(optLabel(o));
95
+ if (nl && norm.indexOf(nl) !== -1) {
96
+ const v = String(optVal(o));
97
+ if (picked.indexOf(v) === -1)
98
+ picked.push(v);
99
+ }
100
+ }
101
+ const toks = norm.split(' ').filter(Boolean);
102
+ for (const [word, idx] of ORDINAL_LIST)
103
+ if (toks.indexOf(word) !== -1 && idx < options.length) {
104
+ const v = String(optVal(options[idx]));
105
+ if (picked.indexOf(v) === -1)
106
+ picked.push(v);
107
+ }
108
+ return picked.length ? picked : null;
109
+ }
110
+ function parseRanking(q, t) {
111
+ const options = getOptions(q);
112
+ if (!options.length)
113
+ return null;
114
+ const norm = normalize(t);
115
+ const pos = [];
116
+ for (const o of options) {
117
+ const nl = normalize(optLabel(o));
118
+ if (!nl)
119
+ continue;
120
+ const p = norm.indexOf(nl);
121
+ if (p >= 0)
122
+ pos.push({ value: optVal(o), pos: p });
123
+ }
124
+ if (pos.length < 2)
125
+ return null;
126
+ pos.sort((a, b) => a.pos - b.pos);
127
+ return pos.map((x) => x.value);
128
+ }
129
+ function parseNumberInRange(t, min, max) {
130
+ const ascii = String(t).replace(/[٠-٩]/g, (d) => String('٠١٢٣٤٥٦٧٨٩'.indexOf(d))).replace(/[۰-۹]/g, (d) => String('۰۱۲۳۴۵۶۷۸۹'.indexOf(d)));
131
+ const norm = normalize(ascii);
132
+ const digits = norm.match(/\d+(?:\.\d+)?/g);
133
+ if (digits)
134
+ for (const m of digits) {
135
+ const n = Math.round(Number(m));
136
+ if (n >= min && n <= max)
137
+ return n;
138
+ }
139
+ const tokens = norm.split(/\s+/).filter(Boolean);
140
+ for (const tok of tokens) {
141
+ const w = NUMWORDS[tok];
142
+ if (w !== undefined && w >= min && w <= max)
143
+ return w;
144
+ }
145
+ return null;
146
+ }
147
+ function parseDate(t) {
148
+ const raw = String(t).trim();
149
+ const cleaned = raw.replace(/\b(the|of|on|at)\b/gi, ' ').replace(/(\d+)(st|nd|rd|th)\b/gi, '$1').replace(/\s+/g, ' ').trim();
150
+ const attempts = [raw, cleaned, raw.replace(/(\d+)(st|nd|rd|th)\b/gi, '$1')];
151
+ for (const c of attempts) {
152
+ if (!c)
153
+ continue;
154
+ const d = new Date(c);
155
+ if (!isNaN(d.getTime())) {
156
+ const m = String(d.getMonth() + 1).padStart(2, '0');
157
+ const day = String(d.getDate()).padStart(2, '0');
158
+ return `${d.getFullYear()}-${m}-${day}`;
159
+ }
160
+ }
161
+ return null;
162
+ }
163
+ function parseAnswer(q, transcript) {
164
+ const type = String(q.type || '').toLowerCase();
165
+ const trimmed = String(transcript || '').trim();
166
+ if (!trimmed)
167
+ return { value: undefined, parsed: false };
168
+ if (['binary', 'boolean', 'yesno', 'yes_no'].indexOf(type) !== -1) {
169
+ const b = parseBinary(q, trimmed);
170
+ return b !== null ? { value: b, parsed: true } : { value: undefined, parsed: false };
171
+ }
172
+ if (['radio', 'radiogroup', 'dropdown', 'select', 'imagepicker'].indexOf(type) !== -1) {
173
+ const c = parseChoice(q, trimmed);
174
+ return c != null ? { value: c, parsed: true } : { value: undefined, parsed: false };
175
+ }
176
+ if (type === 'rating') {
177
+ const r = parseNumberInRange(trimmed, typeof q.minValue === 'number' ? q.minValue : 1, typeof q.maxValue === 'number' ? q.maxValue : 5);
178
+ return r !== null ? { value: r, parsed: true } : { value: undefined, parsed: false };
179
+ }
180
+ if (type === 'nps') {
181
+ const n = parseNumberInRange(trimmed, 0, 10);
182
+ return n !== null ? { value: n, parsed: true } : { value: undefined, parsed: false };
183
+ }
184
+ if (type === 'checkbox') {
185
+ const ck = parseCheckbox(q, trimmed);
186
+ return ck ? { value: ck, parsed: true } : { value: undefined, parsed: false };
187
+ }
188
+ if (type === 'ranking') {
189
+ const rk = parseRanking(q, trimmed);
190
+ return rk ? { value: rk, parsed: true } : { value: undefined, parsed: false };
191
+ }
192
+ if (type === 'date') {
193
+ const dt = parseDate(trimmed);
194
+ return dt ? { value: dt, parsed: true } : { value: undefined, parsed: false };
195
+ }
196
+ if (['text', 'textbox', 'shorttext', 'comment', 'textarea', 'longtext'].indexOf(type) !== -1)
197
+ return { value: trimmed, parsed: true };
198
+ return { value: undefined, parsed: false };
199
+ }
200
+ function voiceSupport(type) {
201
+ switch (String(type || '').toLowerCase()) {
202
+ case 'text':
203
+ case 'textbox':
204
+ case 'shorttext':
205
+ case 'comment':
206
+ case 'textarea':
207
+ case 'longtext':
208
+ case 'binary':
209
+ case 'boolean':
210
+ case 'yesno':
211
+ case 'yes_no':
212
+ case 'radio':
213
+ case 'radiogroup':
214
+ case 'dropdown':
215
+ case 'select':
216
+ case 'rating':
217
+ case 'nps':
218
+ case 'checkbox':
219
+ case 'imagepicker':
220
+ case 'ranking':
221
+ case 'date':
222
+ return 'full';
223
+ case 'matrix': return 'matrix';
224
+ case 'content':
225
+ case 'html':
226
+ case 'image':
227
+ case 'expression':
228
+ case 'pagebreak': return 'read';
229
+ default: return 'partial';
230
+ }
231
+ }
232
+ // Can voice-only fully handle this question? ('partial' can't.)
233
+ function voiceCanHandle(type) { return voiceSupport(type) !== 'partial'; }
234
+ // blocking = required + unsupported (voice must not start); skippable = optional + unsupported.
235
+ function auditVoiceQuestions(questions) {
236
+ const blocking = [], skippable = [];
237
+ (questions || []).forEach((q) => {
238
+ if (!q || String(q.type || '').toLowerCase() === 'pagebreak')
239
+ return;
240
+ if (voiceCanHandle(String(q.type || '')))
241
+ return;
242
+ if (q.isRequired || q.required)
243
+ blocking.push(q);
244
+ else
245
+ skippable.push(q);
246
+ });
247
+ return { blocking, skippable };
248
+ }
249
+ function voiceDisclaimer(n, lang) {
250
+ if (String(lang || '').split('-')[0] === 'ar')
251
+ return 'ملاحظة: ' + n + (n > 1 ? ' أسئلة' : ' سؤال') + ' لا يمكن الإجابة عليها صوتياً وسيتم تخطيها.';
252
+ return 'Heads up — ' + n + ' question' + (n > 1 ? 's' : '') + " can't be answered by voice and will be skipped.";
253
+ }
254
+ function skipNoteText(lang) {
255
+ if (String(lang || '').split('-')[0] === 'ar')
256
+ return 'هذا السؤال يحتاج إدخالاً يدوياً — سيتم تخطيه.';
257
+ return 'This one needs manual input — skipping it.';
258
+ }
259
+ // ── Prompt scaffolds (en / ar) ──────────────────────────────────────
260
+ const SCAFFOLDS = {
261
+ en: {
262
+ scale: (min, max, minL, maxL) => { const r = `Answer with a number from ${min} to ${max}`; return (minL && maxL) ? `${r}. ${min} means ${minL}, ${max} means ${maxL}.` : `${r}.`; },
263
+ binary: (y, n) => `Please answer ${y || 'yes'} or ${n || 'no'}.`,
264
+ options: (o) => `Options are: ${o.join(', ')}.`,
265
+ multi: (o) => `Multiple choices allowed. Options are: ${o.join(', ')}. Say the ones that apply.`,
266
+ ranking: (o) => `Rank in order of preference: ${o.join(', ')}.`,
267
+ date: () => `Please say the date.`,
268
+ matrixStem: (stem) => `${stem}. I’ll ask you row by row.`,
269
+ matrixRow: (row, cols) => `${row}. Options: ${cols.join(', ')}.`,
270
+ },
271
+ ar: {
272
+ scale: (min, max, minL, maxL) => { const r = `أجب برقم من ${min} إلى ${max}`; return (minL && maxL) ? `${r}. ${min} يعني ${minL}، ${max} يعني ${maxL}.` : `${r}.`; },
273
+ binary: (y, n) => `يرجى الإجابة ${y || 'نعم'} أو ${n || 'لا'}.`,
274
+ options: (o) => `الخيارات: ${o.join('، ')}.`,
275
+ multi: (o) => `يمكن اختيار أكثر من إجابة. الخيارات: ${o.join('، ')}. قل الخيارات التي تنطبق.`,
276
+ ranking: (o) => `رتب حسب الأفضلية: ${o.join('، ')}.`,
277
+ date: () => `يرجى قول التاريخ.`,
278
+ matrixStem: (stem) => `${stem}. سأسألك عن كل صف على حدة.`,
279
+ matrixRow: (row, cols) => `${row}. الخيارات: ${cols.join('، ')}.`,
280
+ },
281
+ };
282
+ function scaffoldFor(lang) { return SCAFFOLDS[String(lang || 'en').split('-')[0].toLowerCase()] || SCAFFOLDS.en; }
283
+ function optionLabels(q) { return getOptions(q).map(optLabel).filter(Boolean); }
284
+ function buildPromptText(q, lang) {
285
+ const s = scaffoldFor(lang);
286
+ const base = String(q.title || q.text || q.label || '').replace(/<[^>]*>/g, '').trim();
287
+ const type = String(q.type || '').toLowerCase();
288
+ if (['binary', 'boolean', 'yesno', 'yes_no'].indexOf(type) !== -1)
289
+ return `${base}. ${s.binary(typeof q.yesLabel === 'string' ? q.yesLabel : '', typeof q.noLabel === 'string' ? q.noLabel : '')}`;
290
+ if (type === 'rating')
291
+ return `${base}. ${s.scale(typeof q.minValue === 'number' ? q.minValue : 1, typeof q.maxValue === 'number' ? q.maxValue : 5, typeof q.minLabel === 'string' ? q.minLabel : undefined, typeof q.maxLabel === 'string' ? q.maxLabel : undefined)}`;
292
+ if (type === 'nps')
293
+ return `${base}. ${s.scale(0, 10, typeof q.minLabel === 'string' ? q.minLabel : 'Not at all likely', typeof q.maxLabel === 'string' ? q.maxLabel : 'Extremely likely')}`;
294
+ if (['radio', 'radiogroup', 'dropdown', 'select', 'imagepicker'].indexOf(type) !== -1) {
295
+ const o = optionLabels(q);
296
+ return o.length ? `${base}. ${s.options(o)}` : base;
297
+ }
298
+ if (type === 'checkbox') {
299
+ const o = optionLabels(q);
300
+ return o.length ? `${base}. ${s.multi(o)}` : base;
301
+ }
302
+ if (type === 'ranking') {
303
+ const o = optionLabels(q);
304
+ return o.length ? `${base}. ${s.ranking(o)}` : base;
305
+ }
306
+ if (type === 'date')
307
+ return `${base}. ${s.date()}`;
308
+ return base;
309
+ }
310
+ //# sourceMappingURL=voiceParsers.js.map