@leanbase-giangnd/js 0.0.5 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +13 -182
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +13 -182
- package/dist/index.mjs.map +1 -1
- package/dist/leanbase.iife.js +3016 -8525
- package/dist/leanbase.iife.js.map +1 -1
- package/package.json +47 -46
- package/src/extensions/replay/external/lazy-loaded-session-recorder.ts +8 -27
- package/src/leanbase.ts +3 -127
- package/src/types/fflate.d.ts +0 -1
- package/src/version.ts +1 -1
- package/LICENSE +0 -37
- package/src/extensions/replay/external/snapshot-transport.ts +0 -50
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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 { gzipSync, strToU8, strFromU8, decompressSync } from 'fflate';
|
|
3
2
|
import { record } from '@rrweb/record';
|
|
3
|
+
import { strFromU8, gzipSync, strToU8 } from 'fflate';
|
|
4
4
|
|
|
5
5
|
const breaker = {};
|
|
6
6
|
const ArrayProto = Array.prototype;
|
|
@@ -1183,7 +1183,7 @@ const detectDeviceType = function (user_agent) {
|
|
|
1183
1183
|
}
|
|
1184
1184
|
};
|
|
1185
1185
|
|
|
1186
|
-
var version = "0.0
|
|
1186
|
+
var version = "0.1.0";
|
|
1187
1187
|
var packageInfo = {
|
|
1188
1188
|
version: version};
|
|
1189
1189
|
|
|
@@ -4415,53 +4415,6 @@ 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
|
-
|
|
4465
4418
|
function simpleHash(str) {
|
|
4466
4419
|
let hash = 0;
|
|
4467
4420
|
for (let i = 0; i < str.length; i++) {
|
|
@@ -4489,6 +4442,7 @@ const FIVE_MINUTES = ONE_MINUTE * 5;
|
|
|
4489
4442
|
const RECORDING_IDLE_THRESHOLD_MS = FIVE_MINUTES;
|
|
4490
4443
|
const RECORDING_MAX_EVENT_SIZE = ONE_KB * ONE_KB * 0.9; // ~1mb (with some wiggle room)
|
|
4491
4444
|
const RECORDING_BUFFER_TIMEOUT = 2000; // 2 seconds
|
|
4445
|
+
const SESSION_RECORDING_BATCH_KEY = 'recordings';
|
|
4492
4446
|
const LOGGER_PREFIX$1 = '[SessionRecording]';
|
|
4493
4447
|
const logger = createLogger(LOGGER_PREFIX$1);
|
|
4494
4448
|
const ACTIVE_SOURCES = [IncrementalSource.MouseMove, IncrementalSource.MouseInteraction, IncrementalSource.Scroll, IncrementalSource.ViewportResize, IncrementalSource.Input, IncrementalSource.TouchMove, IncrementalSource.MediaInteraction, IncrementalSource.Drag];
|
|
@@ -5206,31 +5160,14 @@ class LazyLoadedSessionRecording {
|
|
|
5206
5160
|
}, RECORDING_BUFFER_TIMEOUT);
|
|
5207
5161
|
}
|
|
5208
5162
|
}
|
|
5209
|
-
|
|
5210
|
-
//
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
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
|
-
}
|
|
5163
|
+
_captureSnapshot(properties) {
|
|
5164
|
+
// :TRICKY: Make sure we batch these requests, use a custom endpoint and don't truncate the strings.
|
|
5165
|
+
this._instance.capture('$snapshot', properties, {
|
|
5166
|
+
_url: this._snapshotUrl(),
|
|
5167
|
+
_noTruncate: true,
|
|
5168
|
+
_batchKey: SESSION_RECORDING_BATCH_KEY,
|
|
5169
|
+
skip_client_rate_limiting: true
|
|
5170
|
+
});
|
|
5234
5171
|
}
|
|
5235
5172
|
_snapshotUrl() {
|
|
5236
5173
|
const host = this._instance.config.host || '';
|
|
@@ -5878,116 +5815,10 @@ class Leanbase extends PostHogCore {
|
|
|
5878
5815
|
this.replayAutocapture?.onRemoteConfig(config);
|
|
5879
5816
|
this.sessionRecording?.onRemoteConfig(config);
|
|
5880
5817
|
}
|
|
5881
|
-
|
|
5818
|
+
fetch(url, options) {
|
|
5882
5819
|
const fetchFn = getFetch();
|
|
5883
5820
|
if (!fetchFn) {
|
|
5884
|
-
|
|
5885
|
-
}
|
|
5886
|
-
try {
|
|
5887
|
-
const isPost = !options.method || options.method.toUpperCase() === 'POST';
|
|
5888
|
-
const isBatchEndpoint = typeof url === 'string' && url.endsWith('/batch/');
|
|
5889
|
-
if (isPost && isBatchEndpoint && options && options.body) {
|
|
5890
|
-
let parsed = null;
|
|
5891
|
-
try {
|
|
5892
|
-
const headers = options.headers || {};
|
|
5893
|
-
const contentEncoding = (headers['Content-Encoding'] || headers['content-encoding'] || '').toLowerCase();
|
|
5894
|
-
const toUint8 = async body => {
|
|
5895
|
-
if (typeof body === 'string') return new TextEncoder().encode(body);
|
|
5896
|
-
if (typeof Blob !== 'undefined' && body instanceof Blob) {
|
|
5897
|
-
const ab = await body.arrayBuffer();
|
|
5898
|
-
return new Uint8Array(ab);
|
|
5899
|
-
}
|
|
5900
|
-
if (body instanceof ArrayBuffer) return new Uint8Array(body);
|
|
5901
|
-
if (ArrayBuffer.isView(body)) return new Uint8Array(body.buffer ?? body);
|
|
5902
|
-
try {
|
|
5903
|
-
return new TextEncoder().encode(String(body));
|
|
5904
|
-
} catch {
|
|
5905
|
-
return null;
|
|
5906
|
-
}
|
|
5907
|
-
};
|
|
5908
|
-
if (contentEncoding === 'gzip' || contentEncoding === 'deflate') {
|
|
5909
|
-
const u8 = await toUint8(options.body);
|
|
5910
|
-
if (u8) {
|
|
5911
|
-
try {
|
|
5912
|
-
const dec = decompressSync(u8);
|
|
5913
|
-
const s = strFromU8(dec);
|
|
5914
|
-
parsed = JSON.parse(s);
|
|
5915
|
-
} catch {
|
|
5916
|
-
parsed = null;
|
|
5917
|
-
}
|
|
5918
|
-
}
|
|
5919
|
-
} else {
|
|
5920
|
-
if (typeof options.body === 'string') {
|
|
5921
|
-
parsed = JSON.parse(options.body);
|
|
5922
|
-
} else {
|
|
5923
|
-
const u8 = await toUint8(options.body);
|
|
5924
|
-
if (u8) {
|
|
5925
|
-
try {
|
|
5926
|
-
parsed = JSON.parse(new TextDecoder().decode(u8));
|
|
5927
|
-
} catch {
|
|
5928
|
-
parsed = null;
|
|
5929
|
-
}
|
|
5930
|
-
} else {
|
|
5931
|
-
try {
|
|
5932
|
-
parsed = JSON.parse(String(options.body));
|
|
5933
|
-
} catch {
|
|
5934
|
-
parsed = null;
|
|
5935
|
-
}
|
|
5936
|
-
}
|
|
5937
|
-
}
|
|
5938
|
-
}
|
|
5939
|
-
} catch {
|
|
5940
|
-
parsed = null;
|
|
5941
|
-
}
|
|
5942
|
-
if (parsed && isArray(parsed.batch)) {
|
|
5943
|
-
const hasSnapshot = parsed.batch.some(item => item && item.event === '$snapshot');
|
|
5944
|
-
// Debug logging to help diagnose routing issues
|
|
5945
|
-
try {
|
|
5946
|
-
// eslint-disable-next-line no-console
|
|
5947
|
-
console.debug('[Leanbase.fetch] parsed.batch.length=', parsed.batch.length, 'hasSnapshot=', hasSnapshot);
|
|
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 {}
|
|
5978
|
-
if (hasSnapshot) {
|
|
5979
|
-
const host = this.config && this.config.host || '';
|
|
5980
|
-
const newUrl = host ? `${host.replace(/\/$/, '')}/s/` : url;
|
|
5981
|
-
try {
|
|
5982
|
-
// eslint-disable-next-line no-console
|
|
5983
|
-
console.debug('[Leanbase.fetch] routing snapshot batch to', newUrl);
|
|
5984
|
-
} catch {}
|
|
5985
|
-
return fetchFn(newUrl, options);
|
|
5986
|
-
}
|
|
5987
|
-
}
|
|
5988
|
-
}
|
|
5989
|
-
} catch {
|
|
5990
|
-
return fetchFn(url, options);
|
|
5821
|
+
return Promise.reject(new Error('Fetch API is not available in this environment.'));
|
|
5991
5822
|
}
|
|
5992
5823
|
return fetchFn(url, options);
|
|
5993
5824
|
}
|