@posthog/browser-common 0.9.5 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +6 -0
- package/dist/config.js +1 -1
- package/dist/config.mjs +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +58 -10
- package/dist/index.mjs +1 -0
- package/dist/survey-action-matcher.d.ts +27 -0
- package/dist/survey-action-matcher.js +169 -0
- package/dist/survey-action-matcher.mjs +131 -0
- package/dist/survey-constants.d.ts +86 -0
- package/dist/survey-constants.js +144 -0
- package/dist/survey-constants.mjs +76 -0
- package/dist/survey-event-host.d.ts +15 -0
- package/dist/survey-event-host.js +18 -0
- package/dist/survey-event-host.mjs +1 -0
- package/dist/survey-event-receiver-base.d.ts +180 -0
- package/dist/survey-event-receiver-base.js +354 -0
- package/dist/survey-event-receiver-base.mjs +316 -0
- package/dist/survey-event-receiver.d.ts +36 -0
- package/dist/survey-event-receiver.js +133 -0
- package/dist/survey-event-receiver.mjs +95 -0
- package/dist/survey-render-context.d.ts +13 -0
- package/dist/survey-render-context.js +45 -0
- package/dist/survey-render-context.mjs +7 -0
- package/dist/surveys/components/BottomSection.d.ts +12 -0
- package/dist/surveys/components/BottomSection.js +82 -0
- package/dist/surveys/components/BottomSection.mjs +44 -0
- package/dist/surveys/components/ConfirmationMessage.d.ts +10 -0
- package/dist/surveys/components/ConfirmationMessage.js +92 -0
- package/dist/surveys/components/ConfirmationMessage.mjs +54 -0
- package/dist/surveys/components/IntroScreen.d.ts +10 -0
- package/dist/surveys/components/IntroScreen.js +78 -0
- package/dist/surveys/components/IntroScreen.mjs +40 -0
- package/dist/surveys/components/PostHogLogo.d.ts +5 -0
- package/dist/surveys/components/PostHogLogo.js +54 -0
- package/dist/surveys/components/PostHogLogo.mjs +16 -0
- package/dist/surveys/components/QuestionHeader.d.ts +10 -0
- package/dist/surveys/components/QuestionHeader.js +82 -0
- package/dist/surveys/components/QuestionHeader.mjs +41 -0
- package/dist/surveys/components/QuestionTypes.d.ts +31 -0
- package/dist/surveys/components/QuestionTypes.js +519 -0
- package/dist/surveys/components/QuestionTypes.mjs +469 -0
- package/dist/surveys/icons.d.ts +12 -0
- package/dist/surveys/icons.js +212 -0
- package/dist/surveys/icons.mjs +147 -0
- package/dist/surveys/survey-branching.d.ts +12 -0
- package/dist/surveys/survey-branching.js +101 -0
- package/dist/surveys/survey-branching.mjs +60 -0
- package/dist/surveys/survey-styles.d.ts +1 -0
- package/dist/surveys/survey-styles.js +40 -0
- package/dist/surveys/survey-styles.mjs +2 -0
- package/dist/surveys/survey-translations.d.ts +22 -0
- package/dist/surveys/survey-translations.js +68 -0
- package/dist/surveys/survey-translations.mjs +27 -0
- package/dist/surveys/survey-url-prefill.d.ts +36 -0
- package/dist/surveys/survey-url-prefill.js +162 -0
- package/dist/surveys/survey-url-prefill.mjs +118 -0
- package/dist/surveys/surveys-extension-utils.d.ts +133 -0
- package/dist/surveys/surveys-extension-utils.js +739 -0
- package/dist/surveys/surveys-extension-utils.mjs +608 -0
- package/dist/surveys-config.d.ts +60 -0
- package/dist/surveys-config.js +68 -0
- package/dist/surveys-config.mjs +9 -0
- package/dist/surveys-renderer.d.ts +248 -0
- package/dist/surveys-renderer.js +1299 -0
- package/dist/surveys-renderer.mjs +1230 -0
- package/dist/surveys.d.ts +117 -0
- package/dist/surveys.js +566 -0
- package/dist/surveys.mjs +495 -0
- package/dist/types/surveys.d.ts +83 -86
- package/dist/types/surveys.js +1 -127
- package/dist/types/surveys.mjs +1 -76
- package/dist/utils/autocapture-utils.js +8 -9
- package/dist/utils/autocapture-utils.mjs +8 -9
- package/dist/utils/matcher-utils.d.ts +1 -1
- package/dist/utils/matcher-utils.js +13 -10
- package/dist/utils/matcher-utils.mjs +10 -7
- package/dist/utils/survey-utils.d.ts +16 -0
- package/dist/utils/survey-utils.js +121 -0
- package/dist/utils/survey-utils.mjs +38 -0
- package/dist/utils/url-targeting-utils.d.ts +3 -1
- package/dist/utils/url-targeting-utils.js +1 -2
- package/dist/utils/url-targeting-utils.mjs +1 -2
- package/dist/utils/uuidv7.js +4 -4
- package/dist/utils/uuidv7.mjs +4 -4
- package/package.json +96 -2
package/dist/surveys.mjs
ADDED
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import { DisplaySurveyType, SurveyEventName, SurveyEventProperties, SurveyEventType, SurveyPosition, SurveyQuestionBranchingType, SurveyQuestionType, SurveySchedule, SurveyTabPosition, SurveyType, SurveyWidgetType } from "./survey-constants.mjs";
|
|
2
|
+
import { SurveyEventReceiver } from "./survey-event-receiver.mjs";
|
|
3
|
+
import { continueWith } from "./utils/promise-utils.mjs";
|
|
4
|
+
import { isBoolean, isNullish, isNumber, isUndefined } from "@posthog/core";
|
|
5
|
+
import { LOAD_EXT_NOT_FOUND, SURVEYS, SURVEYS_CACHE_TTL_MS, SURVEYS_LOADED_AT, SURVEYS_REFRESH_BACKOFF_MS } from "./surveys-config.mjs";
|
|
6
|
+
import { IN_APP_SURVEY_TYPES, SURVEY_CAPTURING_DISABLED, SURVEY_IN_PROGRESS_PREFIX, SURVEY_LOGGER, SURVEY_SEEN_PREFIX, doesSurveyActivateByAction, doesSurveyActivateByEvent, isSurveyRunning, setSurveySeenOnLocalStorage } from "./utils/survey-utils.mjs";
|
|
7
|
+
const SURVEY_NOT_LOADED = 'SDK is not enabled or survey functionality is not yet loaded';
|
|
8
|
+
const SURVEY_DISABLED = 'Disabled. Not loading surveys.';
|
|
9
|
+
class PostHogSurveys {
|
|
10
|
+
constructor(_configSource){
|
|
11
|
+
this._configSource = _configSource;
|
|
12
|
+
this.name = 'surveys';
|
|
13
|
+
this._surveyEventReceiver = null;
|
|
14
|
+
this._surveyManager = null;
|
|
15
|
+
this._isInitializingSurveys = false;
|
|
16
|
+
this._surveyInitializationFailed = false;
|
|
17
|
+
this._surveyCallbacks = [];
|
|
18
|
+
this._activeMatchingSurveyCallbacks = [];
|
|
19
|
+
this._getSurveysInFlightPromise = null;
|
|
20
|
+
this._lastSurveyRefreshFailedAt = null;
|
|
21
|
+
this._disposed = false;
|
|
22
|
+
this._renderTimeouts = new Set();
|
|
23
|
+
this.onRemoteConfig = (result)=>{
|
|
24
|
+
if (this._disposed) return;
|
|
25
|
+
if (this._config.disableSurveys) return;
|
|
26
|
+
if (!result.ok) {
|
|
27
|
+
this._surveyInitializationFailed = true;
|
|
28
|
+
this._notifyActiveMatchingSurveyCallbacks({
|
|
29
|
+
isLoaded: false,
|
|
30
|
+
error: 'Remote config unavailable. Not loading surveys.'
|
|
31
|
+
});
|
|
32
|
+
return SURVEY_LOGGER.warn('Remote config unavailable. Not loading surveys.');
|
|
33
|
+
}
|
|
34
|
+
const surveys = result.config['surveys'];
|
|
35
|
+
if (isNullish(surveys)) return SURVEY_LOGGER.warn('Flags not loaded yet. Not loading surveys.');
|
|
36
|
+
this._isSurveysEnabled = isBoolean(surveys) ? surveys : surveys.length > 0;
|
|
37
|
+
SURVEY_LOGGER.info(`flags response received, isSurveysEnabled: ${this._isSurveysEnabled}`);
|
|
38
|
+
this.loadIfEnabled();
|
|
39
|
+
};
|
|
40
|
+
this._notifyActiveMatchingSurveyCallbacks = (context)=>{
|
|
41
|
+
this._activeMatchingSurveyCallbacks.slice().forEach((subscription)=>this._notifyActiveMatchingSurveyCallback(subscription, context));
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
setup(client) {
|
|
45
|
+
if (this._disposed) return;
|
|
46
|
+
this._initializingClient = client;
|
|
47
|
+
return continueWith(client.kv.initialize(), ()=>{
|
|
48
|
+
if (this._initializingClient !== client || this._disposed) return;
|
|
49
|
+
this._initializingClient = void 0;
|
|
50
|
+
this._client = client;
|
|
51
|
+
const subscription = client.onRemoteConfig(this.onRemoteConfig);
|
|
52
|
+
if (this._disposed) return void subscription.dispose();
|
|
53
|
+
this._remoteConfigSubscription = subscription;
|
|
54
|
+
this.loadIfEnabled();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
dispose() {
|
|
58
|
+
if (this._disposed) return;
|
|
59
|
+
this._disposed = true;
|
|
60
|
+
this._initializingClient = void 0;
|
|
61
|
+
this._client = void 0;
|
|
62
|
+
this._remoteConfigSubscription?.dispose();
|
|
63
|
+
this._remoteConfigSubscription = void 0;
|
|
64
|
+
this._surveyEventReceiver?.dispose();
|
|
65
|
+
this._surveyEventReceiver = null;
|
|
66
|
+
this._surveyManager?.dispose?.();
|
|
67
|
+
this._surveyManager = null;
|
|
68
|
+
this._surveyCallbacks = [];
|
|
69
|
+
this._activeMatchingSurveyCallbacks.forEach((subscription)=>{
|
|
70
|
+
subscription.active = false;
|
|
71
|
+
});
|
|
72
|
+
this._activeMatchingSurveyCallbacks = [];
|
|
73
|
+
this._activeMatchingSurveyConditionsUnsubscribe?.();
|
|
74
|
+
this._activeMatchingSurveyConditionsUnsubscribe = void 0;
|
|
75
|
+
this._getSurveysInFlightPromise = null;
|
|
76
|
+
this._renderTimeouts.forEach((timeout)=>clearTimeout(timeout));
|
|
77
|
+
this._renderTimeouts.clear();
|
|
78
|
+
}
|
|
79
|
+
get _config() {
|
|
80
|
+
return this._configSource.get();
|
|
81
|
+
}
|
|
82
|
+
getRenderContext() {
|
|
83
|
+
if (!this._client) return;
|
|
84
|
+
const surveys = this;
|
|
85
|
+
return {
|
|
86
|
+
client: this._client,
|
|
87
|
+
get config () {
|
|
88
|
+
return surveys._config;
|
|
89
|
+
},
|
|
90
|
+
surveys
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
initialize() {
|
|
94
|
+
this.loadIfEnabled();
|
|
95
|
+
}
|
|
96
|
+
reset() {
|
|
97
|
+
this._surveyManager?.clearInMemoryInProgressSurveyState?.();
|
|
98
|
+
try {
|
|
99
|
+
this._surveyEventReceiver?.reset();
|
|
100
|
+
localStorage.removeItem('lastSeenSurveyDate');
|
|
101
|
+
const surveyKeys = [];
|
|
102
|
+
for(let i = 0; i < localStorage.length; i++){
|
|
103
|
+
const key = localStorage.key(i);
|
|
104
|
+
if (key?.startsWith(SURVEY_SEEN_PREFIX) || key?.startsWith(SURVEY_IN_PROGRESS_PREFIX)) surveyKeys.push(key);
|
|
105
|
+
}
|
|
106
|
+
surveyKeys.forEach((key)=>localStorage.removeItem(key));
|
|
107
|
+
} catch {}
|
|
108
|
+
this._notifyActiveMatchingSurveyCallbacks();
|
|
109
|
+
}
|
|
110
|
+
loadIfEnabled() {
|
|
111
|
+
if (this._disposed || !this._client) return;
|
|
112
|
+
const config = this._config;
|
|
113
|
+
if (this._surveyManager) return;
|
|
114
|
+
if (this._isInitializingSurveys) return void SURVEY_LOGGER.info('Already initializing surveys, skipping...');
|
|
115
|
+
if (config.disableSurveys) return void SURVEY_LOGGER.info(SURVEY_DISABLED);
|
|
116
|
+
if (config.cookielessMode && this._client.isOptedOut) return void SURVEY_LOGGER.info('Not loading surveys in cookieless mode without consent.');
|
|
117
|
+
const phExtensions = this._configSource.getExtensions();
|
|
118
|
+
if (!phExtensions) return void this._handleSurveyLoadError('PostHog Extensions not found.');
|
|
119
|
+
if (isUndefined(this._isSurveysEnabled) && !config.advancedEnableSurveys) return;
|
|
120
|
+
const isSurveysEnabled = this._isSurveysEnabled || config.advancedEnableSurveys;
|
|
121
|
+
this._isInitializingSurveys = true;
|
|
122
|
+
this._surveyInitializationFailed = false;
|
|
123
|
+
try {
|
|
124
|
+
const generateSurveys = phExtensions.generateSurveys;
|
|
125
|
+
if (generateSurveys) {
|
|
126
|
+
this._completeSurveyInitialization(generateSurveys, isSurveysEnabled);
|
|
127
|
+
this._isInitializingSurveys = false;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const loadExternalDependency = phExtensions.loadExternalDependency;
|
|
131
|
+
if (!loadExternalDependency) {
|
|
132
|
+
this._handleSurveyLoadError(LOAD_EXT_NOT_FOUND);
|
|
133
|
+
this._isInitializingSurveys = false;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
loadExternalDependency((err)=>{
|
|
137
|
+
try {
|
|
138
|
+
if (this._disposed) return;
|
|
139
|
+
const loadedExtensions = this._configSource.getExtensions();
|
|
140
|
+
if (err || !loadedExtensions?.generateSurveys) this._handleSurveyLoadError("Could not load surveys script", err);
|
|
141
|
+
else this._completeSurveyInitialization(loadedExtensions.generateSurveys, isSurveysEnabled);
|
|
142
|
+
} finally{
|
|
143
|
+
this._isInitializingSurveys = false;
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
} catch (e) {
|
|
147
|
+
this._isInitializingSurveys = false;
|
|
148
|
+
this._handleSurveyLoadError('Error initializing surveys', e);
|
|
149
|
+
throw e;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
_completeSurveyInitialization(generateSurveysFn, isSurveysEnabled) {
|
|
153
|
+
if (this._disposed) return;
|
|
154
|
+
this._surveyManager = generateSurveysFn(isSurveysEnabled);
|
|
155
|
+
this._surveyEventReceiver = new SurveyEventReceiver(this._client, this, this._notifyActiveMatchingSurveyCallbacks);
|
|
156
|
+
const cachedSurveys = this._client?.kv.get(SURVEYS);
|
|
157
|
+
if (cachedSurveys) this._registerEventOrActionBasedSurveys(cachedSurveys);
|
|
158
|
+
SURVEY_LOGGER.info('Surveys loaded successfully');
|
|
159
|
+
this._startActiveMatchingSurveyConditions();
|
|
160
|
+
this._notifyActiveMatchingSurveyCallbacks();
|
|
161
|
+
this._notifySurveyCallbacks({
|
|
162
|
+
isLoaded: true
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
_handleSurveyLoadError(message, error) {
|
|
166
|
+
this._surveyInitializationFailed = true;
|
|
167
|
+
SURVEY_LOGGER.error(message, error);
|
|
168
|
+
this._notifyActiveMatchingSurveyCallbacks({
|
|
169
|
+
isLoaded: false,
|
|
170
|
+
error: message
|
|
171
|
+
});
|
|
172
|
+
this._notifySurveyCallbacks({
|
|
173
|
+
isLoaded: false,
|
|
174
|
+
error: message
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
onSurveysLoaded(callback) {
|
|
178
|
+
this._surveyCallbacks.push(callback);
|
|
179
|
+
if (this._surveyManager) this._notifySurveyCallbacks({
|
|
180
|
+
isLoaded: true
|
|
181
|
+
});
|
|
182
|
+
return ()=>{
|
|
183
|
+
this._surveyCallbacks = this._surveyCallbacks.filter((cb)=>cb !== callback);
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
getSurveys(callback, forceReload = false) {
|
|
187
|
+
if (this._disposed) return;
|
|
188
|
+
const client = this._client;
|
|
189
|
+
if (!client) return callback([], {
|
|
190
|
+
isLoaded: false,
|
|
191
|
+
error: SURVEY_NOT_LOADED
|
|
192
|
+
});
|
|
193
|
+
if (this._config.disableSurveys) {
|
|
194
|
+
SURVEY_LOGGER.info(SURVEY_DISABLED);
|
|
195
|
+
return callback([]);
|
|
196
|
+
}
|
|
197
|
+
const surveys = client.kv.get(SURVEYS);
|
|
198
|
+
if (surveys && !forceReload) {
|
|
199
|
+
callback(surveys, {
|
|
200
|
+
isLoaded: true
|
|
201
|
+
});
|
|
202
|
+
if (this._shouldBackgroundRefreshSurveys()) this.getSurveys(()=>{}, true);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (this._getSurveysInFlightPromise) return void this._getSurveysInFlightPromise.then(({ surveys, context })=>{
|
|
206
|
+
if (!this._disposed) callback(surveys, context);
|
|
207
|
+
}).catch((error)=>SURVEY_LOGGER.error('Error in survey callback', error));
|
|
208
|
+
const request = client.sendRequest('/api/surveys/', {
|
|
209
|
+
method: 'GET',
|
|
210
|
+
query: {
|
|
211
|
+
token: client.projectToken
|
|
212
|
+
},
|
|
213
|
+
sentAt: 'query',
|
|
214
|
+
timeoutMs: this._config.requestTimeoutMs
|
|
215
|
+
}).then((response)=>{
|
|
216
|
+
try {
|
|
217
|
+
return this._handleSurveyResponse(client, response);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
SURVEY_LOGGER.error('Error processing surveys response', error);
|
|
220
|
+
return this._handleSurveyResponse(client, {
|
|
221
|
+
statusCode: 0,
|
|
222
|
+
error
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}, (error)=>this._handleSurveyResponse(client, {
|
|
226
|
+
statusCode: 0,
|
|
227
|
+
error
|
|
228
|
+
}));
|
|
229
|
+
this._getSurveysInFlightPromise = request;
|
|
230
|
+
const clearInFlight = ()=>{
|
|
231
|
+
if (this._getSurveysInFlightPromise === request) this._getSurveysInFlightPromise = null;
|
|
232
|
+
};
|
|
233
|
+
request.then((result)=>{
|
|
234
|
+
clearInFlight();
|
|
235
|
+
if (!this._disposed) {
|
|
236
|
+
this._notifyActiveMatchingSurveyCallbacks(result.context);
|
|
237
|
+
callback(result.surveys, result.context);
|
|
238
|
+
}
|
|
239
|
+
}, clearInFlight).catch((error)=>SURVEY_LOGGER.error('Error in survey callback', error));
|
|
240
|
+
}
|
|
241
|
+
_handleSurveyResponse(client, response) {
|
|
242
|
+
if (this._disposed) return {
|
|
243
|
+
surveys: [],
|
|
244
|
+
context: {
|
|
245
|
+
isLoaded: false,
|
|
246
|
+
error: SURVEY_NOT_LOADED
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
const statusCode = response.statusCode;
|
|
250
|
+
if (200 !== statusCode || !response.json) {
|
|
251
|
+
const error = `Surveys API could not be loaded, status: ${statusCode}`;
|
|
252
|
+
if (0 !== statusCode) SURVEY_LOGGER.error(error);
|
|
253
|
+
else if (!response.error) SURVEY_LOGGER.warn(error);
|
|
254
|
+
this._lastSurveyRefreshFailedAt = Date.now();
|
|
255
|
+
return {
|
|
256
|
+
surveys: [],
|
|
257
|
+
context: {
|
|
258
|
+
isLoaded: false,
|
|
259
|
+
error
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
this._lastSurveyRefreshFailedAt = null;
|
|
264
|
+
const surveys = response.json.surveys || [];
|
|
265
|
+
client.kv.set({
|
|
266
|
+
[SURVEYS]: surveys,
|
|
267
|
+
[SURVEYS_LOADED_AT]: Date.now()
|
|
268
|
+
});
|
|
269
|
+
this._registerEventOrActionBasedSurveys(surveys);
|
|
270
|
+
return {
|
|
271
|
+
surveys,
|
|
272
|
+
context: {
|
|
273
|
+
isLoaded: true
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
_registerEventOrActionBasedSurveys(surveys) {
|
|
278
|
+
const eventOrActionBasedSurveys = surveys.filter((survey)=>isSurveyRunning(survey) && (doesSurveyActivateByEvent(survey) || doesSurveyActivateByAction(survey)));
|
|
279
|
+
this._surveyEventReceiver?.replace(eventOrActionBasedSurveys);
|
|
280
|
+
}
|
|
281
|
+
_shouldBackgroundRefreshSurveys() {
|
|
282
|
+
return this._isSurveyCacheStale() && !this._getSurveysInFlightPromise && !this._isSurveyRefreshBackingOff();
|
|
283
|
+
}
|
|
284
|
+
_isSurveyCacheStale() {
|
|
285
|
+
const surveysLoadedAt = this._client?.kv.get(SURVEYS_LOADED_AT);
|
|
286
|
+
return isNumber(surveysLoadedAt) && Date.now() - surveysLoadedAt > SURVEYS_CACHE_TTL_MS;
|
|
287
|
+
}
|
|
288
|
+
_isSurveyRefreshBackingOff() {
|
|
289
|
+
return isNumber(this._lastSurveyRefreshFailedAt) && Date.now() - this._lastSurveyRefreshFailedAt < SURVEYS_REFRESH_BACKOFF_MS;
|
|
290
|
+
}
|
|
291
|
+
markSurveyAsSeen(surveyId, options) {
|
|
292
|
+
const survey = {
|
|
293
|
+
id: surveyId,
|
|
294
|
+
current_iteration: options?.iteration ?? null
|
|
295
|
+
};
|
|
296
|
+
setSurveySeenOnLocalStorage(survey);
|
|
297
|
+
try {
|
|
298
|
+
localStorage.setItem('lastSeenSurveyDate', new Date().toISOString());
|
|
299
|
+
} catch {}
|
|
300
|
+
this._notifyActiveMatchingSurveyCallbacks();
|
|
301
|
+
}
|
|
302
|
+
_notifySurveyCallbacks(context) {
|
|
303
|
+
for (const callback of this._surveyCallbacks){
|
|
304
|
+
if (this._disposed) return;
|
|
305
|
+
try {
|
|
306
|
+
if (context.isLoaded) this.getSurveys(callback);
|
|
307
|
+
else callback([], context);
|
|
308
|
+
} catch (error) {
|
|
309
|
+
SURVEY_LOGGER.error('Error in survey callback', error);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
getActiveMatchingSurveys(callback, forceReload = false) {
|
|
314
|
+
if (isNullish(this._surveyManager)) return void SURVEY_LOGGER.warn('init was not called');
|
|
315
|
+
return this._surveyManager.getActiveMatchingSurveys(callback, forceReload);
|
|
316
|
+
}
|
|
317
|
+
onActiveMatchingSurveysChanged(callback) {
|
|
318
|
+
if (this._disposed) return ()=>{};
|
|
319
|
+
const subscription = {
|
|
320
|
+
callback,
|
|
321
|
+
active: true,
|
|
322
|
+
revision: 0
|
|
323
|
+
};
|
|
324
|
+
this._activeMatchingSurveyCallbacks.push(subscription);
|
|
325
|
+
this._startActiveMatchingSurveyConditions();
|
|
326
|
+
const cached = this._client?.kv.get(SURVEYS);
|
|
327
|
+
const unavailable = this._config.disableSurveys || !this._surveyManager && this._surveyInitializationFailed && !this._isInitializingSurveys || !cached && !isNullish(this._lastSurveyRefreshFailedAt) && !this._getSurveysInFlightPromise;
|
|
328
|
+
if (unavailable) this._notifyActiveMatchingSurveyCallback(subscription, {
|
|
329
|
+
isLoaded: false
|
|
330
|
+
});
|
|
331
|
+
else if (this._surveyManager) this._notifyActiveMatchingSurveyCallback(subscription);
|
|
332
|
+
return ()=>{
|
|
333
|
+
subscription.active = false;
|
|
334
|
+
this._activeMatchingSurveyCallbacks = this._activeMatchingSurveyCallbacks.filter((current)=>current !== subscription);
|
|
335
|
+
if (0 === this._activeMatchingSurveyCallbacks.length) {
|
|
336
|
+
this._activeMatchingSurveyConditionsUnsubscribe?.();
|
|
337
|
+
this._activeMatchingSurveyConditionsUnsubscribe = void 0;
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
_startActiveMatchingSurveyConditions() {
|
|
342
|
+
if (this._surveyManager && this._activeMatchingSurveyCallbacks.length > 0) this._activeMatchingSurveyConditionsUnsubscribe ??= this._configSource.onMatchingConditionsChanged?.(this._notifyActiveMatchingSurveyCallbacks);
|
|
343
|
+
}
|
|
344
|
+
_notifyActiveMatchingSurveyCallback(subscription, context) {
|
|
345
|
+
if (this._disposed || !subscription.active) return;
|
|
346
|
+
const revision = ++subscription.revision;
|
|
347
|
+
const deliver = (surveys)=>{
|
|
348
|
+
if (this._disposed || !subscription.active || revision !== subscription.revision) return;
|
|
349
|
+
try {
|
|
350
|
+
const resultContext = context ?? {
|
|
351
|
+
isLoaded: true
|
|
352
|
+
};
|
|
353
|
+
const result = JSON.stringify([
|
|
354
|
+
surveys,
|
|
355
|
+
resultContext
|
|
356
|
+
]);
|
|
357
|
+
if (result === subscription.lastResult) return;
|
|
358
|
+
subscription.lastResult = result;
|
|
359
|
+
subscription.callback(surveys, resultContext);
|
|
360
|
+
} catch (error) {
|
|
361
|
+
SURVEY_LOGGER.error('Error in active matching surveys callback', error);
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
try {
|
|
365
|
+
if (context && !context.isLoaded) deliver([]);
|
|
366
|
+
else this.getActiveMatchingSurveys(deliver);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
SURVEY_LOGGER.error('Error in active matching surveys callback', error);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
_getSurveyById(surveyId) {
|
|
372
|
+
let survey = null;
|
|
373
|
+
this.getSurveys((surveys)=>{
|
|
374
|
+
survey = surveys.find((x)=>x.id === surveyId) ?? null;
|
|
375
|
+
});
|
|
376
|
+
return survey;
|
|
377
|
+
}
|
|
378
|
+
_checkSurveyEligibility(surveyId) {
|
|
379
|
+
if (isNullish(this._surveyManager)) return {
|
|
380
|
+
eligible: false,
|
|
381
|
+
reason: SURVEY_NOT_LOADED
|
|
382
|
+
};
|
|
383
|
+
const survey = 'string' == typeof surveyId ? this._getSurveyById(surveyId) : surveyId;
|
|
384
|
+
if (!survey) return {
|
|
385
|
+
eligible: false,
|
|
386
|
+
reason: 'Survey not found'
|
|
387
|
+
};
|
|
388
|
+
return this._surveyManager.checkSurveyEligibility(survey);
|
|
389
|
+
}
|
|
390
|
+
_checkSurveyRenderability(surveyId) {
|
|
391
|
+
if (!this._client?.canCapture) return {
|
|
392
|
+
eligible: false,
|
|
393
|
+
reason: SURVEY_CAPTURING_DISABLED
|
|
394
|
+
};
|
|
395
|
+
if (isNullish(this._surveyManager)) return {
|
|
396
|
+
eligible: false,
|
|
397
|
+
reason: SURVEY_NOT_LOADED
|
|
398
|
+
};
|
|
399
|
+
const survey = 'string' == typeof surveyId ? this._getSurveyById(surveyId) : surveyId;
|
|
400
|
+
if (!survey) return {
|
|
401
|
+
eligible: false,
|
|
402
|
+
reason: 'Survey not found'
|
|
403
|
+
};
|
|
404
|
+
return this._surveyManager.checkSurveyRenderability(survey);
|
|
405
|
+
}
|
|
406
|
+
canRenderSurvey(surveyId) {
|
|
407
|
+
if (isNullish(this._surveyManager)) {
|
|
408
|
+
SURVEY_LOGGER.warn('init was not called');
|
|
409
|
+
return {
|
|
410
|
+
visible: false,
|
|
411
|
+
disabledReason: SURVEY_NOT_LOADED
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const eligibility = this._checkSurveyRenderability(surveyId);
|
|
415
|
+
return {
|
|
416
|
+
visible: eligibility.eligible,
|
|
417
|
+
disabledReason: eligibility.reason
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
canRenderSurveyAsync(surveyId, forceReload) {
|
|
421
|
+
if (isNullish(this._surveyManager)) {
|
|
422
|
+
SURVEY_LOGGER.warn('init was not called');
|
|
423
|
+
return Promise.resolve({
|
|
424
|
+
visible: false,
|
|
425
|
+
disabledReason: SURVEY_NOT_LOADED
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
return new Promise((resolve)=>{
|
|
429
|
+
this.getSurveys((surveys)=>{
|
|
430
|
+
const survey = surveys.find((x)=>x.id === surveyId) ?? null;
|
|
431
|
+
if (survey) {
|
|
432
|
+
const eligibility = this._checkSurveyRenderability(survey);
|
|
433
|
+
resolve({
|
|
434
|
+
visible: eligibility.eligible,
|
|
435
|
+
disabledReason: eligibility.reason
|
|
436
|
+
});
|
|
437
|
+
} else resolve({
|
|
438
|
+
visible: false,
|
|
439
|
+
disabledReason: 'Survey not found'
|
|
440
|
+
});
|
|
441
|
+
}, forceReload);
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
renderSurvey(surveyId, selector, properties) {
|
|
445
|
+
if (!this._client?.canCapture) return;
|
|
446
|
+
if (isNullish(this._surveyManager)) return void SURVEY_LOGGER.warn('init was not called');
|
|
447
|
+
const survey = 'string' == typeof surveyId ? this._getSurveyById(surveyId) : surveyId;
|
|
448
|
+
if (!survey?.id) return void SURVEY_LOGGER.warn('Survey not found');
|
|
449
|
+
if (!IN_APP_SURVEY_TYPES.includes(survey.type)) return void SURVEY_LOGGER.warn(`Surveys of type ${survey.type} cannot be rendered in the app`);
|
|
450
|
+
const elem = ("u" < typeof document ? void 0 : document)?.querySelector(selector);
|
|
451
|
+
if (!elem) return void SURVEY_LOGGER.warn('Survey element not found');
|
|
452
|
+
if (survey.appearance?.surveyPopupDelaySeconds) {
|
|
453
|
+
SURVEY_LOGGER.info(`Rendering survey ${survey.id} with delay of ${survey.appearance.surveyPopupDelaySeconds} seconds`);
|
|
454
|
+
const timeout = setTimeout(()=>{
|
|
455
|
+
this._renderTimeouts.delete(timeout);
|
|
456
|
+
if (this._disposed || !this._client?.canCapture) return;
|
|
457
|
+
SURVEY_LOGGER.info(`Rendering survey ${survey.id} with delay of ${survey.appearance?.surveyPopupDelaySeconds} seconds`);
|
|
458
|
+
this._surveyManager?.renderSurvey(survey, elem, properties);
|
|
459
|
+
SURVEY_LOGGER.info(`Survey ${survey.id} rendered`);
|
|
460
|
+
}, 1000 * survey.appearance.surveyPopupDelaySeconds);
|
|
461
|
+
this._renderTimeouts.add(timeout);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
this._surveyManager.renderSurvey(survey, elem, properties);
|
|
465
|
+
}
|
|
466
|
+
displaySurvey(surveyId, options) {
|
|
467
|
+
if (!this._client?.canCapture) return;
|
|
468
|
+
if (isNullish(this._surveyManager)) return void SURVEY_LOGGER.warn('init was not called');
|
|
469
|
+
const survey = this._getSurveyById(surveyId);
|
|
470
|
+
if (!survey) return void SURVEY_LOGGER.warn('Survey not found');
|
|
471
|
+
let surveyToDisplay = survey;
|
|
472
|
+
if (survey.appearance?.surveyPopupDelaySeconds && options.ignoreDelay) surveyToDisplay = {
|
|
473
|
+
...survey,
|
|
474
|
+
appearance: {
|
|
475
|
+
...survey.appearance,
|
|
476
|
+
surveyPopupDelaySeconds: 0
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
if (options.displayType !== DisplaySurveyType.Popover && options.initialResponses) SURVEY_LOGGER.warn('initialResponses is only supported for popover surveys. prefill will not be applied.');
|
|
480
|
+
if (false === options.ignoreConditions) {
|
|
481
|
+
const eligibility = this._checkSurveyEligibility(survey);
|
|
482
|
+
if (!eligibility.eligible) return void SURVEY_LOGGER.warn('Survey is not eligible to be displayed: ', eligibility.reason);
|
|
483
|
+
}
|
|
484
|
+
if (options.displayType === DisplaySurveyType.Inline) return void this.renderSurvey(surveyToDisplay, options.selector, options.properties);
|
|
485
|
+
this._surveyManager.handlePopoverSurvey(surveyToDisplay, options);
|
|
486
|
+
}
|
|
487
|
+
cancelPendingSurvey(surveyId) {
|
|
488
|
+
if (isNullish(this._surveyManager)) return void SURVEY_LOGGER.warn('init was not called');
|
|
489
|
+
this._surveyManager.cancelSurvey(surveyId);
|
|
490
|
+
}
|
|
491
|
+
handlePageUnload() {
|
|
492
|
+
this._surveyManager?.handlePageUnload?.();
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
export { DisplaySurveyType, PostHogSurveys, SurveyEventName, SurveyEventProperties, SurveyEventType, SurveyPosition, SurveyQuestionBranchingType, SurveyQuestionType, SurveySchedule, SurveyTabPosition, SurveyType, SurveyWidgetType };
|
package/dist/types/surveys.d.ts
CHANGED
|
@@ -1,91 +1,9 @@
|
|
|
1
|
+
import type { SurveyWidgetType, SurveyPosition, SurveyTabPosition, SurveyType, SurveyQuestionType, SurveyQuestionBranchingType, SurveySchedule, DisplaySurveyType } from '../survey-constants';
|
|
2
|
+
export type { SurveyEventType, SurveyWidgetType, SurveyPosition, SurveyTabPosition, SurveyType, SurveyQuestionType, SurveyQuestionBranchingType, SurveySchedule, SurveyEventName, SurveyEventProperties, DisplaySurveyType, } from '../survey-constants';
|
|
3
|
+
import type { Properties } from '@posthog/types';
|
|
4
|
+
import type { SurveyResponseValue as CoreSurveyResponseValue } from '@posthog/core';
|
|
1
5
|
import type { PropertyMatchType, SurveyAppearance as CoreSurveyAppearance, SurveyEventWithFilters, SurveyQuestionTranslation, SurveyTranslation, SurveyValidationRule } from '@posthog/core';
|
|
2
6
|
export type { PropertyFilters, PropertyMatchType, PropertyOperator, SurveyEventWithFilters } from '@posthog/core';
|
|
3
|
-
export declare const SurveyEventType: {
|
|
4
|
-
readonly Activation: 'events';
|
|
5
|
-
readonly Cancellation: 'cancelEvents';
|
|
6
|
-
};
|
|
7
|
-
export type SurveyEventType = (typeof SurveyEventType)[keyof typeof SurveyEventType];
|
|
8
|
-
export declare const SurveyWidgetType: {
|
|
9
|
-
readonly Button: 'button';
|
|
10
|
-
readonly Tab: 'tab';
|
|
11
|
-
readonly Selector: 'selector';
|
|
12
|
-
};
|
|
13
|
-
export type SurveyWidgetType = (typeof SurveyWidgetType)[keyof typeof SurveyWidgetType];
|
|
14
|
-
export declare const SurveyPosition: {
|
|
15
|
-
readonly TopLeft: 'top_left';
|
|
16
|
-
readonly TopRight: 'top_right';
|
|
17
|
-
readonly TopCenter: 'top_center';
|
|
18
|
-
readonly MiddleLeft: 'middle_left';
|
|
19
|
-
readonly MiddleRight: 'middle_right';
|
|
20
|
-
readonly MiddleCenter: 'middle_center';
|
|
21
|
-
readonly Left: 'left';
|
|
22
|
-
readonly Center: 'center';
|
|
23
|
-
readonly Right: 'right';
|
|
24
|
-
readonly NextToTrigger: 'next_to_trigger';
|
|
25
|
-
};
|
|
26
|
-
export type SurveyPosition = (typeof SurveyPosition)[keyof typeof SurveyPosition];
|
|
27
|
-
export declare const SurveyTabPosition: {
|
|
28
|
-
readonly Top: 'top';
|
|
29
|
-
readonly Left: 'left';
|
|
30
|
-
readonly Right: 'right';
|
|
31
|
-
readonly Bottom: 'bottom';
|
|
32
|
-
};
|
|
33
|
-
export type SurveyTabPosition = (typeof SurveyTabPosition)[keyof typeof SurveyTabPosition];
|
|
34
|
-
export declare const SurveyType: {
|
|
35
|
-
readonly Popover: 'popover';
|
|
36
|
-
readonly API: 'api';
|
|
37
|
-
readonly Widget: 'widget';
|
|
38
|
-
readonly ExternalSurvey: 'external_survey';
|
|
39
|
-
};
|
|
40
|
-
export type SurveyType = (typeof SurveyType)[keyof typeof SurveyType];
|
|
41
|
-
export declare const SurveyQuestionType: {
|
|
42
|
-
readonly Open: 'open';
|
|
43
|
-
readonly MultipleChoice: 'multiple_choice';
|
|
44
|
-
readonly SingleChoice: 'single_choice';
|
|
45
|
-
readonly Rating: 'rating';
|
|
46
|
-
readonly Link: 'link';
|
|
47
|
-
};
|
|
48
|
-
export type SurveyQuestionType = (typeof SurveyQuestionType)[keyof typeof SurveyQuestionType];
|
|
49
|
-
export declare const SurveyQuestionBranchingType: {
|
|
50
|
-
readonly NextQuestion: 'next_question';
|
|
51
|
-
readonly End: 'end';
|
|
52
|
-
readonly ResponseBased: 'response_based';
|
|
53
|
-
readonly SpecificQuestion: 'specific_question';
|
|
54
|
-
};
|
|
55
|
-
export type SurveyQuestionBranchingType = (typeof SurveyQuestionBranchingType)[keyof typeof SurveyQuestionBranchingType];
|
|
56
|
-
export declare const SurveySchedule: {
|
|
57
|
-
readonly Once: 'once';
|
|
58
|
-
readonly Recurring: 'recurring';
|
|
59
|
-
readonly Always: 'always';
|
|
60
|
-
};
|
|
61
|
-
export type SurveySchedule = (typeof SurveySchedule)[keyof typeof SurveySchedule];
|
|
62
|
-
export declare const SurveyEventName: {
|
|
63
|
-
readonly SHOWN: 'survey shown';
|
|
64
|
-
readonly DISMISSED: 'survey dismissed';
|
|
65
|
-
readonly SENT: 'survey sent';
|
|
66
|
-
readonly ABANDONED: 'survey abandoned';
|
|
67
|
-
};
|
|
68
|
-
export type SurveyEventName = (typeof SurveyEventName)[keyof typeof SurveyEventName];
|
|
69
|
-
export declare const SurveyEventProperties: {
|
|
70
|
-
readonly SURVEY_ID: '$survey_id';
|
|
71
|
-
readonly SURVEY_NAME: '$survey_name';
|
|
72
|
-
readonly SURVEY_RESPONSE: '$survey_response';
|
|
73
|
-
readonly SURVEY_ITERATION: '$survey_iteration';
|
|
74
|
-
readonly SURVEY_ITERATION_START_DATE: '$survey_iteration_start_date';
|
|
75
|
-
readonly SURVEY_PARTIALLY_COMPLETED: '$survey_partially_completed';
|
|
76
|
-
readonly SURVEY_SUBMISSION_ID: '$survey_submission_id';
|
|
77
|
-
readonly SURVEY_QUESTIONS: '$survey_questions';
|
|
78
|
-
readonly SURVEY_COMPLETED: '$survey_completed';
|
|
79
|
-
readonly PRODUCT_TOUR_ID: '$product_tour_id';
|
|
80
|
-
readonly SURVEY_LAST_SEEN_DATE: '$survey_last_seen_date';
|
|
81
|
-
readonly SURVEY_LANGUAGE: '$survey_language';
|
|
82
|
-
};
|
|
83
|
-
export type SurveyEventProperties = (typeof SurveyEventProperties)[keyof typeof SurveyEventProperties];
|
|
84
|
-
export declare const DisplaySurveyType: {
|
|
85
|
-
readonly Popover: 'popover';
|
|
86
|
-
readonly Inline: 'inline';
|
|
87
|
-
};
|
|
88
|
-
export type DisplaySurveyType = (typeof DisplaySurveyType)[keyof typeof DisplaySurveyType];
|
|
89
7
|
export type SurveyQuestionDescriptionContentType = 'html' | 'text';
|
|
90
8
|
/** Browser survey appearance, including browser-only placement and rendering options. */
|
|
91
9
|
export interface SurveyAppearance extends Omit<CoreSurveyAppearance, 'position' | 'widgetType'> {
|
|
@@ -228,3 +146,82 @@ export interface ActionStepType {
|
|
|
228
146
|
type?: string;
|
|
229
147
|
}[];
|
|
230
148
|
}
|
|
149
|
+
export type SurveyCallback = (surveys: Survey[], context?: {
|
|
150
|
+
isLoaded: boolean;
|
|
151
|
+
error?: string;
|
|
152
|
+
}) => void;
|
|
153
|
+
export interface SurveyElement {
|
|
154
|
+
text?: string;
|
|
155
|
+
$el_text?: string;
|
|
156
|
+
tag_name?: string;
|
|
157
|
+
href?: string;
|
|
158
|
+
attr_id?: string;
|
|
159
|
+
attr_class?: string[];
|
|
160
|
+
nth_child?: number;
|
|
161
|
+
nth_of_type?: number;
|
|
162
|
+
attributes?: Record<string, any>;
|
|
163
|
+
event_id?: number;
|
|
164
|
+
order?: number;
|
|
165
|
+
group_id?: number;
|
|
166
|
+
}
|
|
167
|
+
export type { SurveyRenderReason } from '@posthog/types';
|
|
168
|
+
interface DisplaySurveyOptionsBase {
|
|
169
|
+
/**
|
|
170
|
+
* Whether to bypass the survey's targeting and display conditions.
|
|
171
|
+
* @default false
|
|
172
|
+
*/
|
|
173
|
+
ignoreConditions: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* Whether to bypass the survey's configured popup delay.
|
|
176
|
+
* @default false
|
|
177
|
+
*/
|
|
178
|
+
ignoreDelay: boolean;
|
|
179
|
+
/**
|
|
180
|
+
* How the survey should be displayed.
|
|
181
|
+
* @default DisplaySurveyType.Popover
|
|
182
|
+
*/
|
|
183
|
+
displayType: DisplaySurveyType;
|
|
184
|
+
/** Additional properties to include in all survey events (shown, sent, dismissed). */
|
|
185
|
+
properties?: Properties;
|
|
186
|
+
/** Pre-filled responses by question index (0-based). Only supported for popover surveys. */
|
|
187
|
+
initialResponses?: Record<number, SurveyResponseValue>;
|
|
188
|
+
}
|
|
189
|
+
/** Options for displaying a survey as a popover. */
|
|
190
|
+
export interface DisplaySurveyPopoverOptions extends DisplaySurveyOptionsBase {
|
|
191
|
+
displayType: typeof DisplaySurveyType.Popover;
|
|
192
|
+
/** Override the survey's configured position. */
|
|
193
|
+
position?: SurveyPosition;
|
|
194
|
+
/** CSS selector for the element to position the survey next to (when position is NextToTrigger). */
|
|
195
|
+
selector?: string;
|
|
196
|
+
/** When true, `survey shown` events will not be emitted automatically. */
|
|
197
|
+
skipShownEvent?: boolean;
|
|
198
|
+
}
|
|
199
|
+
interface DisplaySurveyInlineOptions extends DisplaySurveyOptionsBase {
|
|
200
|
+
displayType: typeof DisplaySurveyType.Inline;
|
|
201
|
+
/** CSS selector for the element where the inline survey should render. */
|
|
202
|
+
selector: string;
|
|
203
|
+
}
|
|
204
|
+
/** Options for `posthog.displaySurvey()`. */
|
|
205
|
+
export type DisplaySurveyOptions = DisplaySurveyPopoverOptions | DisplaySurveyInlineOptions;
|
|
206
|
+
export interface SurveyConfig {
|
|
207
|
+
/**
|
|
208
|
+
* Prefill survey responses from matching URL parameters.
|
|
209
|
+
*
|
|
210
|
+
* @default undefined
|
|
211
|
+
*/
|
|
212
|
+
prefillFromUrl?: boolean;
|
|
213
|
+
/**
|
|
214
|
+
* @deprecated No longer used. Surveys will automatically advance past
|
|
215
|
+
* prefilled questions with skipSubmitButton enabled. If partial response
|
|
216
|
+
* collection is enabled, partial responses for pre-filled questions will
|
|
217
|
+
* be submitted automatically on page load.
|
|
218
|
+
*/
|
|
219
|
+
autoSubmitIfComplete?: boolean;
|
|
220
|
+
/**
|
|
221
|
+
* @deprecated No longer used. Pre-filled responses are now sent
|
|
222
|
+
* immediately when partial responses are enabled, or all required
|
|
223
|
+
* questions have been pre-filled.
|
|
224
|
+
*/
|
|
225
|
+
autoSubmitDelay?: number;
|
|
226
|
+
}
|
|
227
|
+
export type SurveyResponseValue = CoreSurveyResponseValue;
|