replaySegments.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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. childNodes: [
  60. ReplayRRWebNode({
  61. tagName: 'body',
  62. attributes: {
  63. style:
  64. 'margin:0; font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu;',
  65. },
  66. childNodes,
  67. }),
  68. ],
  69. },
  70. },
  71. },
  72. ];
  73. }
  74. export function ReplaySegmentConsole({timestamp = new Date()}: BaseReplayProps) {
  75. return ReplaySegmentBreadcrumb({
  76. timestamp,
  77. payload: {
  78. timestamp: timestamp.getTime() / 1000, // sentry data inside rrweb is in seconds
  79. type: 'default',
  80. category: 'console',
  81. data: {
  82. arguments: [
  83. './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.',
  84. ],
  85. logger: 'console',
  86. },
  87. level: 'warning',
  88. message:
  89. './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.',
  90. },
  91. });
  92. }
  93. export function ReplaySegmentNavigation({
  94. timestamp = new Date(),
  95. hrefFrom = '/',
  96. hrefTo = '/profile/',
  97. }: BaseReplayProps & {hrefFrom: string; hrefTo: string}) {
  98. return ReplaySegmentBreadcrumb({
  99. timestamp,
  100. payload: {
  101. type: 'default',
  102. category: 'navigation',
  103. data: {
  104. from: hrefFrom,
  105. to: hrefTo,
  106. },
  107. },
  108. });
  109. }
  110. export function ReplaySegmentBreadcrumb({
  111. timestamp = new Date(),
  112. payload,
  113. }: BaseReplayProps & {payload: any}) {
  114. return [
  115. {
  116. type: EventType.Custom,
  117. timestamp: timestamp.getTime(), // rrweb timestamps are in ms
  118. data: {
  119. tag: 'breadcrumb',
  120. payload,
  121. },
  122. },
  123. ];
  124. }
  125. const nextRRWebId = (function () {
  126. let __rrwebID = 0;
  127. return () => ++__rrwebID;
  128. })();
  129. export function ReplayRRWebNode({
  130. id,
  131. tagName,
  132. attributes,
  133. childNodes,
  134. textContent,
  135. }: {
  136. attributes?: Record<string, string>;
  137. childNodes?: serializedNodeWithId[];
  138. id?: number;
  139. tagName?: string;
  140. textContent?: string;
  141. }): serializedNodeWithId {
  142. id = id ?? nextRRWebId();
  143. if (tagName) {
  144. return {
  145. type: 2, // NodeType.Element
  146. id,
  147. tagName,
  148. attributes: attributes ?? {},
  149. childNodes: childNodes ?? [],
  150. };
  151. }
  152. return {
  153. type: 3, // NodeType.Text
  154. id,
  155. textContent: textContent ?? '',
  156. };
  157. }
  158. export function ReplayRRWebDivHelloWorld() {
  159. return ReplayRRWebNode({
  160. tagName: 'div',
  161. childNodes: [
  162. ReplayRRWebNode({
  163. tagName: 'h1',
  164. attributes: {style: 'text-align: center;'},
  165. childNodes: [
  166. ReplayRRWebNode({
  167. textContent: 'Hello World',
  168. }),
  169. ],
  170. }),
  171. ],
  172. });
  173. }