@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 CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var core = require('@posthog/core');
4
- var fflate = require('fflate');
5
4
  var record = require('@rrweb/record');
5
+ var fflate = require('fflate');
6
6
 
7
7
  const breaker = {};
8
8
  const ArrayProto = Array.prototype;
@@ -1185,7 +1185,7 @@ const detectDeviceType = function (user_agent) {
1185
1185
  }
1186
1186
  };
1187
1187
 
1188
- var version = "0.0.5";
1188
+ var version = "0.1.0";
1189
1189
  var packageInfo = {
1190
1190
  version: version};
1191
1191
 
@@ -4417,53 +4417,6 @@ class MutationThrottler {
4417
4417
  }
4418
4418
  }
4419
4419
 
4420
- /**
4421
- * Leanbase-only SnapshotTransport
4422
- * Sends gzip-compressed snapshot payloads directly to the /s/ endpoint.
4423
- * This bypasses `posthog.capture()` and the analytics /batch/ queue.
4424
- *
4425
- * Reasoning: session replay snapshots must not enter the batch/persisted
4426
- * queues managed by @posthog/core. This transport posts directly to the
4427
- * recorder endpoint and uses the instance.fetch() implementation so it
4428
- * runs in the same environment the SDK uses.
4429
- */
4430
- async function sendSnapshotDirect(instance, url, payload) {
4431
- const fetchFn = instance && instance.fetch ? instance.fetch.bind(instance) : globalThis.fetch;
4432
- const body = JSON.stringify(payload);
4433
- const compressed = fflate.gzipSync(fflate.strToU8(body));
4434
- const headers = {
4435
- 'Content-Encoding': 'gzip',
4436
- 'Content-Type': 'application/json',
4437
- Accept: 'application/json'
4438
- };
4439
- if (!fetchFn) {
4440
- // best-effort: if no fetch available, fail silently (recorder should not crash app)
4441
- try {
4442
- // eslint-disable-next-line no-console
4443
- console.debug('[SnapshotTransport] no fetch available, dropping snapshot');
4444
- } catch {}
4445
- return {
4446
- status: 0
4447
- };
4448
- }
4449
- try {
4450
- // Use the instance.fetch wrapper so environment-specific shims are used.
4451
- return await fetchFn(url, {
4452
- method: 'POST',
4453
- body: compressed,
4454
- headers
4455
- });
4456
- } catch (err) {
4457
- try {
4458
- // eslint-disable-next-line no-console
4459
- console.debug('[SnapshotTransport] send failed', err);
4460
- } catch {}
4461
- return {
4462
- status: 0
4463
- };
4464
- }
4465
- }
4466
-
4467
4420
  function simpleHash(str) {
4468
4421
  let hash = 0;
4469
4422
  for (let i = 0; i < str.length; i++) {
@@ -4491,6 +4444,7 @@ const FIVE_MINUTES = ONE_MINUTE * 5;
4491
4444
  const RECORDING_IDLE_THRESHOLD_MS = FIVE_MINUTES;
4492
4445
  const RECORDING_MAX_EVENT_SIZE = ONE_KB * ONE_KB * 0.9; // ~1mb (with some wiggle room)
4493
4446
  const RECORDING_BUFFER_TIMEOUT = 2000; // 2 seconds
4447
+ const SESSION_RECORDING_BATCH_KEY = 'recordings';
4494
4448
  const LOGGER_PREFIX$1 = '[SessionRecording]';
4495
4449
  const logger = createLogger(LOGGER_PREFIX$1);
4496
4450
  const ACTIVE_SOURCES = [IncrementalSource.MouseMove, IncrementalSource.MouseInteraction, IncrementalSource.Scroll, IncrementalSource.ViewportResize, IncrementalSource.Input, IncrementalSource.TouchMove, IncrementalSource.MediaInteraction, IncrementalSource.Drag];
@@ -5208,31 +5162,14 @@ class LazyLoadedSessionRecording {
5208
5162
  }, RECORDING_BUFFER_TIMEOUT);
5209
5163
  }
5210
5164
  }
5211
- async _captureSnapshot(properties) {
5212
- // Send snapshots directly to the recorder endpoint to avoid the core
5213
- // analytics batching and persisted queue. The lifecycle gate in
5214
- // SessionRecording ensures this method is only called when recording
5215
- // is allowed.
5216
- try {
5217
- const url = this._snapshotUrl();
5218
- // include minimal metadata expected by /s/ endpoint
5219
- const payload = {
5220
- $snapshot_bytes: properties.$snapshot_bytes,
5221
- $snapshot_data: properties.$snapshot_data,
5222
- $session_id: properties.$session_id,
5223
- $window_id: properties.$window_id,
5224
- $lib: properties.$lib,
5225
- $lib_version: properties.$lib_version,
5226
- // include distinct_id from persistence for server-side association
5227
- distinct_id: this._instance.persistence?.get_property('distinct_id')
5228
- };
5229
- await sendSnapshotDirect(this._instance, url, payload);
5230
- } catch (err) {
5231
- try {
5232
- // eslint-disable-next-line no-console
5233
- console.debug('[SessionRecording] snapshot send failed', err);
5234
- } catch {}
5235
- }
5165
+ _captureSnapshot(properties) {
5166
+ // :TRICKY: Make sure we batch these requests, use a custom endpoint and don't truncate the strings.
5167
+ this._instance.capture('$snapshot', properties, {
5168
+ _url: this._snapshotUrl(),
5169
+ _noTruncate: true,
5170
+ _batchKey: SESSION_RECORDING_BATCH_KEY,
5171
+ skip_client_rate_limiting: true
5172
+ });
5236
5173
  }
5237
5174
  _snapshotUrl() {
5238
5175
  const host = this._instance.config.host || '';
@@ -5880,116 +5817,10 @@ class Leanbase extends core.PostHogCore {
5880
5817
  this.replayAutocapture?.onRemoteConfig(config);
5881
5818
  this.sessionRecording?.onRemoteConfig(config);
5882
5819
  }
5883
- async fetch(url, options) {
5820
+ fetch(url, options) {
5884
5821
  const fetchFn = core.getFetch();
5885
5822
  if (!fetchFn) {
5886
- throw new Error('Fetch API is not available in this environment.');
5887
- }
5888
- try {
5889
- const isPost = !options.method || options.method.toUpperCase() === 'POST';
5890
- const isBatchEndpoint = typeof url === 'string' && url.endsWith('/batch/');
5891
- if (isPost && isBatchEndpoint && options && options.body) {
5892
- let parsed = null;
5893
- try {
5894
- const headers = options.headers || {};
5895
- const contentEncoding = (headers['Content-Encoding'] || headers['content-encoding'] || '').toLowerCase();
5896
- const toUint8 = async body => {
5897
- if (typeof body === 'string') return new TextEncoder().encode(body);
5898
- if (typeof Blob !== 'undefined' && body instanceof Blob) {
5899
- const ab = await body.arrayBuffer();
5900
- return new Uint8Array(ab);
5901
- }
5902
- if (body instanceof ArrayBuffer) return new Uint8Array(body);
5903
- if (ArrayBuffer.isView(body)) return new Uint8Array(body.buffer ?? body);
5904
- try {
5905
- return new TextEncoder().encode(String(body));
5906
- } catch {
5907
- return null;
5908
- }
5909
- };
5910
- if (contentEncoding === 'gzip' || contentEncoding === 'deflate') {
5911
- const u8 = await toUint8(options.body);
5912
- if (u8) {
5913
- try {
5914
- const dec = fflate.decompressSync(u8);
5915
- const s = fflate.strFromU8(dec);
5916
- parsed = JSON.parse(s);
5917
- } catch {
5918
- parsed = null;
5919
- }
5920
- }
5921
- } else {
5922
- if (typeof options.body === 'string') {
5923
- parsed = JSON.parse(options.body);
5924
- } else {
5925
- const u8 = await toUint8(options.body);
5926
- if (u8) {
5927
- try {
5928
- parsed = JSON.parse(new TextDecoder().decode(u8));
5929
- } catch {
5930
- parsed = null;
5931
- }
5932
- } else {
5933
- try {
5934
- parsed = JSON.parse(String(options.body));
5935
- } catch {
5936
- parsed = null;
5937
- }
5938
- }
5939
- }
5940
- }
5941
- } catch {
5942
- parsed = null;
5943
- }
5944
- if (parsed && core.isArray(parsed.batch)) {
5945
- const hasSnapshot = parsed.batch.some(item => item && item.event === '$snapshot');
5946
- // Debug logging to help diagnose routing issues
5947
- try {
5948
- // eslint-disable-next-line no-console
5949
- console.debug('[Leanbase.fetch] parsed.batch.length=', parsed.batch.length, 'hasSnapshot=', hasSnapshot);
5950
- } catch {}
5951
- // If remote config has explicitly disabled session recording, drop snapshot events
5952
- try {
5953
- // Read persisted remote config that SessionRecording stores
5954
- const persisted = this.get_property(SESSION_RECORDING_REMOTE_CONFIG);
5955
- const serverAllowsRecording = !(persisted && persisted.enabled === false || this.config.disable_session_recording === true);
5956
- if (!serverAllowsRecording && hasSnapshot) {
5957
- // remove snapshot events from the batch before sending to /batch/
5958
- parsed.batch = parsed.batch.filter(item => !(item && item.event === '$snapshot'));
5959
- // If no events remain, short-circuit and avoid sending an empty batch
5960
- if (!parsed.batch.length) {
5961
- try {
5962
- // eslint-disable-next-line no-console
5963
- console.debug('[Leanbase.fetch] sessionRecording disabled, dropping snapshot-only batch');
5964
- } catch {}
5965
- return {
5966
- status: 200,
5967
- json: async () => ({})
5968
- };
5969
- }
5970
- // re-encode the body so the underlying fetch receives the modified batch
5971
- try {
5972
- const newBody = JSON.stringify(parsed);
5973
- options = {
5974
- ...options,
5975
- body: newBody
5976
- };
5977
- } catch {}
5978
- }
5979
- } catch {}
5980
- if (hasSnapshot) {
5981
- const host = this.config && this.config.host || '';
5982
- const newUrl = host ? `${host.replace(/\/$/, '')}/s/` : url;
5983
- try {
5984
- // eslint-disable-next-line no-console
5985
- console.debug('[Leanbase.fetch] routing snapshot batch to', newUrl);
5986
- } catch {}
5987
- return fetchFn(newUrl, options);
5988
- }
5989
- }
5990
- }
5991
- } catch {
5992
- return fetchFn(url, options);
5823
+ return Promise.reject(new Error('Fetch API is not available in this environment.'));
5993
5824
  }
5994
5825
  return fetchFn(url, options);
5995
5826
  }