replaySegments.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import {EventType} from '@sentry-internal/rrweb';
  2. import {serializedNodeWithId} from '@sentry-internal/rrweb-snapshot';
  3. type FullSnapshotEvent = {
  4. data: {
  5. initialOffset: {
  6. left: number;
  7. top: number;
  8. };
  9. node: serializedNodeWithId;
  10. };
  11. timestamp: number;
  12. type: EventType.FullSnapshot;
  13. };
  14. type BaseReplayProps = {
  15. timestamp: Date;
  16. };
  17. export function ReplaySegmentInit({
  18. height = 600,
  19. href = 'http://localhost/',
  20. timestamp = new Date(),
  21. width = 800,
  22. }: BaseReplayProps & {
  23. height: number;
  24. href: string;
  25. width: number;
  26. }) {
  27. return [
  28. {
  29. type: EventType.DomContentLoaded,
  30. timestamp: timestamp.getTime(), // rrweb timestamps are in ms
  31. },
  32. {
  33. type: EventType.Load,
  34. timestamp: timestamp.getTime(), // rrweb timestamps are in ms
  35. },
  36. {
  37. type: EventType.Meta,
  38. data: {href, width, height},
  39. timestamp: timestamp.getTime(), // rrweb timestamps are in ms
  40. },
  41. ];
  42. }
  43. export function ReplaySegmentFullsnapshot({
  44. timestamp,
  45. childNodes,
  46. }: BaseReplayProps & {childNodes: serializedNodeWithId[]}): [FullSnapshotEvent] {
  47. return [
  48. {
  49. type: EventType.FullSnapshot,
  50. timestamp: timestamp.getTime(),
  51. data: {
  52. initialOffset: {
  53. top: 0,
  54. left: 0,
  55. },
  56. node: {
  57. type: 0, // NodeType.DocumentType
  58. id: 0,
  59. tagName: 'html',
  60. attributes: {},
  61. childNodes: [
  62. ReplayRRWebNode({
  63. tagName: 'body',
  64. attributes: {
  65. style:
  66. 'margin:0; font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu;',
  67. },
  68. childNodes,
  69. }),
  70. ],
  71. },
  72. },
  73. },
  74. ];
  75. }
  76. export function ReplaySegmentConsole({timestamp = new Date()}: BaseReplayProps) {
  77. return ReplaySegmentBreadcrumb({
  78. timestamp,
  79. payload: {
  80. timestamp: timestamp.getTime() / 1000, // sentry data inside rrweb is in seconds
  81. type: 'default',
  82. category: 'console',
  83. data: {
  84. arguments: [
  85. './src/pages/template/Header.js\n Line 14: The href attribute requires a valid value to be accessible. Provide a valid, navigable address as the href value.',
  86. ],
  87. logger: 'console',
  88. },
  89. level: 'warning',
  90. message:
  91. './src/pages/template/Header.js\n Line 14: The href attribute requires a valid value to be accessible. Provide a valid, navigable address as the href value.',
  92. },
  93. });
  94. }
  95. export function ReplaySegmentNavigation({
  96. timestamp = new Date(),
  97. hrefFrom = '/',
  98. hrefTo = '/profile/',
  99. }: BaseReplayProps & {hrefFrom: string; hrefTo: string}) {
  100. return ReplaySegmentBreadcrumb({
  101. timestamp,
  102. payload: {
  103. type: 'default',
  104. category: 'navigation',
  105. data: {
  106. from: hrefFrom,
  107. to: hrefTo,
  108. },
  109. },
  110. });
  111. }
  112. export function ReplaySegmentBreadcrumb({
  113. timestamp = new Date(),
  114. payload,
  115. }: BaseReplayProps & {payload: any}) {
  116. return [
  117. {
  118. type: EventType.Custom,
  119. timestamp: timestamp.getTime(), // rrweb timestamps are in ms
  120. data: {
  121. tag: 'breadcrumb',
  122. payload,
  123. },
  124. },
  125. ];
  126. }
  127. const nextRRWebId = (function () {
  128. let __rrwebID = 0;
  129. return () => ++__rrwebID;
  130. })();
  131. export function ReplayRRWebNode({
  132. id,
  133. tagName,
  134. attributes,
  135. childNodes,
  136. textContent,
  137. }: {
  138. attributes?: Record<string, string>;
  139. childNodes?: serializedNodeWithId[];
  140. id?: number;
  141. tagName?: string;
  142. textContent?: string;
  143. }): serializedNodeWithId {
  144. id = id ?? nextRRWebId();
  145. if (tagName) {
  146. return {
  147. type: 2, // NodeType.Element
  148. id,
  149. tagName,
  150. attributes: attributes ?? {},
  151. childNodes: childNodes ?? [],
  152. };
  153. }
  154. return {
  155. type: 3, // NodeType.Text
  156. id,
  157. textContent: textContent ?? '',
  158. };
  159. }
  160. export function ReplayRRWebDivHelloWorld() {
  161. return ReplayRRWebNode({
  162. tagName: 'div',
  163. childNodes: [
  164. ReplayRRWebNode({
  165. tagName: 'h1',
  166. attributes: {style: 'text-align: center;'},
  167. childNodes: [
  168. ReplayRRWebNode({
  169. textContent: 'Hello World',
  170. }),
  171. ],
  172. }),
  173. ],
  174. });
  175. }