@openreplay/tracker 12.0.11 → 12.1.0-beta.99

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/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 12.0.12
2
+
3
+ - fix for potential redux plugin issues after .11 ...
4
+
1
5
  # 12.0.11
2
6
 
3
7
  - better restart on unauth (new token assign for long sessions)
@@ -11,7 +11,7 @@ import type { Options as ObserverOptions } from './observer/top_observer.js';
11
11
  import type { Options as SanitizerOptions } from './sanitizer.js';
12
12
  import type { Options as SessOptions } from './session.js';
13
13
  import type { Options as NetworkOptions } from '../modules/network.js';
14
- import type { Options as WebworkerOptions } from '../common/interaction.js';
14
+ import type { Options as WebworkerOptions, FromWorkerData } from '../common/interaction.js';
15
15
  export interface StartOptions {
16
16
  userID?: string;
17
17
  metadata?: Record<string, string>;
@@ -90,7 +90,7 @@ export default class App {
90
90
  private readonly revID;
91
91
  private activityState;
92
92
  private readonly version;
93
- private readonly worker?;
93
+ private readonly workerManager?;
94
94
  private compressionThreshold;
95
95
  private restartAttempts;
96
96
  private readonly bc;
@@ -100,9 +100,10 @@ export default class App {
100
100
  private uxtManager;
101
101
  private conditionsManager;
102
102
  featureFlags: FeatureFlags;
103
- private tagWatcher;
103
+ private readonly tagWatcher;
104
104
  constructor(projectKey: string, sessionToken: string | undefined, options: Partial<Options>, signalError: (error: string, apis: string[]) => void);
105
- private _debug;
105
+ handleWorkerMsg(data: FromWorkerData): void;
106
+ private readonly _debug;
106
107
  private _usingOldFetchPlugin;
107
108
  send(message: Message, urgent?: boolean): void;
108
109
  /**
@@ -160,7 +161,7 @@ export default class App {
160
161
  private checkSessionToken;
161
162
  /**
162
163
  * start buffering messages without starting the actual session, which gives
163
- * user 30 seconds to "activate" and record session by calling `start()` on conditional trigger
164
+ * user 30 seconds to "activate" and record session by calling `start()` on conditional trigger,
164
165
  * and we will then send buffered batch, so it won't get lost
165
166
  * */
166
167
  coldStart(startOpts?: StartOptions, conditional?: boolean): Promise<void>;
@@ -178,7 +179,7 @@ export default class App {
178
179
  /**
179
180
  * Saves the captured messages in localStorage (or whatever is used in its place)
180
181
  *
181
- * Then when this.offlineRecording is called, it will preload this messages and clear the storage item
182
+ * Then, when this.offlineRecording is called, it will preload this messages and clear the storage item
182
183
  *
183
184
  * Keeping the size of local storage reasonable is up to the end users of this library
184
185
  * */
package/cjs/app/index.js CHANGED
@@ -44,6 +44,7 @@ const attributeSender_js_1 = __importDefault(require("../modules/attributeSender
44
44
  const canvas_js_1 = __importDefault(require("./canvas.js"));
45
45
  const index_js_1 = __importDefault(require("../modules/userTesting/index.js"));
46
46
  const tagWatcher_js_1 = __importDefault(require("../modules/tagWatcher.js"));
47
+ const index_js_2 = __importDefault(require("./workerManager/index.js"));
47
48
  const CANCELED = 'canceled';
48
49
  const uxtStorageKey = 'or_uxt_active';
49
50
  const bufferStorageKey = 'or_buffer_1';
@@ -80,12 +81,26 @@ class App {
80
81
  this.stopCallbacks = [];
81
82
  this.commitCallbacks = [];
82
83
  this.activityState = ActivityState.NotActive;
83
- this.version = '12.0.11'; // TODO: version compatability check inside each plugin.
84
+ this.version = '12.1.0-beta.99'; // TODO: version compatability check inside each plugin.
84
85
  this.compressionThreshold = 24 * 1000;
85
86
  this.restartAttempts = 0;
86
87
  this.bc = null;
87
88
  this.canvasRecorder = null;
88
89
  this.conditionsManager = null;
90
+ this._debug = (context, e) => {
91
+ if (this.options.__debug_report_edp !== null) {
92
+ void fetch(this.options.__debug_report_edp, {
93
+ method: 'POST',
94
+ headers: { 'Content-Type': 'application/json' },
95
+ body: JSON.stringify({
96
+ context,
97
+ // @ts-ignore
98
+ error: `${e}`,
99
+ }),
100
+ });
101
+ }
102
+ this.debug.error('OpenReplay error: ', context, e);
103
+ };
89
104
  this._usingOldFetchPlugin = false;
90
105
  this.coldStartCommitN = 0;
91
106
  this.delay = 0;
@@ -175,51 +190,11 @@ class App {
175
190
  this.session.applySessionHash(sessionToken);
176
191
  }
177
192
  try {
178
- this.worker = new Worker(URL.createObjectURL(new Blob(['"use strict";class t{constructor(t,s,i,e=10,n=250,h,r){this.onUnauthorised=s,this.onFailure=i,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.onCompress=h,this.pageNo=r,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.lastBatchNum=0,this.ingestURL=t+"/v1/web/i",this.isCompressing=void 0!==h}getQueueStatus(){return 0===this.queue.length&&!this.busy}authorise(t){this.token=t,this.busy||this.sendNext()}push(t){if(this.busy||!this.token)this.queue.push(t);else if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t);else{const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}}sendNext(){const t=this.queue.shift();if(t)if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t);else{const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}else this.busy=!1}retry(t,s,i){this.attemptsCount>=this.MAX_ATTEMPTS_COUNT?this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`):(this.attemptsCount++,setTimeout((()=>this.sendBatch(t,s,i)),this.ATTEMPT_TIMEOUT*this.attemptsCount))}sendBatch(t,s,i){const e=i?.toString().replace(/^([^_]+)_([^_]+).*/,"$1_$2_$3");this.busy=!0;const n={Authorization:`Bearer ${this.token}`};s&&(n["Content-Encoding"]="gzip"),null!==this.token?fetch(`${this.ingestURL}?batch=${this.pageNo??"noPageNum"}_${e??"noBatchNum"}`,{body:t,method:"POST",headers:n,keepalive:t.length<65536}).then((e=>{if(401===e.status)return this.busy=!1,void this.onUnauthorised();e.status>=400?this.retry(t,s,`${i??"noBatchNum"}_network:${e.status}`):(this.attemptsCount=0,this.sendNext())})).catch((e=>{console.warn("OpenReplay:",e),this.retry(t,s,`${i??"noBatchNum"}_reject:${e.message}`)})):setTimeout((()=>{this.sendBatch(t,s,`${i??"noBatchNum"}_newToken`)}),500)}sendCompressed(t){const s=++this.lastBatchNum;this.sendBatch(t,!0,s)}sendUncompressed(t){const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}clean(){this.sendNext(),setTimeout((()=>{this.token=null,this.queue.length=0}),10)}}const s="function"==typeof TextEncoder?new TextEncoder:{encode(t){const s=t.length,i=new Uint8Array(3*s);let e=-1;for(let n=0,h=0,r=0;r!==s;){if(n=t.charCodeAt(r),r+=1,n>=55296&&n<=56319){if(r===s){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;break}if(h=t.charCodeAt(r),!(h>=56320&&h<=57343)){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;continue}if(n=1024*(n-55296)+h-56320+65536,r+=1,n>65535){i[e+=1]=240|n>>>18,i[e+=1]=128|n>>>12&63,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n;continue}}n<=127?i[e+=1]=0|n:n<=2047?(i[e+=1]=192|n>>>6,i[e+=1]=128|63&n):(i[e+=1]=224|n>>>12,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n)}return i.subarray(0,e+1)}};class i{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}checkpoint(){this.checkpointOffset=this.offset}get isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,s){this.data.set(t,s)}boolean(t){return this.data[this.offset++]=+t,this.offset<=this.size}uint(t){for((t<0||t>Number.MAX_SAFE_INTEGER)&&(t=0);t>=128;)this.data[this.offset++]=t%256|128,t=Math.floor(t/128);return this.data[this.offset++]=t,this.offset<=this.size}int(t){return t=Math.round(t),this.uint(t>=0?2*t:-2*t-1)}string(t){const i=s.encode(t),e=i.byteLength;return!(!this.uint(e)||this.offset+e>this.size)&&(this.data.set(i,this.offset),this.offset+=e,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class e extends i{encode(t){switch(t[0]){case 0:case 11:case 114:case 115:return this.uint(t[1]);case 4:case 44:case 47:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 5:case 20:case 38:case 70:case 75:case 76:case 77:case 82:return this.uint(t[1])&&this.uint(t[2]);case 6:return this.int(t[1])&&this.int(t[2]);case 7:return!0;case 8:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.string(t[4])&&this.boolean(t[5]);case 9:case 10:case 24:case 51:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 12:case 61:case 71:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 13:case 14:case 17:case 50:case 54:return this.uint(t[1])&&this.string(t[2]);case 16:return this.uint(t[1])&&this.int(t[2])&&this.int(t[3]);case 18:return this.uint(t[1])&&this.string(t[2])&&this.int(t[3]);case 19:return this.uint(t[1])&&this.boolean(t[2]);case 21:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8]);case 22:case 27:case 30:case 41:case 45:case 46:case 63:case 64:case 79:return this.string(t[1])&&this.string(t[2]);case 23:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 28:case 29:case 42:case 117:case 118:return this.string(t[1]);case 37:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3]);case 39:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7]);case 40:return this.string(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 48:case 78:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 49:return this.int(t[1])&&this.int(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 53:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8]);case 55:return this.boolean(t[1]);case 57:case 60:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 58:case 120:return this.int(t[1]);case 59:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6])&&this.string(t[7]);case 67:case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 69:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 81:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.int(t[4])&&this.string(t[5]);case 83:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 84:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6]);case 112:return this.uint(t[1])&&this.string(t[2])&&this.boolean(t[3])&&this.string(t[4])&&this.int(t[5])&&this.int(t[6]);case 113:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3]);case 116:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10]);case 119:return this.string(t[1])&&this.uint(t[2]);case 121:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.uint(t[4])}}}class n{constructor(t,s,i,n,h,r){this.pageNo=t,this.timestamp=s,this.url=i,this.onBatch=n,this.tabId=h,this.onOfflineEnd=r,this.nextIndex=0,this.beaconSize=2e5,this.encoder=new e(this.beaconSize),this.sizeBuffer=new Uint8Array(3),this.isEmpty=!0,this.beaconSizeLimit=1e6,this.prepare()}writeType(t){return this.encoder.uint(t[0])}writeFields(t){return this.encoder.encode(t)}writeSizeAt(t,s){for(let s=0;s<3;s++)this.sizeBuffer[s]=t>>8*s;this.encoder.set(this.sizeBuffer,s)}prepare(){if(!this.encoder.isEmpty)return;const t=[81,1,this.pageNo,this.nextIndex,this.timestamp,this.url],s=[118,this.tabId];this.writeType(t),this.writeFields(t),this.writeWithSize(s),this.isEmpty=!0}writeWithSize(t){const s=this.encoder;if(!this.writeType(t)||!s.skip(3))return!1;const i=s.getCurrentOffset(),e=this.writeFields(t);if(e){const e=s.getCurrentOffset()-i;if(e>16777215)return console.warn("OpenReplay: max message size overflow."),!1;this.writeSizeAt(e,i-3),s.checkpoint(),this.isEmpty=this.isEmpty&&0===t[0],this.nextIndex++}return e}setBeaconSizeLimit(t){this.beaconSizeLimit=t}writeMessage(t){if("q_end"===t[0])return this.finaliseBatch(),this.onOfflineEnd();0===t[0]&&(this.timestamp=t[1]),4===t[0]&&(this.url=t[1]),this.writeWithSize(t)||(this.finaliseBatch(),this.writeWithSize(t)||(this.encoder=new e(this.beaconSizeLimit),this.prepare(),this.writeWithSize(t)?this.finaliseBatch():console.warn("OpenReplay: beacon size overflow. Skipping large message.",t,this),this.encoder=new e(this.beaconSize),this.prepare()))}finaliseBatch(){if(this.isEmpty)return;const t=this.encoder.flush();this.onBatch(t),this.prepare()}clean(){this.encoder.reset()}}var h;!function(t){t[t.NotActive=0]="NotActive",t[t.Starting=1]="Starting",t[t.Stopping=2]="Stopping",t[t.Active=3]="Active",t[t.Stopped=4]="Stopped"}(h||(h={}));let r=null,a=null,u=h.NotActive;function o(){a&&a.finaliseBatch()}function c(){return new Promise((t=>{u=h.Stopping,null!==l&&(clearInterval(l),l=null),a&&(a.clean(),a=null),r&&(r.clean(),setTimeout((()=>{r=null}),20)),setTimeout((()=>{u=h.NotActive,t(null)}),100)}))}function p(){u!==h.Stopped&&(postMessage("a_stop"),c().then((()=>{postMessage("a_start")})))}let g,l=null;self.onmessage=({data:s})=>{if(null!=s){if("stop"===s)return o(),void c().then((()=>{u=h.Stopped}));if("forceFlushBatch"!==s){if(!Array.isArray(s)){if("compressed"===s.type){if(!r)return console.debug("OR WebWorker: sender not initialised. Compressed batch."),void p();s.batch&&r.sendCompressed(s.batch)}if("uncompressed"===s.type){if(!r)return console.debug("OR WebWorker: sender not initialised. Uncompressed batch."),void p();s.batch&&r.sendUncompressed(s.batch)}return"start"===s.type?(u=h.Starting,r=new t(s.ingestPoint,(()=>{p()}),(t=>{!function(t){postMessage({type:"failure",reason:t}),c()}(t)}),s.connAttemptCount,s.connAttemptGap,(t=>{postMessage({type:"compress",batch:t},[t.buffer])}),s.pageNo),a=new n(s.pageNo,s.timestamp,s.url,(t=>{r&&r.push(t)}),s.tabId,(()=>postMessage({type:"queue_empty"}))),null===l&&(l=setInterval(o,1e4)),u=h.Active):"auth"===s.type?r?a?(r.authorise(s.token),void(s.beaconSizeLimit&&a.setBeaconSizeLimit(s.beaconSizeLimit))):(console.debug("OR WebWorker: writer not initialised. Received auth."),void p()):(console.debug("OR WebWorker: sender not initialised. Received auth."),void p()):void 0}if(a){const t=a;s.forEach((s=>{55===s[0]&&(s[1]?g=setTimeout((()=>p()),18e5):clearTimeout(g)),t.writeMessage(s)}))}else postMessage("not_init"),p()}else o()}else o()};'], { type: 'text/javascript' })));
179
- this.worker.onerror = (e) => {
180
- this._debug('webworker_error', e);
181
- };
182
- this.worker.onmessage = ({ data }) => {
183
- // handling 401 auth restart (new token assignment)
184
- if (data === 'a_stop') {
185
- this.stop(false);
186
- }
187
- else if (data === 'a_start') {
188
- void this.start({}, true);
189
- }
190
- else if (data === 'not_init') {
191
- this.debug.warn('OR WebWorker: writer not initialised. Restarting tracker');
192
- }
193
- else if (data.type === 'failure') {
194
- this.stop(false);
195
- this.debug.error('worker_failed', data.reason);
196
- this._debug('worker_failed', data.reason);
197
- }
198
- else if (data.type === 'compress') {
199
- const batch = data.batch;
200
- const batchSize = batch.byteLength;
201
- if (batchSize > this.compressionThreshold) {
202
- (0, fflate_1.gzip)(data.batch, { mtime: 0 }, (err, result) => {
203
- if (err) {
204
- this.debug.error('Openreplay compression error:', err);
205
- this.worker?.postMessage({ type: 'uncompressed', batch: batch });
206
- }
207
- else {
208
- this.worker?.postMessage({ type: 'compressed', batch: result });
209
- }
210
- });
211
- }
212
- else {
213
- this.worker?.postMessage({ type: 'uncompressed', batch: batch });
214
- }
215
- }
216
- else if (data.type === 'queue_empty') {
217
- this.onSessionSent();
218
- }
219
- };
193
+ const webworker = new Worker(URL.createObjectURL(new Blob(['"use strict";const t="function"==typeof TextEncoder?new TextEncoder:{encode(t){const i=t.length,s=new Uint8Array(3*i);let e=-1;for(let n=0,r=0,h=0;h!==i;){if(n=t.charCodeAt(h),h+=1,n>=55296&&n<=56319){if(h===i){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;break}if(r=t.charCodeAt(h),!(r>=56320&&r<=57343)){s[e+=1]=239,s[e+=1]=191,s[e+=1]=189;continue}if(n=1024*(n-55296)+r-56320+65536,h+=1,n>65535){s[e+=1]=240|n>>>18,s[e+=1]=128|n>>>12&63,s[e+=1]=128|n>>>6&63,s[e+=1]=128|63&n;continue}}n<=127?s[e+=1]=0|n:n<=2047?(s[e+=1]=192|n>>>6,s[e+=1]=128|63&n):(s[e+=1]=224|n>>>12,s[e+=1]=128|n>>>6&63,s[e+=1]=128|63&n)}return s.subarray(0,e+1)}};class i{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}checkpoint(){this.checkpointOffset=this.offset}get isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,i){this.data.set(t,i)}boolean(t){return this.data[this.offset++]=+t,this.offset<=this.size}uint(t){for((t<0||t>Number.MAX_SAFE_INTEGER)&&(t=0);t>=128;)this.data[this.offset++]=t%256|128,t=Math.floor(t/128);return this.data[this.offset++]=t,this.offset<=this.size}int(t){return t=Math.round(t),this.uint(t>=0?2*t:-2*t-1)}string(i){const s=t.encode(i),e=s.byteLength;return!(!this.uint(e)||this.offset+e>this.size)&&(this.data.set(s,this.offset),this.offset+=e,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class s extends i{encode(t){switch(t[0]){case 0:case 11:case 114:case 115:return this.uint(t[1]);case 4:case 44:case 47:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 5:case 20:case 38:case 70:case 75:case 76:case 77:case 82:return this.uint(t[1])&&this.uint(t[2]);case 6:return this.int(t[1])&&this.int(t[2]);case 7:return!0;case 8:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.string(t[4])&&this.boolean(t[5]);case 9:case 10:case 24:case 51:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 12:case 61:case 71:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 13:case 14:case 17:case 50:case 54:return this.uint(t[1])&&this.string(t[2]);case 16:return this.uint(t[1])&&this.int(t[2])&&this.int(t[3]);case 18:return this.uint(t[1])&&this.string(t[2])&&this.int(t[3]);case 19:return this.uint(t[1])&&this.boolean(t[2]);case 21:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8]);case 22:case 27:case 30:case 41:case 45:case 46:case 63:case 64:case 79:return this.string(t[1])&&this.string(t[2]);case 23:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 28:case 29:case 42:case 117:case 118:return this.string(t[1]);case 37:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3]);case 39:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7]);case 40:return this.string(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 48:case 78:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 49:return this.int(t[1])&&this.int(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 53:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8]);case 55:return this.boolean(t[1]);case 57:case 60:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 58:case 120:return this.int(t[1]);case 59:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6])&&this.string(t[7]);case 67:case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 69:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 81:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.int(t[4])&&this.string(t[5]);case 83:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 84:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6]);case 112:return this.uint(t[1])&&this.string(t[2])&&this.boolean(t[3])&&this.string(t[4])&&this.int(t[5])&&this.int(t[6]);case 113:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3]);case 116:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10]);case 119:return this.string(t[1])&&this.uint(t[2]);case 121:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.uint(t[4])}}}class e{constructor(t,i,e,n,r,h){this.pageNo=t,this.timestamp=i,this.url=e,this.onBatch=n,this.tabId=r,this.onOfflineEnd=h,this.nextIndex=0,this.beaconSize=2e5,this.encoder=new s(this.beaconSize),this.sizeBuffer=new Uint8Array(3),this.isEmpty=!0,this.beaconSizeLimit=1e6,this.prepare()}writeType(t){return this.encoder.uint(t[0])}writeFields(t){return this.encoder.encode(t)}writeSizeAt(t,i){for(let i=0;i<3;i++)this.sizeBuffer[i]=t>>8*i;this.encoder.set(this.sizeBuffer,i)}prepare(){if(!this.encoder.isEmpty)return;const t=[81,1,this.pageNo,this.nextIndex,this.timestamp,this.url],i=[118,this.tabId];this.writeType(t),this.writeFields(t),this.writeWithSize(i),this.isEmpty=!0}writeWithSize(t){const i=this.encoder;if(!this.writeType(t)||!i.skip(3))return!1;const s=i.getCurrentOffset(),e=this.writeFields(t);if(e){const e=i.getCurrentOffset()-s;if(e>16777215)return console.warn("OpenReplay: max message size overflow."),!1;this.writeSizeAt(e,s-3),i.checkpoint(),this.isEmpty=this.isEmpty&&0===t[0],this.nextIndex++}return e}setBeaconSizeLimit(t){this.beaconSizeLimit=t}writeMessage(t){if("q_end"===t[0])return this.finaliseBatch(),this.onOfflineEnd();0===t[0]&&(this.timestamp=t[1]),4===t[0]&&(this.url=t[1]),this.writeWithSize(t)||(this.finaliseBatch(),this.writeWithSize(t)||(this.encoder=new s(this.beaconSizeLimit),this.prepare(),this.writeWithSize(t)?this.finaliseBatch():console.warn("OpenReplay: beacon size overflow. Skipping large message.",t,this),this.encoder=new s(this.beaconSize),this.prepare()))}finaliseBatch(){if(this.isEmpty)return;const t=this.encoder.flush();this.onBatch(t),this.prepare()}clean(){this.encoder.reset()}}var n;!function(t){t[t.NotActive=0]="NotActive",t[t.Starting=1]="Starting",t[t.Stopping=2]="Stopping",t[t.Active=3]="Active",t[t.Stopped=4]="Stopped"}(n||(n={}));let r=null,h=n.NotActive;function a(t){postMessage({type:"status",data:t}),h=t}function u(){r&&r.finaliseBatch()}function c(){a(n.Stopping),null!==f&&(clearInterval(f),f=null),r&&(r.clean(),r=null),setTimeout((()=>{a(n.NotActive)}),100)}function o(){h!==n.Stopped&&(postMessage({type:"restart"}),c())}let f=null;self.onmessage=({data:t})=>{if(null!=t){if("writer_finalize"===t.type)return u(),h=n.Stopped;if("reset_writer"!==t.type){if("forceFlushBatch"!==t.type){if("to_writer"===t.type){let i=!1;t.data.forEach((t=>{r?r.writeMessage(t):i||(i=!0,postMessage({type:"not_init"}),o())}))}return"start"===t.type?(h=n.Starting,r=new e(t.pageNo,t.timestamp,t.url,(t=>{postMessage({type:"batch_ready",data:t},[t.buffer])}),t.tabId,(()=>postMessage({type:"queue_empty"}))),null===f&&(f=setInterval(u,1e4)),h=n.Active):"beacon_size_limit"===t.type?r?void(t.beaconSizeLimit&&r.setBeaconSizeLimit(t.beaconSizeLimit)):(console.debug("OR WebWorker: writer not initialised. Received auth."),void o()):void("restart"===t.type&&o())}u()}else c()}else u()};'], { type: 'text/javascript' })));
194
+ this.workerManager = new index_js_2.default(this, webworker, this._debug);
220
195
  const alertWorker = () => {
221
- if (this.worker) {
222
- this.worker.postMessage(null);
196
+ if (this.workerManager) {
197
+ this.workerManager.processMessage(null);
223
198
  }
224
199
  };
225
200
  // keep better tactics, discard others?
@@ -238,7 +213,7 @@ class App {
238
213
  // yes, there are someone out there
239
214
  resp: 'never-gonna-let-you-down',
240
215
  // you stole someone's identity
241
- reg: 'never-gonna-run-around-and-desert-you',
216
+ regen: 'never-gonna-run-around-and-desert-you',
242
217
  };
243
218
  if (this.bc) {
244
219
  this.bc.postMessage({
@@ -256,7 +231,7 @@ class App {
256
231
  const sessionToken = ev.data.token;
257
232
  this.session.setSessionToken(sessionToken);
258
233
  }
259
- if (ev.data.line === proto.reg) {
234
+ if (ev.data.line === proto.regen) {
260
235
  const sessionToken = ev.data.token;
261
236
  this.session.regenerateTabId();
262
237
  this.session.setSessionToken(sessionToken);
@@ -265,7 +240,7 @@ class App {
265
240
  const token = this.session.getSessionToken();
266
241
  if (token && this.bc) {
267
242
  this.bc.postMessage({
268
- line: ev.data.source === thisTab ? proto.reg : proto.resp,
243
+ line: ev.data.source === thisTab ? proto.regen : proto.resp,
269
244
  token,
270
245
  source: thisTab,
271
246
  context: this.contextId,
@@ -275,19 +250,44 @@ class App {
275
250
  };
276
251
  }
277
252
  }
278
- _debug(context, e) {
279
- if (this.options.__debug_report_edp !== null) {
280
- void fetch(this.options.__debug_report_edp, {
281
- method: 'POST',
282
- headers: { 'Content-Type': 'application/json' },
283
- body: JSON.stringify({
284
- context,
285
- // @ts-ignore
286
- error: `${e}`,
287
- }),
288
- });
253
+ handleWorkerMsg(data) {
254
+ if (data.type === 'restart') {
255
+ this.stop(false);
256
+ void this.start({}, true);
257
+ }
258
+ else if (data.type === 'not_init') {
259
+ this.debug.warn('OR WebWorker: writer not initialised; restarting worker');
260
+ }
261
+ else if (data.type === 'failure') {
262
+ this.stop(false);
263
+ this.debug.error('worker_failed', data.reason);
264
+ this._debug('worker_failed', data.reason);
265
+ }
266
+ else if (data.type === 'compress') {
267
+ const batch = data.batch;
268
+ const batchSize = batch.byteLength;
269
+ if (batchSize > this.compressionThreshold) {
270
+ (0, fflate_1.gzip)(data.batch, { mtime: 0 }, (err, result) => {
271
+ if (err) {
272
+ this.debug.error('Openreplay compression error:', err);
273
+ this.stop(false);
274
+ if (this.restartAttempts < 3) {
275
+ this.restartAttempts += 1;
276
+ void this.start({}, true);
277
+ }
278
+ }
279
+ else {
280
+ this.workerManager?.sendCompressedBatch(result);
281
+ }
282
+ });
283
+ }
284
+ else {
285
+ this.workerManager?.sendUncompressedBatch(batch);
286
+ }
287
+ }
288
+ else if (data.type === 'queue_empty') {
289
+ this.onSessionSent();
289
290
  }
290
- this.debug.error('OpenReplay error: ', context, e);
291
291
  }
292
292
  send(message, urgent = false) {
293
293
  if (this.activityState === ActivityState.NotActive) {
@@ -327,24 +327,15 @@ class App {
327
327
  * every ~30ms
328
328
  * */
329
329
  _nCommit() {
330
- if (this.worker !== undefined && this.messages.length) {
331
- try {
332
- (0, utils_js_1.requestIdleCb)(() => {
333
- this.messages.unshift((0, messages_gen_js_2.TabData)(this.session.getTabId()));
334
- this.messages.unshift((0, messages_gen_js_2.Timestamp)(this.timestamp()));
335
- // why I need to add opt chaining?
336
- this.worker?.postMessage(this.messages);
337
- this.commitCallbacks.forEach((cb) => cb(this.messages));
338
- this.messages.length = 0;
339
- });
340
- }
341
- catch (e) {
342
- this._debug('worker_commit', e);
343
- this.stop(true);
344
- setTimeout(() => {
345
- void this.start();
346
- }, 500);
347
- }
330
+ if (this.workerManager !== undefined && this.messages.length) {
331
+ (0, utils_js_1.requestIdleCb)(() => {
332
+ this.messages.unshift((0, messages_gen_js_2.TabData)(this.session.getTabId()));
333
+ this.messages.unshift((0, messages_gen_js_2.Timestamp)(this.timestamp()));
334
+ // why I need to add opt chaining?
335
+ this.workerManager?.processMessage({ type: 'batch', data: this.messages });
336
+ this.commitCallbacks.forEach((cb) => cb(this.messages));
337
+ this.messages.length = 0;
338
+ });
348
339
  }
349
340
  }
350
341
  /**
@@ -372,7 +363,7 @@ class App {
372
363
  }
373
364
  }
374
365
  postToWorker(messages) {
375
- this.worker?.postMessage(messages);
366
+ this.workerManager?.processMessage({ type: 'batch', data: messages });
376
367
  this.commitCallbacks.forEach((cb) => cb(messages));
377
368
  messages.length = 0;
378
369
  }
@@ -519,7 +510,7 @@ class App {
519
510
  }
520
511
  /**
521
512
  * start buffering messages without starting the actual session, which gives
522
- * user 30 seconds to "activate" and record session by calling `start()` on conditional trigger
513
+ * user 30 seconds to "activate" and record session by calling `start()` on conditional trigger,
523
514
  * and we will then send buffered batch, so it won't get lost
524
515
  * */
525
516
  async coldStart(startOpts = {}, conditional) {
@@ -655,7 +646,7 @@ class App {
655
646
  /**
656
647
  * Saves the captured messages in localStorage (or whatever is used in its place)
657
648
  *
658
- * Then when this.offlineRecording is called, it will preload this messages and clear the storage item
649
+ * Then, when this.offlineRecording is called, it will preload this messages and clear the storage item
659
650
  *
660
651
  * Keeping the size of local storage reasonable is up to the end users of this library
661
652
  * */
@@ -684,7 +675,7 @@ class App {
684
675
  async uploadOfflineRecording() {
685
676
  this.stop(false);
686
677
  const timestamp = (0, utils_js_1.now)();
687
- this.worker?.postMessage({
678
+ this.workerManager?.processMessage({
688
679
  type: 'start',
689
680
  pageNo: this.session.incPageNo(),
690
681
  ingestPoint: this.options.ingestPoint,
@@ -712,8 +703,7 @@ class App {
712
703
  }),
713
704
  });
714
705
  const { token, userBrowser, userCity, userCountry, userDevice, userOS, userState, beaconSizeLimit, projectID, } = await r.json();
715
- this.worker?.postMessage({
716
- type: 'auth',
706
+ this.workerManager?.authorizeWorker({
717
707
  token,
718
708
  beaconSizeLimit,
719
709
  });
@@ -737,7 +727,7 @@ class App {
737
727
  if (isColdStart && this.coldInterval) {
738
728
  clearInterval(this.coldInterval);
739
729
  }
740
- if (!this.worker) {
730
+ if (!this.workerManager) {
741
731
  const reason = 'No worker found: perhaps, CSP is not set.';
742
732
  this.signalError(reason, []);
743
733
  return Promise.resolve(UnsuccessfulStart(reason));
@@ -764,7 +754,7 @@ class App {
764
754
  metadata: startOpts.metadata,
765
755
  });
766
756
  const timestamp = (0, utils_js_1.now)();
767
- this.worker.postMessage({
757
+ this.workerManager?.startWorker({
768
758
  type: 'start',
769
759
  pageNo: this.session.incPageNo(),
770
760
  ingestPoint: this.options.ingestPoint,
@@ -810,7 +800,7 @@ class App {
810
800
  }
811
801
  })
812
802
  .then(async (r) => {
813
- if (!this.worker) {
803
+ if (!this.workerManager) {
814
804
  const reason = 'no worker found after start request (this might not happen)';
815
805
  this.signalError(reason, []);
816
806
  return Promise.reject(reason);
@@ -850,8 +840,7 @@ class App {
850
840
  timestamp: startTimestamp || timestamp,
851
841
  projectID,
852
842
  });
853
- this.worker.postMessage({
854
- type: 'auth',
843
+ this.workerManager?.authorizeWorker({
855
844
  token,
856
845
  beaconSizeLimit,
857
846
  });
@@ -986,7 +975,7 @@ class App {
986
975
  }
987
976
  }
988
977
  forceFlushBatch() {
989
- this.worker?.postMessage('forceFlushBatch');
978
+ this.workerManager?.processMessage({ type: 'forceFlushBatch' });
990
979
  }
991
980
  getTabId() {
992
981
  return this.session.getTabId();
@@ -1023,8 +1012,8 @@ class App {
1023
1012
  this.stopCallbacks.forEach((cb) => cb());
1024
1013
  this.debug.log('OpenReplay tracking stopped.');
1025
1014
  this.tagWatcher.clear();
1026
- if (this.worker && stopWorker) {
1027
- this.worker.postMessage('stop');
1015
+ if (this.workerManager && stopWorker) {
1016
+ this.workerManager?.stopWorker();
1028
1017
  }
1029
1018
  this.canvasRecorder?.clear();
1030
1019
  }
@@ -31,7 +31,7 @@ export declare function Fetch(method: string, url: string, request: string, resp
31
31
  export declare function Profiler(name: string, duration: number, args: string, result: string): Messages.Profiler;
32
32
  export declare function OTable(key: string, value: string): Messages.OTable;
33
33
  export declare function StateAction(type: string): Messages.StateAction;
34
- export declare function ReduxDeprecated(action: string, state: string, duration: number): Messages.ReduxDeprecated;
34
+ export declare function Redux(action: string, state: string, duration: number): Messages.Redux;
35
35
  export declare function Vuex(mutation: string, state: string): Messages.Vuex;
36
36
  export declare function MobX(type: string, payload: string): Messages.MobX;
37
37
  export declare function NgRx(action: string, state: string, duration: number): Messages.NgRx;
@@ -72,4 +72,4 @@ export declare function TabChange(tabId: string): Messages.TabChange;
72
72
  export declare function TabData(tabId: string): Messages.TabData;
73
73
  export declare function CanvasNode(nodeId: string, timestamp: number): Messages.CanvasNode;
74
74
  export declare function TagTrigger(tagId: number): Messages.TagTrigger;
75
- export declare function Redux(action: string, state: string, duration: number, actionTime: number): Messages.Redux;
75
+ export declare function ReduxNew(action: string, state: string, duration: number, actionTime: number): Messages.ReduxNew;
@@ -2,8 +2,8 @@
2
2
  // Auto-generated, do not edit
3
3
  /* eslint-disable */
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.CSSInsertRuleURLBased = exports.CustomIssue = exports.TechnicalInfo = exports.SetCSSDataURLBased = exports.SetNodeAttributeURLBased = exports.LongTask = exports.SetNodeFocus = exports.LoadFontFace = exports.SetPageVisibility = exports.ConnectionInformation = exports.ResourceTimingDeprecated = exports.SetNodeAttributeDict = exports.StringDict = exports.PerformanceTrack = exports.GraphQL = exports.NgRx = exports.MobX = exports.Vuex = exports.ReduxDeprecated = exports.StateAction = exports.OTable = exports.Profiler = exports.Fetch = exports.CSSDeleteRule = exports.CSSInsertRule = exports.Metadata = exports.UserAnonymousID = exports.UserID = exports.CustomEvent = exports.PageRenderTiming = exports.PageLoadTiming = exports.ConsoleLog = exports.NetworkRequestDeprecated = exports.MouseMove = exports.SetInputChecked = exports.SetInputValue = exports.SetInputTarget = exports.SetNodeScroll = exports.SetNodeData = exports.RemoveNodeAttribute = exports.SetNodeAttribute = exports.RemoveNode = exports.MoveNode = exports.CreateTextNode = exports.CreateElementNode = exports.CreateDocument = exports.SetViewportScroll = exports.SetViewportSize = exports.SetPageLocation = exports.Timestamp = void 0;
6
- exports.Redux = exports.TagTrigger = exports.CanvasNode = exports.TabData = exports.TabChange = exports.ResourceTiming = exports.UnbindNodes = exports.MouseThrashing = exports.SelectionChange = exports.InputChange = exports.WSChannel = exports.NetworkRequest = exports.PartitionedMessage = exports.BatchMetadata = exports.Zustand = exports.JSException = exports.AdoptedSSRemoveOwner = exports.AdoptedSSAddOwner = exports.AdoptedSSDeleteRule = exports.AdoptedSSInsertRuleURLBased = exports.AdoptedSSReplaceURLBased = exports.CreateIFrameDocument = exports.MouseClick = void 0;
5
+ exports.CSSInsertRuleURLBased = exports.CustomIssue = exports.TechnicalInfo = exports.SetCSSDataURLBased = exports.SetNodeAttributeURLBased = exports.LongTask = exports.SetNodeFocus = exports.LoadFontFace = exports.SetPageVisibility = exports.ConnectionInformation = exports.ResourceTimingDeprecated = exports.SetNodeAttributeDict = exports.StringDict = exports.PerformanceTrack = exports.GraphQL = exports.NgRx = exports.MobX = exports.Vuex = exports.Redux = exports.StateAction = exports.OTable = exports.Profiler = exports.Fetch = exports.CSSDeleteRule = exports.CSSInsertRule = exports.Metadata = exports.UserAnonymousID = exports.UserID = exports.CustomEvent = exports.PageRenderTiming = exports.PageLoadTiming = exports.ConsoleLog = exports.NetworkRequestDeprecated = exports.MouseMove = exports.SetInputChecked = exports.SetInputValue = exports.SetInputTarget = exports.SetNodeScroll = exports.SetNodeData = exports.RemoveNodeAttribute = exports.SetNodeAttribute = exports.RemoveNode = exports.MoveNode = exports.CreateTextNode = exports.CreateElementNode = exports.CreateDocument = exports.SetViewportScroll = exports.SetViewportSize = exports.SetPageLocation = exports.Timestamp = void 0;
6
+ exports.ReduxNew = exports.TagTrigger = exports.CanvasNode = exports.TabData = exports.TabChange = exports.ResourceTiming = exports.UnbindNodes = exports.MouseThrashing = exports.SelectionChange = exports.InputChange = exports.WSChannel = exports.NetworkRequest = exports.PartitionedMessage = exports.BatchMetadata = exports.Zustand = exports.JSException = exports.AdoptedSSRemoveOwner = exports.AdoptedSSAddOwner = exports.AdoptedSSDeleteRule = exports.AdoptedSSInsertRuleURLBased = exports.AdoptedSSReplaceURLBased = exports.CreateIFrameDocument = exports.MouseClick = void 0;
7
7
  function Timestamp(timestamp) {
8
8
  return [
9
9
  0 /* Messages.Type.Timestamp */,
@@ -276,15 +276,15 @@ function StateAction(type) {
276
276
  ];
277
277
  }
278
278
  exports.StateAction = StateAction;
279
- function ReduxDeprecated(action, state, duration) {
279
+ function Redux(action, state, duration) {
280
280
  return [
281
- 44 /* Messages.Type.ReduxDeprecated */,
281
+ 44 /* Messages.Type.Redux */,
282
282
  action,
283
283
  state,
284
284
  duration,
285
285
  ];
286
286
  }
287
- exports.ReduxDeprecated = ReduxDeprecated;
287
+ exports.Redux = Redux;
288
288
  function Vuex(mutation, state) {
289
289
  return [
290
290
  45 /* Messages.Type.Vuex */,
@@ -656,13 +656,13 @@ function TagTrigger(tagId) {
656
656
  ];
657
657
  }
658
658
  exports.TagTrigger = TagTrigger;
659
- function Redux(action, state, duration, actionTime) {
659
+ function ReduxNew(action, state, duration, actionTime) {
660
660
  return [
661
- 121 /* Messages.Type.Redux */,
661
+ 121 /* Messages.Type.ReduxNew */,
662
662
  action,
663
663
  state,
664
664
  duration,
665
665
  actionTime,
666
666
  ];
667
667
  }
668
- exports.Redux = Redux;
668
+ exports.ReduxNew = ReduxNew;
@@ -0,0 +1,25 @@
1
+ export default class QueueSender {
2
+ private readonly onUnauthorised;
3
+ private readonly onFailure;
4
+ private readonly MAX_ATTEMPTS_COUNT;
5
+ private readonly ATTEMPT_TIMEOUT;
6
+ private readonly onCompress?;
7
+ private readonly pageNo?;
8
+ private attemptsCount;
9
+ private busy;
10
+ private readonly queue;
11
+ private readonly ingestURL;
12
+ private token;
13
+ private readonly isCompressing;
14
+ private lastBatchNum;
15
+ constructor(ingestBaseURL: string, onUnauthorised: () => any, onFailure: (reason: string) => any, MAX_ATTEMPTS_COUNT?: number, ATTEMPT_TIMEOUT?: number, onCompress?: ((batch: Uint8Array) => any) | undefined, pageNo?: number | undefined);
16
+ getQueueStatus(): boolean;
17
+ authorise(token: string): void;
18
+ push(batch: Uint8Array): void;
19
+ private sendNext;
20
+ private retry;
21
+ private sendBatch;
22
+ sendCompressed(batch: Uint8Array): void;
23
+ sendUncompressed(batch: Uint8Array): void;
24
+ clean(): void;
25
+ }
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const INGEST_PATH = '/v1/web/i';
4
+ const KEEPALIVE_SIZE_LIMIT = 64 << 10; // 64 kB
5
+ class QueueSender {
6
+ constructor(ingestBaseURL, onUnauthorised, onFailure, MAX_ATTEMPTS_COUNT = 10, ATTEMPT_TIMEOUT = 250, onCompress, pageNo) {
7
+ this.onUnauthorised = onUnauthorised;
8
+ this.onFailure = onFailure;
9
+ this.MAX_ATTEMPTS_COUNT = MAX_ATTEMPTS_COUNT;
10
+ this.ATTEMPT_TIMEOUT = ATTEMPT_TIMEOUT;
11
+ this.onCompress = onCompress;
12
+ this.pageNo = pageNo;
13
+ this.attemptsCount = 0;
14
+ this.busy = false;
15
+ this.queue = [];
16
+ this.token = null;
17
+ this.lastBatchNum = 0;
18
+ this.ingestURL = ingestBaseURL + INGEST_PATH;
19
+ this.isCompressing = onCompress !== undefined;
20
+ }
21
+ getQueueStatus() {
22
+ return this.queue.length === 0 && !this.busy;
23
+ }
24
+ authorise(token) {
25
+ this.token = token;
26
+ if (!this.busy) {
27
+ // TODO: transparent busy/send logic
28
+ this.sendNext();
29
+ }
30
+ }
31
+ push(batch) {
32
+ if (this.busy || !this.token) {
33
+ this.queue.push(batch);
34
+ }
35
+ else {
36
+ this.busy = true;
37
+ if (this.isCompressing && this.onCompress) {
38
+ this.onCompress(batch);
39
+ }
40
+ else {
41
+ const batchNum = ++this.lastBatchNum;
42
+ this.sendBatch(batch, false, batchNum);
43
+ }
44
+ }
45
+ }
46
+ sendNext() {
47
+ const nextBatch = this.queue.shift();
48
+ if (nextBatch) {
49
+ this.busy = true;
50
+ if (this.isCompressing && this.onCompress) {
51
+ this.onCompress(nextBatch);
52
+ }
53
+ else {
54
+ const batchNum = ++this.lastBatchNum;
55
+ this.sendBatch(nextBatch, false, batchNum);
56
+ }
57
+ }
58
+ else {
59
+ this.busy = false;
60
+ }
61
+ }
62
+ retry(batch, isCompressed, batchNum) {
63
+ if (this.attemptsCount >= this.MAX_ATTEMPTS_COUNT) {
64
+ this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`);
65
+ // remains this.busy === true
66
+ return;
67
+ }
68
+ this.attemptsCount++;
69
+ setTimeout(() => this.sendBatch(batch, isCompressed, batchNum), this.ATTEMPT_TIMEOUT * this.attemptsCount);
70
+ }
71
+ // would be nice to use Beacon API, but it is not available in WebWorker
72
+ sendBatch(batch, isCompressed, batchNum) {
73
+ const batchNumStr = batchNum?.toString().replace(/^([^_]+)_([^_]+).*/, '$1_$2_$3');
74
+ this.busy = true;
75
+ const headers = {
76
+ Authorization: `Bearer ${this.token}`,
77
+ };
78
+ if (isCompressed) {
79
+ headers['Content-Encoding'] = 'gzip';
80
+ }
81
+ /**
82
+ * sometimes happen during assist connects for some reason
83
+ * */
84
+ if (this.token === null) {
85
+ setTimeout(() => {
86
+ this.sendBatch(batch, isCompressed, `${batchNum ?? 'noBatchNum'}_newToken`);
87
+ }, 500);
88
+ return;
89
+ }
90
+ fetch(`${this.ingestURL}?batch=${this.pageNo ?? 'noPageNum'}_${batchNumStr ?? 'noBatchNum'}`, {
91
+ body: batch,
92
+ method: 'POST',
93
+ headers,
94
+ keepalive: batch.length < KEEPALIVE_SIZE_LIMIT,
95
+ })
96
+ .then((r) => {
97
+ if (r.status === 401) {
98
+ // TODO: continuous session ?
99
+ this.busy = false;
100
+ this.onUnauthorised();
101
+ return;
102
+ }
103
+ else if (r.status >= 400) {
104
+ this.retry(batch, isCompressed, `${batchNum ?? 'noBatchNum'}_network:${r.status}`);
105
+ return;
106
+ }
107
+ // Success
108
+ this.attemptsCount = 0;
109
+ this.sendNext();
110
+ })
111
+ .catch((e) => {
112
+ console.warn('OpenReplay:', e);
113
+ this.retry(batch, isCompressed, `${batchNum ?? 'noBatchNum'}_reject:${e.message}`);
114
+ });
115
+ }
116
+ sendCompressed(batch) {
117
+ const batchNum = ++this.lastBatchNum;
118
+ this.sendBatch(batch, true, batchNum);
119
+ }
120
+ sendUncompressed(batch) {
121
+ const batchNum = ++this.lastBatchNum;
122
+ this.sendBatch(batch, false, batchNum);
123
+ }
124
+ clean() {
125
+ // sending last batch and closing the shop
126
+ this.sendNext();
127
+ setTimeout(() => {
128
+ this.token = null;
129
+ this.queue.length = 0;
130
+ }, 10);
131
+ }
132
+ }
133
+ exports.default = QueueSender;