@alwaysmeticulous/recorder-loader 2.161.0 → 2.162.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.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { NetworkResponseSanitizer } from "@alwaysmeticulous/sdk-bundles-api";
1
+ import { NetworkResponseSanitizer, RecorderMiddleware } from "@alwaysmeticulous/sdk-bundles-api";
2
2
  interface LoaderOptions {
3
3
  /**
4
4
  * @deprecated Renamed to recordingToken. Please use the same value but pass it as the recordingToken instead.
@@ -24,32 +24,41 @@ interface LoaderOptions {
24
24
  forceRecording?: boolean;
25
25
  /**
26
26
  * Optional. Allows sanitizing network responses before they are sent to Meticulous's servers.
27
+ *
28
+ * @deprecated Please use `middleware` instead.
27
29
  */
28
30
  responseSanitizers?: NetworkResponseSanitizer[];
31
+ /**
32
+ * Transform the recorded data before it is sent to Meticulous's servers. This is useful for redacting sensitive
33
+ * information when recording production sessions.
34
+ *
35
+ * Please see JSDoc on {@link RecorderMiddleware} before implementing custom middleware.
36
+ */
37
+ middleware?: RecorderMiddleware[];
38
+ }
39
+ interface Recorder {
40
+ /**
41
+ * Disables the recorder for the rest of the user session, and stops sending data to the Meticulous
42
+ * servers.
43
+ *
44
+ * Once this method is called the recorder cannot be restarted (unless the page is reloaded).
45
+ */
46
+ stopRecording: () => Promise<void>;
29
47
  }
30
48
  /**
31
49
  * Load and start the Meticulous Recorder
32
50
  */
33
- export const tryLoadAndStartRecorder: (options: LoaderOptions) => Promise<void>;
51
+ export const tryLoadAndStartRecorder: (options: LoaderOptions) => Promise<Recorder>;
34
52
  /**
35
53
  * @deprecated Use `tryLoadAndStartRecorder` instead.
36
54
  *
37
55
  * Load and start the Meticulous Recorder
38
56
  */
39
- export const loadAndStartRecorder: ({ projectId, recordingToken, uploadIntervalMs, snapshotLinkedStylesheets, commitHash, maxMsToBlockFor: maxMsToBlockFor_, snippetsBaseUrl, forceRecording, responseSanitizers, isProduction, }: LoaderOptions) => Promise<void>;
57
+ export const loadAndStartRecorder: ({ projectId, recordingToken, uploadIntervalMs, snapshotLinkedStylesheets, commitHash, maxMsToBlockFor: maxMsToBlockFor_, snippetsBaseUrl, forceRecording, middleware, responseSanitizers, isProduction, }: LoaderOptions) => Promise<Recorder>;
40
58
  interface Interceptor {
41
59
  startRecordingSession: (options: LoaderOptions) => Promise<Recorder>;
42
60
  stopIntercepting: () => Promise<void>;
43
61
  }
44
- interface Recorder {
45
- /**
46
- * Disables the recorder for the rest of the user session, and stops sending data to the Meticulous
47
- * servers.
48
- *
49
- * Once this method is called the recorder cannot be restarted (unless the page is reloaded).
50
- */
51
- stopRecording: () => Promise<void>;
52
- }
53
62
  /**
54
63
  * Stores a copy of network requests and responses in memory, but doesn't send them to the
55
64
  * server, until the the main recorder is initialised.
@@ -84,5 +93,18 @@ interface Recorder {
84
93
  export const tryInstallMeticulousIntercepts: (options?: {
85
94
  maxMsToBlockFor: number;
86
95
  }) => Promise<Interceptor>;
96
+ /**
97
+ * If you add 'https://snippet.meticulous.ai/record/v1/network-recorder.bundle.js' as a script tag
98
+ * then Meticulous will start off by recording any network requests in memory but not send the data to Meticulous’s
99
+ * servers. Once you have the information to determine whether you want to record and persist the session
100
+ * you can then call {@link tryLoadAndStartRecorder} if you do want to record, or this stopIntercepting method
101
+ * if you do not want to record. When you call stopIntercepting any network requests/responses stored in memory
102
+ * will be dropped and Meticulous will be deactivated. No data will be sent to Meticulous's servers.
103
+ *
104
+ * Please note that if you've already started recording and sending data to Meticulous's services i.e.
105
+ * loaded the main meticulous recorder script or called tryLoadAndStartRecorder then this method will
106
+ * not stop the recording. For that you need to call the stopRecording method returned by tryLoadAndStartRecorder.
107
+ */
108
+ export const stopIntercepting: () => Promise<void>;
87
109
 
88
110
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"mappings":";ACEA;IACE;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IAEvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;OAEG;IACH,kBAAkB,CAAC,EAAE,wBAAwB,EAAE,CAAC;CACjD;AC7BD;;GAEG;AACH,OAAO,MAAM,mCACF,aAAa,KACrB,QAAQ,IAAI,CAYd,CAAC;AA2FF;;;;GAIG;AACH,OAAO,MAAM,sNAnFV,aAAa,KAAG,QAAQ,IAAI,CAmF+B,CAAC;ACzG/D;IACE,qBAAqB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrE,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED;IACE;;;;;OAKG;IACH,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,MAAM,2CACF;IAAE,eAAe,EAAE,MAAM,CAAA;CAAE,KACnC,QAAQ,WAAW,CAmErB,CAAC","sources":["packages/recorder-loader/src/src/constants.ts","packages/recorder-loader/src/src/loader.types.ts","packages/recorder-loader/src/src/loader.ts","packages/recorder-loader/src/src/install-meticulous-intercepts.ts","packages/recorder-loader/src/src/index.ts","packages/recorder-loader/src/index.ts"],"sourcesContent":[null,null,null,null,null,"export { loadAndStartRecorder, tryLoadAndStartRecorder } from \"./loader\";\nexport { tryInstallMeticulousIntercepts } from \"./install-meticulous-intercepts\";\n"],"names":[],"version":3,"file":"index.d.ts.map"}
1
+ {"mappings":";ACKA;IACE;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IAEvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,wBAAwB,EAAE,CAAC;IAEhD;;;;;OAKG;IACH,UAAU,CAAC,EAAE,kBAAkB,EAAE,CAAC;CACnC;AEzCD;IACE;;;;;OAKG;IACH,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,OAAO,MAAM,mCACF,aAAa,KACrB,QAAQ,QAAQ,CAqBlB,CAAC;AAiHF;;;;GAIG;AACH,OAAO,MAAM,kOAxGV,aAAa,KAAG,QAAQ,QAAQ,CAwG2B,CAAC;AC3J/D;IACE,qBAAqB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrE,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,MAAM,2CACF;IAAE,eAAe,EAAE,MAAM,CAAA;CAAE,KACnC,QAAQ,WAAW,CAwDrB,CAAC;ACjGF;;;;;;;;;;;GAWG;AACH,OAAO,MAAM,qCAMZ,CAAC","sources":["packages/recorder-loader/src/src/constants.ts","packages/recorder-loader/src/src/loader.types.ts","packages/recorder-loader/src/src/private-window-api.ts","packages/recorder-loader/src/src/loader.ts","packages/recorder-loader/src/src/install-meticulous-intercepts.ts","packages/recorder-loader/src/src/early-network-recorder.ts","packages/recorder-loader/src/src/index.ts","packages/recorder-loader/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,"export { loadAndStartRecorder, tryLoadAndStartRecorder } from \"./loader\";\nexport { tryInstallMeticulousIntercepts } from \"./install-meticulous-intercepts\";\nexport { stopIntercepting } from \"./early-network-recorder\";\n"],"names":[],"version":3,"file":"index.d.ts.map"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ function $parcel$export(e, n, v, s) {
5
5
  $parcel$export(module.exports, "loadAndStartRecorder", () => $c97024bcb09807fa$export$9da0cde53b499187);
6
6
  $parcel$export(module.exports, "tryLoadAndStartRecorder", () => $c97024bcb09807fa$export$f80553af9d70f9fd);
7
7
  $parcel$export(module.exports, "tryInstallMeticulousIntercepts", () => $f8da777fb815521f$export$c0c9d1ed5ecb2675);
8
+ $parcel$export(module.exports, "stopIntercepting", () => $6795f29cc093eb86$export$e592d04d772ce0ec);
8
9
  const $d2f90b927d91d104$export$d16af6ab0c202bae = "https://snippet.meticulous.ai";
9
10
 
10
11
 
@@ -12,20 +13,33 @@ const $c97024bcb09807fa$var$DEFAULT_MAX_MS_TO_BLOCK_FOR = 2000;
12
13
  const $c97024bcb09807fa$export$f80553af9d70f9fd = async (options)=>{
13
14
  if (window.Meticulous?.isRunningAsTest) {
14
15
  console.debug("Running as part of a Meticulous test case, so skipping loading the Meticulous recorder.");
15
- return;
16
+ return {
17
+ stopRecording: async ()=>{
18
+ // No op
19
+ }
20
+ };
16
21
  }
17
22
  // Try to load the recorder and silence any initialisation error.
18
- await $c97024bcb09807fa$var$unsafeLoadAndStartRecorder(options).catch((error)=>{
23
+ return await $c97024bcb09807fa$var$unsafeLoadAndStartRecorder(options).catch((error)=>{
19
24
  console.error(error);
25
+ return {
26
+ stopRecording: async ()=>{
27
+ // No op
28
+ }
29
+ };
20
30
  });
21
31
  };
22
- const $c97024bcb09807fa$var$unsafeLoadAndStartRecorder = ({ projectId: projectId , recordingToken: recordingToken , uploadIntervalMs: uploadIntervalMs , snapshotLinkedStylesheets: snapshotLinkedStylesheets , commitHash: commitHash , maxMsToBlockFor: maxMsToBlockFor_ , snippetsBaseUrl: snippetsBaseUrl , forceRecording: forceRecording , responseSanitizers: responseSanitizers , isProduction: isProduction })=>{
32
+ const $c97024bcb09807fa$var$unsafeLoadAndStartRecorder = ({ projectId: projectId , recordingToken: recordingToken , uploadIntervalMs: uploadIntervalMs , snapshotLinkedStylesheets: snapshotLinkedStylesheets , commitHash: commitHash , maxMsToBlockFor: maxMsToBlockFor_ , snippetsBaseUrl: snippetsBaseUrl , forceRecording: forceRecording , middleware: middleware , responseSanitizers: responseSanitizers , isProduction: isProduction })=>{
23
33
  let abandoned = false;
24
34
  return new Promise((resolve, reject)=>{
25
35
  const maxMsToBlockFor = maxMsToBlockFor_ ?? $c97024bcb09807fa$var$DEFAULT_MAX_MS_TO_BLOCK_FOR;
26
36
  if (maxMsToBlockFor > 0) setTimeout(()=>{
27
37
  abandoned = true;
28
- resolve();
38
+ resolve({
39
+ stopRecording: async ()=>{
40
+ // No op: we never started recording
41
+ }
42
+ });
29
43
  }, maxMsToBlockFor);
30
44
  const script = document.createElement("script");
31
45
  script.type = "text/javascript";
@@ -40,6 +54,7 @@ const $c97024bcb09807fa$var$unsafeLoadAndStartRecorder = ({ projectId: projectId
40
54
  if (forceRecording !== undefined) typedWindow.METICULOUS_FORCE_RECORDING = forceRecording;
41
55
  if (isProduction !== undefined) typedWindow.METICULOUS_IS_PRODUCTION_ENVIRONMENT = isProduction;
42
56
  if (responseSanitizers != null && responseSanitizers.length > 0) typedWindow.METICULOUS_NETWORK_RESPONSE_SANITIZERS = responseSanitizers;
57
+ if (middleware != null && middleware.length > 0) typedWindow.METICULOUS_RECORDER_MIDDLEWARE_V1 = middleware;
43
58
  script.onload = function() {
44
59
  if (abandoned) {
45
60
  console.debug("Meticulous snippet abandoned due to max blocking time reached.");
@@ -56,7 +71,14 @@ const $c97024bcb09807fa$var$unsafeLoadAndStartRecorder = ({ projectId: projectId
56
71
  } catch (error) {
57
72
  reject(error);
58
73
  }
59
- resolve();
74
+ resolve({
75
+ stopRecording: async ()=>{
76
+ const stopRecording = window.__meticulous?.stopRecording;
77
+ if (!stopRecording) throw new Error("Recorder not initialised: window.__meticulous.stopRecording is not defined.");
78
+ await stopRecording();
79
+ return;
80
+ }
81
+ });
60
82
  };
61
83
  script.onerror = ()=>{
62
84
  reject("Meticulous recorder failed to initialise.");
@@ -82,16 +104,8 @@ const $f8da777fb815521f$export$c0c9d1ed5ecb2675 = async (options = {
82
104
  disposedEarlyNetworkRecorder = true;
83
105
  }
84
106
  };
85
- const startRecordingSession = (options)=>(0, $c97024bcb09807fa$export$f80553af9d70f9fd)(options).then(()=>({
86
- stopRecording: async ()=>{
87
- const stopRecording = window.__meticulous?.stopRecording;
88
- if (!stopRecording) throw new Error("Recorder not initialised: window.__meticulous.stopRecording is not defined.");
89
- await stopRecording();
90
- return;
91
- }
92
- }));
93
107
  const interceptor = {
94
- startRecordingSession: startRecordingSession,
108
+ startRecordingSession: (0, $c97024bcb09807fa$export$f80553af9d70f9fd),
95
109
  stopIntercepting: stopIntercepting
96
110
  };
97
111
  const promise = new Promise((resolve, reject)=>{
@@ -121,6 +135,12 @@ const $f8da777fb815521f$export$c0c9d1ed5ecb2675 = async (options = {
121
135
  };
122
136
 
123
137
 
138
+ const $6795f29cc093eb86$export$e592d04d772ce0ec = async ()=>{
139
+ const disposeFunction = window?.__meticulous?.earlyNetworkRecorder?.dispose;
140
+ if (disposeFunction) await disposeFunction();
141
+ };
142
+
143
+
124
144
 
125
145
 
126
146
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"mappings":";;;;;;;AEAO,MAAM,4CAAoB;;ADAjC;AAGA,MAAM,oDAA8B;AAK7B,MAAM,4CAA0B,OACrC,UACkB;IAClB,IAAI,OAAO,UAAU,EAAE,iBAAiB;QACtC,QAAQ,KAAK,CACX;QAEF;IACF,CAAC;IAED,iEAAiE;IACjE,MAAM,iDAA2B,SAAS,KAAK,CAAC,CAAC,QAAU;QACzD,QAAQ,KAAK,CAAC;IAChB;AACF;AAEA,MAAM,mDAA6B,CAAC,aAClC,UAAS,kBACT,eAAc,oBACd,iBAAgB,6BAChB,0BAAyB,cACzB,WAAU,EACV,iBAAiB,iBAAgB,mBACjC,gBAAe,kBACf,eAAc,sBACd,mBAAkB,gBAClB,aAAY,EACE,GAAoB;IAClC,IAAI,YAAY,KAAK;IAErB,OAAO,IAAI,QAAc,CAAC,SAAS,SAAW;QAC5C,MAAM,kBAAkB,oBAAoB;QAE5C,IAAI,kBAAkB,GACpB,WAAW,IAAM;YACf,YAAY,IAAI;YAChB;QACF,GAAG;QAGL,MAAM,SAAS,SAAS,aAAa,CAAC;QACtC,OAAO,IAAI,GAAG;QACd,MAAM,kBAAkB,mBAAmB,CAAA,GAAA,yCAAiB,AAAD;QAC3D,OAAO,GAAG,GAAG,IAAI,IAAI,gCAAgC,iBAAiB,IAAI;QAE1E,sBAAsB;QACtB,MAAM,cAAc;QACpB,YAAY,0BAA0B,GAAG,kBAAkB;QAE3D,IAAI,qBAAqB,WACvB,YAAY,6BAA6B,GAAG;QAG9C,IAAI,eAAe,WACjB,YAAY,0BAA0B,GAAG;QAG3C,IAAI,8BAA8B,WAChC,YAAY,sCAAsC,GAChD;QAGJ,IAAI,mBAAmB,WACrB,YAAY,0BAA0B,GAAG;QAG3C,IAAI,iBAAiB,WACnB,YAAY,oCAAoC,GAAG;QAGrD,IAAI,sBAAsB,IAAI,IAAI,mBAAmB,MAAM,GAAG,GAC5D,YAAY,sCAAsC,GAAG;QAGvD,OAAO,MAAM,GAAG,WAAY;YAC1B,IAAI,WAAW;gBACb,QAAQ,KAAK,CACX;gBAEF,kDAAkD;gBAClD;YACF,CAAC;YAED,MAAM,qBAAqB,OAAO,YAAY,EAAE;YAChD,IAAI,OAAO,uBAAuB,YAAY;gBAC5C,OAAO;gBACP;YACF,CAAC;YAED,IAAI;gBACF;YACF,EAAE,OAAO,OAAO;gBACd,OAAO;YACT;YAEA;QACF;QACA,OAAO,OAAO,GAAG,IAAM;YACrB,OAAO;QACT;QAEA,SAAS,IAAI,CAAC,WAAW,CAAC;IAC5B;AACF;AAOO,MAAM,4CAAuB;;ADtHpC;AGAA;;AA2DO,MAAM,4CAAiC,OAC5C,UAAuC;IAAE,iBAAiB;AAAK,CAAC,GACvC;IACzB,IAAI,8BAA8B,KAAK;IACvC,IAAI,+BAA+B,KAAK;IACxC,MAAM,mBAAmB,UAAY;QACnC,8BAA8B,IAAI;QAClC,MAAM,kBAAmB,QAAuC,cAC5D,sBAAsB;QAC1B,IAAI,mBAAmB,CAAC,8BAA8B;YACpD,MAAM;YACN,+BAA+B,IAAI;QACrC,CAAC;IACH;IACA,MAAM,wBAAwB,CAAC,UAC7B,CAAA,GAAA,yCAAuB,AAAD,EAAE,SAAS,IAAI,CAAC,IAAO,CAAA;gBAC3C,eAAe,UAAY;oBACzB,MAAM,gBAAgB,AAAC,OACpB,YAAY,EAAE;oBACjB,IAAI,CAAC,eACH,MAAM,IAAI,MACR,+EACA;oBAEJ,MAAM;oBACN;gBACF;YACF,CAAA;IACF,MAAM,cAAc;+BAAE;0BAAuB;IAAiB;IAE9D,MAAM,UAAU,IAAI,QAAqB,CAAC,SAAS,SAAW;QAC5D,MAAM,UACJ,QAAQ,eAAe,GAAG,IACtB,WAAW,IAAM;YACf,QAAQ;QACV,GAAG,QAAQ,eAAe,IAC1B,IAAI;QAEV,MAAM,SAAS,SAAS,aAAa,CAAC;QACtC,OAAO,IAAI,GAAG;QACd,OAAO,GAAG,GAAG,CAAC,EAAE,GAAA,yCAAiB,CAAC,qCAAqC,CAAC;QAExE,OAAO,MAAM,GAAG,WAAY;YAC1B,IAAI,SACF,OAAO,YAAY,CAAC;YAEtB,QAAQ;QACV;QACA,OAAO,OAAO,GAAG,IAAM;YACrB,IAAI,SACF,OAAO,YAAY,CAAC;YAEtB,OAAO;QACT;QAEA,SAAS,IAAI,CAAC,WAAW,CAAC;IAC5B;IAEA,+EAA+E;IAC/E,OAAO,QACJ,KAAK,CAAC,CAAC,QAAU;QAChB,QAAQ,KAAK,CAAC;QACd,OAAO;IACT,GACC,OAAO,CAAC,IAAM;QACb,IAAI,6BACG;IAET;AACJ;","sources":["packages/recorder-loader/src/index.ts","packages/recorder-loader/src/loader.ts","packages/recorder-loader/src/constants.ts","packages/recorder-loader/src/install-meticulous-intercepts.ts"],"sourcesContent":["export { loadAndStartRecorder, tryLoadAndStartRecorder } from \"./loader\";\nexport { tryInstallMeticulousIntercepts } from \"./install-meticulous-intercepts\";\n","import { SNIPPETS_BASE_URL } from \"./constants\";\nimport { LoaderOptions } from \"./loader.types\";\n\nconst DEFAULT_MAX_MS_TO_BLOCK_FOR = 2_000;\n\n/**\n * Load and start the Meticulous Recorder\n */\nexport const tryLoadAndStartRecorder = async (\n options: LoaderOptions\n): Promise<void> => {\n if (window.Meticulous?.isRunningAsTest) {\n console.debug(\n \"Running as part of a Meticulous test case, so skipping loading the Meticulous recorder.\"\n );\n return;\n }\n\n // Try to load the recorder and silence any initialisation error.\n await unsafeLoadAndStartRecorder(options).catch((error) => {\n console.error(error);\n });\n};\n\nconst unsafeLoadAndStartRecorder = ({\n projectId,\n recordingToken,\n uploadIntervalMs,\n snapshotLinkedStylesheets,\n commitHash,\n maxMsToBlockFor: maxMsToBlockFor_,\n snippetsBaseUrl,\n forceRecording,\n responseSanitizers,\n isProduction,\n}: LoaderOptions): Promise<void> => {\n let abandoned = false;\n\n return new Promise<void>((resolve, reject) => {\n const maxMsToBlockFor = maxMsToBlockFor_ ?? DEFAULT_MAX_MS_TO_BLOCK_FOR;\n\n if (maxMsToBlockFor > 0) {\n setTimeout(() => {\n abandoned = true;\n resolve();\n }, maxMsToBlockFor);\n }\n\n const script = document.createElement(\"script\");\n script.type = \"text/javascript\";\n const baseSnippetsUrl = snippetsBaseUrl || SNIPPETS_BASE_URL;\n script.src = new URL(\"v1/meticulous-manual-init.js\", baseSnippetsUrl).href;\n\n // Setup configuration\n const typedWindow = window;\n typedWindow.METICULOUS_RECORDING_TOKEN = recordingToken ?? projectId;\n\n if (uploadIntervalMs !== undefined) {\n typedWindow.METICULOUS_UPLOAD_INTERVAL_MS = uploadIntervalMs;\n }\n\n if (commitHash !== undefined) {\n typedWindow.METICULOUS_APP_COMMIT_HASH = commitHash;\n }\n\n if (snapshotLinkedStylesheets !== undefined) {\n typedWindow.METICULOUS_SNAPSHOT_LINKED_STYLESHEETS =\n snapshotLinkedStylesheets;\n }\n\n if (forceRecording !== undefined) {\n typedWindow.METICULOUS_FORCE_RECORDING = forceRecording;\n }\n\n if (isProduction !== undefined) {\n typedWindow.METICULOUS_IS_PRODUCTION_ENVIRONMENT = isProduction;\n }\n\n if (responseSanitizers != null && responseSanitizers.length > 0) {\n typedWindow.METICULOUS_NETWORK_RESPONSE_SANITIZERS = responseSanitizers;\n }\n\n script.onload = function () {\n if (abandoned) {\n console.debug(\n \"Meticulous snippet abandoned due to max blocking time reached.\"\n );\n // At this point the promise has already resolved.\n return;\n }\n\n const initialiseRecorder = window.__meticulous?.initialiseRecorder;\n if (typeof initialiseRecorder !== \"function\") {\n reject(\"Meticulous recorder failed to initialise.\");\n return;\n }\n\n try {\n initialiseRecorder();\n } catch (error) {\n reject(error);\n }\n\n resolve();\n };\n script.onerror = () => {\n reject(\"Meticulous recorder failed to initialise.\");\n };\n\n document.head.appendChild(script);\n });\n};\n\n/**\n * @deprecated Use `tryLoadAndStartRecorder` instead.\n *\n * Load and start the Meticulous Recorder\n */\nexport const loadAndStartRecorder = unsafeLoadAndStartRecorder;\n","export const SNIPPETS_BASE_URL = \"https://snippet.meticulous.ai\";\n","import { SNIPPETS_BASE_URL } from \"./constants\";\nimport { tryLoadAndStartRecorder } from \"./loader\";\nimport { LoaderOptions } from \"./loader.types\";\n\ninterface EarlyNetworkRecorderWindow {\n __meticulous?: {\n earlyNetworkRecorder?: {\n dispose?: () => Promise<void>;\n };\n stopRecording?: () => void;\n };\n}\n\nexport interface Interceptor {\n startRecordingSession: (options: LoaderOptions) => Promise<Recorder>;\n stopIntercepting: () => Promise<void>;\n}\n\nexport interface Recorder {\n /**\n * Disables the recorder for the rest of the user session, and stops sending data to the Meticulous\n * servers.\n *\n * Once this method is called the recorder cannot be restarted (unless the page is reloaded).\n */\n stopRecording: () => Promise<void>;\n}\n\n/**\n * Stores a copy of network requests and responses in memory, but doesn't send them to the\n * server, until the the main recorder is initialised.\n *\n * This is useful if you only want to record sessions for certain users with certain attributes. In\n * this case you have an issue: you need to wait for the user information to load before you know whether\n * you can enable the recorder, but if you enable the recorder after the user information has loaded\n * then the recorder won't be able to capture the initial request & response to load the user information,\n * or other early network responses.\n *\n * The early network recorder solves this: load the early network recorder for all sessions,\n * but only load the main recorder for sessions that you want to record. If when you load the user data\n * you find out you don't want to record the session then you can call the stopRecording() method returned\n * by this method.\n *\n * Example usage:\n *\n * ```\n * // The below call should happen before your app makes any network requests,\n * // or executes any methods that may store a reference to window.fetch or XMLHttpRequest.\n * const interceptor = await tryInstallMeticulousIntercepts();\n *\n * // Later, when you have loaded user data...\n * const userData = await loadUserInfo();\n * if (shouldRecord(userData)) {\n * await interceptor.startRecordingSession({ ... });\n * } else {\n * interceptor.stopIntercepting();\n * }\n * ```\n */\nexport const tryInstallMeticulousIntercepts = async (\n options: { maxMsToBlockFor: number } = { maxMsToBlockFor: 2000 }\n): Promise<Interceptor> => {\n let requestedToStopIntercepting = false;\n let disposedEarlyNetworkRecorder = false;\n const stopIntercepting = async () => {\n requestedToStopIntercepting = true;\n const disposeFunction = (window as EarlyNetworkRecorderWindow)?.__meticulous\n ?.earlyNetworkRecorder?.dispose;\n if (disposeFunction && !disposedEarlyNetworkRecorder) {\n await disposeFunction();\n disposedEarlyNetworkRecorder = true;\n }\n };\n const startRecordingSession = (options: LoaderOptions) =>\n tryLoadAndStartRecorder(options).then(() => ({\n stopRecording: async () => {\n const stopRecording = (window as EarlyNetworkRecorderWindow)\n .__meticulous?.stopRecording;\n if (!stopRecording) {\n throw new Error(\n \"Recorder not initialised: window.__meticulous.stopRecording is not defined.\"\n );\n }\n await stopRecording();\n return;\n },\n }));\n const interceptor = { startRecordingSession, stopIntercepting };\n\n const promise = new Promise<Interceptor>((resolve, reject) => {\n const timeout =\n options.maxMsToBlockFor > 0\n ? setTimeout(() => {\n resolve(interceptor);\n }, options.maxMsToBlockFor)\n : null;\n\n const script = document.createElement(\"script\");\n script.type = \"text/javascript\";\n script.src = `${SNIPPETS_BASE_URL}/record/v1/network-recorder.bundle.js`;\n\n script.onload = function () {\n if (timeout) {\n window.clearTimeout(timeout);\n }\n resolve(interceptor);\n };\n script.onerror = () => {\n if (timeout) {\n window.clearTimeout(timeout);\n }\n reject(\"Meticulous early network recorder failed to initialise.\");\n };\n\n document.head.appendChild(script);\n });\n\n // Try to load the early network recorder and silence any initialisation error.\n return promise\n .catch((error) => {\n console.error(error);\n return interceptor;\n })\n .finally(() => {\n if (requestedToStopIntercepting) {\n void stopIntercepting();\n }\n });\n};\n"],"names":[],"version":3,"file":"index.js.map"}
1
+ {"mappings":"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","sources":["packages/recorder-loader/src/index.ts","packages/recorder-loader/src/loader.ts","packages/recorder-loader/src/constants.ts","packages/recorder-loader/src/install-meticulous-intercepts.ts","packages/recorder-loader/src/early-network-recorder.ts"],"sourcesContent":["export { loadAndStartRecorder, tryLoadAndStartRecorder } from \"./loader\";\nexport { tryInstallMeticulousIntercepts } from \"./install-meticulous-intercepts\";\nexport { stopIntercepting } from \"./early-network-recorder\";\n","import { SNIPPETS_BASE_URL } from \"./constants\";\nimport { LoaderOptions } from \"./loader.types\";\nimport { PrivateWindowApi } from \"./private-window-api\";\n\nconst DEFAULT_MAX_MS_TO_BLOCK_FOR = 2_000;\n\nexport interface Recorder {\n /**\n * Disables the recorder for the rest of the user session, and stops sending data to the Meticulous\n * servers.\n *\n * Once this method is called the recorder cannot be restarted (unless the page is reloaded).\n */\n stopRecording: () => Promise<void>;\n}\n\n/**\n * Load and start the Meticulous Recorder\n */\nexport const tryLoadAndStartRecorder = async (\n options: LoaderOptions\n): Promise<Recorder> => {\n if (window.Meticulous?.isRunningAsTest) {\n console.debug(\n \"Running as part of a Meticulous test case, so skipping loading the Meticulous recorder.\"\n );\n return {\n stopRecording: async () => {\n // No op\n },\n };\n }\n\n // Try to load the recorder and silence any initialisation error.\n return await unsafeLoadAndStartRecorder(options).catch((error) => {\n console.error(error);\n return {\n stopRecording: async () => {\n // No op\n },\n };\n });\n};\n\nconst unsafeLoadAndStartRecorder = ({\n projectId,\n recordingToken,\n uploadIntervalMs,\n snapshotLinkedStylesheets,\n commitHash,\n maxMsToBlockFor: maxMsToBlockFor_,\n snippetsBaseUrl,\n forceRecording,\n middleware,\n responseSanitizers,\n isProduction,\n}: LoaderOptions): Promise<Recorder> => {\n let abandoned = false;\n\n return new Promise<Recorder>((resolve, reject) => {\n const maxMsToBlockFor = maxMsToBlockFor_ ?? DEFAULT_MAX_MS_TO_BLOCK_FOR;\n\n if (maxMsToBlockFor > 0) {\n setTimeout(() => {\n abandoned = true;\n resolve({\n stopRecording: async () => {\n // No op: we never started recording\n },\n });\n }, maxMsToBlockFor);\n }\n\n const script = document.createElement(\"script\");\n script.type = \"text/javascript\";\n const baseSnippetsUrl = snippetsBaseUrl || SNIPPETS_BASE_URL;\n script.src = new URL(\"v1/meticulous-manual-init.js\", baseSnippetsUrl).href;\n\n // Setup configuration\n const typedWindow = window;\n typedWindow.METICULOUS_RECORDING_TOKEN = recordingToken ?? projectId;\n\n if (uploadIntervalMs !== undefined) {\n typedWindow.METICULOUS_UPLOAD_INTERVAL_MS = uploadIntervalMs;\n }\n\n if (commitHash !== undefined) {\n typedWindow.METICULOUS_APP_COMMIT_HASH = commitHash;\n }\n\n if (snapshotLinkedStylesheets !== undefined) {\n typedWindow.METICULOUS_SNAPSHOT_LINKED_STYLESHEETS =\n snapshotLinkedStylesheets;\n }\n\n if (forceRecording !== undefined) {\n typedWindow.METICULOUS_FORCE_RECORDING = forceRecording;\n }\n\n if (isProduction !== undefined) {\n typedWindow.METICULOUS_IS_PRODUCTION_ENVIRONMENT = isProduction;\n }\n\n if (responseSanitizers != null && responseSanitizers.length > 0) {\n typedWindow.METICULOUS_NETWORK_RESPONSE_SANITIZERS = responseSanitizers;\n }\n\n if (middleware != null && middleware.length > 0) {\n typedWindow.METICULOUS_RECORDER_MIDDLEWARE_V1 = middleware;\n }\n\n script.onload = function () {\n if (abandoned) {\n console.debug(\n \"Meticulous snippet abandoned due to max blocking time reached.\"\n );\n // At this point the promise has already resolved.\n return;\n }\n\n const initialiseRecorder = (window as PrivateWindowApi).__meticulous\n ?.initialiseRecorder;\n if (typeof initialiseRecorder !== \"function\") {\n reject(\"Meticulous recorder failed to initialise.\");\n return;\n }\n\n try {\n initialiseRecorder();\n } catch (error) {\n reject(error);\n }\n\n resolve({\n stopRecording: async () => {\n const stopRecording = (window as PrivateWindowApi).__meticulous\n ?.stopRecording;\n if (!stopRecording) {\n throw new Error(\n \"Recorder not initialised: window.__meticulous.stopRecording is not defined.\"\n );\n }\n await stopRecording();\n return;\n },\n });\n };\n script.onerror = () => {\n reject(\"Meticulous recorder failed to initialise.\");\n };\n\n document.head.appendChild(script);\n });\n};\n\n/**\n * @deprecated Use `tryLoadAndStartRecorder` instead.\n *\n * Load and start the Meticulous Recorder\n */\nexport const loadAndStartRecorder = unsafeLoadAndStartRecorder;\n","export const SNIPPETS_BASE_URL = \"https://snippet.meticulous.ai\";\n","import { SNIPPETS_BASE_URL } from \"./constants\";\nimport { Recorder, tryLoadAndStartRecorder } from \"./loader\";\nimport { LoaderOptions } from \"./loader.types\";\nimport { PrivateWindowApi } from \"./private-window-api\";\n\nexport interface Interceptor {\n startRecordingSession: (options: LoaderOptions) => Promise<Recorder>;\n stopIntercepting: () => Promise<void>;\n}\n\n/**\n * Stores a copy of network requests and responses in memory, but doesn't send them to the\n * server, until the the main recorder is initialised.\n *\n * This is useful if you only want to record sessions for certain users with certain attributes. In\n * this case you have an issue: you need to wait for the user information to load before you know whether\n * you can enable the recorder, but if you enable the recorder after the user information has loaded\n * then the recorder won't be able to capture the initial request & response to load the user information,\n * or other early network responses.\n *\n * The early network recorder solves this: load the early network recorder for all sessions,\n * but only load the main recorder for sessions that you want to record. If when you load the user data\n * you find out you don't want to record the session then you can call the stopRecording() method returned\n * by this method.\n *\n * Example usage:\n *\n * ```\n * // The below call should happen before your app makes any network requests,\n * // or executes any methods that may store a reference to window.fetch or XMLHttpRequest.\n * const interceptor = await tryInstallMeticulousIntercepts();\n *\n * // Later, when you have loaded user data...\n * const userData = await loadUserInfo();\n * if (shouldRecord(userData)) {\n * await interceptor.startRecordingSession({ ... });\n * } else {\n * interceptor.stopIntercepting();\n * }\n * ```\n */\nexport const tryInstallMeticulousIntercepts = async (\n options: { maxMsToBlockFor: number } = { maxMsToBlockFor: 2000 }\n): Promise<Interceptor> => {\n let requestedToStopIntercepting = false;\n let disposedEarlyNetworkRecorder = false;\n const stopIntercepting = async () => {\n requestedToStopIntercepting = true;\n const disposeFunction = (window as PrivateWindowApi)?.__meticulous\n ?.earlyNetworkRecorder?.dispose;\n if (disposeFunction && !disposedEarlyNetworkRecorder) {\n await disposeFunction();\n disposedEarlyNetworkRecorder = true;\n }\n };\n const interceptor = {\n startRecordingSession: tryLoadAndStartRecorder,\n stopIntercepting,\n };\n\n const promise = new Promise<Interceptor>((resolve, reject) => {\n const timeout =\n options.maxMsToBlockFor > 0\n ? setTimeout(() => {\n resolve(interceptor);\n }, options.maxMsToBlockFor)\n : null;\n\n const script = document.createElement(\"script\");\n script.type = \"text/javascript\";\n script.src = `${SNIPPETS_BASE_URL}/record/v1/network-recorder.bundle.js`;\n\n script.onload = function () {\n if (timeout) {\n window.clearTimeout(timeout);\n }\n resolve(interceptor);\n };\n script.onerror = () => {\n if (timeout) {\n window.clearTimeout(timeout);\n }\n reject(\"Meticulous early network recorder failed to initialise.\");\n };\n\n document.head.appendChild(script);\n });\n\n // Try to load the early network recorder and silence any initialisation error.\n return promise\n .catch((error) => {\n console.error(error);\n return interceptor;\n })\n .finally(() => {\n if (requestedToStopIntercepting) {\n void stopIntercepting();\n }\n });\n};\n","import { PrivateWindowApi } from \"./private-window-api\";\n\n/**\n * If you add 'https://snippet.meticulous.ai/record/v1/network-recorder.bundle.js' as a script tag\n * then Meticulous will start off by recording any network requests in memory but not send the data to Meticulous’s\n * servers. Once you have the information to determine whether you want to record and persist the session\n * you can then call {@link tryLoadAndStartRecorder} if you do want to record, or this stopIntercepting method\n * if you do not want to record. When you call stopIntercepting any network requests/responses stored in memory\n * will be dropped and Meticulous will be deactivated. No data will be sent to Meticulous's servers.\n *\n * Please note that if you've already started recording and sending data to Meticulous's services i.e.\n * loaded the main meticulous recorder script or called tryLoadAndStartRecorder then this method will\n * not stop the recording. For that you need to call the stopRecording method returned by tryLoadAndStartRecorder.\n */\nexport const stopIntercepting = async () => {\n const disposeFunction = (window as PrivateWindowApi)?.__meticulous\n ?.earlyNetworkRecorder?.dispose;\n if (disposeFunction) {\n await disposeFunction();\n }\n};\n"],"names":[],"version":3,"file":"index.js.map"}
package/dist/module.js CHANGED
@@ -5,20 +5,33 @@ const $5d774bdb3a883001$var$DEFAULT_MAX_MS_TO_BLOCK_FOR = 2000;
5
5
  const $5d774bdb3a883001$export$f80553af9d70f9fd = async (options)=>{
6
6
  if (window.Meticulous?.isRunningAsTest) {
7
7
  console.debug("Running as part of a Meticulous test case, so skipping loading the Meticulous recorder.");
8
- return;
8
+ return {
9
+ stopRecording: async ()=>{
10
+ // No op
11
+ }
12
+ };
9
13
  }
10
14
  // Try to load the recorder and silence any initialisation error.
11
- await $5d774bdb3a883001$var$unsafeLoadAndStartRecorder(options).catch((error)=>{
15
+ return await $5d774bdb3a883001$var$unsafeLoadAndStartRecorder(options).catch((error)=>{
12
16
  console.error(error);
17
+ return {
18
+ stopRecording: async ()=>{
19
+ // No op
20
+ }
21
+ };
13
22
  });
14
23
  };
15
- const $5d774bdb3a883001$var$unsafeLoadAndStartRecorder = ({ projectId: projectId , recordingToken: recordingToken , uploadIntervalMs: uploadIntervalMs , snapshotLinkedStylesheets: snapshotLinkedStylesheets , commitHash: commitHash , maxMsToBlockFor: maxMsToBlockFor_ , snippetsBaseUrl: snippetsBaseUrl , forceRecording: forceRecording , responseSanitizers: responseSanitizers , isProduction: isProduction })=>{
24
+ const $5d774bdb3a883001$var$unsafeLoadAndStartRecorder = ({ projectId: projectId , recordingToken: recordingToken , uploadIntervalMs: uploadIntervalMs , snapshotLinkedStylesheets: snapshotLinkedStylesheets , commitHash: commitHash , maxMsToBlockFor: maxMsToBlockFor_ , snippetsBaseUrl: snippetsBaseUrl , forceRecording: forceRecording , middleware: middleware , responseSanitizers: responseSanitizers , isProduction: isProduction })=>{
16
25
  let abandoned = false;
17
26
  return new Promise((resolve, reject)=>{
18
27
  const maxMsToBlockFor = maxMsToBlockFor_ ?? $5d774bdb3a883001$var$DEFAULT_MAX_MS_TO_BLOCK_FOR;
19
28
  if (maxMsToBlockFor > 0) setTimeout(()=>{
20
29
  abandoned = true;
21
- resolve();
30
+ resolve({
31
+ stopRecording: async ()=>{
32
+ // No op: we never started recording
33
+ }
34
+ });
22
35
  }, maxMsToBlockFor);
23
36
  const script = document.createElement("script");
24
37
  script.type = "text/javascript";
@@ -33,6 +46,7 @@ const $5d774bdb3a883001$var$unsafeLoadAndStartRecorder = ({ projectId: projectId
33
46
  if (forceRecording !== undefined) typedWindow.METICULOUS_FORCE_RECORDING = forceRecording;
34
47
  if (isProduction !== undefined) typedWindow.METICULOUS_IS_PRODUCTION_ENVIRONMENT = isProduction;
35
48
  if (responseSanitizers != null && responseSanitizers.length > 0) typedWindow.METICULOUS_NETWORK_RESPONSE_SANITIZERS = responseSanitizers;
49
+ if (middleware != null && middleware.length > 0) typedWindow.METICULOUS_RECORDER_MIDDLEWARE_V1 = middleware;
36
50
  script.onload = function() {
37
51
  if (abandoned) {
38
52
  console.debug("Meticulous snippet abandoned due to max blocking time reached.");
@@ -49,7 +63,14 @@ const $5d774bdb3a883001$var$unsafeLoadAndStartRecorder = ({ projectId: projectId
49
63
  } catch (error) {
50
64
  reject(error);
51
65
  }
52
- resolve();
66
+ resolve({
67
+ stopRecording: async ()=>{
68
+ const stopRecording = window.__meticulous?.stopRecording;
69
+ if (!stopRecording) throw new Error("Recorder not initialised: window.__meticulous.stopRecording is not defined.");
70
+ await stopRecording();
71
+ return;
72
+ }
73
+ });
53
74
  };
54
75
  script.onerror = ()=>{
55
76
  reject("Meticulous recorder failed to initialise.");
@@ -75,16 +96,8 @@ const $d91c442ec5d010f4$export$c0c9d1ed5ecb2675 = async (options = {
75
96
  disposedEarlyNetworkRecorder = true;
76
97
  }
77
98
  };
78
- const startRecordingSession = (options)=>(0, $5d774bdb3a883001$export$f80553af9d70f9fd)(options).then(()=>({
79
- stopRecording: async ()=>{
80
- const stopRecording = window.__meticulous?.stopRecording;
81
- if (!stopRecording) throw new Error("Recorder not initialised: window.__meticulous.stopRecording is not defined.");
82
- await stopRecording();
83
- return;
84
- }
85
- }));
86
99
  const interceptor = {
87
- startRecordingSession: startRecordingSession,
100
+ startRecordingSession: (0, $5d774bdb3a883001$export$f80553af9d70f9fd),
88
101
  stopIntercepting: stopIntercepting
89
102
  };
90
103
  const promise = new Promise((resolve, reject)=>{
@@ -114,7 +127,13 @@ const $d91c442ec5d010f4$export$c0c9d1ed5ecb2675 = async (options = {
114
127
  };
115
128
 
116
129
 
130
+ const $57efbd113493cb78$export$e592d04d772ce0ec = async ()=>{
131
+ const disposeFunction = window?.__meticulous?.earlyNetworkRecorder?.dispose;
132
+ if (disposeFunction) await disposeFunction();
133
+ };
134
+
135
+
117
136
 
118
137
 
119
- export {$5d774bdb3a883001$export$9da0cde53b499187 as loadAndStartRecorder, $5d774bdb3a883001$export$f80553af9d70f9fd as tryLoadAndStartRecorder, $d91c442ec5d010f4$export$c0c9d1ed5ecb2675 as tryInstallMeticulousIntercepts};
138
+ export {$5d774bdb3a883001$export$9da0cde53b499187 as loadAndStartRecorder, $5d774bdb3a883001$export$f80553af9d70f9fd as tryLoadAndStartRecorder, $d91c442ec5d010f4$export$c0c9d1ed5ecb2675 as tryInstallMeticulousIntercepts, $57efbd113493cb78$export$e592d04d772ce0ec as stopIntercepting};
120
139
  //# sourceMappingURL=module.js.map
@@ -1 +1 @@
1
- {"mappings":"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","sources":["packages/recorder-loader/src/index.ts","packages/recorder-loader/src/loader.ts","packages/recorder-loader/src/constants.ts","packages/recorder-loader/src/install-meticulous-intercepts.ts"],"sourcesContent":["export { loadAndStartRecorder, tryLoadAndStartRecorder } from \"./loader\";\nexport { tryInstallMeticulousIntercepts } from \"./install-meticulous-intercepts\";\n","import { SNIPPETS_BASE_URL } from \"./constants\";\nimport { LoaderOptions } from \"./loader.types\";\n\nconst DEFAULT_MAX_MS_TO_BLOCK_FOR = 2_000;\n\n/**\n * Load and start the Meticulous Recorder\n */\nexport const tryLoadAndStartRecorder = async (\n options: LoaderOptions\n): Promise<void> => {\n if (window.Meticulous?.isRunningAsTest) {\n console.debug(\n \"Running as part of a Meticulous test case, so skipping loading the Meticulous recorder.\"\n );\n return;\n }\n\n // Try to load the recorder and silence any initialisation error.\n await unsafeLoadAndStartRecorder(options).catch((error) => {\n console.error(error);\n });\n};\n\nconst unsafeLoadAndStartRecorder = ({\n projectId,\n recordingToken,\n uploadIntervalMs,\n snapshotLinkedStylesheets,\n commitHash,\n maxMsToBlockFor: maxMsToBlockFor_,\n snippetsBaseUrl,\n forceRecording,\n responseSanitizers,\n isProduction,\n}: LoaderOptions): Promise<void> => {\n let abandoned = false;\n\n return new Promise<void>((resolve, reject) => {\n const maxMsToBlockFor = maxMsToBlockFor_ ?? DEFAULT_MAX_MS_TO_BLOCK_FOR;\n\n if (maxMsToBlockFor > 0) {\n setTimeout(() => {\n abandoned = true;\n resolve();\n }, maxMsToBlockFor);\n }\n\n const script = document.createElement(\"script\");\n script.type = \"text/javascript\";\n const baseSnippetsUrl = snippetsBaseUrl || SNIPPETS_BASE_URL;\n script.src = new URL(\"v1/meticulous-manual-init.js\", baseSnippetsUrl).href;\n\n // Setup configuration\n const typedWindow = window;\n typedWindow.METICULOUS_RECORDING_TOKEN = recordingToken ?? projectId;\n\n if (uploadIntervalMs !== undefined) {\n typedWindow.METICULOUS_UPLOAD_INTERVAL_MS = uploadIntervalMs;\n }\n\n if (commitHash !== undefined) {\n typedWindow.METICULOUS_APP_COMMIT_HASH = commitHash;\n }\n\n if (snapshotLinkedStylesheets !== undefined) {\n typedWindow.METICULOUS_SNAPSHOT_LINKED_STYLESHEETS =\n snapshotLinkedStylesheets;\n }\n\n if (forceRecording !== undefined) {\n typedWindow.METICULOUS_FORCE_RECORDING = forceRecording;\n }\n\n if (isProduction !== undefined) {\n typedWindow.METICULOUS_IS_PRODUCTION_ENVIRONMENT = isProduction;\n }\n\n if (responseSanitizers != null && responseSanitizers.length > 0) {\n typedWindow.METICULOUS_NETWORK_RESPONSE_SANITIZERS = responseSanitizers;\n }\n\n script.onload = function () {\n if (abandoned) {\n console.debug(\n \"Meticulous snippet abandoned due to max blocking time reached.\"\n );\n // At this point the promise has already resolved.\n return;\n }\n\n const initialiseRecorder = window.__meticulous?.initialiseRecorder;\n if (typeof initialiseRecorder !== \"function\") {\n reject(\"Meticulous recorder failed to initialise.\");\n return;\n }\n\n try {\n initialiseRecorder();\n } catch (error) {\n reject(error);\n }\n\n resolve();\n };\n script.onerror = () => {\n reject(\"Meticulous recorder failed to initialise.\");\n };\n\n document.head.appendChild(script);\n });\n};\n\n/**\n * @deprecated Use `tryLoadAndStartRecorder` instead.\n *\n * Load and start the Meticulous Recorder\n */\nexport const loadAndStartRecorder = unsafeLoadAndStartRecorder;\n","export const SNIPPETS_BASE_URL = \"https://snippet.meticulous.ai\";\n","import { SNIPPETS_BASE_URL } from \"./constants\";\nimport { tryLoadAndStartRecorder } from \"./loader\";\nimport { LoaderOptions } from \"./loader.types\";\n\ninterface EarlyNetworkRecorderWindow {\n __meticulous?: {\n earlyNetworkRecorder?: {\n dispose?: () => Promise<void>;\n };\n stopRecording?: () => void;\n };\n}\n\nexport interface Interceptor {\n startRecordingSession: (options: LoaderOptions) => Promise<Recorder>;\n stopIntercepting: () => Promise<void>;\n}\n\nexport interface Recorder {\n /**\n * Disables the recorder for the rest of the user session, and stops sending data to the Meticulous\n * servers.\n *\n * Once this method is called the recorder cannot be restarted (unless the page is reloaded).\n */\n stopRecording: () => Promise<void>;\n}\n\n/**\n * Stores a copy of network requests and responses in memory, but doesn't send them to the\n * server, until the the main recorder is initialised.\n *\n * This is useful if you only want to record sessions for certain users with certain attributes. In\n * this case you have an issue: you need to wait for the user information to load before you know whether\n * you can enable the recorder, but if you enable the recorder after the user information has loaded\n * then the recorder won't be able to capture the initial request & response to load the user information,\n * or other early network responses.\n *\n * The early network recorder solves this: load the early network recorder for all sessions,\n * but only load the main recorder for sessions that you want to record. If when you load the user data\n * you find out you don't want to record the session then you can call the stopRecording() method returned\n * by this method.\n *\n * Example usage:\n *\n * ```\n * // The below call should happen before your app makes any network requests,\n * // or executes any methods that may store a reference to window.fetch or XMLHttpRequest.\n * const interceptor = await tryInstallMeticulousIntercepts();\n *\n * // Later, when you have loaded user data...\n * const userData = await loadUserInfo();\n * if (shouldRecord(userData)) {\n * await interceptor.startRecordingSession({ ... });\n * } else {\n * interceptor.stopIntercepting();\n * }\n * ```\n */\nexport const tryInstallMeticulousIntercepts = async (\n options: { maxMsToBlockFor: number } = { maxMsToBlockFor: 2000 }\n): Promise<Interceptor> => {\n let requestedToStopIntercepting = false;\n let disposedEarlyNetworkRecorder = false;\n const stopIntercepting = async () => {\n requestedToStopIntercepting = true;\n const disposeFunction = (window as EarlyNetworkRecorderWindow)?.__meticulous\n ?.earlyNetworkRecorder?.dispose;\n if (disposeFunction && !disposedEarlyNetworkRecorder) {\n await disposeFunction();\n disposedEarlyNetworkRecorder = true;\n }\n };\n const startRecordingSession = (options: LoaderOptions) =>\n tryLoadAndStartRecorder(options).then(() => ({\n stopRecording: async () => {\n const stopRecording = (window as EarlyNetworkRecorderWindow)\n .__meticulous?.stopRecording;\n if (!stopRecording) {\n throw new Error(\n \"Recorder not initialised: window.__meticulous.stopRecording is not defined.\"\n );\n }\n await stopRecording();\n return;\n },\n }));\n const interceptor = { startRecordingSession, stopIntercepting };\n\n const promise = new Promise<Interceptor>((resolve, reject) => {\n const timeout =\n options.maxMsToBlockFor > 0\n ? setTimeout(() => {\n resolve(interceptor);\n }, options.maxMsToBlockFor)\n : null;\n\n const script = document.createElement(\"script\");\n script.type = \"text/javascript\";\n script.src = `${SNIPPETS_BASE_URL}/record/v1/network-recorder.bundle.js`;\n\n script.onload = function () {\n if (timeout) {\n window.clearTimeout(timeout);\n }\n resolve(interceptor);\n };\n script.onerror = () => {\n if (timeout) {\n window.clearTimeout(timeout);\n }\n reject(\"Meticulous early network recorder failed to initialise.\");\n };\n\n document.head.appendChild(script);\n });\n\n // Try to load the early network recorder and silence any initialisation error.\n return promise\n .catch((error) => {\n console.error(error);\n return interceptor;\n })\n .finally(() => {\n if (requestedToStopIntercepting) {\n void stopIntercepting();\n }\n });\n};\n"],"names":[],"version":3,"file":"module.js.map"}
1
+ {"mappings":"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","sources":["packages/recorder-loader/src/index.ts","packages/recorder-loader/src/loader.ts","packages/recorder-loader/src/constants.ts","packages/recorder-loader/src/install-meticulous-intercepts.ts","packages/recorder-loader/src/early-network-recorder.ts"],"sourcesContent":["export { loadAndStartRecorder, tryLoadAndStartRecorder } from \"./loader\";\nexport { tryInstallMeticulousIntercepts } from \"./install-meticulous-intercepts\";\nexport { stopIntercepting } from \"./early-network-recorder\";\n","import { SNIPPETS_BASE_URL } from \"./constants\";\nimport { LoaderOptions } from \"./loader.types\";\nimport { PrivateWindowApi } from \"./private-window-api\";\n\nconst DEFAULT_MAX_MS_TO_BLOCK_FOR = 2_000;\n\nexport interface Recorder {\n /**\n * Disables the recorder for the rest of the user session, and stops sending data to the Meticulous\n * servers.\n *\n * Once this method is called the recorder cannot be restarted (unless the page is reloaded).\n */\n stopRecording: () => Promise<void>;\n}\n\n/**\n * Load and start the Meticulous Recorder\n */\nexport const tryLoadAndStartRecorder = async (\n options: LoaderOptions\n): Promise<Recorder> => {\n if (window.Meticulous?.isRunningAsTest) {\n console.debug(\n \"Running as part of a Meticulous test case, so skipping loading the Meticulous recorder.\"\n );\n return {\n stopRecording: async () => {\n // No op\n },\n };\n }\n\n // Try to load the recorder and silence any initialisation error.\n return await unsafeLoadAndStartRecorder(options).catch((error) => {\n console.error(error);\n return {\n stopRecording: async () => {\n // No op\n },\n };\n });\n};\n\nconst unsafeLoadAndStartRecorder = ({\n projectId,\n recordingToken,\n uploadIntervalMs,\n snapshotLinkedStylesheets,\n commitHash,\n maxMsToBlockFor: maxMsToBlockFor_,\n snippetsBaseUrl,\n forceRecording,\n middleware,\n responseSanitizers,\n isProduction,\n}: LoaderOptions): Promise<Recorder> => {\n let abandoned = false;\n\n return new Promise<Recorder>((resolve, reject) => {\n const maxMsToBlockFor = maxMsToBlockFor_ ?? DEFAULT_MAX_MS_TO_BLOCK_FOR;\n\n if (maxMsToBlockFor > 0) {\n setTimeout(() => {\n abandoned = true;\n resolve({\n stopRecording: async () => {\n // No op: we never started recording\n },\n });\n }, maxMsToBlockFor);\n }\n\n const script = document.createElement(\"script\");\n script.type = \"text/javascript\";\n const baseSnippetsUrl = snippetsBaseUrl || SNIPPETS_BASE_URL;\n script.src = new URL(\"v1/meticulous-manual-init.js\", baseSnippetsUrl).href;\n\n // Setup configuration\n const typedWindow = window;\n typedWindow.METICULOUS_RECORDING_TOKEN = recordingToken ?? projectId;\n\n if (uploadIntervalMs !== undefined) {\n typedWindow.METICULOUS_UPLOAD_INTERVAL_MS = uploadIntervalMs;\n }\n\n if (commitHash !== undefined) {\n typedWindow.METICULOUS_APP_COMMIT_HASH = commitHash;\n }\n\n if (snapshotLinkedStylesheets !== undefined) {\n typedWindow.METICULOUS_SNAPSHOT_LINKED_STYLESHEETS =\n snapshotLinkedStylesheets;\n }\n\n if (forceRecording !== undefined) {\n typedWindow.METICULOUS_FORCE_RECORDING = forceRecording;\n }\n\n if (isProduction !== undefined) {\n typedWindow.METICULOUS_IS_PRODUCTION_ENVIRONMENT = isProduction;\n }\n\n if (responseSanitizers != null && responseSanitizers.length > 0) {\n typedWindow.METICULOUS_NETWORK_RESPONSE_SANITIZERS = responseSanitizers;\n }\n\n if (middleware != null && middleware.length > 0) {\n typedWindow.METICULOUS_RECORDER_MIDDLEWARE_V1 = middleware;\n }\n\n script.onload = function () {\n if (abandoned) {\n console.debug(\n \"Meticulous snippet abandoned due to max blocking time reached.\"\n );\n // At this point the promise has already resolved.\n return;\n }\n\n const initialiseRecorder = (window as PrivateWindowApi).__meticulous\n ?.initialiseRecorder;\n if (typeof initialiseRecorder !== \"function\") {\n reject(\"Meticulous recorder failed to initialise.\");\n return;\n }\n\n try {\n initialiseRecorder();\n } catch (error) {\n reject(error);\n }\n\n resolve({\n stopRecording: async () => {\n const stopRecording = (window as PrivateWindowApi).__meticulous\n ?.stopRecording;\n if (!stopRecording) {\n throw new Error(\n \"Recorder not initialised: window.__meticulous.stopRecording is not defined.\"\n );\n }\n await stopRecording();\n return;\n },\n });\n };\n script.onerror = () => {\n reject(\"Meticulous recorder failed to initialise.\");\n };\n\n document.head.appendChild(script);\n });\n};\n\n/**\n * @deprecated Use `tryLoadAndStartRecorder` instead.\n *\n * Load and start the Meticulous Recorder\n */\nexport const loadAndStartRecorder = unsafeLoadAndStartRecorder;\n","export const SNIPPETS_BASE_URL = \"https://snippet.meticulous.ai\";\n","import { SNIPPETS_BASE_URL } from \"./constants\";\nimport { Recorder, tryLoadAndStartRecorder } from \"./loader\";\nimport { LoaderOptions } from \"./loader.types\";\nimport { PrivateWindowApi } from \"./private-window-api\";\n\nexport interface Interceptor {\n startRecordingSession: (options: LoaderOptions) => Promise<Recorder>;\n stopIntercepting: () => Promise<void>;\n}\n\n/**\n * Stores a copy of network requests and responses in memory, but doesn't send them to the\n * server, until the the main recorder is initialised.\n *\n * This is useful if you only want to record sessions for certain users with certain attributes. In\n * this case you have an issue: you need to wait for the user information to load before you know whether\n * you can enable the recorder, but if you enable the recorder after the user information has loaded\n * then the recorder won't be able to capture the initial request & response to load the user information,\n * or other early network responses.\n *\n * The early network recorder solves this: load the early network recorder for all sessions,\n * but only load the main recorder for sessions that you want to record. If when you load the user data\n * you find out you don't want to record the session then you can call the stopRecording() method returned\n * by this method.\n *\n * Example usage:\n *\n * ```\n * // The below call should happen before your app makes any network requests,\n * // or executes any methods that may store a reference to window.fetch or XMLHttpRequest.\n * const interceptor = await tryInstallMeticulousIntercepts();\n *\n * // Later, when you have loaded user data...\n * const userData = await loadUserInfo();\n * if (shouldRecord(userData)) {\n * await interceptor.startRecordingSession({ ... });\n * } else {\n * interceptor.stopIntercepting();\n * }\n * ```\n */\nexport const tryInstallMeticulousIntercepts = async (\n options: { maxMsToBlockFor: number } = { maxMsToBlockFor: 2000 }\n): Promise<Interceptor> => {\n let requestedToStopIntercepting = false;\n let disposedEarlyNetworkRecorder = false;\n const stopIntercepting = async () => {\n requestedToStopIntercepting = true;\n const disposeFunction = (window as PrivateWindowApi)?.__meticulous\n ?.earlyNetworkRecorder?.dispose;\n if (disposeFunction && !disposedEarlyNetworkRecorder) {\n await disposeFunction();\n disposedEarlyNetworkRecorder = true;\n }\n };\n const interceptor = {\n startRecordingSession: tryLoadAndStartRecorder,\n stopIntercepting,\n };\n\n const promise = new Promise<Interceptor>((resolve, reject) => {\n const timeout =\n options.maxMsToBlockFor > 0\n ? setTimeout(() => {\n resolve(interceptor);\n }, options.maxMsToBlockFor)\n : null;\n\n const script = document.createElement(\"script\");\n script.type = \"text/javascript\";\n script.src = `${SNIPPETS_BASE_URL}/record/v1/network-recorder.bundle.js`;\n\n script.onload = function () {\n if (timeout) {\n window.clearTimeout(timeout);\n }\n resolve(interceptor);\n };\n script.onerror = () => {\n if (timeout) {\n window.clearTimeout(timeout);\n }\n reject(\"Meticulous early network recorder failed to initialise.\");\n };\n\n document.head.appendChild(script);\n });\n\n // Try to load the early network recorder and silence any initialisation error.\n return promise\n .catch((error) => {\n console.error(error);\n return interceptor;\n })\n .finally(() => {\n if (requestedToStopIntercepting) {\n void stopIntercepting();\n }\n });\n};\n","import { PrivateWindowApi } from \"./private-window-api\";\n\n/**\n * If you add 'https://snippet.meticulous.ai/record/v1/network-recorder.bundle.js' as a script tag\n * then Meticulous will start off by recording any network requests in memory but not send the data to Meticulous’s\n * servers. Once you have the information to determine whether you want to record and persist the session\n * you can then call {@link tryLoadAndStartRecorder} if you do want to record, or this stopIntercepting method\n * if you do not want to record. When you call stopIntercepting any network requests/responses stored in memory\n * will be dropped and Meticulous will be deactivated. No data will be sent to Meticulous's servers.\n *\n * Please note that if you've already started recording and sending data to Meticulous's services i.e.\n * loaded the main meticulous recorder script or called tryLoadAndStartRecorder then this method will\n * not stop the recording. For that you need to call the stopRecording method returned by tryLoadAndStartRecorder.\n */\nexport const stopIntercepting = async () => {\n const disposeFunction = (window as PrivateWindowApi)?.__meticulous\n ?.earlyNetworkRecorder?.dispose;\n if (disposeFunction) {\n await disposeFunction();\n }\n};\n"],"names":[],"version":3,"file":"module.js.map"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alwaysmeticulous/recorder-loader",
3
- "version": "2.161.0",
3
+ "version": "2.162.0",
4
4
  "license": "ISC",
5
5
  "source": "src/index.ts",
6
6
  "main": "dist/index.js",
@@ -21,7 +21,7 @@
21
21
  "depcheck": "depcheck --ignore-patterns=dist"
22
22
  },
23
23
  "devDependencies": {
24
- "@alwaysmeticulous/sdk-bundles-api": "^2.161.0"
24
+ "@alwaysmeticulous/sdk-bundles-api": "^2.162.0"
25
25
  },
26
26
  "author": {
27
27
  "name": "The Meticulous Team",
@@ -37,5 +37,5 @@
37
37
  "bugs": {
38
38
  "url": "https://github.com/alwaysmeticulous/meticulous-sdk/issues"
39
39
  },
40
- "gitHead": "64e42104344377fca631912b5447ec16a7b29d12"
40
+ "gitHead": "d71c448c4d1856a0b62e13439d83bc8081c995b2"
41
41
  }