Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@posthog/core

Package Overview
Dependencies
Maintainers
17
Versions
94
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@posthog/core - npm Package Compare versions

Comparing version
1.27.9
to
1.28.0
+22
dist/surveys/events.d.ts
import { SurveyResponses, SurveyResponseValue } from '../types';
export declare const SURVEY_LANGUAGE_PROPERTY = "$survey_language";
export declare function getSurveyResponseKey(questionId: string): string;
export declare function getSurveyOldResponseKey(originalQuestionIndex: number): string;
export declare function getSurveyResponseValue(responses: SurveyResponses, questionId?: string): SurveyResponseValue | undefined;
export declare function buildSurveyResponseProperties(responses: SurveyResponses | undefined, survey: SurveyForResponses): Record<string, unknown>;
export declare function surveyHasResponses(responses?: SurveyResponses): boolean;
export declare function getSurveyInteractionProperty(survey: SurveyWithIteration, action: string): string;
type SurveyQuestionForResponses = {
id?: string;
question: string;
originalQuestionIndex?: number;
};
type SurveyForResponses = {
questions: SurveyQuestionForResponses[];
};
type SurveyWithIteration = {
id: string;
current_iteration?: number | null;
};
export {};
//# sourceMappingURL=events.d.ts.map
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/surveys/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAG/D,eAAO,MAAM,wBAAwB,qBAAqB,CAAA;AAE1D,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,uBAAuB,CAAC,qBAAqB,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,eAAe,EAC1B,UAAU,CAAC,EAAE,MAAM,GAClB,mBAAmB,GAAG,SAAS,CASjC;AAED,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,eAAe,YAAK,EAC/B,MAAM,EAAE,kBAAkB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsBzB;AAED,wBAAgB,kBAAkB,CAAC,SAAS,GAAE,eAAoB,GAAG,OAAO,CAE3E;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAOhG;AACD,KAAK,0BAA0B,GAAG;IAChC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,qBAAqB,CAAC,EAAE,MAAM,CAAA;CAC/B,CAAA;AAED,KAAK,kBAAkB,GAAG;IACxB,SAAS,EAAE,0BAA0B,EAAE,CAAA;CACxC,CAAA;AAED,KAAK,mBAAmB,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC,CAAA"}
"use strict";
var __webpack_require__ = {};
(()=>{
__webpack_require__.d = (exports1, definition)=>{
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
enumerable: true,
get: definition[key]
});
};
})();
(()=>{
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
})();
(()=>{
__webpack_require__.r = (exports1)=>{
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
value: 'Module'
});
Object.defineProperty(exports1, '__esModule', {
value: true
});
};
})();
var __webpack_exports__ = {};
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
getSurveyInteractionProperty: ()=>getSurveyInteractionProperty,
buildSurveyResponseProperties: ()=>buildSurveyResponseProperties,
SURVEY_LANGUAGE_PROPERTY: ()=>SURVEY_LANGUAGE_PROPERTY,
getSurveyOldResponseKey: ()=>getSurveyOldResponseKey,
getSurveyResponseKey: ()=>getSurveyResponseKey,
getSurveyResponseValue: ()=>getSurveyResponseValue,
surveyHasResponses: ()=>surveyHasResponses
});
const index_js_namespaceObject = require("../utils/index.js");
const SURVEY_LANGUAGE_PROPERTY = '$survey_language';
function getSurveyResponseKey(questionId) {
return `$survey_response_${questionId}`;
}
function getSurveyOldResponseKey(originalQuestionIndex) {
return 0 === originalQuestionIndex ? '$survey_response' : `$survey_response_${originalQuestionIndex}`;
}
function getSurveyResponseValue(responses, questionId) {
if (!questionId) return null;
const response = responses[getSurveyResponseKey(questionId)];
if ((0, index_js_namespaceObject.isArray)(response)) return [
...response
];
return response;
}
function buildSurveyResponseProperties(responses = {}, survey) {
const oldFormatResponses = {};
survey.questions.forEach((question)=>{
if ((0, index_js_namespaceObject.isUndefined)(question.originalQuestionIndex)) return;
const oldResponseKey = getSurveyOldResponseKey(question.originalQuestionIndex);
const response = getSurveyResponseValue(responses, question.id);
if (!(0, index_js_namespaceObject.isUndefined)(response)) oldFormatResponses[oldResponseKey] = response;
});
return {
$survey_questions: survey.questions.map((question)=>({
id: question.id,
question: question.question,
response: getSurveyResponseValue(responses, question.id)
})),
...responses,
...oldFormatResponses
};
}
function surveyHasResponses(responses = {}) {
return Object.values(responses).some((response)=>!(0, index_js_namespaceObject.isNullish)(response));
}
function getSurveyInteractionProperty(survey, action) {
let surveyProperty = `$survey_${action}/${survey.id}`;
if (survey.current_iteration && survey.current_iteration > 0) surveyProperty = `$survey_${action}/${survey.id}/${survey.current_iteration}`;
return surveyProperty;
}
exports.SURVEY_LANGUAGE_PROPERTY = __webpack_exports__.SURVEY_LANGUAGE_PROPERTY;
exports.buildSurveyResponseProperties = __webpack_exports__.buildSurveyResponseProperties;
exports.getSurveyInteractionProperty = __webpack_exports__.getSurveyInteractionProperty;
exports.getSurveyOldResponseKey = __webpack_exports__.getSurveyOldResponseKey;
exports.getSurveyResponseKey = __webpack_exports__.getSurveyResponseKey;
exports.getSurveyResponseValue = __webpack_exports__.getSurveyResponseValue;
exports.surveyHasResponses = __webpack_exports__.surveyHasResponses;
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
"SURVEY_LANGUAGE_PROPERTY",
"buildSurveyResponseProperties",
"getSurveyInteractionProperty",
"getSurveyOldResponseKey",
"getSurveyResponseKey",
"getSurveyResponseValue",
"surveyHasResponses"
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
Object.defineProperty(exports, '__esModule', {
value: true
});
import { isArray, isNullish, isUndefined } from "../utils/index.mjs";
const SURVEY_LANGUAGE_PROPERTY = '$survey_language';
function getSurveyResponseKey(questionId) {
return `$survey_response_${questionId}`;
}
function getSurveyOldResponseKey(originalQuestionIndex) {
return 0 === originalQuestionIndex ? '$survey_response' : `$survey_response_${originalQuestionIndex}`;
}
function getSurveyResponseValue(responses, questionId) {
if (!questionId) return null;
const response = responses[getSurveyResponseKey(questionId)];
if (isArray(response)) return [
...response
];
return response;
}
function buildSurveyResponseProperties(responses = {}, survey) {
const oldFormatResponses = {};
survey.questions.forEach((question)=>{
if (isUndefined(question.originalQuestionIndex)) return;
const oldResponseKey = getSurveyOldResponseKey(question.originalQuestionIndex);
const response = getSurveyResponseValue(responses, question.id);
if (!isUndefined(response)) oldFormatResponses[oldResponseKey] = response;
});
return {
$survey_questions: survey.questions.map((question)=>({
id: question.id,
question: question.question,
response: getSurveyResponseValue(responses, question.id)
})),
...responses,
...oldFormatResponses
};
}
function surveyHasResponses(responses = {}) {
return Object.values(responses).some((response)=>!isNullish(response));
}
function getSurveyInteractionProperty(survey, action) {
let surveyProperty = `$survey_${action}/${survey.id}`;
if (survey.current_iteration && survey.current_iteration > 0) surveyProperty = `$survey_${action}/${survey.id}/${survey.current_iteration}`;
return surveyProperty;
}
export { SURVEY_LANGUAGE_PROPERTY, buildSurveyResponseProperties, getSurveyInteractionProperty, getSurveyOldResponseKey, getSurveyResponseKey, getSurveyResponseValue, surveyHasResponses };
export { getValidationError, getLengthFromRules, getRequirementsHint } from './validation';
export { buildSurveyResponseProperties, getSurveyInteractionProperty, getSurveyOldResponseKey, getSurveyResponseKey, getSurveyResponseValue, SURVEY_LANGUAGE_PROPERTY, surveyHasResponses, } from './events';
export { applySurveyTranslation, detectSurveyLanguage, findBestTranslationMatch, getBaseLanguage, getLanguageFromStoredPersonProperties, normalizeLanguageCode, } from './translations';
//# sourceMappingURL=index.d.ts.map
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/surveys/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAC1F,OAAO,EACL,6BAA6B,EAC7B,4BAA4B,EAC5B,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,UAAU,CAAA;AACjB,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,wBAAwB,EACxB,eAAe,EACf,qCAAqC,EACrC,qBAAqB,GACtB,MAAM,gBAAgB,CAAA"}
"use strict";
var __webpack_require__ = {};
(()=>{
__webpack_require__.d = (exports1, definition)=>{
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
enumerable: true,
get: definition[key]
});
};
})();
(()=>{
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
})();
(()=>{
__webpack_require__.r = (exports1)=>{
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
value: 'Module'
});
Object.defineProperty(exports1, '__esModule', {
value: true
});
};
})();
var __webpack_exports__ = {};
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
getSurveyInteractionProperty: ()=>external_events_js_namespaceObject.getSurveyInteractionProperty,
getSurveyOldResponseKey: ()=>external_events_js_namespaceObject.getSurveyOldResponseKey,
getBaseLanguage: ()=>external_translations_js_namespaceObject.getBaseLanguage,
getSurveyResponseValue: ()=>external_events_js_namespaceObject.getSurveyResponseValue,
SURVEY_LANGUAGE_PROPERTY: ()=>external_events_js_namespaceObject.SURVEY_LANGUAGE_PROPERTY,
detectSurveyLanguage: ()=>external_translations_js_namespaceObject.detectSurveyLanguage,
applySurveyTranslation: ()=>external_translations_js_namespaceObject.applySurveyTranslation,
getValidationError: ()=>external_validation_js_namespaceObject.getValidationError,
getLanguageFromStoredPersonProperties: ()=>external_translations_js_namespaceObject.getLanguageFromStoredPersonProperties,
buildSurveyResponseProperties: ()=>external_events_js_namespaceObject.buildSurveyResponseProperties,
getRequirementsHint: ()=>external_validation_js_namespaceObject.getRequirementsHint,
findBestTranslationMatch: ()=>external_translations_js_namespaceObject.findBestTranslationMatch,
normalizeLanguageCode: ()=>external_translations_js_namespaceObject.normalizeLanguageCode,
getSurveyResponseKey: ()=>external_events_js_namespaceObject.getSurveyResponseKey,
getLengthFromRules: ()=>external_validation_js_namespaceObject.getLengthFromRules,
surveyHasResponses: ()=>external_events_js_namespaceObject.surveyHasResponses
});
const external_validation_js_namespaceObject = require("./validation.js");
const external_events_js_namespaceObject = require("./events.js");
const external_translations_js_namespaceObject = require("./translations.js");
exports.SURVEY_LANGUAGE_PROPERTY = __webpack_exports__.SURVEY_LANGUAGE_PROPERTY;
exports.applySurveyTranslation = __webpack_exports__.applySurveyTranslation;
exports.buildSurveyResponseProperties = __webpack_exports__.buildSurveyResponseProperties;
exports.detectSurveyLanguage = __webpack_exports__.detectSurveyLanguage;
exports.findBestTranslationMatch = __webpack_exports__.findBestTranslationMatch;
exports.getBaseLanguage = __webpack_exports__.getBaseLanguage;
exports.getLanguageFromStoredPersonProperties = __webpack_exports__.getLanguageFromStoredPersonProperties;
exports.getLengthFromRules = __webpack_exports__.getLengthFromRules;
exports.getRequirementsHint = __webpack_exports__.getRequirementsHint;
exports.getSurveyInteractionProperty = __webpack_exports__.getSurveyInteractionProperty;
exports.getSurveyOldResponseKey = __webpack_exports__.getSurveyOldResponseKey;
exports.getSurveyResponseKey = __webpack_exports__.getSurveyResponseKey;
exports.getSurveyResponseValue = __webpack_exports__.getSurveyResponseValue;
exports.getValidationError = __webpack_exports__.getValidationError;
exports.normalizeLanguageCode = __webpack_exports__.normalizeLanguageCode;
exports.surveyHasResponses = __webpack_exports__.surveyHasResponses;
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
"SURVEY_LANGUAGE_PROPERTY",
"applySurveyTranslation",
"buildSurveyResponseProperties",
"detectSurveyLanguage",
"findBestTranslationMatch",
"getBaseLanguage",
"getLanguageFromStoredPersonProperties",
"getLengthFromRules",
"getRequirementsHint",
"getSurveyInteractionProperty",
"getSurveyOldResponseKey",
"getSurveyResponseKey",
"getSurveyResponseValue",
"getValidationError",
"normalizeLanguageCode",
"surveyHasResponses"
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
Object.defineProperty(exports, '__esModule', {
value: true
});
import { getLengthFromRules, getRequirementsHint, getValidationError } from "./validation.mjs";
import { SURVEY_LANGUAGE_PROPERTY, buildSurveyResponseProperties, getSurveyInteractionProperty, getSurveyOldResponseKey, getSurveyResponseKey, getSurveyResponseValue, surveyHasResponses } from "./events.mjs";
import { applySurveyTranslation, detectSurveyLanguage, findBestTranslationMatch, getBaseLanguage, getLanguageFromStoredPersonProperties, normalizeLanguageCode } from "./translations.mjs";
export { SURVEY_LANGUAGE_PROPERTY, applySurveyTranslation, buildSurveyResponseProperties, detectSurveyLanguage, findBestTranslationMatch, getBaseLanguage, getLanguageFromStoredPersonProperties, getLengthFromRules, getRequirementsHint, getSurveyInteractionProperty, getSurveyOldResponseKey, getSurveyResponseKey, getSurveyResponseValue, getValidationError, normalizeLanguageCode, surveyHasResponses };
import { SurveyQuestionTranslation, SurveyTranslation } from '../types';
export type DetectSurveyLanguageOptions = {
overrideLanguage?: unknown;
storedPersonProperties?: unknown;
locale?: unknown;
};
export declare function getLanguageFromStoredPersonProperties(storedPersonProperties: unknown): string | null;
export declare function detectSurveyLanguage({ overrideLanguage, storedPersonProperties, locale, }: DetectSurveyLanguageOptions): string | null;
export declare function normalizeLanguageCode(languageCode: string): string;
export declare function getBaseLanguage(languageCode: string): string;
export declare function findBestTranslationMatch(translations: Record<string, unknown> | undefined, targetLanguage: string): string | null;
type TranslatableSurveyAppearance = {
thankYouMessageHeader?: string;
thankYouMessageDescription?: string | null;
thankYouMessageCloseButtonText?: string;
};
type TranslatableSurveyQuestion = {
question: string;
description?: string | null;
buttonText?: string;
link?: string | null;
lowerBoundLabel?: string;
upperBoundLabel?: string;
choices?: string[];
translations?: Record<string, SurveyQuestionTranslation>;
};
type TranslatableSurvey<TQuestion extends TranslatableSurveyQuestion = TranslatableSurveyQuestion> = {
name: string;
translations?: Record<string, SurveyTranslation>;
appearance?: TranslatableSurveyAppearance | null;
questions: TQuestion[];
};
export declare function applySurveyTranslation<TQuestion extends TranslatableSurveyQuestion, TSurvey extends TranslatableSurvey<TQuestion>>(survey: TSurvey, targetLanguage: string): {
survey: TSurvey;
matchedKey: string | null;
};
export {};
//# sourceMappingURL=translations.d.ts.map
{"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../src/surveys/translations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAKvE,MAAM,MAAM,2BAA2B,GAAG;IACxC,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAMD,wBAAgB,qCAAqC,CAAC,sBAAsB,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAUpG;AAED,wBAAgB,oBAAoB,CAAC,EACnC,gBAAgB,EAChB,sBAAsB,EACtB,MAAM,GACP,EAAE,2BAA2B,GAAG,MAAM,GAAG,IAAI,CAqB7C;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,wBAAwB,CACtC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EACjD,cAAc,EAAE,MAAM,GACrB,MAAM,GAAG,IAAI,CAuBf;AAgBD,KAAK,4BAA4B,GAAG;IAClC,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,0BAA0B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1C,8BAA8B,CAAC,EAAE,MAAM,CAAA;CACxC,CAAA;AAED,KAAK,0BAA0B,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAA;CACzD,CAAA;AAED,KAAK,kBAAkB,CAAC,SAAS,SAAS,0BAA0B,GAAG,0BAA0B,IAAI;IACnG,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;IAChD,UAAU,CAAC,EAAE,4BAA4B,GAAG,IAAI,CAAA;IAChD,SAAS,EAAE,SAAS,EAAE,CAAA;CACvB,CAAA;AA0DD,wBAAgB,sBAAsB,CACpC,SAAS,SAAS,0BAA0B,EAC5C,OAAO,SAAS,kBAAkB,CAAC,SAAS,CAAC,EAC7C,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAwDzF"}
"use strict";
var __webpack_require__ = {};
(()=>{
__webpack_require__.d = (exports1, definition)=>{
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
enumerable: true,
get: definition[key]
});
};
})();
(()=>{
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
})();
(()=>{
__webpack_require__.r = (exports1)=>{
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
value: 'Module'
});
Object.defineProperty(exports1, '__esModule', {
value: true
});
};
})();
var __webpack_exports__ = {};
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
normalizeLanguageCode: ()=>normalizeLanguageCode,
getBaseLanguage: ()=>getBaseLanguage,
detectSurveyLanguage: ()=>detectSurveyLanguage,
applySurveyTranslation: ()=>applySurveyTranslation,
findBestTranslationMatch: ()=>findBestTranslationMatch,
getLanguageFromStoredPersonProperties: ()=>getLanguageFromStoredPersonProperties
});
const index_js_namespaceObject = require("../utils/index.js");
const logger = (0, index_js_namespaceObject.createLogger)('[SurveyTranslations]');
function getTrimmedLanguage(value) {
return 'string' == typeof value && value.trim() ? value.trim() : null;
}
function getLanguageFromStoredPersonProperties(storedPersonProperties) {
if (!storedPersonProperties || 'object' != typeof storedPersonProperties || !('language' in storedPersonProperties)) return null;
return getTrimmedLanguage(storedPersonProperties.language);
}
function detectSurveyLanguage({ overrideLanguage, storedPersonProperties, locale }) {
const explicitLanguage = getTrimmedLanguage(overrideLanguage);
if (explicitLanguage) {
logger.info(`Using override display language: ${explicitLanguage}`);
return explicitLanguage;
}
const personLanguage = getLanguageFromStoredPersonProperties(storedPersonProperties);
if (personLanguage) {
logger.info(`Using person property language: ${personLanguage}`);
return personLanguage;
}
const detectedLocale = getTrimmedLanguage(locale);
if (detectedLocale) {
logger.info(`Using detected locale: ${detectedLocale}`);
return detectedLocale;
}
logger.info('No user language detected');
return null;
}
function normalizeLanguageCode(languageCode) {
return languageCode.toLowerCase();
}
function getBaseLanguage(languageCode) {
return languageCode.split('-')[0];
}
function findBestTranslationMatch(translations, targetLanguage) {
if (!translations || !targetLanguage) return null;
const normalizedTarget = normalizeLanguageCode(targetLanguage);
const exactMatch = Object.keys(translations).find((key)=>normalizeLanguageCode(key) === normalizedTarget);
if (exactMatch) {
logger.debug(`Found exact translation match: ${exactMatch}`);
return exactMatch;
}
if (normalizedTarget.includes('-')) {
const baseLanguage = getBaseLanguage(normalizedTarget);
const baseMatch = Object.keys(translations).find((key)=>normalizeLanguageCode(key) === baseLanguage);
if (baseMatch) {
logger.debug(`Found base language translation match: ${baseMatch} (from ${targetLanguage})`);
return baseMatch;
}
}
return null;
}
function isTranslatedChoices(questionTranslation) {
return (0, index_js_namespaceObject.isArray)(questionTranslation.choices);
}
function hasThankYouTranslation(translation) {
return !(0, index_js_namespaceObject.isUndefined)(translation.thankYouMessageHeader) || !(0, index_js_namespaceObject.isUndefined)(translation.thankYouMessageDescription) || !(0, index_js_namespaceObject.isUndefined)(translation.thankYouMessageCloseButtonText);
}
function mergeQuestionTranslation(question, targetLanguage) {
const translationKey = findBestTranslationMatch(question.translations, targetLanguage);
if (!translationKey) return {
question,
matchedKey: null,
hasChanges: false
};
const questionTranslation = question.translations?.[translationKey];
if (!questionTranslation) return {
question,
matchedKey: null,
hasChanges: false
};
const translated = {
...question
};
let hasChanges = false;
if (!(0, index_js_namespaceObject.isUndefined)(questionTranslation.question)) {
translated.question = questionTranslation.question;
hasChanges = true;
}
if (!(0, index_js_namespaceObject.isUndefined)(questionTranslation.description)) {
translated.description = questionTranslation.description;
hasChanges = true;
}
if (!(0, index_js_namespaceObject.isUndefined)(questionTranslation.buttonText)) {
translated.buttonText = questionTranslation.buttonText;
hasChanges = true;
}
if ('link' in translated && !(0, index_js_namespaceObject.isUndefined)(questionTranslation.link)) {
translated.link = questionTranslation.link;
hasChanges = true;
}
if ('lowerBoundLabel' in translated && !(0, index_js_namespaceObject.isUndefined)(questionTranslation.lowerBoundLabel)) {
translated.lowerBoundLabel = questionTranslation.lowerBoundLabel;
hasChanges = true;
}
if ('upperBoundLabel' in translated && !(0, index_js_namespaceObject.isUndefined)(questionTranslation.upperBoundLabel)) {
translated.upperBoundLabel = questionTranslation.upperBoundLabel;
hasChanges = true;
}
if ('choices' in translated && isTranslatedChoices(questionTranslation)) {
translated.choices = questionTranslation.choices;
hasChanges = true;
}
return {
question: hasChanges ? translated : question,
matchedKey: hasChanges ? translationKey : null,
hasChanges
};
}
function applySurveyTranslation(survey, targetLanguage) {
const translationKey = findBestTranslationMatch(survey.translations, targetLanguage);
const translated = {
...survey
};
let hasTranslation = false;
if (translationKey) {
const translation = survey.translations?.[translationKey];
if (translation) {
logger.info(`Applying survey-level translation for language: ${translationKey}`);
if (!(0, index_js_namespaceObject.isUndefined)(translation.name)) {
translated.name = translation.name;
hasTranslation = true;
}
if (translated.appearance) {
translated.appearance = {
...translated.appearance
};
if (!(0, index_js_namespaceObject.isUndefined)(translation.thankYouMessageHeader)) {
translated.appearance.thankYouMessageHeader = translation.thankYouMessageHeader;
hasTranslation = true;
}
if (!(0, index_js_namespaceObject.isUndefined)(translation.thankYouMessageDescription)) {
translated.appearance.thankYouMessageDescription = translation.thankYouMessageDescription;
hasTranslation = true;
}
if (!(0, index_js_namespaceObject.isUndefined)(translation.thankYouMessageCloseButtonText)) {
translated.appearance.thankYouMessageCloseButtonText = translation.thankYouMessageCloseButtonText;
hasTranslation = true;
}
} else if (hasThankYouTranslation(translation)) hasTranslation = true;
}
}
const translatedResults = survey.questions.map((question)=>mergeQuestionTranslation(question, targetLanguage));
const translatedQuestions = translatedResults.map((result)=>result.question);
const anyQuestionTranslated = translatedResults.some((result)=>result.hasChanges);
let questionMatchedKey = null;
if (!translationKey) questionMatchedKey = translatedResults.find((result)=>result.matchedKey)?.matchedKey || null;
if (anyQuestionTranslated) {
translated.questions = translatedQuestions;
hasTranslation = true;
logger.info(`Applied question-level translations for language: ${targetLanguage}`);
}
return {
survey: translated,
matchedKey: hasTranslation ? translationKey || questionMatchedKey : null
};
}
exports.applySurveyTranslation = __webpack_exports__.applySurveyTranslation;
exports.detectSurveyLanguage = __webpack_exports__.detectSurveyLanguage;
exports.findBestTranslationMatch = __webpack_exports__.findBestTranslationMatch;
exports.getBaseLanguage = __webpack_exports__.getBaseLanguage;
exports.getLanguageFromStoredPersonProperties = __webpack_exports__.getLanguageFromStoredPersonProperties;
exports.normalizeLanguageCode = __webpack_exports__.normalizeLanguageCode;
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
"applySurveyTranslation",
"detectSurveyLanguage",
"findBestTranslationMatch",
"getBaseLanguage",
"getLanguageFromStoredPersonProperties",
"normalizeLanguageCode"
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
Object.defineProperty(exports, '__esModule', {
value: true
});
import { createLogger, isArray, isUndefined } from "../utils/index.mjs";
const logger = createLogger('[SurveyTranslations]');
function getTrimmedLanguage(value) {
return 'string' == typeof value && value.trim() ? value.trim() : null;
}
function getLanguageFromStoredPersonProperties(storedPersonProperties) {
if (!storedPersonProperties || 'object' != typeof storedPersonProperties || !('language' in storedPersonProperties)) return null;
return getTrimmedLanguage(storedPersonProperties.language);
}
function detectSurveyLanguage({ overrideLanguage, storedPersonProperties, locale }) {
const explicitLanguage = getTrimmedLanguage(overrideLanguage);
if (explicitLanguage) {
logger.info(`Using override display language: ${explicitLanguage}`);
return explicitLanguage;
}
const personLanguage = getLanguageFromStoredPersonProperties(storedPersonProperties);
if (personLanguage) {
logger.info(`Using person property language: ${personLanguage}`);
return personLanguage;
}
const detectedLocale = getTrimmedLanguage(locale);
if (detectedLocale) {
logger.info(`Using detected locale: ${detectedLocale}`);
return detectedLocale;
}
logger.info('No user language detected');
return null;
}
function normalizeLanguageCode(languageCode) {
return languageCode.toLowerCase();
}
function getBaseLanguage(languageCode) {
return languageCode.split('-')[0];
}
function findBestTranslationMatch(translations, targetLanguage) {
if (!translations || !targetLanguage) return null;
const normalizedTarget = normalizeLanguageCode(targetLanguage);
const exactMatch = Object.keys(translations).find((key)=>normalizeLanguageCode(key) === normalizedTarget);
if (exactMatch) {
logger.debug(`Found exact translation match: ${exactMatch}`);
return exactMatch;
}
if (normalizedTarget.includes('-')) {
const baseLanguage = getBaseLanguage(normalizedTarget);
const baseMatch = Object.keys(translations).find((key)=>normalizeLanguageCode(key) === baseLanguage);
if (baseMatch) {
logger.debug(`Found base language translation match: ${baseMatch} (from ${targetLanguage})`);
return baseMatch;
}
}
return null;
}
function isTranslatedChoices(questionTranslation) {
return isArray(questionTranslation.choices);
}
function hasThankYouTranslation(translation) {
return !isUndefined(translation.thankYouMessageHeader) || !isUndefined(translation.thankYouMessageDescription) || !isUndefined(translation.thankYouMessageCloseButtonText);
}
function mergeQuestionTranslation(question, targetLanguage) {
const translationKey = findBestTranslationMatch(question.translations, targetLanguage);
if (!translationKey) return {
question,
matchedKey: null,
hasChanges: false
};
const questionTranslation = question.translations?.[translationKey];
if (!questionTranslation) return {
question,
matchedKey: null,
hasChanges: false
};
const translated = {
...question
};
let hasChanges = false;
if (!isUndefined(questionTranslation.question)) {
translated.question = questionTranslation.question;
hasChanges = true;
}
if (!isUndefined(questionTranslation.description)) {
translated.description = questionTranslation.description;
hasChanges = true;
}
if (!isUndefined(questionTranslation.buttonText)) {
translated.buttonText = questionTranslation.buttonText;
hasChanges = true;
}
if ('link' in translated && !isUndefined(questionTranslation.link)) {
translated.link = questionTranslation.link;
hasChanges = true;
}
if ('lowerBoundLabel' in translated && !isUndefined(questionTranslation.lowerBoundLabel)) {
translated.lowerBoundLabel = questionTranslation.lowerBoundLabel;
hasChanges = true;
}
if ('upperBoundLabel' in translated && !isUndefined(questionTranslation.upperBoundLabel)) {
translated.upperBoundLabel = questionTranslation.upperBoundLabel;
hasChanges = true;
}
if ('choices' in translated && isTranslatedChoices(questionTranslation)) {
translated.choices = questionTranslation.choices;
hasChanges = true;
}
return {
question: hasChanges ? translated : question,
matchedKey: hasChanges ? translationKey : null,
hasChanges
};
}
function applySurveyTranslation(survey, targetLanguage) {
const translationKey = findBestTranslationMatch(survey.translations, targetLanguage);
const translated = {
...survey
};
let hasTranslation = false;
if (translationKey) {
const translation = survey.translations?.[translationKey];
if (translation) {
logger.info(`Applying survey-level translation for language: ${translationKey}`);
if (!isUndefined(translation.name)) {
translated.name = translation.name;
hasTranslation = true;
}
if (translated.appearance) {
translated.appearance = {
...translated.appearance
};
if (!isUndefined(translation.thankYouMessageHeader)) {
translated.appearance.thankYouMessageHeader = translation.thankYouMessageHeader;
hasTranslation = true;
}
if (!isUndefined(translation.thankYouMessageDescription)) {
translated.appearance.thankYouMessageDescription = translation.thankYouMessageDescription;
hasTranslation = true;
}
if (!isUndefined(translation.thankYouMessageCloseButtonText)) {
translated.appearance.thankYouMessageCloseButtonText = translation.thankYouMessageCloseButtonText;
hasTranslation = true;
}
} else if (hasThankYouTranslation(translation)) hasTranslation = true;
}
}
const translatedResults = survey.questions.map((question)=>mergeQuestionTranslation(question, targetLanguage));
const translatedQuestions = translatedResults.map((result)=>result.question);
const anyQuestionTranslated = translatedResults.some((result)=>result.hasChanges);
let questionMatchedKey = null;
if (!translationKey) questionMatchedKey = translatedResults.find((result)=>result.matchedKey)?.matchedKey || null;
if (anyQuestionTranslated) {
translated.questions = translatedQuestions;
hasTranslation = true;
logger.info(`Applied question-level translations for language: ${targetLanguage}`);
}
return {
survey: translated,
matchedKey: hasTranslation ? translationKey || questionMatchedKey : null
};
}
export { applySurveyTranslation, detectSurveyLanguage, findBestTranslationMatch, getBaseLanguage, getLanguageFromStoredPersonProperties, normalizeLanguageCode };
import { describe, expect, it } from '@jest/globals'
import {
buildSurveyResponseProperties,
getSurveyInteractionProperty,
getSurveyResponseKey,
getSurveyResponseValue,
surveyHasResponses,
} from './events'
describe('survey event helpers', () => {
const survey = {
id: 'survey-1',
current_iteration: 2,
questions: [
{ id: 'q1', question: 'Rate us', originalQuestionIndex: 0 },
{ id: 'q2', question: 'Anything else?', originalQuestionIndex: 1 },
],
}
it('builds response properties with current and legacy response keys', () => {
const responses = {
[getSurveyResponseKey('q1')]: 5,
[getSurveyResponseKey('q2')]: ['fast', 'clear'],
}
expect(buildSurveyResponseProperties(responses, survey)).toEqual({
$survey_questions: [
{ id: 'q1', question: 'Rate us', response: 5 },
{ id: 'q2', question: 'Anything else?', response: ['fast', 'clear'] },
],
$survey_response_q1: 5,
$survey_response_q2: ['fast', 'clear'],
$survey_response: 5,
$survey_response_1: ['fast', 'clear'],
})
})
it('copies array response values before returning them', () => {
const responses = { [getSurveyResponseKey('q1')]: ['a'] }
const response = getSurveyResponseValue(responses, 'q1')
expect(response).toEqual(['a'])
expect(response).not.toBe(responses.$survey_response_q1)
})
it('detects non-nullish responses and builds interaction property names', () => {
expect(surveyHasResponses({ $survey_response_q1: null })).toBe(false)
expect(surveyHasResponses({ $survey_response_q1: 0 })).toBe(true)
expect(getSurveyInteractionProperty(survey, 'responded')).toBe('$survey_responded/survey-1/2')
})
})
import { SurveyResponses, SurveyResponseValue } from '../types'
import { isArray, isNullish, isUndefined } from '../utils'
export const SURVEY_LANGUAGE_PROPERTY = '$survey_language'
export function getSurveyResponseKey(questionId: string): string {
return `$survey_response_${questionId}`
}
export function getSurveyOldResponseKey(originalQuestionIndex: number): string {
return originalQuestionIndex === 0 ? '$survey_response' : `$survey_response_${originalQuestionIndex}`
}
export function getSurveyResponseValue(
responses: SurveyResponses,
questionId?: string
): SurveyResponseValue | undefined {
if (!questionId) {
return null
}
const response = responses[getSurveyResponseKey(questionId)]
if (isArray(response)) {
return [...response]
}
return response
}
export function buildSurveyResponseProperties(
responses: SurveyResponses = {},
survey: SurveyForResponses
): Record<string, unknown> {
const oldFormatResponses: SurveyResponses = {}
survey.questions.forEach((question: SurveyQuestionForResponses) => {
if (isUndefined(question.originalQuestionIndex)) {
return
}
const oldResponseKey = getSurveyOldResponseKey(question.originalQuestionIndex)
const response = getSurveyResponseValue(responses, question.id)
if (!isUndefined(response)) {
oldFormatResponses[oldResponseKey] = response
}
})
return {
$survey_questions: survey.questions.map((question: SurveyQuestionForResponses) => ({
id: question.id,
question: question.question,
response: getSurveyResponseValue(responses, question.id),
})),
...responses,
...oldFormatResponses,
}
}
export function surveyHasResponses(responses: SurveyResponses = {}): boolean {
return Object.values(responses).some((response) => !isNullish(response))
}
export function getSurveyInteractionProperty(survey: SurveyWithIteration, action: string): string {
let surveyProperty = `$survey_${action}/${survey.id}`
if (survey.current_iteration && survey.current_iteration > 0) {
surveyProperty = `$survey_${action}/${survey.id}/${survey.current_iteration}`
}
return surveyProperty
}
type SurveyQuestionForResponses = {
id?: string
question: string
originalQuestionIndex?: number
}
type SurveyForResponses = {
questions: SurveyQuestionForResponses[]
}
type SurveyWithIteration = {
id: string
current_iteration?: number | null
}
export { getValidationError, getLengthFromRules, getRequirementsHint } from './validation'
export {
buildSurveyResponseProperties,
getSurveyInteractionProperty,
getSurveyOldResponseKey,
getSurveyResponseKey,
getSurveyResponseValue,
SURVEY_LANGUAGE_PROPERTY,
surveyHasResponses,
} from './events'
export {
applySurveyTranslation,
detectSurveyLanguage,
findBestTranslationMatch,
getBaseLanguage,
getLanguageFromStoredPersonProperties,
normalizeLanguageCode,
} from './translations'
import { describe, expect, it } from '@jest/globals'
import {
applySurveyTranslation,
detectSurveyLanguage,
findBestTranslationMatch,
getLanguageFromStoredPersonProperties,
} from './translations'
import { Survey, SurveyQuestionType, SurveyType } from '../types'
const createBaseSurvey = (): Survey => ({
id: 'test-survey',
name: 'Test Survey',
description: 'Test Description',
type: SurveyType.Popover,
questions: [
{
type: SurveyQuestionType.Open,
question: 'What do you think?',
id: 'q1',
originalQuestionIndex: 0,
},
],
appearance: {
thankYouMessageHeader: 'Thank you!',
thankYouMessageDescription: 'We appreciate your feedback',
thankYouMessageCloseButtonText: 'Close',
},
})
describe('survey translations', () => {
describe('detectSurveyLanguage', () => {
it.each([
{
name: 'prioritizes override language',
input: {
overrideLanguage: 'de',
storedPersonProperties: { language: 'es' },
locale: 'fr',
},
expected: 'de',
},
{
name: 'uses person language when override is missing',
input: {
storedPersonProperties: { language: 'es' },
locale: 'fr',
},
expected: 'es',
},
{
name: 'falls back to locale',
input: {
storedPersonProperties: { some_other_property: 'value' },
locale: 'fr-CA',
},
expected: 'fr-CA',
},
{
name: 'returns null when no sources exist',
input: {
storedPersonProperties: { some_other_property: 'value' },
},
expected: null,
},
{
name: 'trims override language',
input: {
overrideLanguage: ' es ',
},
expected: 'es',
},
])('$name', ({ input, expected }) => {
expect(detectSurveyLanguage(input)).toBe(expected)
})
it('reads language from stored person properties', () => {
expect(getLanguageFromStoredPersonProperties({ language: 'it' })).toBe('it')
expect(getLanguageFromStoredPersonProperties({ language: ' ' })).toBeNull()
expect(getLanguageFromStoredPersonProperties({})).toBeNull()
})
})
describe('findBestTranslationMatch', () => {
it('supports exact and base-language matches', () => {
expect(findBestTranslationMatch({ fr: {}, 'fr-CA': {} }, 'FR-ca')).toBe('fr-CA')
expect(findBestTranslationMatch({ fr: {} }, 'fr-CA')).toBe('fr')
expect(findBestTranslationMatch({ es: {} }, 'de')).toBeNull()
})
})
describe('applySurveyTranslation', () => {
it('returns original survey when no translations exist', () => {
const survey = createBaseSurvey()
const result = applySurveyTranslation(survey, 'fr')
expect(result.survey).toEqual(survey)
expect(result.matchedKey).toBeNull()
})
it('applies survey-level translations', () => {
const survey = createBaseSurvey()
survey.translations = {
fr: {
name: 'Enquete Test',
thankYouMessageHeader: 'Merci',
thankYouMessageDescription: 'Merci pour votre retour',
thankYouMessageCloseButtonText: 'Fermer',
},
}
const result = applySurveyTranslation(survey, 'fr')
expect(result.survey.name).toBe('Enquete Test')
expect(result.survey.appearance?.thankYouMessageHeader).toBe('Merci')
expect(result.survey.appearance?.thankYouMessageDescription).toBe('Merci pour votre retour')
expect(result.survey.appearance?.thankYouMessageCloseButtonText).toBe('Fermer')
expect(result.matchedKey).toBe('fr')
})
it('applies question-level translations', () => {
const survey = createBaseSurvey()
survey.questions = [
{
type: SurveyQuestionType.Rating,
question: 'How was it?',
id: 'q1',
originalQuestionIndex: 0,
display: 'number',
scale: 5,
lowerBoundLabel: 'Bad',
upperBoundLabel: 'Great',
translations: {
pt: {
question: 'Como foi?',
lowerBoundLabel: 'Ruim',
upperBoundLabel: 'Otimo',
},
},
},
{
type: SurveyQuestionType.MultipleChoice,
question: 'Pick one',
id: 'q2',
originalQuestionIndex: 1,
choices: ['One', 'Other'],
hasOpenChoice: true,
translations: {
pt: {
choices: ['Um', 'Outro'],
},
},
},
]
const result = applySurveyTranslation(survey, 'pt-BR')
expect(result.survey.questions[0].question).toBe('Como foi?')
expect('lowerBoundLabel' in result.survey.questions[0] && result.survey.questions[0].lowerBoundLabel).toBe('Ruim')
expect('upperBoundLabel' in result.survey.questions[0] && result.survey.questions[0].upperBoundLabel).toBe(
'Otimo'
)
expect('choices' in result.survey.questions[1] && result.survey.questions[1].choices).toEqual(['Um', 'Outro'])
expect(result.matchedKey).toBe('pt')
})
it('uses question-level match when there is no survey-level translation', () => {
const survey = createBaseSurvey()
survey.questions[0].translations = {
de: {
question: 'Was denkst du?',
},
}
const result = applySurveyTranslation(survey, 'de')
expect(result.survey.questions[0].question).toBe('Was denkst du?')
expect(result.matchedKey).toBe('de')
})
it('preserves custom survey and question fields for shared consumers', () => {
const survey = {
name: 'Custom Survey',
customSurveyField: true,
questions: [
{
question: 'Pick one',
customQuestionField: 'native-renderer',
translations: {
fr: {
question: 'Choisissez une option',
},
},
},
],
}
const result = applySurveyTranslation(survey, 'fr')
expect(result.survey.customSurveyField).toBe(true)
expect(result.survey.questions[0].question).toBe('Choisissez une option')
expect(result.survey.questions[0].customQuestionField).toBe('native-renderer')
expect(result.matchedKey).toBe('fr')
})
})
})
import { SurveyQuestionTranslation, SurveyTranslation } from '../types'
import { createLogger, isArray, isUndefined } from '../utils'
const logger = createLogger('[SurveyTranslations]')
export type DetectSurveyLanguageOptions = {
overrideLanguage?: unknown
storedPersonProperties?: unknown
locale?: unknown
}
function getTrimmedLanguage(value: unknown): string | null {
return typeof value === 'string' && value.trim() ? value.trim() : null
}
export function getLanguageFromStoredPersonProperties(storedPersonProperties: unknown): string | null {
if (
!storedPersonProperties ||
typeof storedPersonProperties !== 'object' ||
!('language' in storedPersonProperties)
) {
return null
}
return getTrimmedLanguage(storedPersonProperties.language)
}
export function detectSurveyLanguage({
overrideLanguage,
storedPersonProperties,
locale,
}: DetectSurveyLanguageOptions): string | null {
const explicitLanguage = getTrimmedLanguage(overrideLanguage)
if (explicitLanguage) {
logger.info(`Using override display language: ${explicitLanguage}`)
return explicitLanguage
}
const personLanguage = getLanguageFromStoredPersonProperties(storedPersonProperties)
if (personLanguage) {
logger.info(`Using person property language: ${personLanguage}`)
return personLanguage
}
const detectedLocale = getTrimmedLanguage(locale)
if (detectedLocale) {
logger.info(`Using detected locale: ${detectedLocale}`)
return detectedLocale
}
logger.info('No user language detected')
return null
}
export function normalizeLanguageCode(languageCode: string): string {
return languageCode.toLowerCase()
}
export function getBaseLanguage(languageCode: string): string {
return languageCode.split('-')[0]
}
export function findBestTranslationMatch(
translations: Record<string, unknown> | undefined,
targetLanguage: string
): string | null {
if (!translations || !targetLanguage) {
return null
}
const normalizedTarget = normalizeLanguageCode(targetLanguage)
const exactMatch = Object.keys(translations).find((key) => normalizeLanguageCode(key) === normalizedTarget)
if (exactMatch) {
logger.debug(`Found exact translation match: ${exactMatch}`)
return exactMatch
}
if (normalizedTarget.includes('-')) {
const baseLanguage = getBaseLanguage(normalizedTarget)
const baseMatch = Object.keys(translations).find((key) => normalizeLanguageCode(key) === baseLanguage)
if (baseMatch) {
logger.debug(`Found base language translation match: ${baseMatch} (from ${targetLanguage})`)
return baseMatch
}
}
return null
}
function isTranslatedChoices(
questionTranslation: SurveyQuestionTranslation
): questionTranslation is SurveyQuestionTranslation & { choices: string[] } {
return isArray(questionTranslation.choices)
}
function hasThankYouTranslation(translation: SurveyTranslation): boolean {
return (
!isUndefined(translation.thankYouMessageHeader) ||
!isUndefined(translation.thankYouMessageDescription) ||
!isUndefined(translation.thankYouMessageCloseButtonText)
)
}
type TranslatableSurveyAppearance = {
thankYouMessageHeader?: string
thankYouMessageDescription?: string | null
thankYouMessageCloseButtonText?: string
}
type TranslatableSurveyQuestion = {
question: string
description?: string | null
buttonText?: string
link?: string | null
lowerBoundLabel?: string
upperBoundLabel?: string
choices?: string[]
translations?: Record<string, SurveyQuestionTranslation>
}
type TranslatableSurvey<TQuestion extends TranslatableSurveyQuestion = TranslatableSurveyQuestion> = {
name: string
translations?: Record<string, SurveyTranslation>
appearance?: TranslatableSurveyAppearance | null
questions: TQuestion[]
}
function mergeQuestionTranslation<TQuestion extends TranslatableSurveyQuestion>(
question: TQuestion,
targetLanguage: string
): { question: TQuestion; matchedKey: string | null; hasChanges: boolean } {
const translationKey = findBestTranslationMatch(question.translations, targetLanguage)
if (!translationKey) {
return { question, matchedKey: null, hasChanges: false }
}
const questionTranslation = question.translations?.[translationKey]
if (!questionTranslation) {
return { question, matchedKey: null, hasChanges: false }
}
const translated: TQuestion = { ...question }
let hasChanges = false
if (!isUndefined(questionTranslation.question)) {
translated.question = questionTranslation.question
hasChanges = true
}
if (!isUndefined(questionTranslation.description)) {
translated.description = questionTranslation.description
hasChanges = true
}
if (!isUndefined(questionTranslation.buttonText)) {
translated.buttonText = questionTranslation.buttonText
hasChanges = true
}
if ('link' in translated && !isUndefined(questionTranslation.link)) {
translated.link = questionTranslation.link
hasChanges = true
}
if ('lowerBoundLabel' in translated && !isUndefined(questionTranslation.lowerBoundLabel)) {
translated.lowerBoundLabel = questionTranslation.lowerBoundLabel
hasChanges = true
}
if ('upperBoundLabel' in translated && !isUndefined(questionTranslation.upperBoundLabel)) {
translated.upperBoundLabel = questionTranslation.upperBoundLabel
hasChanges = true
}
if ('choices' in translated && isTranslatedChoices(questionTranslation)) {
translated.choices = questionTranslation.choices
hasChanges = true
}
return {
question: hasChanges ? translated : question,
matchedKey: hasChanges ? translationKey : null,
hasChanges,
}
}
export function applySurveyTranslation<
TQuestion extends TranslatableSurveyQuestion,
TSurvey extends TranslatableSurvey<TQuestion>,
>(survey: TSurvey, targetLanguage: string): { survey: TSurvey; matchedKey: string | null } {
const translationKey = findBestTranslationMatch(survey.translations, targetLanguage)
const translated: TSurvey = { ...survey }
let hasTranslation = false
if (translationKey) {
const translation = survey.translations?.[translationKey]
if (translation) {
logger.info(`Applying survey-level translation for language: ${translationKey}`)
if (!isUndefined(translation.name)) {
translated.name = translation.name
hasTranslation = true
}
if (translated.appearance) {
translated.appearance = { ...translated.appearance }
if (!isUndefined(translation.thankYouMessageHeader)) {
translated.appearance.thankYouMessageHeader = translation.thankYouMessageHeader
hasTranslation = true
}
if (!isUndefined(translation.thankYouMessageDescription)) {
translated.appearance.thankYouMessageDescription = translation.thankYouMessageDescription
hasTranslation = true
}
if (!isUndefined(translation.thankYouMessageCloseButtonText)) {
translated.appearance.thankYouMessageCloseButtonText = translation.thankYouMessageCloseButtonText
hasTranslation = true
}
} else if (hasThankYouTranslation(translation)) {
hasTranslation = true
}
}
}
const translatedResults = survey.questions.map((question) => mergeQuestionTranslation(question, targetLanguage))
const translatedQuestions = translatedResults.map((result) => result.question)
const anyQuestionTranslated = translatedResults.some((result) => result.hasChanges)
let questionMatchedKey: string | null = null
if (!translationKey) {
questionMatchedKey = translatedResults.find((result) => result.matchedKey)?.matchedKey || null
}
if (anyQuestionTranslated) {
translated.questions = translatedQuestions
hasTranslation = true
logger.info(`Applied question-level translations for language: ${targetLanguage}`)
}
return {
survey: translated,
matchedKey: hasTranslation ? translationKey || questionMatchedKey : null,
}
}
+2
-1

@@ -7,3 +7,4 @@ export { getFeatureFlagValue } from './featureFlagUtils';

export { PostHogLogs } from './logs';
export type { BufferedLogEntry, PostHogLogsConfig, ResolvedPostHogLogsConfig } from './logs/types';
export type { BeforeSendLogFn, BufferedLogEntry, CaptureLogger, LogSdkContext, PostHogLogsConfig, ResolvedPostHogLogsConfig, } from './logs/types';
export type { CaptureLogOptions, LogAttributeValue, LogAttributes, LogSeverityLevel } from './logs/types';
export { uuidv7 } from './vendor/uuidv7';

@@ -10,0 +11,0 @@ export * from './posthog-core';

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,QAAQ,CAAA;AACjE,cAAc,SAAS,CAAA;AACvB,OAAO,KAAK,aAAa,MAAM,kBAAkB,CAAA;AACjD,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,kBAAkB,GACnB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AACpC,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AAClG,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,SAAS,CAAA;AACvB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,QAAQ,CAAA;AACjE,cAAc,SAAS,CAAA;AACvB,OAAO,KAAK,aAAa,MAAM,kBAAkB,CAAA;AACjD,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,kBAAkB,GACnB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AACpC,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,cAAc,CAAA;AAIrB,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACzG,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,SAAS,CAAA;AACvB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA"}

@@ -1,5 +0,4 @@

import type { LogSdkContext } from '@posthog/types';
import { Logger } from '../types';
import type { PostHogCoreStateless } from '../posthog-core-stateless';
import type { CaptureLogOptions, ResolvedPostHogLogsConfig } from './types';
import type { CaptureLogOptions, LogSdkContext, ResolvedPostHogLogsConfig } from './types';
export declare class PostHogLogs {

@@ -11,8 +10,99 @@ private readonly _instance;

private readonly _onReady;
private _localEnabled;
private readonly _waitForStoragePersist;
private _maxBufferSize;
constructor(_instance: PostHogCoreStateless, _config: ResolvedPostHogLogsConfig, _logger: Logger, _getContext: () => LogSdkContext, _onReady: (fn: () => void) => void);
private _flushIntervalMs;
private _maxBatchRecordsPerPost;
private _flushTimer?;
private _flushPromise;
private _rateCapWindowMs;
private _maxLogsPerInterval?;
private _intervalWindowStart;
private _intervalLogCount;
private _droppedWarned;
constructor(_instance: PostHogCoreStateless, _config: ResolvedPostHogLogsConfig, _logger: Logger, _getContext: () => LogSdkContext, _onReady: (fn: () => void) => void, _waitForStoragePersist?: () => Promise<void>);
captureLog(options: CaptureLogOptions): void;
/**
* Runs the configured `beforeSend` hook(s) on a capture record:
* - single fn OR array of fns (chain, left-to-right)
* - returning `null` drops the record (logged at info)
* - a thrown error is logged and the chain *continues* with the previous
* result — a buggy user filter must never crash the caller's
* `captureLog()` call
*/
private _runBeforeSend;
/**
* Returns `true` if this capture fits within the current rate-cap window,
* `false` if it should be dropped.
*
* Fixed (tumbling) window: the counter resets the first time `captureLog`
* fires after `rateCapWindowMs` has elapsed — no timer needed.
* `maxLogsPerInterval === undefined` means unbounded.
*
* Wall-clock safety: if `Date.now()` jumps backward (manual device-clock
* change, big NTP correction), `elapsed` goes negative. We treat that the
* same as "window expired" and reset — otherwise the rate cap would be
* stuck until the clock caught up to the old window start, potentially
* dropping logs for hours.
*
* Pre-init note: the counter increments here, before `_onReady` defers
* `_enqueue` to the init promise. If init resolves slowly and the user is
* later opted out, the counter has already consumed budget for records
* that won't enqueue. Cosmetic — no record is "lost" beyond what's
* already gated, and the window rolls on its own.
*/
private _checkRateLimit;
/**
* Drains `LogsQueue` in `maxBatchRecordsPerPost` slices, POSTing each as an
* OTLP payload.
* - Network error → keep items in queue, re-throw (caller retries later)
* - 413 → halve batch size, retry same records (do not advance)
* - Any other error → drop the batch (avoid infinite loop on malformed data),
* re-throw so callers can log/report
* Concurrent calls are serialized through `_flushPromise` so records at the
* head of the queue can't be sent twice.
*/
flush(): Promise<void>;
private _flushInner;
private _persistQueueAdvance;
/**
* OTLP resource attributes for every batch.
*
* Layout: user `resourceAttributes` spread first, then SDK-controlled
* keys layered on top so users cannot accidentally clobber them. Most logs
* backends index on `service.name` and `telemetry.sdk.*` for routing,
* SDK-version dashboards, and bug-correlation; letting a stray user key
* overwrite them silently breaks ingestion attribution. The dedicated
* `serviceName` / `environment` / `serviceVersion` config fields are the
* supported way to override `service.name` / `deployment.environment` /
* `service.version`.
*/
private _buildResourceAttributes;
private _enqueue;
/**
* Stops the timer-based flush and sends anything still in the queue.
* Intended for process-teardown paths (RN `_shutdown` override). Swallows
* errors so a failing final flush can't block the broader shutdown.
*
* If `timeoutMs` is provided, the final flush races against that budget so
* a slow network/storage can't hold up shutdown indefinitely. Without it,
* flush time is bounded only by `fetchRetryCount * (requestTimeout +
* fetchRetryDelay)`, which can exceed the caller's shutdown SLA.
*/
shutdown(timeoutMs?: number): Promise<void>;
/**
* Time-bounded flush for transient lifecycle events (e.g. RN
* foreground→background) that must complete inside an OS-imposed window.
* Unlike `shutdown`, this leaves the periodic flush timer in place so the
* pipeline keeps draining if the process is resumed instead of suspended.
*
* Errors propagate so the host SDK can route them through its standard
* lifecycle error handler (e.g. RN's `logFlushError`). If the timer wins
* the race, a late rejection from the in-flight flush is silenced via a
* no-op handler attached after the race settles, to avoid noisy
* unhandled-rejection logs — the next regular flush cycle will retry.
*/
flushWithTimeout(timeoutMs: number): Promise<void>;
private _flushInBackground;
private _clearFlushTimer;
}
//# sourceMappingURL=index.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/logs/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,EAAE,MAAM,EAA4B,MAAM,UAAU,CAAA;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AACrE,OAAO,KAAK,EAAoB,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAA;AAE7F,qBAAa,WAAW;IAKpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR3B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,cAAc,CAAQ;gBAGX,SAAS,EAAE,oBAAoB,EAC/B,OAAO,EAAE,yBAAyB,EAClC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,aAAa,EAChC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI;IAMrD,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAoB5C,OAAO,CAAC,QAAQ;CAgBjB"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/logs/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAA4B,MAAM,UAAU,CAAA;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAErE,OAAO,KAAK,EAGV,iBAAiB,EACjB,aAAa,EACb,yBAAyB,EAC1B,MAAM,SAAS,CAAA;AAEhB,qBAAa,WAAW;IAuBpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAMzB,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IAhCzC,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,gBAAgB,CAAQ;IAIhC,OAAO,CAAC,uBAAuB,CAAQ;IACvC,OAAO,CAAC,WAAW,CAAC,CAAmC;IAGvD,OAAO,CAAC,aAAa,CAA6B;IAMlD,OAAO,CAAC,gBAAgB,CAAQ;IAChC,OAAO,CAAC,mBAAmB,CAAC,CAAQ;IACpC,OAAO,CAAC,oBAAoB,CAAI;IAChC,OAAO,CAAC,iBAAiB,CAAI;IAC7B,OAAO,CAAC,cAAc,CAAQ;gBAGX,SAAS,EAAE,oBAAoB,EAC/B,OAAO,EAAE,yBAAyB,EAClC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,aAAa,EAChC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,EAMlC,sBAAsB,GAAE,MAAM,OAAO,CAAC,IAAI,CAA2B;IASxF,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAgC5C;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc;IAwBtB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,eAAe;IAwBvB;;;;;;;;;OASG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAUd,WAAW;YAiEX,oBAAoB;IAUlC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,wBAAwB;IAWhC,OAAO,CAAC,QAAQ;IAgChB;;;;;;;;;OASG;IACG,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcjD;;;;;;;;;;;OAWG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBxD,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,gBAAgB;CAMzB"}

@@ -31,4 +31,5 @@ "use strict";

const external_types_js_namespaceObject = require("../types.js");
const index_js_namespaceObject = require("../utils/index.js");
class PostHogLogs {
constructor(_instance, _config, _logger, _getContext, _onReady){
constructor(_instance, _config, _logger, _getContext, _onReady, _waitForStoragePersist = ()=>Promise.resolve()){
this._instance = _instance;

@@ -39,10 +40,21 @@ this._config = _config;

this._onReady = _onReady;
this._localEnabled = false !== _config.enabled;
this._waitForStoragePersist = _waitForStoragePersist;
this._flushPromise = null;
this._intervalWindowStart = 0;
this._intervalLogCount = 0;
this._droppedWarned = false;
this._maxBufferSize = _config.maxBufferSize;
this._flushIntervalMs = _config.flushIntervalMs;
this._maxBatchRecordsPerPost = _config.maxBatchRecordsPerPost;
this._rateCapWindowMs = _config.rateCapWindowMs;
this._maxLogsPerInterval = _config.maxLogsPerInterval;
}
captureLog(options) {
if (!this._localEnabled) return;
if (this._instance.optedOut) return;
if (!options?.body) return;
const record = (0, external_logs_utils_js_namespaceObject.buildOtlpLogRecord)(options, this._getContext());
const filtered = this._runBeforeSend(options);
if (null === filtered) return;
if (!filtered.body) return;
if (!this._checkRateLimit()) return;
const record = (0, external_logs_utils_js_namespaceObject.buildOtlpLogRecord)(filtered, this._getContext());
const entry = {

@@ -53,2 +65,92 @@ record

}
_runBeforeSend(options) {
const beforeSend = this._config.beforeSend;
if (!beforeSend) return options;
const fns = (0, index_js_namespaceObject.isArray)(beforeSend) ? beforeSend : [
beforeSend
];
let result = options;
for (const fn of fns)try {
const next = fn(result);
if (!next) {
this._logger.info("Log was rejected in beforeSend function");
return null;
}
result = next;
} catch (e) {
this._logger.error("Error in beforeSend function for log:", e);
}
return result;
}
_checkRateLimit() {
if (void 0 === this._maxLogsPerInterval) return true;
const now = Date.now();
const elapsed = now - this._intervalWindowStart;
if (elapsed >= this._rateCapWindowMs || elapsed < 0) {
this._intervalWindowStart = now;
this._intervalLogCount = 0;
this._droppedWarned = false;
}
if (this._intervalLogCount >= this._maxLogsPerInterval) {
if (!this._droppedWarned) {
this._logger.warn(`captureLog dropping logs: exceeded ${this._maxLogsPerInterval} logs per ${this._rateCapWindowMs}ms`);
this._droppedWarned = true;
}
return false;
}
this._intervalLogCount++;
return true;
}
async flush() {
if (this._flushPromise) return this._flushPromise;
this._flushPromise = this._flushInner().finally(()=>{
this._flushPromise = null;
});
return this._flushPromise;
}
async _flushInner() {
this._clearFlushTimer();
let queue = this._instance.getPersistedProperty(external_types_js_namespaceObject.PostHogPersistedProperty.LogsQueue) ?? [];
if (0 === queue.length) return;
const originalQueueLength = queue.length;
let sentCount = 0;
while(queue.length > 0 && sentCount < originalQueueLength){
const batchSize = Math.min(queue.length, this._maxBatchRecordsPerPost);
const batch = queue.slice(0, batchSize);
const records = batch.map((e)=>e.record);
const payload = (0, external_logs_utils_js_namespaceObject.buildOtlpLogsPayload)(records, this._buildResourceAttributes(), this._instance.getLibraryId(), this._instance.getLibraryVersion());
const outcome = await this._instance._sendLogsBatch(payload);
if ('too-large' === outcome.kind && batch.length > 1) {
this._maxBatchRecordsPerPost = Math.max(1, Math.floor(batch.length / 2));
this._logger.warn(`Received 413 when sending logs batch of size ${batch.length}, reducing batch size to ${this._maxBatchRecordsPerPost}`);
continue;
}
if ('retry-later' === outcome.kind) throw outcome.error;
if ('too-large' === outcome.kind) this._logger.warn("Dropping a single log record after 413 with batch size 1 \u2014 the record is larger than the server cap and cannot be split further.");
else if ('ok' === outcome.kind && this._maxBatchRecordsPerPost < this._config.maxBatchRecordsPerPost) this._maxBatchRecordsPerPost = Math.min(this._config.maxBatchRecordsPerPost, this._maxBatchRecordsPerPost + 1);
await this._persistQueueAdvance(batch.length);
queue = this._instance.getPersistedProperty(external_types_js_namespaceObject.PostHogPersistedProperty.LogsQueue) ?? [];
sentCount += batch.length;
if ('fatal' === outcome.kind) throw outcome.error;
}
}
async _persistQueueAdvance(consumed) {
const refreshed = this._instance.getPersistedProperty(external_types_js_namespaceObject.PostHogPersistedProperty.LogsQueue) ?? [];
this._instance.setPersistedProperty(external_types_js_namespaceObject.PostHogPersistedProperty.LogsQueue, refreshed.slice(consumed));
await this._waitForStoragePersist();
}
_buildResourceAttributes() {
return {
...this._config.resourceAttributes,
'service.name': this._config.serviceName || 'unknown_service',
...this._config.environment && {
'deployment.environment': this._config.environment
},
...this._config.serviceVersion && {
'service.version': this._config.serviceVersion
},
'telemetry.sdk.name': this._instance.getLibraryId(),
'telemetry.sdk.version': this._instance.getLibraryVersion()
};
}
_enqueue(entry) {

@@ -63,3 +165,44 @@ if (this._instance.optedOut) return;

this._instance.setPersistedProperty(external_types_js_namespaceObject.PostHogPersistedProperty.LogsQueue, queue);
if (queue.length >= this._maxBufferSize) return void this._flushInBackground();
if (!this._flushTimer) this._flushTimer = (0, index_js_namespaceObject.safeSetTimeout)(()=>{
this._flushTimer = void 0;
this._flushInBackground();
}, this._flushIntervalMs);
}
async shutdown(timeoutMs) {
this._clearFlushTimer();
const flushPromise = this.flush().catch(()=>{});
if (void 0 === timeoutMs) return void await flushPromise;
await Promise.race([
flushPromise,
new Promise((resolve)=>(0, index_js_namespaceObject.safeSetTimeout)(resolve, timeoutMs))
]);
}
async flushWithTimeout(timeoutMs) {
let timedOut = false;
const flushPromise = this.flush();
const timerPromise = new Promise((resolve)=>(0, index_js_namespaceObject.safeSetTimeout)(()=>{
timedOut = true;
resolve();
}, timeoutMs));
try {
await Promise.race([
flushPromise,
timerPromise
]);
} finally{
if (timedOut) flushPromise.catch(()=>{});
}
}
_flushInBackground() {
this.flush().catch((err)=>{
this._logger.error('PostHog logs flush failed:', err);
});
}
_clearFlushTimer() {
if (this._flushTimer) {
clearTimeout(this._flushTimer);
this._flushTimer = void 0;
}
}
}

@@ -66,0 +209,0 @@ exports.PostHogLogs = __webpack_exports__.PostHogLogs;

@@ -1,5 +0,6 @@

import { buildOtlpLogRecord } from "./logs-utils.mjs";
import { buildOtlpLogRecord, buildOtlpLogsPayload } from "./logs-utils.mjs";
import { PostHogPersistedProperty } from "../types.mjs";
import { isArray, safeSetTimeout } from "../utils/index.mjs";
class PostHogLogs {
constructor(_instance, _config, _logger, _getContext, _onReady){
constructor(_instance, _config, _logger, _getContext, _onReady, _waitForStoragePersist = ()=>Promise.resolve()){
this._instance = _instance;

@@ -10,10 +11,21 @@ this._config = _config;

this._onReady = _onReady;
this._localEnabled = false !== _config.enabled;
this._waitForStoragePersist = _waitForStoragePersist;
this._flushPromise = null;
this._intervalWindowStart = 0;
this._intervalLogCount = 0;
this._droppedWarned = false;
this._maxBufferSize = _config.maxBufferSize;
this._flushIntervalMs = _config.flushIntervalMs;
this._maxBatchRecordsPerPost = _config.maxBatchRecordsPerPost;
this._rateCapWindowMs = _config.rateCapWindowMs;
this._maxLogsPerInterval = _config.maxLogsPerInterval;
}
captureLog(options) {
if (!this._localEnabled) return;
if (this._instance.optedOut) return;
if (!options?.body) return;
const record = buildOtlpLogRecord(options, this._getContext());
const filtered = this._runBeforeSend(options);
if (null === filtered) return;
if (!filtered.body) return;
if (!this._checkRateLimit()) return;
const record = buildOtlpLogRecord(filtered, this._getContext());
const entry = {

@@ -24,2 +36,92 @@ record

}
_runBeforeSend(options) {
const beforeSend = this._config.beforeSend;
if (!beforeSend) return options;
const fns = isArray(beforeSend) ? beforeSend : [
beforeSend
];
let result = options;
for (const fn of fns)try {
const next = fn(result);
if (!next) {
this._logger.info("Log was rejected in beforeSend function");
return null;
}
result = next;
} catch (e) {
this._logger.error("Error in beforeSend function for log:", e);
}
return result;
}
_checkRateLimit() {
if (void 0 === this._maxLogsPerInterval) return true;
const now = Date.now();
const elapsed = now - this._intervalWindowStart;
if (elapsed >= this._rateCapWindowMs || elapsed < 0) {
this._intervalWindowStart = now;
this._intervalLogCount = 0;
this._droppedWarned = false;
}
if (this._intervalLogCount >= this._maxLogsPerInterval) {
if (!this._droppedWarned) {
this._logger.warn(`captureLog dropping logs: exceeded ${this._maxLogsPerInterval} logs per ${this._rateCapWindowMs}ms`);
this._droppedWarned = true;
}
return false;
}
this._intervalLogCount++;
return true;
}
async flush() {
if (this._flushPromise) return this._flushPromise;
this._flushPromise = this._flushInner().finally(()=>{
this._flushPromise = null;
});
return this._flushPromise;
}
async _flushInner() {
this._clearFlushTimer();
let queue = this._instance.getPersistedProperty(PostHogPersistedProperty.LogsQueue) ?? [];
if (0 === queue.length) return;
const originalQueueLength = queue.length;
let sentCount = 0;
while(queue.length > 0 && sentCount < originalQueueLength){
const batchSize = Math.min(queue.length, this._maxBatchRecordsPerPost);
const batch = queue.slice(0, batchSize);
const records = batch.map((e)=>e.record);
const payload = buildOtlpLogsPayload(records, this._buildResourceAttributes(), this._instance.getLibraryId(), this._instance.getLibraryVersion());
const outcome = await this._instance._sendLogsBatch(payload);
if ('too-large' === outcome.kind && batch.length > 1) {
this._maxBatchRecordsPerPost = Math.max(1, Math.floor(batch.length / 2));
this._logger.warn(`Received 413 when sending logs batch of size ${batch.length}, reducing batch size to ${this._maxBatchRecordsPerPost}`);
continue;
}
if ('retry-later' === outcome.kind) throw outcome.error;
if ('too-large' === outcome.kind) this._logger.warn("Dropping a single log record after 413 with batch size 1 \u2014 the record is larger than the server cap and cannot be split further.");
else if ('ok' === outcome.kind && this._maxBatchRecordsPerPost < this._config.maxBatchRecordsPerPost) this._maxBatchRecordsPerPost = Math.min(this._config.maxBatchRecordsPerPost, this._maxBatchRecordsPerPost + 1);
await this._persistQueueAdvance(batch.length);
queue = this._instance.getPersistedProperty(PostHogPersistedProperty.LogsQueue) ?? [];
sentCount += batch.length;
if ('fatal' === outcome.kind) throw outcome.error;
}
}
async _persistQueueAdvance(consumed) {
const refreshed = this._instance.getPersistedProperty(PostHogPersistedProperty.LogsQueue) ?? [];
this._instance.setPersistedProperty(PostHogPersistedProperty.LogsQueue, refreshed.slice(consumed));
await this._waitForStoragePersist();
}
_buildResourceAttributes() {
return {
...this._config.resourceAttributes,
'service.name': this._config.serviceName || 'unknown_service',
...this._config.environment && {
'deployment.environment': this._config.environment
},
...this._config.serviceVersion && {
'service.version': this._config.serviceVersion
},
'telemetry.sdk.name': this._instance.getLibraryId(),
'telemetry.sdk.version': this._instance.getLibraryVersion()
};
}
_enqueue(entry) {

@@ -34,4 +136,45 @@ if (this._instance.optedOut) return;

this._instance.setPersistedProperty(PostHogPersistedProperty.LogsQueue, queue);
if (queue.length >= this._maxBufferSize) return void this._flushInBackground();
if (!this._flushTimer) this._flushTimer = safeSetTimeout(()=>{
this._flushTimer = void 0;
this._flushInBackground();
}, this._flushIntervalMs);
}
async shutdown(timeoutMs) {
this._clearFlushTimer();
const flushPromise = this.flush().catch(()=>{});
if (void 0 === timeoutMs) return void await flushPromise;
await Promise.race([
flushPromise,
new Promise((resolve)=>safeSetTimeout(resolve, timeoutMs))
]);
}
async flushWithTimeout(timeoutMs) {
let timedOut = false;
const flushPromise = this.flush();
const timerPromise = new Promise((resolve)=>safeSetTimeout(()=>{
timedOut = true;
resolve();
}, timeoutMs));
try {
await Promise.race([
flushPromise,
timerPromise
]);
} finally{
if (timedOut) flushPromise.catch(()=>{});
}
}
_flushInBackground() {
this.flush().catch((err)=>{
this._logger.error('PostHog logs flush failed:', err);
});
}
_clearFlushTimer() {
if (this._flushTimer) {
clearTimeout(this._flushTimer);
this._flushTimer = void 0;
}
}
}
export { PostHogLogs };

@@ -1,2 +0,3 @@

import type { CaptureLogOptions, LogAttributeValue, LogSdkContext, LogSeverityLevel, OtlpAnyValue, OtlpKeyValue, OtlpLogRecord, OtlpLogsPayload, OtlpSeverityText } from '@posthog/types';
import type { CaptureLogOptions, LogAttributeValue, LogSeverityLevel, OtlpAnyValue, OtlpKeyValue, OtlpLogRecord, OtlpLogsPayload, OtlpSeverityText } from '@posthog/types';
import type { LogSdkContext } from './types';
export declare function getOtlpSeverityText(level: LogSeverityLevel): OtlpSeverityText;

@@ -3,0 +4,0 @@ export declare function getOtlpSeverityNumber(level: LogSeverityLevel): number;

@@ -1,1 +0,1 @@

{"version":3,"file":"logs-utils.d.ts","sourceRoot":"","sources":["../../src/logs/logs-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,eAAe,EAEf,gBAAgB,EACjB,MAAM,gBAAgB,CAAA;AAkBvB,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,CAE7E;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAErE;AAMD,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CAiCrE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,YAAY,EAAE,CAU3F;AAiBD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,aAAa,GAAG,aAAa,CAmDvG;AAMD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,aAAa,EAAE,EAC3B,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,eAAe,CAcjB"}
{"version":3,"file":"logs-utils.d.ts","sourceRoot":"","sources":["../../src/logs/logs-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,eAAe,EAEf,gBAAgB,EACjB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAkB5C,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,CAE7E;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAErE;AAMD,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CAiCrE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,YAAY,EAAE,CAU3F;AAiBD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,aAAa,GAAG,aAAa,CAmDvG;AAMD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,aAAa,EAAE,EAC3B,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,eAAe,CAcjB"}

@@ -1,2 +0,21 @@

export type { LogSeverityLevel, OtlpSeverityText, OtlpSeverityEntry, LogAttributeValue, LogAttributes, CaptureLogOptions, OtlpAnyValue, OtlpKeyValue, OtlpLogRecord, OtlpLogsPayload, LogSdkContext, } from '@posthog/types';
export type { LogSeverityLevel, OtlpSeverityText, OtlpSeverityEntry, LogAttributeValue, LogAttributes, CaptureLogOptions, OtlpAnyValue, OtlpKeyValue, OtlpLogRecord, OtlpLogsPayload, } from '@posthog/types';
/**
* SDK-internal context the host SDK passes to `buildOtlpLogRecord` at capture
* time. Each SDK populates the fields that apply to it: browser fills
* `currentUrl`, mobile fills `screenName` / `appState`. Missing fields are
* omitted from the OTLP record (no stray attributes).
*
* Internal to `@posthog/core` — customers don't see this in autocomplete.
*/
export interface LogSdkContext {
distinctId?: string;
sessionId?: string;
/** Web-only — current page URL */
currentUrl?: string;
/** Mobile-only — current screen / view name */
screenName?: string;
/** Mobile-only — app foreground/background state at capture time */
appState?: 'foreground' | 'background';
activeFeatureFlags?: string[];
}
import type { Logger as CaptureLoggerType } from '@posthog/types';

@@ -8,20 +27,133 @@ export type CaptureLogger = CaptureLoggerType;

}
/**
* Pre-send filter. Inspect, mutate, or drop a captured record before it
* enters the rate-cap or the queue. Return the (possibly transformed) record
* to keep it; return `null` to drop it.
*
* Configure as a single fn or an array. Arrays form a left-to-right chain:
* each fn receives the previous fn's return value. A `null` from any link
* short-circuits the chain and drops the record.
*
* Runs *before* the rate cap so dropped records don't consume the
* per-interval budget. Throwing fns are logged and skipped — the chain
* continues with the previous return value, so a buggy filter degrades to a
* no-op rather than crashing `captureLog()`.
*
* @example Redact secrets from log bodies
* ```ts
* logs: {
* beforeSend: (record) => ({
* ...record,
* body: record.body.replace(/api_key=\S+/g, 'api_key=[REDACTED]'),
* }),
* }
* ```
*
* @example Drop noisy debug logs in production
* ```ts
* logs: {
* beforeSend: (record) => (record.level === 'debug' ? null : record),
* }
* ```
*/
export type BeforeSendLogFn = (record: CaptureLogOptions) => CaptureLogOptions | null;
/**
* Configuration for the logs feature on `new PostHog(key, { logs: ... })`.
* All fields are optional; per-SDK defaults apply (mobile vs browser tune
* differently for cellular cost vs tab-suspension behavior).
*/
export interface PostHogLogsConfig {
enabled?: boolean;
/**
* Service name attached to every record as the OTLP `service.name`
* resource attribute. Used by the Logs UI for filtering / grouping.
* Default: `'unknown_service'`.
*/
serviceName?: string;
/**
* Service version attached as OTLP `service.version`. Useful for
* correlating regressions to specific app releases.
*/
serviceVersion?: string;
/**
* Deployment environment attached as OTLP `deployment.environment`
* (e.g. `'production'`, `'staging'`, `'dev'`).
*/
environment?: string;
/**
* Extra OTLP resource attributes attached to every record. Spread first;
* SDK-controlled keys (`service.name`, `telemetry.sdk.*`, RN's `os.*`)
* are layered on top so users cannot accidentally clobber them. Use the
* dedicated `serviceName` / `environment` / `serviceVersion` fields to
* override those keys.
*/
resourceAttributes?: Record<string, LogAttributeValue>;
/**
* How often the periodic background flush fires (ms). Records also flush
* eagerly when the buffer fills, on AppState changes (RN), and on
* `shutdown()`. Lower values trade battery/bandwidth for fresher data.
* Default: 10000 (RN) / 3000 (browser).
*/
flushIntervalMs?: number;
rateCapWindowMs?: number;
/**
* Max records held in memory before the queue evicts the oldest on push
* (FIFO). Bounds memory footprint and on-disk-queue size. When the buffer
* hits this size, an immediate flush is triggered to reclaim space; if
* the flush hasn't completed before the next capture, the oldest record
* is shifted out. Default: 100.
*/
maxBufferSize?: number;
maxLogsPerInterval?: number;
/**
* Max records per outbound POST. Keeps each request under the server's
* 2 MB cap. On a 413 response, the SDK halves this value, retries the
* same records, then ramps back up by 1 per healthy send. A 413 on a
* single-record batch drops the record (it's larger than the server can
* accept regardless of batch size). Default: 50 (RN) / 100 (browser).
*/
maxBatchRecordsPerPost?: number;
backgroundFlushBudgetMs?: number;
terminationFlushBudgetMs?: number;
beforeSend?: (record: CaptureLogOptions) => CaptureLogOptions | null;
/**
* Tumbling-window rate cap. Bounds how many records can be captured
* within a sliding (technically tumbling) time window. Records exceeding
* the cap are dropped synchronously at `captureLog()` (they never enter
* the buffer or consume bandwidth). A single warn line is logged per
* window when the cap is hit.
*
* Defaults are per-SDK; on RN the default is `{ maxLogs: 500, windowMs:
* 10000 }` (≈50 logs/sec ceiling, tuned for cellular bandwidth).
*
* @example Allow brief bursts up to 1000/min
* ```ts
* logs: { rateCap: { maxLogs: 1000, windowMs: 60000 } }
* ```
*
* @example Disable the cap entirely (unbounded)
* ```ts
* logs: { rateCap: { maxLogs: undefined } }
* ```
*/
rateCap?: {
/**
* Max records accepted per `windowMs` window. `undefined` = unbounded.
*/
maxLogs?: number;
/**
* Window length in ms. Tumbling, not sliding — the counter resets the
* first time a capture fires after the window expires.
*/
windowMs?: number;
};
/**
* Pre-send filter. See {@link BeforeSendLogFn} for shape and examples.
* Configure as a single function or a chain.
*/
beforeSend?: BeforeSendLogFn | BeforeSendLogFn[];
}
export interface ResolvedPostHogLogsConfig extends PostHogLogsConfig {
export interface ResolvedPostHogLogsConfig extends Omit<PostHogLogsConfig, 'rateCap'> {
maxBufferSize: number;
flushIntervalMs: number;
maxBatchRecordsPerPost: number;
rateCapWindowMs: number;
maxLogsPerInterval?: number;
backgroundFlushBudgetMs: number;
terminationFlushBudgetMs: number;
}
//# sourceMappingURL=types.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/logs/types.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,eAAe,EACf,aAAa,GACd,MAAM,gBAAgB,CAAA;AAKvB,OAAO,KAAK,EAAE,MAAM,IAAI,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACjE,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAA;AAE7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAKzF,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,aAAa,CAAA;CACtB;AAID,MAAM,WAAW,iBAAiB;IAEhC,OAAO,CAAC,EAAE,OAAO,CAAA;IAGjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;IAGtD,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAI/B,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,wBAAwB,CAAC,EAAE,MAAM,CAAA;IAIjC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,iBAAiB,GAAG,IAAI,CAAA;CACrE;AAKD,MAAM,WAAW,yBAA0B,SAAQ,iBAAiB;IAClE,aAAa,EAAE,MAAM,CAAA;CACtB"}
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/logs/types.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,eAAe,GAChB,MAAM,gBAAgB,CAAA;AAEvB;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,YAAY,GAAG,YAAY,CAAA;IACtC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC9B;AAKD,OAAO,KAAK,EAAE,MAAM,IAAI,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACjE,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAA;AAE7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEzF,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,aAAa,CAAA;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,iBAAiB,KAAK,iBAAiB,GAAG,IAAI,CAAA;AAErF;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IAEvB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;IAEtD;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IAExB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAE/B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,EAAE;QACR;;WAEG;QACH,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB;;;WAGG;QACH,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,CAAA;IAED;;;OAGG;IACH,UAAU,CAAC,EAAE,eAAe,GAAG,eAAe,EAAE,CAAA;CACjD;AAKD,MAAM,WAAW,yBAA0B,SAAQ,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC;IACnF,aAAa,EAAE,MAAM,CAAA;IACrB,eAAe,EAAE,MAAM,CAAA;IACvB,sBAAsB,EAAE,MAAM,CAAA;IAC9B,eAAe,EAAE,MAAM,CAAA;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,uBAAuB,EAAE,MAAM,CAAA;IAC/B,wBAAwB,EAAE,MAAM,CAAA;CACjC"}

@@ -0,1 +1,2 @@

import type { OtlpLogsPayload } from '@posthog/types';
import { SimpleEventEmitter } from './eventemitter';

@@ -6,2 +7,24 @@ import { PostHogFlagsResponse, PostHogCoreOptions, PostHogEventProperties, PostHogCaptureOptions, JsonType, PostHogRemoteConfig, FeatureFlagValue, PostHogFeatureFlagDetails, FeatureFlagDetail, SurveyResponse, PostHogFetchResponse, PostHogFetchOptions, PostHogPersistedProperty, Logger, GetFlagsResult } from './types';

export declare function logFlushError(err: any): Promise<void>;
/**
* Outcome of a logs batch send. Keeps HTTP error classification inside core
* (single source of truth — same policy events already use in `_flush()`) so
* PostHogLogs doesn't need to know about specific error types.
*
* - ok → records are accepted; drop them from the queue
* - too-large → 413; caller should halve batch size and retry same records
* - retry-later → network error; caller keeps records and retries next cycle
* - fatal → anything else (auth, malformed, etc.); caller drops the
* batch and surfaces the error
*/
export type SendLogsBatchOutcome = {
kind: 'ok';
} | {
kind: 'too-large';
} | {
kind: 'retry-later';
error: unknown;
} | {
kind: 'fatal';
error: unknown;
};
export declare enum QuotaLimitedFeature {

@@ -201,2 +224,13 @@ FeatureFlags = "feature_flags",

private _flush;
/**
* Sends a pre-built OTLP logs payload to `/i/v1/logs`. Returns a tagged
* outcome instead of throwing so PostHogLogs doesn't have to know about the
* core's error class hierarchy. Error classification lives here (single
* source of truth, same policy the events `_flush()` uses for its own
* 413 / network / fatal handling).
*
* 413 is passed through as `too-large` (not auto-retried) so the caller can
* shrink `maxBatchRecordsPerPost` and retry the same records.
*/
_sendLogsBatch(payload: OtlpLogsPayload): Promise<SendLogsBatchOutcome>;
private fetchWithRetry;

@@ -203,0 +237,0 @@ _shutdown(shutdownTimeoutMs?: number): Promise<void>;

@@ -1,1 +0,1 @@

{"version":3,"file":"posthog-core-stateless.d.ts","sourceRoot":"","sources":["../src/posthog-core-stateless.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"posthog-core-stateless.d.ts","sourceRoot":"","sources":["../src/posthog-core-stateless.ts"],"names":[],"mappings":"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"}

@@ -639,2 +639,41 @@ "use strict";

}
async _sendLogsBatch(payload) {
const serialized = JSON.stringify(payload);
const url = `${this.host}/i/v1/logs?token=${encodeURIComponent(this.apiKey)}`;
const gzippedPayload = this.disableCompression ? null : await (0, external_gzip_js_namespaceObject.gzipCompress)(serialized, this.isDebug);
const fetchOptions = {
method: 'POST',
headers: {
...this.getCustomHeaders(),
'Content-Type': 'application/json',
...null !== gzippedPayload && {
'Content-Encoding': 'gzip'
}
},
body: gzippedPayload || serialized
};
try {
await this.fetchWithRetry(url, fetchOptions, {
retryCheck: (err)=>{
if (isPostHogFetchContentTooLargeError(err)) return false;
return isPostHogFetchError(err);
}
});
return {
kind: 'ok'
};
} catch (err) {
if (isPostHogFetchContentTooLargeError(err)) return {
kind: 'too-large'
};
if (err instanceof PostHogFetchNetworkError) return {
kind: 'retry-later',
error: err
};
return {
kind: 'fatal',
error: err
};
}
}
async fetchWithRetry(url, options, retryOptions, requestTimeout) {

@@ -641,0 +680,0 @@ const body = options.body ? options.body : '';

@@ -608,2 +608,41 @@ import { SimpleEventEmitter } from "./eventemitter.mjs";

}
async _sendLogsBatch(payload) {
const serialized = JSON.stringify(payload);
const url = `${this.host}/i/v1/logs?token=${encodeURIComponent(this.apiKey)}`;
const gzippedPayload = this.disableCompression ? null : await gzipCompress(serialized, this.isDebug);
const fetchOptions = {
method: 'POST',
headers: {
...this.getCustomHeaders(),
'Content-Type': 'application/json',
...null !== gzippedPayload && {
'Content-Encoding': 'gzip'
}
},
body: gzippedPayload || serialized
};
try {
await this.fetchWithRetry(url, fetchOptions, {
retryCheck: (err)=>{
if (isPostHogFetchContentTooLargeError(err)) return false;
return isPostHogFetchError(err);
}
});
return {
kind: 'ok'
};
} catch (err) {
if (isPostHogFetchContentTooLargeError(err)) return {
kind: 'too-large'
};
if (err instanceof PostHogFetchNetworkError) return {
kind: 'retry-later',
error: err
};
return {
kind: 'fatal',
error: err
};
}
}
async fetchWithRetry(url, options, retryOptions, requestTimeout) {

@@ -610,0 +649,0 @@ const body = options.body ? options.body : '';

@@ -1,1 +0,1 @@

{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../src/testing/test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAEhC,eAAO,MAAM,IAAI,GAAU,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CAElD,CAAA;AAED,eAAO,MAAM,eAAe,QAAa,OAAO,CAAC,IAAI,CAQpD,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,UAAU,GAAG,KAAG,GAIzC,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,CAAC,OAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAM5E,CAAA;AAED,eAAO,MAAM,KAAK,GAAI,IAAI,MAAM,KAAG,OAAO,CAAC,IAAI,CAI9C,CAAA;AAED,eAAO,MAAM,gBAAgB,QAAO,MAQnC,CAAA"}
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../../src/testing/test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAEhC,eAAO,MAAM,IAAI,GAAU,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CAElD,CAAA;AAED,eAAO,MAAM,eAAe,QAAa,OAAO,CAAC,IAAI,CAQpD,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,UAAU,GAAG,KAAG,GAIzC,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,CAAC,OAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAM5E,CAAA;AAED,eAAO,MAAM,KAAK,GAAI,IAAI,MAAM,KAAG,OAAO,CAAC,IAAI,CAI9C,CAAA;AAED,eAAO,MAAM,gBAAgB,QAAO,MASnC,CAAA"}

@@ -63,2 +63,3 @@ "use strict";

const createMockLogger = ()=>({
debug: jest.fn((...args)=>console.debug(...args)),
info: jest.fn((...args)=>console.log(...args)),

@@ -65,0 +66,0 @@ warn: jest.fn((...args)=>console.warn(...args)),

@@ -30,2 +30,3 @@ const wait = async (t)=>{

const createMockLogger = ()=>({
debug: jest.fn((...args)=>console.debug(...args)),
info: jest.fn((...args)=>console.log(...args)),

@@ -32,0 +33,0 @@ warn: jest.fn((...args)=>console.warn(...args)),

@@ -321,2 +321,11 @@ export type PostHogCoreOptions = {

};
/**
* Logs feature remote config. When a map, `captureConsoleLogs` (boolean)
* is the local opt-in flag for `console.*` autocapture (read by the JS
* SDK's `PostHogLogs` extension to decide whether to load the autocapture
* bundle).
*/
logs?: boolean | {
[key: string]: JsonType;
};
};

@@ -538,6 +547,21 @@ export type FeatureFlagValue = string | boolean;

}
export interface SurveyTranslation {
name?: string;
thankYouMessageHeader?: string;
thankYouMessageDescription?: string;
thankYouMessageCloseButtonText?: string;
}
export interface SurveyQuestionTranslation {
question?: string;
description?: string | null;
buttonText?: string;
link?: string | null;
lowerBoundLabel?: string;
upperBoundLabel?: string;
choices?: string[];
}
type SurveyQuestionBase = {
question: string;
id: string;
description?: string;
description?: string | null;
descriptionContentType?: SurveyQuestionDescriptionContentType;

@@ -549,2 +573,3 @@ optional?: boolean;

validation?: SurveyValidationRule[];
translations?: Record<string, SurveyQuestionTranslation>;
};

@@ -556,3 +581,3 @@ export type BasicSurveyQuestion = SurveyQuestionBase & {

type: SurveyQuestionType.Link;
link?: string;
link?: string | null;
};

@@ -608,2 +633,4 @@ export type RatingSurveyQuestion = SurveyQuestionBase & {

};
export type SurveyResponseValue = string | number | string[] | null;
export type SurveyResponses = Record<string, SurveyResponseValue>;
export type SurveyCallback = (surveys: Survey[]) => void;

@@ -646,2 +673,3 @@ export declare enum SurveyMatchType {

type: SurveyType;
translations?: Record<string, SurveyTranslation>;
feature_flag_keys?: {

@@ -705,2 +733,3 @@ key: string;

export type Logger = {
debug: (...args: any[]) => void;
info: (...args: any[]) => void;

@@ -707,0 +736,0 @@ warn: (...args: any[]) => void;

@@ -1,1 +0,1 @@

{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
import { Logger } from '../types';
type ConsoleLike = {
debug: (...args: any[]) => void;
log: (...args: any[]) => void;
warn: (...args: any[]) => void;
error: (...args: any[]) => void;
debug: (...args: any[]) => void;
};

@@ -8,0 +8,0 @@ export declare const _createLogger: (prefix: string, maybeCall: (fn: () => void) => void, consoleLike: ConsoleLike) => Logger;

@@ -1,1 +0,1 @@

{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAGjC,KAAK,WAAW,GAAG;IACjB,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAC7B,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAC9B,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAC/B,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;CAChC,CAAA;AAYD,eAAO,MAAM,aAAa,GACxB,QAAQ,MAAM,EACd,WAAW,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,EACnC,aAAa,WAAW,KACvB,MA6BF,CAAA;AAID,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,GAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAkB,UAE7F"}
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAGjC,KAAK,WAAW,GAAG;IACjB,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAC/B,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAC7B,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;IAC9B,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;CAChC,CAAA;AAYD,eAAO,MAAM,aAAa,GACxB,QAAQ,MAAM,EACd,WAAW,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,EACnC,aAAa,WAAW,KACvB,MAiCF,CAAA;AAID,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,GAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAkB,UAE7F"}

@@ -47,2 +47,5 @@ "use strict";

const logger = {
debug: (...args)=>{
_log('debug', ...args);
},
info: (...args)=>{

@@ -49,0 +52,0 @@ _log('log', ...args);

@@ -18,2 +18,5 @@ function createConsole(consoleLike = console) {

const logger = {
debug: (...args)=>{
_log('debug', ...args);
},
info: (...args)=>{

@@ -20,0 +23,0 @@ _log('log', ...args);

{
"name": "@posthog/core",
"version": "1.27.9",
"version": "1.28.0",
"license": "MIT",

@@ -8,2 +8,21 @@ "main": "dist/index.js",

"types": "dist/index.d.ts",
"typesVersions": {
"*": {
"error-tracking": [
"dist/error-tracking/index.d.ts"
],
"surveys": [
"dist/surveys/index.d.ts"
],
"testing": [
"dist/testing/index.d.ts"
],
"utils": [
"dist/utils/index.d.ts"
],
"vendor/*": [
"dist/vendor/*.d.ts"
]
}
},
"repository": {

@@ -40,2 +59,7 @@ "type": "git",

},
"./surveys": {
"types": "./dist/surveys/index.d.ts",
"require": "./dist/surveys/index.js",
"import": "./dist/surveys/index.mjs"
},
"./utils": {

@@ -48,3 +72,3 @@ "types": "./dist/utils/index.d.ts",

"dependencies": {
"@posthog/types": "1.372.5"
"@posthog/types": "1.372.6"
},

@@ -51,0 +75,0 @@ "devDependencies": {

@@ -14,3 +14,14 @@ export { getFeatureFlagValue } from './featureFlagUtils'

export { PostHogLogs } from './logs'
export type { BufferedLogEntry, PostHogLogsConfig, ResolvedPostHogLogsConfig } from './logs/types'
export type {
BeforeSendLogFn,
BufferedLogEntry,
CaptureLogger,
LogSdkContext,
PostHogLogsConfig,
ResolvedPostHogLogsConfig,
} from './logs/types'
// Re-export the user-facing OTLP log types straight from `@posthog/types`
// via the `logs/types` barrel so consumers don't have to import from two
// packages to type their `captureLog` calls.
export type { CaptureLogOptions, LogAttributeValue, LogAttributes, LogSeverityLevel } from './logs/types'
export { uuidv7 } from './vendor/uuidv7'

@@ -17,0 +28,0 @@ export * from './posthog-core'

import { PostHogPersistedProperty } from '../types'
import type { Logger } from '../types'
import { PostHogLogs } from './index'
import type { BufferedLogEntry, PostHogLogsConfig, ResolvedPostHogLogsConfig } from './types'
import type { BufferedLogEntry, ResolvedPostHogLogsConfig } from './types'
// Default resolved config for tests — mirrors what each SDK would build by
// merging user config onto its own defaults. Test-only fixture; the real
// defaults live per-SDK.
// defaults live per-SDK. Takes the resolved (flat) shape directly so tests
// can override `maxLogsPerInterval` / `rateCapWindowMs` without going through
// the public `rateCap: { maxLogs, windowMs }` wrapper.
const DEFAULT_MAX_BUFFER_SIZE = 100
const resolveForTest = (partial?: PostHogLogsConfig): ResolvedPostHogLogsConfig => ({
const DEFAULT_FLUSH_INTERVAL_MS = 10000
const DEFAULT_MAX_BATCH_RECORDS_PER_POST = 50
const DEFAULT_RATE_CAP_WINDOW_MS = 10000
const DEFAULT_BACKGROUND_FLUSH_BUDGET_MS = 25000
const DEFAULT_TERMINATION_FLUSH_BUDGET_MS = 2000
const resolveForTest = (partial?: Partial<ResolvedPostHogLogsConfig>): ResolvedPostHogLogsConfig => ({
...partial,
maxBufferSize: partial?.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,
flushIntervalMs: partial?.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS,
maxBatchRecordsPerPost: partial?.maxBatchRecordsPerPost ?? DEFAULT_MAX_BATCH_RECORDS_PER_POST,
rateCapWindowMs: partial?.rateCapWindowMs ?? DEFAULT_RATE_CAP_WINDOW_MS,
backgroundFlushBudgetMs: DEFAULT_BACKGROUND_FLUSH_BUDGET_MS,
terminationFlushBudgetMs: DEFAULT_TERMINATION_FLUSH_BUDGET_MS,
// Uncapped by default so existing tests aren't affected. The rate-limit
// describe block opts in explicitly via { maxLogsPerInterval: N }.
maxLogsPerInterval: partial?.maxLogsPerInterval,
})

@@ -23,2 +38,4 @@

getSessionId: jest.fn(() => 'sess-456'),
getLibraryId: jest.fn(() => 'posthog-core-tests'),
getLibraryVersion: jest.fn(() => '0.0.0-test'),
getPersistedProperty: jest.fn((key: string) => store[key]),

@@ -32,2 +49,3 @@ setPersistedProperty: jest.fn((key: string, value: any) => {

}),
_sendLogsBatch: jest.fn(() => Promise.resolve({ kind: 'ok' })),
addPendingPromise: jest.fn(<T>(promise: Promise<T>) => promise),

@@ -46,2 +64,3 @@ _store: store,

const logger: any = {
debug: jest.fn(),
info: jest.fn(),

@@ -193,6 +212,6 @@ warn: jest.fn(),

it('is a no-op when config.enabled is false', () => {
it('captures unconditionally — only optedOut, missing body, and beforeSend can drop', () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ enabled: false }),
resolveForTest(),
logger,

@@ -202,14 +221,2 @@ getContextFor(mockInstance),

)
logs.captureLog({ body: 'should be dropped' })
expect(readQueue(mockInstance)).toHaveLength(0)
})
it('captures when config is provided with enabled undefined (defaults to true)', () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest({}),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'kept' })

@@ -365,2 +372,869 @@ expect(readQueue(mockInstance)).toHaveLength(1)

})
describe('flush', () => {
it('is a no-op when the queue is empty', async () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest(),
logger,
getContextFor(mockInstance),
immediateOnReady
)
await logs.flush()
expect(mockInstance._sendLogsBatch).not.toHaveBeenCalled()
})
it('drains the queue and sends an OTLP payload with resource + scope attrs', async () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ serviceName: 'my-service', environment: 'prod', serviceVersion: '1.2.3' }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'one' })
logs.captureLog({ body: 'two' })
await logs.flush()
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(1)
const payload = mockInstance._sendLogsBatch.mock.calls[0][0]
const resourceAttrs = Object.fromEntries(
payload.resourceLogs[0].resource.attributes.map((a: any) => [a.key, a.value])
)
expect(resourceAttrs['service.name']).toEqual({ stringValue: 'my-service' })
expect(resourceAttrs['deployment.environment']).toEqual({ stringValue: 'prod' })
expect(resourceAttrs['service.version']).toEqual({ stringValue: '1.2.3' })
// OTLP-standard SDK identification — pulled from the instance's
// getLibraryId/Version so every SDK self-identifies.
expect(resourceAttrs['telemetry.sdk.name']).toEqual({ stringValue: 'posthog-core-tests' })
expect(resourceAttrs['telemetry.sdk.version']).toEqual({ stringValue: '0.0.0-test' })
const scope = payload.resourceLogs[0].scopeLogs[0].scope
expect(scope).toEqual({ name: 'posthog-core-tests', version: '0.0.0-test' })
const bodies = payload.resourceLogs[0].scopeLogs[0].logRecords.map((r: any) => r.body.stringValue)
expect(bodies).toEqual(['one', 'two'])
expect(readQueue(mockInstance)).toHaveLength(0)
})
it('defaults service.name to "unknown_service" when not configured', async () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest(),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'hi' })
await logs.flush()
const attrs = Object.fromEntries(
mockInstance._sendLogsBatch.mock.calls[0][0].resourceLogs[0].resource.attributes.map((a: any) => [
a.key,
a.value,
])
)
expect(attrs['service.name']).toEqual({ stringValue: 'unknown_service' })
})
it('SDK-controlled telemetry.sdk.* and service.name win over user resourceAttributes', async () => {
// Most logs backends index on these keys for routing, SDK-version
// dashboards, and bug-correlation. Letting a stray user key clobber
// them silently breaks ingestion attribution, so the layout puts
// user attrs first and SDK identity attrs on top.
const logs = new PostHogLogs(
mockInstance,
resolveForTest({
resourceAttributes: {
'telemetry.sdk.name': 'my-wrapper',
'service.name': 'user-supplied-service',
// Non-protected user keys still pass through.
'host.name': 'my-host',
},
}),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'hi' })
await logs.flush()
const attrs = Object.fromEntries(
mockInstance._sendLogsBatch.mock.calls[0][0].resourceLogs[0].resource.attributes.map((a: any) => [
a.key,
a.value,
])
)
expect(attrs['telemetry.sdk.name']).toEqual({ stringValue: 'posthog-core-tests' })
expect(attrs['telemetry.sdk.version']).toEqual({ stringValue: '0.0.0-test' })
expect(attrs['service.name']).toEqual({ stringValue: 'unknown_service' })
expect(attrs['host.name']).toEqual({ stringValue: 'my-host' })
})
it('splits a large queue into multiple batches of maxBatchRecordsPerPost and persists after each', async () => {
const sendOrder: number[] = []
let persistCallsBeforeSecondSend = 0
mockInstance._sendLogsBatch = jest.fn(async (payload: any) => {
// Record the persist count *at the start of* send #2. The first send
// must have already persisted its queue advance by then — otherwise a
// crash between sends could double-send the first batch.
if (sendOrder.length === 1) {
persistCallsBeforeSecondSend = mockInstance.setPersistedProperty.mock.calls.length
}
sendOrder.push(payload.resourceLogs[0].scopeLogs[0].logRecords.length)
return { kind: 'ok' }
})
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxBatchRecordsPerPost: 2, maxBufferSize: 10 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
for (let i = 0; i < 5; i++) {
logs.captureLog({ body: `msg-${i}` })
}
await logs.flush()
// 2 + 2 + 1 = 5 records across 3 POSTs
expect(sendOrder).toEqual([2, 2, 1])
// After the first send, the queue must have been persisted before the second send —
// otherwise a crash between sends could double-send the first batch.
expect(persistCallsBeforeSecondSend).toBeGreaterThan(5 /* enqueue writes */)
expect(readQueue(mockInstance)).toHaveLength(0)
})
it('halves maxBatchRecordsPerPost and retries the same records on too-large outcome', async () => {
const sendSizes: number[] = []
mockInstance._sendLogsBatch = jest.fn(async (payload: any) => {
const size = payload.resourceLogs[0].scopeLogs[0].logRecords.length
sendSizes.push(size)
if (sendSizes.length === 1) {
return { kind: 'too-large' }
}
return { kind: 'ok' }
})
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxBatchRecordsPerPost: 4, maxBufferSize: 10 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
for (let i = 0; i < 4; i++) {
logs.captureLog({ body: `msg-${i}` })
}
await logs.flush()
// First POST: 4 records → too-large. Retry with halved cap = 2, so: 2 + 2.
expect(sendSizes).toEqual([4, 2, 2])
expect(readQueue(mockInstance)).toHaveLength(0)
})
it('ramps maxBatchRecordsPerPost back toward the configured cap after a healthy streak', async () => {
// Reproduces the Greptile P1 concern: a one-off oversized payload
// should not permanently degrade throughput. After a 413 halves the
// cap, each healthy send grows it back by 1 until the configured
// maximum is reached.
const sendSizes: number[] = []
mockInstance._sendLogsBatch = jest.fn(async (payload: any) => {
const size = payload.resourceLogs[0].scopeLogs[0].logRecords.length
sendSizes.push(size)
// First POST is rejected as too-large; everything else succeeds.
if (sendSizes.length === 1) {
return { kind: 'too-large' }
}
return { kind: 'ok' }
})
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxBatchRecordsPerPost: 4, maxBufferSize: 100 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
// Enqueue plenty so the recovery has room to ramp.
for (let i = 0; i < 16; i++) {
logs.captureLog({ body: `msg-${i}` })
}
await logs.flush()
// First POST: 4 records → too-large. Cap halves to 2. From there each
// healthy send grows the cap by 1 toward the configured 4:
// sizes: [4 (413), 2, 3, 4, 4, ...] (the trailing 3 drains the
// remainder of the 16-record queue).
expect(sendSizes).toEqual([4, 2, 3, 4, 4, 3])
expect(readQueue(mockInstance)).toHaveLength(0)
})
it('drops the only record when too-large arrives on a batch of size 1', async () => {
mockInstance._sendLogsBatch = jest.fn(() => Promise.resolve({ kind: 'too-large' }))
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxBatchRecordsPerPost: 1 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'too-big' })
await logs.flush()
// Batch of 1 that's rejected as too-large is permanent — drop it rather
// than spin on the same record forever.
expect(readQueue(mockInstance)).toHaveLength(0)
})
it('warns explicitly when dropping a size-1 413 (visibility for the lost record)', async () => {
mockInstance._sendLogsBatch = jest.fn(() => Promise.resolve({ kind: 'too-large' }))
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxBatchRecordsPerPost: 1 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'oversized' })
await logs.flush()
expect(logger.warn).toHaveBeenCalledWith(
expect.stringContaining('Dropping a single log record after 413 with batch size 1')
)
})
it('keeps draining the queue after a size-1 413 drop (one bad record does not stall the pipeline)', async () => {
// First record returns too-large with size 1 (drops and warns), then
// the rest of the queue should continue flushing normally.
let callCount = 0
mockInstance._sendLogsBatch = jest.fn(() => {
callCount++
return Promise.resolve(callCount === 1 ? { kind: 'too-large' } : { kind: 'ok' })
})
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxBatchRecordsPerPost: 1, maxBufferSize: 10 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'oversized' })
logs.captureLog({ body: 'ok-1' })
logs.captureLog({ body: 'ok-2' })
await logs.flush()
// Three sends: oversized (dropped), ok-1, ok-2. Queue is empty.
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(3)
expect(readQueue(mockInstance)).toHaveLength(0)
})
it('size-1 413 retry-shrink path: starts at maxBatchRecordsPerPost, halves to 1, drops at 1', async () => {
// Realistic flow: batch=N gets too-large, halves to N/2, halves to 1,
// then 413 on size 1 is the permanent drop. Verifies the cap actually
// shrinks all the way down before the size-1 drop fires.
const sendSizes: number[] = []
mockInstance._sendLogsBatch = jest.fn(async (payload: any) => {
const size = payload.resourceLogs[0].scopeLogs[0].logRecords.length
sendSizes.push(size)
return { kind: 'too-large' }
})
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxBatchRecordsPerPost: 4, maxBufferSize: 10 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
// Single oversized record. With maxBatchRecordsPerPost=4 but only 1 record
// in the queue, the first send is size 1 — going straight to the drop path.
logs.captureLog({ body: 'huge' })
await logs.flush()
// Single send of size 1, dropped immediately (no halving rounds because
// batch was already at 1).
expect(sendSizes).toEqual([1])
expect(readQueue(mockInstance)).toHaveLength(0)
expect(logger.warn).toHaveBeenCalledWith(
expect.stringContaining('Dropping a single log record after 413 with batch size 1')
)
})
it('keeps records in the queue on retry-later outcome and re-throws the carried error', async () => {
const netErr = new Error('offline')
mockInstance._sendLogsBatch = jest.fn(() => Promise.resolve({ kind: 'retry-later', error: netErr }))
const logs = new PostHogLogs(
mockInstance,
resolveForTest(),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'queued' })
await expect(logs.flush()).rejects.toBe(netErr)
expect(readQueue(mockInstance)).toHaveLength(1)
})
it('drops the batch on fatal outcome and re-throws the carried error', async () => {
const bogus = new Error('malformed')
mockInstance._sendLogsBatch = jest.fn(() => Promise.resolve({ kind: 'fatal', error: bogus }))
const logs = new PostHogLogs(
mockInstance,
resolveForTest(),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'doomed' })
await expect(logs.flush()).rejects.toBe(bogus)
expect(readQueue(mockInstance)).toHaveLength(0)
})
it('awaits _waitForStoragePersist between batches so a crash can’t replay records', async () => {
const sequence: string[] = []
mockInstance._sendLogsBatch = jest.fn(async (payload: any) => {
sequence.push(`send:${payload.resourceLogs[0].scopeLogs[0].logRecords.length}`)
return { kind: 'ok' }
})
const waitForStoragePersist = jest.fn(async () => {
sequence.push('waitForPersist')
})
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxBatchRecordsPerPost: 2, maxBufferSize: 10 }),
logger,
getContextFor(mockInstance),
immediateOnReady,
waitForStoragePersist
)
for (let i = 0; i < 3; i++) {
logs.captureLog({ body: `msg-${i}` })
}
await logs.flush()
// Send 2 → waitForPersist → send 1 → waitForPersist. If the wait
// landed out-of-order (e.g. before send), a crash mid-batch could
// replay records on the next startup.
expect(sequence).toEqual(['send:2', 'waitForPersist', 'send:1', 'waitForPersist'])
expect(waitForStoragePersist).toHaveBeenCalledTimes(2)
})
it('serializes concurrent flush calls rather than racing them', async () => {
let resolveFirst: (v: any) => void = () => {}
mockInstance._sendLogsBatch = jest.fn(
() =>
new Promise((r) => {
resolveFirst = r
})
)
const logs = new PostHogLogs(
mockInstance,
resolveForTest(),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'a' })
const first = logs.flush()
const second = logs.flush()
// Both callers observe the same in-flight promise, so only one POST happens.
resolveFirst({ kind: 'ok' })
await Promise.all([first, second])
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(1)
})
})
describe('flush triggers', () => {
beforeEach(() => jest.useFakeTimers())
afterEach(() => jest.useRealTimers())
it('fires a flush when the buffer hits maxBufferSize', () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxBufferSize: 3 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'a' })
logs.captureLog({ body: 'b' })
expect(mockInstance._sendLogsBatch).not.toHaveBeenCalled()
logs.captureLog({ body: 'c' })
// Threshold trigger fires `flush()` fire-and-forget; the call happens
// synchronously on the hot path.
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(1)
})
it('schedules one timer per idle window and fires flush on expiry', () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ flushIntervalMs: 5000 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'first' })
logs.captureLog({ body: 'second' })
logs.captureLog({ body: 'third' })
// Only one timer armed, not three — subsequent enqueues inside the
// window must not push the flush out.
expect(mockInstance._sendLogsBatch).not.toHaveBeenCalled()
jest.advanceTimersByTime(4999)
expect(mockInstance._sendLogsBatch).not.toHaveBeenCalled()
jest.advanceTimersByTime(1)
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(1)
})
it('does not schedule a timer for the threshold-triggered path', () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxBufferSize: 2, flushIntervalMs: 5000 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'a' })
logs.captureLog({ body: 'b' })
// Threshold path flushed already; advancing time must not trigger a second send.
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(1)
jest.advanceTimersByTime(5000)
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(1)
})
})
describe('shutdown', () => {
beforeEach(() => jest.useFakeTimers())
afterEach(() => jest.useRealTimers())
it('drains the queue and clears any armed flush timer', async () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ flushIntervalMs: 5000 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'a' })
// A timer is now armed — shutdown must cancel it so the process can
// exit cleanly even if the final flush triggers a duplicate send.
await logs.shutdown()
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(1)
// Advancing past the original interval must not produce a second flush.
jest.advanceTimersByTime(10000)
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(1)
})
it('swallows flush errors so shutdown can complete', async () => {
mockInstance._sendLogsBatch = jest.fn(() => Promise.resolve({ kind: 'fatal', error: new Error('boom') }))
const logs = new PostHogLogs(
mockInstance,
resolveForTest(),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'doomed' })
await expect(logs.shutdown()).resolves.toBeUndefined()
})
it('is a no-op when the queue is empty and no timer is armed', async () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest(),
logger,
getContextFor(mockInstance),
immediateOnReady
)
await logs.shutdown()
expect(mockInstance._sendLogsBatch).not.toHaveBeenCalled()
})
it('called twice is idempotent (second call is a no-op once queue drains)', async () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest(),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'x' })
await logs.shutdown()
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(1)
// Queue is empty now — a second shutdown shouldn't re-send.
await logs.shutdown()
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(1)
})
it('while a flush is in flight, the shared promise coordinates a single drain', async () => {
let resolveFirst: (v: any) => void = () => {}
mockInstance._sendLogsBatch = jest.fn(
() =>
new Promise((r) => {
resolveFirst = r
})
)
const logs = new PostHogLogs(
mockInstance,
resolveForTest(),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'a' })
// Real timers only here — shutdown(timeoutMs) path uses safeSetTimeout,
// which is incompatible with the default `jest.useFakeTimers()`.
jest.useRealTimers()
const flushP = logs.flush()
const shutdownP = logs.shutdown()
resolveFirst({ kind: 'ok' })
await Promise.all([flushP, shutdownP])
// Both callers joined the same in-flight flush — no double-send.
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(1)
})
it('races the final flush against timeoutMs so a stalled send does not hang shutdown', async () => {
jest.useRealTimers()
// _sendLogsBatch never resolves — the budget must force shutdown to return.
mockInstance._sendLogsBatch = jest.fn(() => new Promise(() => {}))
const logs = new PostHogLogs(
mockInstance,
resolveForTest(),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'stuck' })
const start = Date.now()
await logs.shutdown(30)
const elapsed = Date.now() - start
// Loose upper bound — just prove we didn't wait forever.
expect(elapsed).toBeLessThan(500)
})
it('propagates a _waitForStoragePersist rejection out of flush (so callers can react)', async () => {
const persistErr = new Error('disk is gone')
const logs = new PostHogLogs(
mockInstance,
resolveForTest(),
logger,
getContextFor(mockInstance),
immediateOnReady,
// Persist fails AFTER the HTTP send succeeds — records were sent but
// the queue-advance didn't reach disk. Surface the error so the
// caller knows a retry on restart may re-send.
() => Promise.reject(persistErr)
)
logs.captureLog({ body: 'sent-but-not-persisted' })
await expect(logs.flush()).rejects.toBe(persistErr)
})
})
describe('beforeSend hook', () => {
// Helper that hides the constructor boilerplate so the table-driven
// cases below can be a single line of setup each.
const makeLogs = (beforeSend: PostHogLogsConfig['beforeSend']): PostHogLogs =>
new PostHogLogs(
mockInstance,
resolveForTest({ beforeSend }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
// Cases that share a "captureLog → assert queue body" shape. Bespoke
// assertions (logger expectations, throw-doesn't-crash, post-chain
// continuation after throw) live in their own `it` blocks below — those
// were warping the table when forced into it.
type Case = {
name: string
beforeSend: PostHogLogsConfig['beforeSend']
input: string
expectedQueueLen: number
expectedBody?: string
}
const cases: Case[] = [
{
name: 'transforms body when fn returns mutated value',
beforeSend: (r) => ({ ...r, body: r.body.toUpperCase() }),
input: 'hello',
expectedQueueLen: 1,
expectedBody: 'HELLO',
},
{
name: 'drops the record when fn returns null',
beforeSend: () => null,
input: 'silent',
expectedQueueLen: 0,
},
{
name: 'chains an array left-to-right (each fn sees previous result)',
beforeSend: [
(r) => ({ ...r, body: `${r.body}-1` }),
(r) => ({ ...r, body: `${r.body}-2` }),
(r) => ({ ...r, body: `${r.body}-3` }),
],
input: 'x',
expectedQueueLen: 1,
expectedBody: 'x-1-2-3',
},
{
name: 'short-circuits the chain when any fn returns null',
beforeSend: [(r) => r, () => null, (r) => r],
input: 'dropped',
expectedQueueLen: 0,
},
{
name: 'treats an empty body returned by beforeSend as a drop',
beforeSend: (r) => ({ ...r, body: '' }),
input: 'will-be-emptied',
expectedQueueLen: 0,
},
]
it.each(cases)('$name', ({ beforeSend, input, expectedQueueLen, expectedBody }) => {
const logs = makeLogs(beforeSend)
logs.captureLog({ body: input })
const queue = readQueue(mockInstance)
expect(queue).toHaveLength(expectedQueueLen)
if (expectedBody !== undefined) {
expect(queue[0].record.body.stringValue).toBe(expectedBody)
}
})
it('logs an info line when a fn returns null', () => {
// Carved out because the table only asserts queue shape; this
// verifies the diagnostic path that warns the user a record was
// dropped by their filter (no other knob to surface that).
const logs = makeLogs(() => null)
logs.captureLog({ body: 'silent' })
expect(logger.info).toHaveBeenCalledWith('Log was rejected in beforeSend function')
})
it('never crashes the caller when a fn throws — the chain continues with the prior result', () => {
// Bespoke: needs to verify (a) no throw escapes captureLog, (b) the
// chain continues with the previous result so a buggy filter degrades
// to a no-op, and (c) the failure is logged. Doesn't fit the table.
const thrower = jest.fn(() => {
throw new Error('bad filter')
})
const after = jest.fn((r: any) => ({ ...r, body: `${r.body}!` }))
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ beforeSend: [thrower, after] }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
expect(() => logs.captureLog({ body: 'hi' })).not.toThrow()
expect(readQueue(mockInstance)[0].record.body.stringValue).toBe('hi!')
expect(logger.error).toHaveBeenCalledWith(
expect.stringContaining('Error in beforeSend function for log:'),
expect.any(Error)
)
})
})
describe('rate limiting', () => {
beforeEach(() => jest.useFakeTimers({ now: 0 }))
afterEach(() => jest.useRealTimers())
// Tabular form for the simple in-window cap cases. Bespoke ones
// (warn-once, window-roll reset, clock-jump backward, beforeSend
// accounting) keep their own `it` blocks because they assert
// multi-window or interleaving behavior.
type CapCase = {
name: string
maxLogsPerInterval: number | undefined
capturesInWindow: number
expectedQueueLen: number
}
const capCases: CapCase[] = [
{
name: 'is uncapped when maxLogsPerInterval is undefined (default)',
maxLogsPerInterval: undefined,
capturesInWindow: 50,
expectedQueueLen: 50,
},
{
name: 'drops captures beyond maxLogsPerInterval within the window',
maxLogsPerInterval: 3,
capturesInWindow: 5,
expectedQueueLen: 3,
},
]
it.each(capCases)('$name', ({ maxLogsPerInterval, capturesInWindow, expectedQueueLen }) => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxLogsPerInterval, rateCapWindowMs: 1000 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
for (let i = 0; i < capturesInWindow; i++) {
logs.captureLog({ body: `msg-${i}` })
}
expect(readQueue(mockInstance)).toHaveLength(expectedQueueLen)
})
it('warns exactly once per window when dropping, regardless of how many drops', () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxLogsPerInterval: 2, rateCapWindowMs: 1000 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
for (let i = 0; i < 10; i++) {
logs.captureLog({ body: `msg-${i}` })
}
expect(logger.warn).toHaveBeenCalledTimes(1)
expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('captureLog dropping logs'))
})
it('resets the counter when the window rolls (and warns again on next overflow)', () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxLogsPerInterval: 1, rateCapWindowMs: 1000 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'window-1-kept' })
logs.captureLog({ body: 'window-1-dropped' })
expect(readQueue(mockInstance)).toHaveLength(1)
expect(logger.warn).toHaveBeenCalledTimes(1)
jest.setSystemTime(1001)
logs.captureLog({ body: 'window-2-kept' })
logs.captureLog({ body: 'window-2-dropped' })
expect(readQueue(mockInstance)).toHaveLength(2)
expect(logger.warn).toHaveBeenCalledTimes(2)
})
it('resets the window when the clock jumps backward (NTP correction / manual clock change)', () => {
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxLogsPerInterval: 2, rateCapWindowMs: 1000 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
// Seed the window at t=5000, fill the budget.
jest.setSystemTime(5000)
logs.captureLog({ body: 'a' })
logs.captureLog({ body: 'b' })
logs.captureLog({ body: 'dropped-pre-jump' })
expect(readQueue(mockInstance)).toHaveLength(2)
// Clock jumps backward by 1 hour (e.g. user reset device time).
// Without the `elapsed < 0` guard, the rate cap would stay "stuck"
// until `now` exceeds the old window-start again — potentially
// dropping every log for the duration of the backward jump.
jest.setSystemTime(5000 - 60 * 60 * 1000)
logs.captureLog({ body: 'accepted-post-jump' })
expect(readQueue(mockInstance)).toHaveLength(3)
expect(readQueue(mockInstance)[2].record.body.stringValue).toBe('accepted-post-jump')
})
it('beforeSend-rejected records do not consume the per-interval budget', () => {
// beforeSend drops the first record; rate cap is 1 per window. The
// SECOND capture should still succeed — if beforeSend consumed the
// budget, it'd be dropped.
const beforeSend = jest
.fn()
.mockReturnValueOnce(null)
.mockImplementation((r: any) => r)
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxLogsPerInterval: 1, rateCapWindowMs: 1000, beforeSend }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'pre-filtered-out' })
logs.captureLog({ body: 'should-still-fit' })
expect(readQueue(mockInstance)).toHaveLength(1)
expect(readQueue(mockInstance)[0].record.body.stringValue).toBe('should-still-fit')
})
})
describe('concurrent capture during flush', () => {
it('mid-flush captures land in the queue for the next cycle — not lost, not double-sent', async () => {
let resolveSend: (v: any) => void = () => {}
let captureDuringSend: (() => void) | null = null
mockInstance._sendLogsBatch = jest.fn(
() =>
new Promise((r) => {
if (captureDuringSend) {
captureDuringSend()
captureDuringSend = null
}
resolveSend = (v) => r(v)
})
)
const logs = new PostHogLogs(
mockInstance,
resolveForTest({ maxBatchRecordsPerPost: 1, maxBufferSize: 10 }),
logger,
getContextFor(mockInstance),
immediateOnReady
)
logs.captureLog({ body: 'first' })
captureDuringSend = (): void => {
logs.captureLog({ body: 'mid-flight' })
}
const flushP = logs.flush()
await new Promise((r) => setImmediate(r))
resolveSend({ kind: 'ok' })
await flushP
// flush() uses `originalQueueLength` at entry, so a mid-flight capture
// is intentionally left for the NEXT flush (matches events semantics).
// The invariant we care about: not lost, not double-sent.
expect(readQueue(mockInstance)).toHaveLength(1)
expect(readQueue(mockInstance)[0].record.body.stringValue).toBe('mid-flight')
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(1)
// A subsequent flush picks it up — no data lost.
const flushP2 = logs.flush()
await new Promise((r) => setImmediate(r))
resolveSend({ kind: 'ok' })
await flushP2
expect(readQueue(mockInstance)).toHaveLength(0)
expect(mockInstance._sendLogsBatch).toHaveBeenCalledTimes(2)
})
})
})

@@ -1,11 +0,36 @@

import type { LogSdkContext } from '@posthog/types'
import { buildOtlpLogRecord } from './logs-utils'
import type { LogAttributeValue } from '@posthog/types'
import { buildOtlpLogRecord, buildOtlpLogsPayload } from './logs-utils'
import { Logger, PostHogPersistedProperty } from '../types'
import type { PostHogCoreStateless } from '../posthog-core-stateless'
import type { BufferedLogEntry, CaptureLogOptions, ResolvedPostHogLogsConfig } from './types'
import { isArray, safeSetTimeout } from '../utils'
import type {
BeforeSendLogFn,
BufferedLogEntry,
CaptureLogOptions,
LogSdkContext,
ResolvedPostHogLogsConfig,
} from './types'
export class PostHogLogs {
private _localEnabled: boolean
private _maxBufferSize: number
private _flushIntervalMs: number
// Mutable — halved on 413 to shrink the next POST, and ramped back up by
// one record after each successful send so a one-off oversized payload
// (e.g. a giant stack trace) doesn't permanently degrade throughput.
private _maxBatchRecordsPerPost: number
private _flushTimer?: ReturnType<typeof safeSetTimeout>
// Serializes concurrent flushes — the second caller awaits the first rather
// than racing it and double-sending the same head-of-queue records.
private _flushPromise: Promise<void> | null = null
// Fixed-window rate cap. Tumbling (not sliding) for cheap arithmetic on the
// hot path. Window rolls the first time `captureLog` fires after the window
// expires — no background timer needed. `_droppedWarned` keeps the log noise
// to one line per window regardless of how many records got dropped.
private _rateCapWindowMs: number
private _maxLogsPerInterval?: number
private _intervalWindowStart = 0
private _intervalLogCount = 0
private _droppedWarned = false
constructor(

@@ -16,12 +41,18 @@ private readonly _instance: PostHogCoreStateless,

private readonly _getContext: () => LogSdkContext,
private readonly _onReady: (fn: () => void) => void
private readonly _onReady: (fn: () => void) => void,
// Waits for the logs-storage persist to hit disk. Called between batches
// so a crash after a successful HTTP send but before the queue-advance
// reaches disk can't cause duplicate records on next startup. SDKs with
// synchronous storage (or no async persist layer) can pass a no-op. RN
// wires this to its dedicated `_logsStorage.waitForPersist()`.
private readonly _waitForStoragePersist: () => Promise<void> = () => Promise.resolve()
) {
this._localEnabled = _config.enabled !== false
this._maxBufferSize = _config.maxBufferSize
this._flushIntervalMs = _config.flushIntervalMs
this._maxBatchRecordsPerPost = _config.maxBatchRecordsPerPost
this._rateCapWindowMs = _config.rateCapWindowMs
this._maxLogsPerInterval = _config.maxLogsPerInterval
}
captureLog(options: CaptureLogOptions): void {
if (!this._localEnabled) {
return
}
if (this._instance.optedOut) {

@@ -34,6 +65,21 @@ return

// Ordering: beforeSend → rate cap → OTLP build. beforeSend runs first so
// user-dropped records don't consume the per-interval budget.
const filtered = this._runBeforeSend(options)
if (filtered === null) {
return
}
// beforeSend could return a record with empty body — treat as drop.
if (!filtered.body) {
return
}
if (!this._checkRateLimit()) {
return
}
// Build before deferring so attributes reflect state at capture time, not
// at drain time (identity/session changes between capture and drain must
// not corrupt recorded attributes).
const record = buildOtlpLogRecord(options, this._getContext())
const record = buildOtlpLogRecord(filtered, this._getContext())
const entry: BufferedLogEntry = { record }

@@ -44,6 +90,199 @@

/**
* Runs the configured `beforeSend` hook(s) on a capture record:
* - single fn OR array of fns (chain, left-to-right)
* - returning `null` drops the record (logged at info)
* - a thrown error is logged and the chain *continues* with the previous
* result — a buggy user filter must never crash the caller's
* `captureLog()` call
*/
private _runBeforeSend(options: CaptureLogOptions): CaptureLogOptions | null {
const beforeSend = this._config.beforeSend
if (!beforeSend) {
return options
}
const fns = isArray(beforeSend) ? beforeSend : [beforeSend]
let result: CaptureLogOptions = options
for (const fn of fns) {
try {
const next = fn(result)
if (!next) {
this._logger.info(`Log was rejected in beforeSend function`)
return null
}
result = next
} catch (e) {
// Swallow the throw — the chain continues with `result` unchanged so
// a buggy filter degrades to a no-op rather than crashing the app.
this._logger.error(`Error in beforeSend function for log:`, e)
}
}
return result
}
/**
* Returns `true` if this capture fits within the current rate-cap window,
* `false` if it should be dropped.
*
* Fixed (tumbling) window: the counter resets the first time `captureLog`
* fires after `rateCapWindowMs` has elapsed — no timer needed.
* `maxLogsPerInterval === undefined` means unbounded.
*
* Wall-clock safety: if `Date.now()` jumps backward (manual device-clock
* change, big NTP correction), `elapsed` goes negative. We treat that the
* same as "window expired" and reset — otherwise the rate cap would be
* stuck until the clock caught up to the old window start, potentially
* dropping logs for hours.
*
* Pre-init note: the counter increments here, before `_onReady` defers
* `_enqueue` to the init promise. If init resolves slowly and the user is
* later opted out, the counter has already consumed budget for records
* that won't enqueue. Cosmetic — no record is "lost" beyond what's
* already gated, and the window rolls on its own.
*/
private _checkRateLimit(): boolean {
if (this._maxLogsPerInterval === undefined) {
return true
}
const now = Date.now()
const elapsed = now - this._intervalWindowStart
if (elapsed >= this._rateCapWindowMs || elapsed < 0) {
this._intervalWindowStart = now
this._intervalLogCount = 0
this._droppedWarned = false
}
if (this._intervalLogCount >= this._maxLogsPerInterval) {
if (!this._droppedWarned) {
this._logger.warn(
`captureLog dropping logs: exceeded ${this._maxLogsPerInterval} logs per ${this._rateCapWindowMs}ms`
)
this._droppedWarned = true
}
return false
}
this._intervalLogCount++
return true
}
/**
* Drains `LogsQueue` in `maxBatchRecordsPerPost` slices, POSTing each as an
* OTLP payload.
* - Network error → keep items in queue, re-throw (caller retries later)
* - 413 → halve batch size, retry same records (do not advance)
* - Any other error → drop the batch (avoid infinite loop on malformed data),
* re-throw so callers can log/report
* Concurrent calls are serialized through `_flushPromise` so records at the
* head of the queue can't be sent twice.
*/
async flush(): Promise<void> {
if (this._flushPromise) {
return this._flushPromise
}
this._flushPromise = this._flushInner().finally(() => {
this._flushPromise = null
})
return this._flushPromise
}
private async _flushInner(): Promise<void> {
this._clearFlushTimer()
let queue = this._instance.getPersistedProperty<BufferedLogEntry[]>(PostHogPersistedProperty.LogsQueue) ?? []
if (queue.length === 0) {
return
}
const originalQueueLength = queue.length
let sentCount = 0
while (queue.length > 0 && sentCount < originalQueueLength) {
const batchSize = Math.min(queue.length, this._maxBatchRecordsPerPost)
const batch = queue.slice(0, batchSize)
const records = batch.map((e) => e.record)
const payload = buildOtlpLogsPayload(
records,
this._buildResourceAttributes(),
this._instance.getLibraryId(),
this._instance.getLibraryVersion()
)
const outcome = await this._instance._sendLogsBatch(payload)
if (outcome.kind === 'too-large' && batch.length > 1) {
this._maxBatchRecordsPerPost = Math.max(1, Math.floor(batch.length / 2))
this._logger.warn(
`Received 413 when sending logs batch of size ${batch.length}, reducing batch size to ${this._maxBatchRecordsPerPost}`
)
// Don't advance the queue — retry the same records with the smaller cap.
continue
}
if (outcome.kind === 'retry-later') {
// Network error: keep records in the queue for the next flush cycle
// and surface the error so the caller can log/react.
throw outcome.error
}
// ok | fatal | too-large-with-batch-of-1 → records are leaving the
// queue. 'fatal' and size-1 413s are dropped so we don't spin on the
// same record forever. Surface the size-1 413 explicitly so a single
// oversized record (e.g. a giant body field) is visible in logs
// instead of silently disappearing.
if (outcome.kind === 'too-large') {
this._logger.warn(
'Dropping a single log record after 413 with batch size 1 — the record is larger than the server cap and cannot be split further.'
)
} else if (outcome.kind === 'ok' && this._maxBatchRecordsPerPost < this._config.maxBatchRecordsPerPost) {
// Linear recovery: each healthy send pushes the cap back up by 1
// toward the configured maximum. Linear (not doubling) so we don't
// immediately retrigger a 413 if the previous shrink was justified.
this._maxBatchRecordsPerPost = Math.min(this._config.maxBatchRecordsPerPost, this._maxBatchRecordsPerPost + 1)
}
await this._persistQueueAdvance(batch.length)
queue = this._instance.getPersistedProperty<BufferedLogEntry[]>(PostHogPersistedProperty.LogsQueue) ?? []
sentCount += batch.length
if (outcome.kind === 'fatal') {
throw outcome.error
}
}
}
private async _persistQueueAdvance(consumed: number): Promise<void> {
// Re-read the queue in case captures landed mid-flush, then drop the head.
const refreshed = this._instance.getPersistedProperty<BufferedLogEntry[]>(PostHogPersistedProperty.LogsQueue) ?? []
this._instance.setPersistedProperty(PostHogPersistedProperty.LogsQueue, refreshed.slice(consumed))
// Wait for the advance to hit disk before the next batch sends, matching
// events' `flushStorage()` contract. Prevents duplicates if the app crashes
// after the HTTP success but before the queue-advance persists.
await this._waitForStoragePersist()
}
/**
* OTLP resource attributes for every batch.
*
* Layout: user `resourceAttributes` spread first, then SDK-controlled
* keys layered on top so users cannot accidentally clobber them. Most logs
* backends index on `service.name` and `telemetry.sdk.*` for routing,
* SDK-version dashboards, and bug-correlation; letting a stray user key
* overwrite them silently breaks ingestion attribution. The dedicated
* `serviceName` / `environment` / `serviceVersion` config fields are the
* supported way to override `service.name` / `deployment.environment` /
* `service.version`.
*/
private _buildResourceAttributes(): Record<string, LogAttributeValue> {
return {
...this._config.resourceAttributes,
'service.name': this._config.serviceName || 'unknown_service',
...(this._config.environment && { 'deployment.environment': this._config.environment }),
...(this._config.serviceVersion && { 'service.version': this._config.serviceVersion }),
'telemetry.sdk.name': this._instance.getLibraryId(),
'telemetry.sdk.version': this._instance.getLibraryVersion(),
}
}
private _enqueue(entry: BufferedLogEntry): void {
// Re-check: optedOut can flip between captureLog and here — preload may
// have hydrated the real persisted value, or optIn/optOut may have fired
// while this fn was deferred.
// Re-check optedOut: preload may have hydrated the real persisted value,
// or optIn/optOut may have fired while this fn was deferred.
if (this._instance.optedOut) {

@@ -60,3 +299,88 @@ return

this._instance.setPersistedProperty(PostHogPersistedProperty.LogsQueue, queue)
// Threshold trigger: at-capacity means flushing now reclaims space before
// the next capture has to shift something out.
if (queue.length >= this._maxBufferSize) {
this._flushInBackground()
return
}
// Timer trigger: only arm one timer at a time. A subsequent enqueue within
// the window shouldn't reschedule — that would keep pushing the flush out.
if (!this._flushTimer) {
this._flushTimer = safeSetTimeout(() => {
this._flushTimer = undefined
this._flushInBackground()
}, this._flushIntervalMs)
}
}
/**
* Stops the timer-based flush and sends anything still in the queue.
* Intended for process-teardown paths (RN `_shutdown` override). Swallows
* errors so a failing final flush can't block the broader shutdown.
*
* If `timeoutMs` is provided, the final flush races against that budget so
* a slow network/storage can't hold up shutdown indefinitely. Without it,
* flush time is bounded only by `fetchRetryCount * (requestTimeout +
* fetchRetryDelay)`, which can exceed the caller's shutdown SLA.
*/
async shutdown(timeoutMs?: number): Promise<void> {
this._clearFlushTimer()
const flushPromise = this.flush().catch(() => {
// Best-effort: a logs-flush failure during shutdown is not actionable
// and must not prevent the rest of shutdown from running. Errors are
// still surfaced from the regular `flush()` path in normal operation.
})
if (timeoutMs === undefined) {
await flushPromise
return
}
await Promise.race([flushPromise, new Promise<void>((resolve) => safeSetTimeout(resolve, timeoutMs))])
}
/**
* Time-bounded flush for transient lifecycle events (e.g. RN
* foreground→background) that must complete inside an OS-imposed window.
* Unlike `shutdown`, this leaves the periodic flush timer in place so the
* pipeline keeps draining if the process is resumed instead of suspended.
*
* Errors propagate so the host SDK can route them through its standard
* lifecycle error handler (e.g. RN's `logFlushError`). If the timer wins
* the race, a late rejection from the in-flight flush is silenced via a
* no-op handler attached after the race settles, to avoid noisy
* unhandled-rejection logs — the next regular flush cycle will retry.
*/
async flushWithTimeout(timeoutMs: number): Promise<void> {
let timedOut = false
const flushPromise = this.flush()
const timerPromise = new Promise<void>((resolve) =>
safeSetTimeout(() => {
timedOut = true
resolve()
}, timeoutMs)
)
try {
await Promise.race([flushPromise, timerPromise])
} finally {
if (timedOut) {
// Race lost — flush is still in flight. Attach a no-op rejection
// handler so a late failure isn't logged as unhandled.
void flushPromise.catch(() => {})
}
}
}
private _flushInBackground(): void {
void this.flush().catch((err) => {
this._logger.error('PostHog logs flush failed:', err)
})
}
private _clearFlushTimer(): void {
if (this._flushTimer) {
clearTimeout(this._flushTimer)
this._flushTimer = undefined
}
}
}

@@ -1,2 +0,3 @@

import type { CaptureLogOptions, LogSdkContext, LogSeverityLevel } from '@posthog/types'
import type { CaptureLogOptions, LogSeverityLevel } from '@posthog/types'
import type { LogSdkContext } from './types'
import {

@@ -3,0 +4,0 @@ buildOtlpLogRecord,

import type {
CaptureLogOptions,
LogAttributeValue,
LogSdkContext,
LogSeverityLevel,

@@ -13,2 +12,3 @@ OtlpAnyValue,

} from '@posthog/types'
import type { LogSdkContext } from './types'
import { isArray, isBoolean, isNull, isUndefined } from '../utils'

@@ -15,0 +15,0 @@

@@ -14,5 +14,24 @@ // Re-export OTLP/log types from @posthog/types so the rest of the logs module can

OtlpLogsPayload,
LogSdkContext,
} from '@posthog/types'
/**
* SDK-internal context the host SDK passes to `buildOtlpLogRecord` at capture
* time. Each SDK populates the fields that apply to it: browser fills
* `currentUrl`, mobile fills `screenName` / `appState`. Missing fields are
* omitted from the OTLP record (no stray attributes).
*
* Internal to `@posthog/core` — customers don't see this in autocomplete.
*/
export interface LogSdkContext {
distinctId?: string
sessionId?: string
/** Web-only — current page URL */
currentUrl?: string
/** Mobile-only — current screen / view name */
screenName?: string
/** Mobile-only — app foreground/background state at capture time */
appState?: 'foreground' | 'background'
activeFeatureFlags?: string[]
}
// The public capture-logger interface lives in @posthog/types as `Logger`. Core

@@ -26,5 +45,2 @@ // also exports a `Logger` (the SDK's internal warn/info/error logger). Alias the

// Wrapper around OtlpLogRecord for queue entries. Parallels events' queue
// item shape (`{ message }`) — future additions like `retryCount` or
// `enqueuedAt` can be added without migrating the queue format.
export interface BufferedLogEntry {

@@ -34,36 +50,145 @@ record: OtlpLogRecord

// Public configuration for the logs module. Per-SDK defaults diverge (mobile
// cellular radio cost, browser tab suspension, node process lifecycle).
/**
* Pre-send filter. Inspect, mutate, or drop a captured record before it
* enters the rate-cap or the queue. Return the (possibly transformed) record
* to keep it; return `null` to drop it.
*
* Configure as a single fn or an array. Arrays form a left-to-right chain:
* each fn receives the previous fn's return value. A `null` from any link
* short-circuits the chain and drops the record.
*
* Runs *before* the rate cap so dropped records don't consume the
* per-interval budget. Throwing fns are logged and skipped — the chain
* continues with the previous return value, so a buggy filter degrades to a
* no-op rather than crashing `captureLog()`.
*
* @example Redact secrets from log bodies
* ```ts
* logs: {
* beforeSend: (record) => ({
* ...record,
* body: record.body.replace(/api_key=\S+/g, 'api_key=[REDACTED]'),
* }),
* }
* ```
*
* @example Drop noisy debug logs in production
* ```ts
* logs: {
* beforeSend: (record) => (record.level === 'debug' ? null : record),
* }
* ```
*/
export type BeforeSendLogFn = (record: CaptureLogOptions) => CaptureLogOptions | null
/**
* Configuration for the logs feature on `new PostHog(key, { logs: ... })`.
* All fields are optional; per-SDK defaults apply (mobile vs browser tune
* differently for cellular cost vs tab-suspension behavior).
*/
export interface PostHogLogsConfig {
// Master switch. Default: true when a config object is provided.
enabled?: boolean
/**
* Service name attached to every record as the OTLP `service.name`
* resource attribute. Used by the Logs UI for filtering / grouping.
* Default: `'unknown_service'`.
*/
serviceName?: string
// Resource attributes
serviceName?: string
/**
* Service version attached as OTLP `service.version`. Useful for
* correlating regressions to specific app releases.
*/
serviceVersion?: string
/**
* Deployment environment attached as OTLP `deployment.environment`
* (e.g. `'production'`, `'staging'`, `'dev'`).
*/
environment?: string
/**
* Extra OTLP resource attributes attached to every record. Spread first;
* SDK-controlled keys (`service.name`, `telemetry.sdk.*`, RN's `os.*`)
* are layered on top so users cannot accidentally clobber them. Use the
* dedicated `serviceName` / `environment` / `serviceVersion` fields to
* override those keys.
*/
resourceAttributes?: Record<string, LogAttributeValue>
// Buffering
/**
* How often the periodic background flush fires (ms). Records also flush
* eagerly when the buffer fills, on AppState changes (RN), and on
* `shutdown()`. Lower values trade battery/bandwidth for fresher data.
* Default: 10000 (RN) / 3000 (browser).
*/
flushIntervalMs?: number
rateCapWindowMs?: number // separate from flushIntervalMs so flush cadence does not move the rate-cap window
/**
* Max records held in memory before the queue evicts the oldest on push
* (FIFO). Bounds memory footprint and on-disk-queue size. When the buffer
* hits this size, an immediate flush is triggered to reclaim space; if
* the flush hasn't completed before the next capture, the oldest record
* is shifted out. Default: 100.
*/
maxBufferSize?: number
maxLogsPerInterval?: number
maxBatchRecordsPerPost?: number // keeps each POST under the 2 MB server cap
// Shutdown — separate budgets because foreground→background and app-terminate
// have different OS-imposed windows.
backgroundFlushBudgetMs?: number
terminationFlushBudgetMs?: number
/**
* Max records per outbound POST. Keeps each request under the server's
* 2 MB cap. On a 413 response, the SDK halves this value, retries the
* same records, then ramps back up by 1 per healthy send. A 413 on a
* single-record batch drops the record (it's larger than the server can
* accept regardless of batch size). Default: 50 (RN) / 100 (browser).
*/
maxBatchRecordsPerPost?: number
// Filtering. Runs synchronously before the rate cap so beforeSend-dropped
// records do not consume the per-interval budget.
beforeSend?: (record: CaptureLogOptions) => CaptureLogOptions | null
/**
* Tumbling-window rate cap. Bounds how many records can be captured
* within a sliding (technically tumbling) time window. Records exceeding
* the cap are dropped synchronously at `captureLog()` (they never enter
* the buffer or consume bandwidth). A single warn line is logged per
* window when the cap is hit.
*
* Defaults are per-SDK; on RN the default is `{ maxLogs: 500, windowMs:
* 10000 }` (≈50 logs/sec ceiling, tuned for cellular bandwidth).
*
* @example Allow brief bursts up to 1000/min
* ```ts
* logs: { rateCap: { maxLogs: 1000, windowMs: 60000 } }
* ```
*
* @example Disable the cap entirely (unbounded)
* ```ts
* logs: { rateCap: { maxLogs: undefined } }
* ```
*/
rateCap?: {
/**
* Max records accepted per `windowMs` window. `undefined` = unbounded.
*/
maxLogs?: number
/**
* Window length in ms. Tumbling, not sliding — the counter resets the
* first time a capture fires after the window expires.
*/
windowMs?: number
}
/**
* Pre-send filter. See {@link BeforeSendLogFn} for shape and examples.
* Configure as a single function or a chain.
*/
beforeSend?: BeforeSendLogFn | BeforeSendLogFn[]
}
// Fields PostHogLogs needs resolved at runtime. Each SDK supplies its own
// defaults (mobile, browser, node have different right answers) and hands the
// filled-in config to the PostHogLogs constructor.
export interface ResolvedPostHogLogsConfig extends PostHogLogsConfig {
// Fields PostHogLogs needs resolved at runtime. The host SDK fills in its
// defaults and hands the resolved config to the PostHogLogs constructor.
// Flat names internally — public API uses `rateCap: { maxLogs, windowMs }`.
export interface ResolvedPostHogLogsConfig extends Omit<PostHogLogsConfig, 'rateCap'> {
maxBufferSize: number
flushIntervalMs: number
maxBatchRecordsPerPost: number
rateCapWindowMs: number
maxLogsPerInterval?: number
backgroundFlushBudgetMs: number
terminationFlushBudgetMs: number
}

@@ -0,1 +1,2 @@

import type { OtlpLogsPayload } from '@posthog/types'
import { SimpleEventEmitter } from './eventemitter'

@@ -97,2 +98,19 @@ import { getFeatureFlagValue, normalizeFlagsResponse } from './featureFlagUtils'

/**
* Outcome of a logs batch send. Keeps HTTP error classification inside core
* (single source of truth — same policy events already use in `_flush()`) so
* PostHogLogs doesn't need to know about specific error types.
*
* - ok → records are accepted; drop them from the queue
* - too-large → 413; caller should halve batch size and retry same records
* - retry-later → network error; caller keeps records and retries next cycle
* - fatal → anything else (auth, malformed, etc.); caller drops the
* batch and surfaces the error
*/
export type SendLogsBatchOutcome =
| { kind: 'ok' }
| { kind: 'too-large' }
| { kind: 'retry-later'; error: unknown }
| { kind: 'fatal'; error: unknown }
export enum QuotaLimitedFeature {

@@ -1183,2 +1201,48 @@ FeatureFlags = 'feature_flags',

/**
* Sends a pre-built OTLP logs payload to `/i/v1/logs`. Returns a tagged
* outcome instead of throwing so PostHogLogs doesn't have to know about the
* core's error class hierarchy. Error classification lives here (single
* source of truth, same policy the events `_flush()` uses for its own
* 413 / network / fatal handling).
*
* 413 is passed through as `too-large` (not auto-retried) so the caller can
* shrink `maxBatchRecordsPerPost` and retry the same records.
*/
async _sendLogsBatch(payload: OtlpLogsPayload): Promise<SendLogsBatchOutcome> {
const serialized = JSON.stringify(payload)
const url = `${this.host}/i/v1/logs?token=${encodeURIComponent(this.apiKey)}`
const gzippedPayload = !this.disableCompression ? await gzipCompress(serialized, this.isDebug) : null
const fetchOptions: PostHogFetchOptions = {
method: 'POST',
headers: {
...this.getCustomHeaders(),
'Content-Type': 'application/json',
...(gzippedPayload !== null && { 'Content-Encoding': 'gzip' }),
},
body: gzippedPayload || serialized,
}
try {
await this.fetchWithRetry(url, fetchOptions, {
retryCheck: (err) => {
if (isPostHogFetchContentTooLargeError(err)) {
return false
}
return isPostHogFetchError(err)
},
})
return { kind: 'ok' }
} catch (err) {
if (isPostHogFetchContentTooLargeError(err)) {
return { kind: 'too-large' }
}
if (err instanceof PostHogFetchNetworkError) {
return { kind: 'retry-later', error: err }
}
return { kind: 'fatal', error: err }
}
}
private async fetchWithRetry(

@@ -1185,0 +1249,0 @@ url: string,

@@ -39,2 +39,3 @@ import { Logger } from '@/types'

return {
debug: jest.fn((...args) => console.debug(...args)),
info: jest.fn((...args) => console.log(...args)),

@@ -41,0 +42,0 @@ warn: jest.fn((...args) => console.warn(...args)),

@@ -347,2 +347,14 @@ export type PostHogCoreOptions = {

}
/**
* Logs feature remote config. When a map, `captureConsoleLogs` (boolean)
* is the local opt-in flag for `console.*` autocapture (read by the JS
* SDK's `PostHogLogs` extension to decide whether to load the autocapture
* bundle).
*/
logs?:
| boolean
| {
[key: string]: JsonType
}
}

@@ -610,6 +622,23 @@

export interface SurveyTranslation {
name?: string
thankYouMessageHeader?: string
thankYouMessageDescription?: string
thankYouMessageCloseButtonText?: string
}
export interface SurveyQuestionTranslation {
question?: string
description?: string | null
buttonText?: string
link?: string | null
lowerBoundLabel?: string
upperBoundLabel?: string
choices?: string[]
}
type SurveyQuestionBase = {
question: string
id: string
description?: string
description?: string | null
descriptionContentType?: SurveyQuestionDescriptionContentType

@@ -621,2 +650,3 @@ optional?: boolean

validation?: SurveyValidationRule[]
translations?: Record<string, SurveyQuestionTranslation>
}

@@ -630,3 +660,3 @@

type: SurveyQuestionType.Link
link?: string
link?: string | null
}

@@ -693,2 +723,6 @@

export type SurveyResponseValue = string | number | string[] | null
export type SurveyResponses = Record<string, SurveyResponseValue>
export type SurveyCallback = (surveys: Survey[]) => void

@@ -736,2 +770,3 @@

type: SurveyType
translations?: Record<string, SurveyTranslation>
feature_flag_keys?: {

@@ -799,2 +834,3 @@ key: string

export type Logger = {
debug: (...args: any[]) => void
info: (...args: any[]) => void

@@ -801,0 +837,0 @@ warn: (...args: any[]) => void

@@ -5,6 +5,6 @@ import { Logger } from '../types'

type ConsoleLike = {
debug: (...args: any[]) => void
log: (...args: any[]) => void
warn: (...args: any[]) => void
error: (...args: any[]) => void
debug: (...args: any[]) => void
}

@@ -27,3 +27,3 @@

): Logger => {
function _log(level: 'log' | 'warn' | 'error', ...args: any[]) {
function _log(level: 'debug' | 'log' | 'warn' | 'error', ...args: any[]) {
maybeCall(() => {

@@ -36,2 +36,6 @@ const consoleMethod = consoleLike[level]

const logger: Logger = {
debug: (...args: any[]) => {
_log('debug', ...args)
},
info: (...args: any[]) => {

@@ -38,0 +42,0 @@ _log('log', ...args)