Skip to main content
Glama
posthog-surveys.js15.5 kB
import { SURVEYS } from './constants'; import { getSurveySeenStorageKeys } from './extensions/surveys/surveys-utils'; import { Info } from './utils/event-utils'; import { assignableWindow, document, userAgent, window } from './utils/globals'; import { createLogger } from './utils/logger'; import { isMatchingRegex } from './utils/regex-utils'; import { SurveyEventReceiver } from './utils/survey-event-receiver'; import { isNullish } from './utils/type-utils'; var logger = createLogger('[Surveys]'); export var surveyValidationMap = { icontains: function (targets, value) { return targets.some(function (target) { return value.toLowerCase().includes(target.toLowerCase()); }); }, not_icontains: function (targets, value) { return targets.every(function (target) { return !value.toLowerCase().includes(target.toLowerCase()); }); }, regex: function (targets, value) { return targets.some(function (target) { return isMatchingRegex(value, target); }); }, not_regex: function (targets, value) { return targets.every(function (target) { return !isMatchingRegex(value, target); }); }, exact: function (targets, value) { return targets.some(function (target) { return value === target; }); }, is_not: function (targets, value) { return targets.every(function (target) { return value !== target; }); }, }; function defaultMatchType(matchType) { return matchType !== null && matchType !== void 0 ? matchType : 'icontains'; } // use urlMatchType to validate url condition, fallback to contains for backwards compatibility export function doesSurveyUrlMatch(survey) { var _a, _b, _c; if (!((_a = survey.conditions) === null || _a === void 0 ? void 0 : _a.url)) { return true; } // if we dont know the url, assume it is not a match var href = (_b = window === null || window === void 0 ? void 0 : window.location) === null || _b === void 0 ? void 0 : _b.href; if (!href) { return false; } var targets = [survey.conditions.url]; return surveyValidationMap[defaultMatchType((_c = survey.conditions) === null || _c === void 0 ? void 0 : _c.urlMatchType)](targets, href); } export function doesSurveyDeviceTypesMatch(survey) { var _a, _b, _c; if (!((_a = survey.conditions) === null || _a === void 0 ? void 0 : _a.deviceTypes) || ((_b = survey.conditions) === null || _b === void 0 ? void 0 : _b.deviceTypes.length) === 0) { return true; } // if we dont know the device type, assume it is not a match if (!userAgent) { return false; } var deviceType = Info.deviceType(userAgent); return surveyValidationMap[defaultMatchType((_c = survey.conditions) === null || _c === void 0 ? void 0 : _c.deviceTypesMatchType)](survey.conditions.deviceTypes, deviceType); } var PostHogSurveys = /** @class */ (function () { function PostHogSurveys(instance) { this.instance = instance; this._isFetchingSurveys = false; this._isInitializingSurveys = false; // we set this to undefined here because we need the persistence storage for this type // but that's not initialized until loadIfEnabled is called. this._surveyEventReceiver = null; } PostHogSurveys.prototype.onRemoteConfig = function (response) { this._decideServerResponse = !!response['surveys']; logger.info("decideServerResponse set to ".concat(this._decideServerResponse)); this.loadIfEnabled(); }; PostHogSurveys.prototype.reset = function () { localStorage.removeItem('lastSeenSurveyDate'); var surveyKeys = getSurveySeenStorageKeys(); surveyKeys.forEach(function (key) { return localStorage.removeItem(key); }); }; PostHogSurveys.prototype.loadIfEnabled = function () { var _this = this; if (this._surveyManager) { // Surveys already loaded. return; } if (this._isInitializingSurveys) { logger.info('Already initializing surveys, skipping...'); return; } var disableSurveys = this.instance.config.disable_surveys; if (disableSurveys) { logger.info('Disabled. Not loading surveys.'); return; } var phExtensions = assignableWindow === null || assignableWindow === void 0 ? void 0 : assignableWindow.__PosthogExtensions__; if (!phExtensions) { logger.error('PostHog Extensions not found.'); return; } if (!this._decideServerResponse) { logger.warn('Decide not loaded yet. Not loading surveys.'); return; } this._isInitializingSurveys = true; try { var generateSurveys = phExtensions.generateSurveys; if (!generateSurveys) { var loadExternalDependency = phExtensions.loadExternalDependency; if (loadExternalDependency) { loadExternalDependency(this.instance, 'surveys', function (err) { if (err || !phExtensions.generateSurveys) { logger.error('Could not load surveys script', err); _this._isInitializingSurveys = false; return; } _this._surveyManager = phExtensions.generateSurveys(_this.instance); _this._isInitializingSurveys = false; _this._surveyEventReceiver = new SurveyEventReceiver(_this.instance); logger.info('Surveys loaded successfully'); }); } else { logger.error('PostHog loadExternalDependency extension not found. Cannot load remote config.'); this._isInitializingSurveys = false; } } else { this._surveyManager = generateSurveys(this.instance); this._isInitializingSurveys = false; this._surveyEventReceiver = new SurveyEventReceiver(this.instance); logger.info('Surveys loaded successfully'); } } catch (e) { logger.error('Error initializing surveys', e); this._isInitializingSurveys = false; throw e; } }; PostHogSurveys.prototype.getSurveys = function (callback, forceReload) { var _this = this; if (forceReload === void 0) { forceReload = false; } // In case we manage to load the surveys script, but config says not to load surveys // then we shouldn't return survey data if (this.instance.config.disable_surveys) { logger.info('Disabled. Not loading surveys.'); return callback([]); } var existingSurveys = this.instance.get_property(SURVEYS); if (!existingSurveys || forceReload) { // Prevent concurrent API calls if (this._isFetchingSurveys) { return callback([]); } try { this._isFetchingSurveys = true; this.instance._send_request({ url: this.instance.requestRouter.endpointFor('api', "/api/surveys/?token=".concat(this.instance.config.token)), method: 'GET', timeout: this.instance.config.surveys_request_timeout_ms, callback: function (response) { var _a; var _b, _c; _this._isFetchingSurveys = false; var statusCode = response.statusCode; if (statusCode !== 200 || !response.json) { logger.error("Surveys API could not be loaded, status: ".concat(statusCode)); return callback([]); } var surveys = response.json.surveys || []; var eventOrActionBasedSurveys = surveys.filter(function (survey) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m; return (((_a = survey.conditions) === null || _a === void 0 ? void 0 : _a.events) && ((_c = (_b = survey.conditions) === null || _b === void 0 ? void 0 : _b.events) === null || _c === void 0 ? void 0 : _c.values) && ((_f = (_e = (_d = survey.conditions) === null || _d === void 0 ? void 0 : _d.events) === null || _e === void 0 ? void 0 : _e.values) === null || _f === void 0 ? void 0 : _f.length) > 0) || (((_g = survey.conditions) === null || _g === void 0 ? void 0 : _g.actions) && ((_j = (_h = survey.conditions) === null || _h === void 0 ? void 0 : _h.actions) === null || _j === void 0 ? void 0 : _j.values) && ((_m = (_l = (_k = survey.conditions) === null || _k === void 0 ? void 0 : _k.actions) === null || _l === void 0 ? void 0 : _l.values) === null || _m === void 0 ? void 0 : _m.length) > 0); }); if (eventOrActionBasedSurveys.length > 0) { (_b = _this._surveyEventReceiver) === null || _b === void 0 ? void 0 : _b.register(eventOrActionBasedSurveys); } (_c = _this.instance.persistence) === null || _c === void 0 ? void 0 : _c.register((_a = {}, _a[SURVEYS] = surveys, _a)); return callback(surveys); }, }); } catch (e) { this._isFetchingSurveys = false; throw e; } } else { return callback(existingSurveys); } }; PostHogSurveys.prototype.isSurveyFeatureFlagEnabled = function (flagKey) { if (!flagKey) { return true; } return this.instance.featureFlags.isFeatureEnabled(flagKey); }; PostHogSurveys.prototype.getActiveMatchingSurveys = function (callback, forceReload) { var _this = this; if (forceReload === void 0) { forceReload = false; } this.getSurveys(function (surveys) { var _a; var activeSurveys = surveys.filter(function (survey) { return !!(survey.start_date && !survey.end_date); }); var conditionMatchedSurveys = activeSurveys.filter(function (survey) { var _a; if (!survey.conditions) { return true; } var urlCheck = doesSurveyUrlMatch(survey); var selectorCheck = ((_a = survey.conditions) === null || _a === void 0 ? void 0 : _a.selector) ? document === null || document === void 0 ? void 0 : document.querySelector(survey.conditions.selector) : true; var deviceTypeCheck = doesSurveyDeviceTypesMatch(survey); return urlCheck && selectorCheck && deviceTypeCheck; }); // get all the surveys that have been activated so far with user actions. var activatedSurveys = (_a = _this._surveyEventReceiver) === null || _a === void 0 ? void 0 : _a.getSurveys(); var targetingMatchedSurveys = conditionMatchedSurveys.filter(function (survey) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; if (!survey.linked_flag_key && !survey.targeting_flag_key && !survey.internal_targeting_flag_key && !((_a = survey.feature_flag_keys) === null || _a === void 0 ? void 0 : _a.length)) { return true; } var linkedFlagCheck = _this.isSurveyFeatureFlagEnabled(survey.linked_flag_key); var targetingFlagCheck = _this.isSurveyFeatureFlagEnabled(survey.targeting_flag_key); var hasEvents = ((_e = (_d = (_c = (_b = survey.conditions) === null || _b === void 0 ? void 0 : _b.events) === null || _c === void 0 ? void 0 : _c.values) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0) > 0; var hasActions = ((_j = (_h = (_g = (_f = survey.conditions) === null || _f === void 0 ? void 0 : _f.actions) === null || _g === void 0 ? void 0 : _g.values) === null || _h === void 0 ? void 0 : _h.length) !== null && _j !== void 0 ? _j : 0) > 0; var eventBasedTargetingFlagCheck = hasEvents || hasActions ? activatedSurveys === null || activatedSurveys === void 0 ? void 0 : activatedSurveys.includes(survey.id) : true; var overrideInternalTargetingFlagCheck = _this._canActivateRepeatedly(survey); var internalTargetingFlagCheck = overrideInternalTargetingFlagCheck || _this.isSurveyFeatureFlagEnabled(survey.internal_targeting_flag_key); var flagsCheck = _this.checkFlags(survey); return (linkedFlagCheck && targetingFlagCheck && internalTargetingFlagCheck && eventBasedTargetingFlagCheck && flagsCheck); }); return callback(targetingMatchedSurveys); }, forceReload); }; PostHogSurveys.prototype.checkFlags = function (survey) { var _this = this; var _a; if (!((_a = survey.feature_flag_keys) === null || _a === void 0 ? void 0 : _a.length)) { return true; } return survey.feature_flag_keys.every(function (_a) { var key = _a.key, value = _a.value; if (!key || !value) { return true; } return _this.instance.featureFlags.isFeatureEnabled(value); }); }; // this method is lazily loaded onto the window to avoid loading preact and other dependencies if surveys is not enabled PostHogSurveys.prototype._canActivateRepeatedly = function (survey) { var _a; if (isNullish((_a = assignableWindow.__PosthogExtensions__) === null || _a === void 0 ? void 0 : _a.canActivateRepeatedly)) { logger.warn('init was not called'); return false; // TODO does it make sense to have a default here? } return assignableWindow.__PosthogExtensions__.canActivateRepeatedly(survey); }; PostHogSurveys.prototype.canRenderSurvey = function (surveyId) { var _this = this; if (isNullish(this._surveyManager)) { logger.warn('init was not called'); return; } this.getSurveys(function (surveys) { var survey = surveys.filter(function (x) { return x.id === surveyId; })[0]; _this._surveyManager.canRenderSurvey(survey); }); }; PostHogSurveys.prototype.renderSurvey = function (surveyId, selector) { var _this = this; if (isNullish(this._surveyManager)) { logger.warn('init was not called'); return; } this.getSurveys(function (surveys) { var survey = surveys.filter(function (x) { return x.id === surveyId; })[0]; _this._surveyManager.renderSurvey(survey, document === null || document === void 0 ? void 0 : document.querySelector(selector)); }); }; return PostHogSurveys; }()); export { PostHogSurveys }; //# sourceMappingURL=posthog-surveys.js.map

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sadiuysal/mem0-mcp-server-ts'

If you have feedback or need assistance with the MCP directory API, please join our Discord server