@leanbase-giangnd/js 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +102 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +103 -11
- package/dist/index.mjs.map +1 -1
- package/dist/leanbase.iife.js +102 -10
- package/dist/leanbase.iife.js.map +1 -1
- package/package.json +3 -3
- package/src/extensions/replay/external/lazy-loaded-session-recorder.ts +27 -8
- package/src/extensions/replay/external/snapshot-transport.ts +50 -0
- package/src/leanbase.ts +31 -1
- package/src/version.ts +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isArray, isString, isNullish, isFormData, hasOwnProperty, isNumber, isNull, PostHogPersistedProperty, isUndefined, isFile, isFunction, includes, stripLeadingDollar, isObject, isEmptyObject, isBoolean, trim, clampToRange, BucketedRateLimiter, PostHogCore, getFetch, isEmptyString } from '@posthog/core';
|
|
2
|
-
import {
|
|
2
|
+
import { gzipSync, strToU8, strFromU8, decompressSync } from 'fflate';
|
|
3
3
|
import { record } from '@rrweb/record';
|
|
4
4
|
|
|
5
5
|
const breaker = {};
|
|
@@ -1183,7 +1183,7 @@ const detectDeviceType = function (user_agent) {
|
|
|
1183
1183
|
}
|
|
1184
1184
|
};
|
|
1185
1185
|
|
|
1186
|
-
var version = "0.0.
|
|
1186
|
+
var version = "0.0.5";
|
|
1187
1187
|
var packageInfo = {
|
|
1188
1188
|
version: version};
|
|
1189
1189
|
|
|
@@ -4415,6 +4415,53 @@ class MutationThrottler {
|
|
|
4415
4415
|
}
|
|
4416
4416
|
}
|
|
4417
4417
|
|
|
4418
|
+
/**
|
|
4419
|
+
* Leanbase-only SnapshotTransport
|
|
4420
|
+
* Sends gzip-compressed snapshot payloads directly to the /s/ endpoint.
|
|
4421
|
+
* This bypasses `posthog.capture()` and the analytics /batch/ queue.
|
|
4422
|
+
*
|
|
4423
|
+
* Reasoning: session replay snapshots must not enter the batch/persisted
|
|
4424
|
+
* queues managed by @posthog/core. This transport posts directly to the
|
|
4425
|
+
* recorder endpoint and uses the instance.fetch() implementation so it
|
|
4426
|
+
* runs in the same environment the SDK uses.
|
|
4427
|
+
*/
|
|
4428
|
+
async function sendSnapshotDirect(instance, url, payload) {
|
|
4429
|
+
const fetchFn = instance && instance.fetch ? instance.fetch.bind(instance) : globalThis.fetch;
|
|
4430
|
+
const body = JSON.stringify(payload);
|
|
4431
|
+
const compressed = gzipSync(strToU8(body));
|
|
4432
|
+
const headers = {
|
|
4433
|
+
'Content-Encoding': 'gzip',
|
|
4434
|
+
'Content-Type': 'application/json',
|
|
4435
|
+
Accept: 'application/json'
|
|
4436
|
+
};
|
|
4437
|
+
if (!fetchFn) {
|
|
4438
|
+
// best-effort: if no fetch available, fail silently (recorder should not crash app)
|
|
4439
|
+
try {
|
|
4440
|
+
// eslint-disable-next-line no-console
|
|
4441
|
+
console.debug('[SnapshotTransport] no fetch available, dropping snapshot');
|
|
4442
|
+
} catch {}
|
|
4443
|
+
return {
|
|
4444
|
+
status: 0
|
|
4445
|
+
};
|
|
4446
|
+
}
|
|
4447
|
+
try {
|
|
4448
|
+
// Use the instance.fetch wrapper so environment-specific shims are used.
|
|
4449
|
+
return await fetchFn(url, {
|
|
4450
|
+
method: 'POST',
|
|
4451
|
+
body: compressed,
|
|
4452
|
+
headers
|
|
4453
|
+
});
|
|
4454
|
+
} catch (err) {
|
|
4455
|
+
try {
|
|
4456
|
+
// eslint-disable-next-line no-console
|
|
4457
|
+
console.debug('[SnapshotTransport] send failed', err);
|
|
4458
|
+
} catch {}
|
|
4459
|
+
return {
|
|
4460
|
+
status: 0
|
|
4461
|
+
};
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
|
|
4418
4465
|
function simpleHash(str) {
|
|
4419
4466
|
let hash = 0;
|
|
4420
4467
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -4442,7 +4489,6 @@ const FIVE_MINUTES = ONE_MINUTE * 5;
|
|
|
4442
4489
|
const RECORDING_IDLE_THRESHOLD_MS = FIVE_MINUTES;
|
|
4443
4490
|
const RECORDING_MAX_EVENT_SIZE = ONE_KB * ONE_KB * 0.9; // ~1mb (with some wiggle room)
|
|
4444
4491
|
const RECORDING_BUFFER_TIMEOUT = 2000; // 2 seconds
|
|
4445
|
-
const SESSION_RECORDING_BATCH_KEY = 'recordings';
|
|
4446
4492
|
const LOGGER_PREFIX$1 = '[SessionRecording]';
|
|
4447
4493
|
const logger = createLogger(LOGGER_PREFIX$1);
|
|
4448
4494
|
const ACTIVE_SOURCES = [IncrementalSource.MouseMove, IncrementalSource.MouseInteraction, IncrementalSource.Scroll, IncrementalSource.ViewportResize, IncrementalSource.Input, IncrementalSource.TouchMove, IncrementalSource.MediaInteraction, IncrementalSource.Drag];
|
|
@@ -5160,14 +5206,31 @@ class LazyLoadedSessionRecording {
|
|
|
5160
5206
|
}, RECORDING_BUFFER_TIMEOUT);
|
|
5161
5207
|
}
|
|
5162
5208
|
}
|
|
5163
|
-
_captureSnapshot(properties) {
|
|
5164
|
-
//
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5209
|
+
async _captureSnapshot(properties) {
|
|
5210
|
+
// Send snapshots directly to the recorder endpoint to avoid the core
|
|
5211
|
+
// analytics batching and persisted queue. The lifecycle gate in
|
|
5212
|
+
// SessionRecording ensures this method is only called when recording
|
|
5213
|
+
// is allowed.
|
|
5214
|
+
try {
|
|
5215
|
+
const url = this._snapshotUrl();
|
|
5216
|
+
// include minimal metadata expected by /s/ endpoint
|
|
5217
|
+
const payload = {
|
|
5218
|
+
$snapshot_bytes: properties.$snapshot_bytes,
|
|
5219
|
+
$snapshot_data: properties.$snapshot_data,
|
|
5220
|
+
$session_id: properties.$session_id,
|
|
5221
|
+
$window_id: properties.$window_id,
|
|
5222
|
+
$lib: properties.$lib,
|
|
5223
|
+
$lib_version: properties.$lib_version,
|
|
5224
|
+
// include distinct_id from persistence for server-side association
|
|
5225
|
+
distinct_id: this._instance.persistence?.get_property('distinct_id')
|
|
5226
|
+
};
|
|
5227
|
+
await sendSnapshotDirect(this._instance, url, payload);
|
|
5228
|
+
} catch (err) {
|
|
5229
|
+
try {
|
|
5230
|
+
// eslint-disable-next-line no-console
|
|
5231
|
+
console.debug('[SessionRecording] snapshot send failed', err);
|
|
5232
|
+
} catch {}
|
|
5233
|
+
}
|
|
5171
5234
|
}
|
|
5172
5235
|
_snapshotUrl() {
|
|
5173
5236
|
const host = this._instance.config.host || '';
|
|
@@ -5883,6 +5946,35 @@ class Leanbase extends PostHogCore {
|
|
|
5883
5946
|
// eslint-disable-next-line no-console
|
|
5884
5947
|
console.debug('[Leanbase.fetch] parsed.batch.length=', parsed.batch.length, 'hasSnapshot=', hasSnapshot);
|
|
5885
5948
|
} catch {}
|
|
5949
|
+
// If remote config has explicitly disabled session recording, drop snapshot events
|
|
5950
|
+
try {
|
|
5951
|
+
// Read persisted remote config that SessionRecording stores
|
|
5952
|
+
const persisted = this.get_property(SESSION_RECORDING_REMOTE_CONFIG);
|
|
5953
|
+
const serverAllowsRecording = !(persisted && persisted.enabled === false || this.config.disable_session_recording === true);
|
|
5954
|
+
if (!serverAllowsRecording && hasSnapshot) {
|
|
5955
|
+
// remove snapshot events from the batch before sending to /batch/
|
|
5956
|
+
parsed.batch = parsed.batch.filter(item => !(item && item.event === '$snapshot'));
|
|
5957
|
+
// If no events remain, short-circuit and avoid sending an empty batch
|
|
5958
|
+
if (!parsed.batch.length) {
|
|
5959
|
+
try {
|
|
5960
|
+
// eslint-disable-next-line no-console
|
|
5961
|
+
console.debug('[Leanbase.fetch] sessionRecording disabled, dropping snapshot-only batch');
|
|
5962
|
+
} catch {}
|
|
5963
|
+
return {
|
|
5964
|
+
status: 200,
|
|
5965
|
+
json: async () => ({})
|
|
5966
|
+
};
|
|
5967
|
+
}
|
|
5968
|
+
// re-encode the body so the underlying fetch receives the modified batch
|
|
5969
|
+
try {
|
|
5970
|
+
const newBody = JSON.stringify(parsed);
|
|
5971
|
+
options = {
|
|
5972
|
+
...options,
|
|
5973
|
+
body: newBody
|
|
5974
|
+
};
|
|
5975
|
+
} catch {}
|
|
5976
|
+
}
|
|
5977
|
+
} catch {}
|
|
5886
5978
|
if (hasSnapshot) {
|
|
5887
5979
|
const host = this.config && this.config.host || '';
|
|
5888
5980
|
const newUrl = host ? `${host.replace(/\/$/, '')}/s/` : url;
|