@loopcheck/qa-sdk 1.0.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.
Files changed (124) hide show
  1. package/ARCHITECTURE.md +59 -0
  2. package/README.md +131 -0
  3. package/dist/config/normalize.d.ts +6 -0
  4. package/dist/config/normalize.d.ts.map +1 -0
  5. package/dist/config/normalize.js +88 -0
  6. package/dist/config/normalize.js.map +1 -0
  7. package/dist/core/QAClient.d.ts +53 -0
  8. package/dist/core/QAClient.d.ts.map +1 -0
  9. package/dist/core/QAClient.js +318 -0
  10. package/dist/core/QAClient.js.map +1 -0
  11. package/dist/core/QAEventBus.d.ts +10 -0
  12. package/dist/core/QAEventBus.d.ts.map +1 -0
  13. package/dist/core/QAEventBus.js +27 -0
  14. package/dist/core/QAEventBus.js.map +1 -0
  15. package/dist/debug/Debugger.d.ts +4 -0
  16. package/dist/debug/Debugger.d.ts.map +1 -0
  17. package/dist/debug/Debugger.js +57 -0
  18. package/dist/debug/Debugger.js.map +1 -0
  19. package/dist/identity/AccessibilityResolver.d.ts +3 -0
  20. package/dist/identity/AccessibilityResolver.d.ts.map +1 -0
  21. package/dist/identity/AccessibilityResolver.js +76 -0
  22. package/dist/identity/AccessibilityResolver.js.map +1 -0
  23. package/dist/identity/DuplicateDetector.d.ts +9 -0
  24. package/dist/identity/DuplicateDetector.d.ts.map +1 -0
  25. package/dist/identity/DuplicateDetector.js +41 -0
  26. package/dist/identity/DuplicateDetector.js.map +1 -0
  27. package/dist/identity/ElementResolver.d.ts +5 -0
  28. package/dist/identity/ElementResolver.d.ts.map +1 -0
  29. package/dist/identity/ElementResolver.js +123 -0
  30. package/dist/identity/ElementResolver.js.map +1 -0
  31. package/dist/identity/FingerprintGenerator.d.ts +19 -0
  32. package/dist/identity/FingerprintGenerator.d.ts.map +1 -0
  33. package/dist/identity/FingerprintGenerator.js +49 -0
  34. package/dist/identity/FingerprintGenerator.js.map +1 -0
  35. package/dist/identity/IdentityValidator.d.ts +6 -0
  36. package/dist/identity/IdentityValidator.d.ts.map +1 -0
  37. package/dist/identity/IdentityValidator.js +28 -0
  38. package/dist/identity/IdentityValidator.js.map +1 -0
  39. package/dist/index.d.ts +10 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +9 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/instrumentation/DOMObserver.d.ts +13 -0
  44. package/dist/instrumentation/DOMObserver.d.ts.map +1 -0
  45. package/dist/instrumentation/DOMObserver.js +45 -0
  46. package/dist/instrumentation/DOMObserver.js.map +1 -0
  47. package/dist/instrumentation/ElementRegistry.d.ts +15 -0
  48. package/dist/instrumentation/ElementRegistry.d.ts.map +1 -0
  49. package/dist/instrumentation/ElementRegistry.js +53 -0
  50. package/dist/instrumentation/ElementRegistry.js.map +1 -0
  51. package/dist/instrumentation/EventDelegator.d.ts +31 -0
  52. package/dist/instrumentation/EventDelegator.d.ts.map +1 -0
  53. package/dist/instrumentation/EventDelegator.js +238 -0
  54. package/dist/instrumentation/EventDelegator.js.map +1 -0
  55. package/dist/instrumentation/FormHarvest.d.ts +33 -0
  56. package/dist/instrumentation/FormHarvest.d.ts.map +1 -0
  57. package/dist/instrumentation/FormHarvest.js +179 -0
  58. package/dist/instrumentation/FormHarvest.js.map +1 -0
  59. package/dist/instrumentation/NavigationObserver.d.ts +11 -0
  60. package/dist/instrumentation/NavigationObserver.d.ts.map +1 -0
  61. package/dist/instrumentation/NavigationObserver.js +52 -0
  62. package/dist/instrumentation/NavigationObserver.js.map +1 -0
  63. package/dist/instrumentation/compiler-contract.d.ts +11 -0
  64. package/dist/instrumentation/compiler-contract.d.ts.map +1 -0
  65. package/dist/instrumentation/compiler-contract.js +11 -0
  66. package/dist/instrumentation/compiler-contract.js.map +1 -0
  67. package/dist/privacy/Redactor.d.ts +4 -0
  68. package/dist/privacy/Redactor.d.ts.map +1 -0
  69. package/dist/privacy/Redactor.js +50 -0
  70. package/dist/privacy/Redactor.js.map +1 -0
  71. package/dist/react/QAButton.d.ts +595 -0
  72. package/dist/react/QAButton.d.ts.map +1 -0
  73. package/dist/react/QAButton.js +22 -0
  74. package/dist/react/QAButton.js.map +1 -0
  75. package/dist/react/QAProvider.d.ts +23 -0
  76. package/dist/react/QAProvider.d.ts.map +1 -0
  77. package/dist/react/QAProvider.js +41 -0
  78. package/dist/react/QAProvider.js.map +1 -0
  79. package/dist/react/index.d.ts +4 -0
  80. package/dist/react/index.d.ts.map +1 -0
  81. package/dist/react/index.js +5 -0
  82. package/dist/react/index.js.map +1 -0
  83. package/dist/react/useQAElement.d.ts +3 -0
  84. package/dist/react/useQAElement.d.ts.map +1 -0
  85. package/dist/react/useQAElement.js +23 -0
  86. package/dist/react/useQAElement.js.map +1 -0
  87. package/dist/recorder/EventNormalizer.d.ts +32 -0
  88. package/dist/recorder/EventNormalizer.d.ts.map +1 -0
  89. package/dist/recorder/EventNormalizer.js +113 -0
  90. package/dist/recorder/EventNormalizer.js.map +1 -0
  91. package/dist/recorder.iife.js +14 -0
  92. package/dist/transport/BatchQueue.d.ts +26 -0
  93. package/dist/transport/BatchQueue.d.ts.map +1 -0
  94. package/dist/transport/BatchQueue.js +110 -0
  95. package/dist/transport/BatchQueue.js.map +1 -0
  96. package/dist/transport/HTTPTransport.d.ts +36 -0
  97. package/dist/transport/HTTPTransport.d.ts.map +1 -0
  98. package/dist/transport/HTTPTransport.js +121 -0
  99. package/dist/transport/HTTPTransport.js.map +1 -0
  100. package/dist/transport/RetryQueue.d.ts +10 -0
  101. package/dist/transport/RetryQueue.d.ts.map +1 -0
  102. package/dist/transport/RetryQueue.js +60 -0
  103. package/dist/transport/RetryQueue.js.map +1 -0
  104. package/dist/types/index.d.ts +147 -0
  105. package/dist/types/index.d.ts.map +1 -0
  106. package/dist/types/index.js +24 -0
  107. package/dist/types/index.js.map +1 -0
  108. package/dist/utils/dom.d.ts +24 -0
  109. package/dist/utils/dom.d.ts.map +1 -0
  110. package/dist/utils/dom.js +152 -0
  111. package/dist/utils/dom.js.map +1 -0
  112. package/dist/utils/env.d.ts +2 -0
  113. package/dist/utils/env.d.ts.map +1 -0
  114. package/dist/utils/env.js +6 -0
  115. package/dist/utils/env.js.map +1 -0
  116. package/dist/utils/id.d.ts +13 -0
  117. package/dist/utils/id.d.ts.map +1 -0
  118. package/dist/utils/id.js +74 -0
  119. package/dist/utils/id.js.map +1 -0
  120. package/dist/utils/safe.d.ts +5 -0
  121. package/dist/utils/safe.d.ts.map +1 -0
  122. package/dist/utils/safe.js +28 -0
  123. package/dist/utils/safe.js.map +1 -0
  124. package/package.json +79 -0
@@ -0,0 +1,23 @@
1
+ import { type ReactNode } from 'react';
2
+ import { QAClient, type QASnapshot } from '../core/QAClient';
3
+ import type { QAConfig, QARecording } from '../types';
4
+ export interface QAContextValue {
5
+ client: QAClient | null;
6
+ snapshot: QASnapshot;
7
+ }
8
+ declare const QAContext: import("react").Context<QAContextValue>;
9
+ export declare function QAProvider({ config, children, }: {
10
+ config: QAConfig;
11
+ children?: ReactNode;
12
+ }): import("react").FunctionComponentElement<import("react").ProviderProps<QAContextValue>>;
13
+ export declare function useQA(): {
14
+ client: QAClient | null;
15
+ startRecording: () => Promise<void>;
16
+ stopRecording: () => QARecording | null;
17
+ pauseRecording: () => void;
18
+ resumeRecording: () => void;
19
+ isRecording: boolean;
20
+ isPaused: boolean;
21
+ };
22
+ export { QAContext };
23
+ //# sourceMappingURL=QAProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QAProvider.d.ts","sourceRoot":"","sources":["../../src/react/QAProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,UAAU,CAAC;CACtB;AAED,QAAA,MAAM,SAAS,yCAGb,CAAC;AAEH,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,2FA2BA;AAED,wBAAgB,KAAK,IAAI;IACvB,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACxC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAWA;AAMD,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+ import { createContext, createElement, useContext, useEffect, useMemo, useRef, useSyncExternalStore, } from 'react';
3
+ import { QAClient } from '../core/QAClient';
4
+ const QAContext = createContext({
5
+ client: null,
6
+ snapshot: QAClient.getServerSnapshot(),
7
+ });
8
+ export function QAProvider({ config, children, }) {
9
+ const clientRef = useRef(null);
10
+ if (typeof window !== 'undefined' && !clientRef.current) {
11
+ clientRef.current = new QAClient(config);
12
+ }
13
+ useEffect(() => {
14
+ const client = clientRef.current;
15
+ client?.start();
16
+ client?.maybeArmFromQuery();
17
+ return () => {
18
+ void client?.shutdown();
19
+ };
20
+ }, []);
21
+ const snapshot = useSyncExternalStore(clientRef.current?.subscribe ?? noopSubscribe, clientRef.current?.getSnapshot ?? QAClient.getServerSnapshot, QAClient.getServerSnapshot);
22
+ const value = useMemo(() => ({ client: clientRef.current, snapshot }), [snapshot]);
23
+ return createElement(QAContext.Provider, { value }, children);
24
+ }
25
+ export function useQA() {
26
+ const { client, snapshot } = useContext(QAContext);
27
+ return {
28
+ client,
29
+ startRecording: () => client?.startRecording() ?? Promise.resolve(),
30
+ stopRecording: () => client?.stopRecording() ?? null,
31
+ pauseRecording: () => client?.pauseRecording(),
32
+ resumeRecording: () => client?.resumeRecording(),
33
+ isRecording: snapshot.isRecording,
34
+ isPaused: snapshot.isPaused,
35
+ };
36
+ }
37
+ function noopSubscribe() {
38
+ return () => undefined;
39
+ }
40
+ export { QAContext };
41
+ //# sourceMappingURL=QAProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QAProvider.js","sourceRoot":"","sources":["../../src/react/QAProvider.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EACL,aAAa,EACb,aAAa,EACb,UAAU,EACV,SAAS,EACT,OAAO,EACP,MAAM,EACN,oBAAoB,GAErB,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,QAAQ,EAAmB,MAAM,kBAAkB,CAAC;AAQ7D,MAAM,SAAS,GAAG,aAAa,CAAiB;IAC9C,MAAM,EAAE,IAAI;IACZ,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,EAAE;CACvC,CAAC,CAAC;AAEH,MAAM,UAAU,UAAU,CAAC,EACzB,MAAM,EACN,QAAQ,GAIT;IACC,MAAM,SAAS,GAAG,MAAM,CAAkB,IAAI,CAAC,CAAC;IAChD,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;QACxD,SAAS,CAAC,OAAO,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC;QACjC,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,MAAM,EAAE,iBAAiB,EAAE,CAAC;QAC5B,OAAO,GAAG,EAAE;YACV,KAAK,MAAM,EAAE,QAAQ,EAAE,CAAC;QAC1B,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,QAAQ,GAAG,oBAAoB,CACnC,SAAS,CAAC,OAAO,EAAE,SAAS,IAAI,aAAa,EAC7C,SAAS,CAAC,OAAO,EAAE,WAAW,IAAI,QAAQ,CAAC,iBAAiB,EAC5D,QAAQ,CAAC,iBAAiB,CAC3B,CAAC;IAEF,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,EAC/C,CAAC,QAAQ,CAAC,CACX,CAAC;IAEF,OAAO,aAAa,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,EAAE,QAAQ,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,KAAK;IASnB,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACnD,OAAO;QACL,MAAM;QACN,cAAc,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE;QACnE,aAAa,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,IAAI;QACpD,cAAc,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE;QAC9C,eAAe,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE;QAChD,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;KAC5B,CAAC;AACJ,CAAC;AAED,SAAS,aAAa;IACpB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC;AACzB,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { QAProvider, useQA } from './QAProvider';
2
+ export { useQAElement } from './useQAElement';
3
+ export { QAButton, QALink } from './QAButton';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,5 @@
1
+ 'use client';
2
+ export { QAProvider, useQA } from './QAProvider';
3
+ export { useQAElement } from './useQAElement';
4
+ export { QAButton, QALink } from './QAButton';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type RefCallback } from 'react';
2
+ export declare function useQAElement<T extends HTMLElement = HTMLElement>(qaId: string, source?: string): RefCallback<T>;
3
+ //# sourceMappingURL=useQAElement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQAElement.d.ts","sourceRoot":"","sources":["../../src/react/useQAElement.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC;AAKtD,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC9D,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GACd,WAAW,CAAC,CAAC,CAAC,CAkBhB"}
@@ -0,0 +1,23 @@
1
+ 'use client';
2
+ import { useCallback } from 'react';
3
+ import { QA_ID_ATTRIBUTE, QA_SOURCE_ATTRIBUTE } from '../types';
4
+ import { validateQaId } from '../identity/IdentityValidator';
5
+ import { isNonProduction } from '../utils/env';
6
+ export function useQAElement(qaId, source) {
7
+ return useCallback((node) => {
8
+ if (!node) {
9
+ return;
10
+ }
11
+ const check = validateQaId(qaId);
12
+ if (check.ok) {
13
+ node.setAttribute(QA_ID_ATTRIBUTE, qaId);
14
+ }
15
+ else if (isNonProduction()) {
16
+ console.warn(`Invalid QA ID "${qaId}": ${check.reason}`);
17
+ }
18
+ if (source) {
19
+ node.setAttribute(QA_SOURCE_ATTRIBUTE, source);
20
+ }
21
+ }, [qaId, source]);
22
+ }
23
+ //# sourceMappingURL=useQAElement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQAElement.js","sourceRoot":"","sources":["../../src/react/useQAElement.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAoB,MAAM,OAAO,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,MAAe;IAEf,OAAO,WAAW,CAChB,CAAC,IAAI,EAAE,EAAE;QACP,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;YACb,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;QAC3C,CAAC;aAAM,IAAI,eAAe,EAAE,EAAE,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,kBAAkB,IAAI,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,EACD,CAAC,IAAI,EAAE,MAAM,CAAC,CACf,CAAC;AACJ,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { QAEvent, QAEventType, QARecording } from '../types';
2
+ import type { QAElementIdentity } from '../types';
3
+ export declare class EventNormalizer {
4
+ private readonly emit;
5
+ private inputTimer;
6
+ private pendingInput;
7
+ constructor(emit: (event: Omit<QAEvent, 'id' | 'schemaVersion' | 'timestamp' | 'page'> & {
8
+ page?: QAEvent['page'];
9
+ }) => void);
10
+ click(target: QAElementIdentity, type?: 'click' | 'dblclick', extra?: Record<string, unknown>): void;
11
+ input(target: QAElementIdentity, value: string | undefined, extra?: Record<string, unknown>): void;
12
+ change(target: QAElementIdentity, value: string | undefined, type?: QAEventType, extra?: Record<string, unknown>): void;
13
+ simple(type: QAEventType, target?: QAElementIdentity, metadata?: Record<string, unknown>): void;
14
+ flushInput(): void;
15
+ dispose(): void;
16
+ }
17
+ export declare class RecordingSession {
18
+ private paused;
19
+ readonly recording: QARecording;
20
+ constructor(initialUrl: string);
21
+ get isPaused(): boolean;
22
+ pause(): void;
23
+ resume(): void;
24
+ push(event: QAEvent): void;
25
+ stop(): QARecording;
26
+ }
27
+ export declare function buildEvent(type: QAEventType, extras?: {
28
+ target?: QAElementIdentity;
29
+ metadata?: Record<string, unknown>;
30
+ page?: QAEvent['page'];
31
+ }): QAEvent;
32
+ //# sourceMappingURL=EventNormalizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventNormalizer.d.ts","sourceRoot":"","sources":["../../src/recorder/EventNormalizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGlE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAIlD,qBAAa,eAAe;IAMd,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,OAAO,CAAC,UAAU,CAA4C;IAC9D,OAAO,CAAC,YAAY,CAEN;gBAEe,IAAI,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,eAAe,GAAG,WAAW,GAAG,MAAM,CAAC,GAAG;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;KAAE,KAAK,IAAI;IAE7I,KAAK,CACH,MAAM,EAAE,iBAAiB,EACzB,IAAI,GAAE,OAAO,GAAG,UAAoB,EACpC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,IAAI;IASP,KAAK,CACH,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,IAAI;IAgBP,MAAM,CACJ,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,IAAI,GAAE,WAAsB,EAC5B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,IAAI;IAaP,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAO/F,UAAU,IAAI,IAAI;IAiBlB,OAAO,IAAI,IAAI;CAGhB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;gBAEpB,UAAU,EAAE,MAAM;IAU9B,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,KAAK,IAAI,IAAI;IAIb,MAAM,IAAI,IAAI;IAId,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAO1B,IAAI,IAAI,WAAW;CAIpB;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,WAAW,EACjB,MAAM,GAAE;IACN,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CACnB,GACL,OAAO,CAUT"}
@@ -0,0 +1,113 @@
1
+ import { createId, safeNow } from '../utils/id';
2
+ import { getPageContext } from '../utils/dom';
3
+ const INPUT_DEBOUNCE_MS = 400;
4
+ export class EventNormalizer {
5
+ emit;
6
+ inputTimer;
7
+ pendingInput;
8
+ constructor(emit) {
9
+ this.emit = emit;
10
+ }
11
+ click(target, type = 'click', extra) {
12
+ this.flushInput();
13
+ this.emit({
14
+ type,
15
+ target,
16
+ metadata: extra && Object.keys(extra).length > 0 ? extra : undefined,
17
+ });
18
+ }
19
+ input(target, value, extra) {
20
+ if (this.pendingInput &&
21
+ this.pendingInput.target?.fingerprint !== target.fingerprint) {
22
+ this.flushInput();
23
+ }
24
+ this.pendingInput = { target, value, extra };
25
+ if (this.inputTimer) {
26
+ clearTimeout(this.inputTimer);
27
+ }
28
+ this.inputTimer = setTimeout(() => {
29
+ this.flushInput();
30
+ }, INPUT_DEBOUNCE_MS);
31
+ }
32
+ change(target, value, type = 'change', extra) {
33
+ this.flushInput();
34
+ const metadata = {
35
+ ...(value === undefined ? {} : { value }),
36
+ ...extra,
37
+ };
38
+ this.emit({
39
+ type,
40
+ target,
41
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
42
+ });
43
+ }
44
+ simple(type, target, metadata) {
45
+ if (type !== 'input') {
46
+ this.flushInput();
47
+ }
48
+ this.emit({ type, target, metadata });
49
+ }
50
+ flushInput() {
51
+ if (this.inputTimer) {
52
+ clearTimeout(this.inputTimer);
53
+ this.inputTimer = undefined;
54
+ }
55
+ if (!this.pendingInput) {
56
+ return;
57
+ }
58
+ const pending = this.pendingInput;
59
+ this.pendingInput = undefined;
60
+ this.emit({
61
+ type: 'input',
62
+ target: pending.target,
63
+ metadata: { value: pending.value, source: 'typed', ...pending.extra },
64
+ });
65
+ }
66
+ dispose() {
67
+ this.flushInput();
68
+ }
69
+ }
70
+ export class RecordingSession {
71
+ paused = false;
72
+ recording;
73
+ constructor(initialUrl) {
74
+ this.recording = {
75
+ id: createId('rec'),
76
+ schemaVersion: '1',
77
+ startedAt: safeNow(),
78
+ initialUrl,
79
+ events: [],
80
+ };
81
+ }
82
+ get isPaused() {
83
+ return this.paused;
84
+ }
85
+ pause() {
86
+ this.paused = true;
87
+ }
88
+ resume() {
89
+ this.paused = false;
90
+ }
91
+ push(event) {
92
+ if (this.paused) {
93
+ return;
94
+ }
95
+ this.recording.events.push(event);
96
+ }
97
+ stop() {
98
+ this.recording.endedAt = safeNow();
99
+ return this.recording;
100
+ }
101
+ }
102
+ export function buildEvent(type, extras = {}) {
103
+ return {
104
+ id: createId('evt'),
105
+ schemaVersion: '1',
106
+ timestamp: safeNow(),
107
+ type,
108
+ target: extras.target,
109
+ page: extras.page ?? getPageContext(),
110
+ metadata: extras.metadata,
111
+ };
112
+ }
113
+ //# sourceMappingURL=EventNormalizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventNormalizer.js","sourceRoot":"","sources":["../../src/recorder/EventNormalizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ "use strict";(()=>{var Ne=Object.defineProperty;var He=(t,e,n)=>e in t?Ne(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var o=(t,e,n)=>He(t,typeof e!="symbol"?e+"":e,n);var p="data-qa-id",A="data-qa-source",le="data-qa-sensitive",I="data-testid",de="[REDACTED]",ce=["button","a","input","textarea","select","option","label",'[role="button"]','[role="link"]','[role="checkbox"]','[role="radio"]','[role="tab"]','[role="menuitem"]','[contenteditable="true"]','[contenteditable=""]'];var Be=new Set(["DATABASE_URL","SECRET_KEY","AUTH_SECRET","PRIVATE_API_KEY","GITHUB_APP_PRIVATE_KEY","ENCRYPTION_MASTER_KEY","SESSION_SECRET"]);function ue(t){return Object.keys(t).filter(e=>e==="publicClientToken"||e==="publishableKey"?!1:Be.has(e)||/secret|private.?key|password/i.test(e))}function pe(t){let e=t.enabled!==!1,n=t.environment??"development",i=t.publishableKey??void 0,r=t.publicClientToken??i,s=t.startOn??"manual",a=!!t.sessionId,u=!!r&&!a,c;u&&t.mode===void 0||n.toLowerCase()==="production"?c="disabled":c="observe";let b=e?t.mode??c:"disabled",E=(t.interactiveSelectors??[]).filter(h=>typeof h=="string"&&h.trim().length>0);return{projectId:t.projectId??"",sessionId:t.sessionId,environment:n,apiUrl:t.apiUrl??"",publishableKey:i,publicClientToken:r,enabled:e,mode:b,startOn:s,debug:!!t.debug,interactiveSelectors:Array.from(new Set([...ce,...E])),redactSelectors:(t.redactSelectors??[]).filter(Boolean),allowUnstableIds:!!t.allowUnstableIds,recordHover:!!t.recordHover,recordInputValues:!!t.recordInputValues,flushIntervalMs:k(t.flushIntervalMs,250,5e3,500),maxBatchSize:k(t.maxBatchSize,1,100,25),maxQueueSize:k(t.maxQueueSize,20,500,200),maxStorageBytes:k(t.maxStorageBytes,4096,64e3,32e3),transport:t.transport,onError:t.onError}}function k(t,e,n,i){return typeof t!="number"||Number.isNaN(t)?i:Math.min(n,Math.max(e,t))}function J(t){return t.enabled?t.mode:"disabled"}function fe(t){if(t!=="query"||typeof window>"u")return!1;try{return new URLSearchParams(window.location.search).get("lc_record")==="1"}catch{return!1}}var C=class{constructor(){o(this,"listeners",new Map)}on(e,n){let i=this.listeners.get(e)??new Set;return i.add(n),this.listeners.set(e,i),()=>this.off(e,n)}off(e,n){this.listeners.get(e)?.delete(n)}emit(e,n){let i=this.listeners.get(e);if(i)for(let r of i)try{r(n)}catch{}}};var _e=/^(?:react-aria|ember\d+|:r[\w-]+:)/i,Fe=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function T(t){return t?_e.test(t)||Fe.test(t):!0}function w(t){if(t)try{return t.startsWith("/")||t.startsWith("#")||t.startsWith("?")?t.split("?")[0]?.split("#")[0]:`${new URL(t,"https://loopcheck.invalid").pathname}`}catch{return}}function l(t,e){let n=t.getAttribute(e);return n&&n.trim()?n.trim():void 0}function G(t){return t?t instanceof Element?t:t instanceof Text?t.parentElement:null:null}function W(t){if(typeof t.composedPath=="function"){for(let e of t.composedPath())if(e instanceof Element)return e}return G(t.target)}function me(){if(typeof window>"u"||window===window.top)return null;try{let t=window.frameElement;if(!(t instanceof HTMLIFrameElement))return{inIframe:!0};if(t.name)return{inIframe:!0,frame:`iframe[name="${t.name}"]`};if(t.id&&!T(t.id))return{inIframe:!0,frame:`iframe#${typeof CSS<"u"&&typeof CSS.escape=="function"?CSS.escape(t.id):t.id.replace(/([^\w-])/g,"\\$1")}`};let e=t.getAttribute("title");return e?{inIframe:!0,frame:`iframe[title="${e.replace(/"/g,'\\"')}"]`}:{inIframe:!0,frame:"iframe"}}catch{return{inIframe:!0,crossOriginFrame:!0}}}function Z(t){if(!t||t.length>200||/javascript:|expression\(|@import/i.test(t))return!1;try{return document.createDocumentFragment().querySelector(t),!0}catch{return!1}}function Oe(t,e){for(let n of e)if(Z(n))try{if(t.matches(n))return!0}catch{}return!1}function P(t,e){let n=t;for(;n;){if(Oe(n,e))return n;n=n.parentElement}return null}function he(){return typeof window>"u"||typeof document>"u"?{url:"",pathname:"/"}:{url:window.location.href,pathname:window.location.pathname||"/",title:document.title||void 0}}function g(t){return!!t.closest("[data-loopcheck-ui],[data-loopcheck-recorder]")}function f(){return typeof window<"u"&&typeof document<"u"}function L(){return Date.now()}function X(t="qa"){let e=new Uint8Array(16),n=globalThis.crypto;if(n?.getRandomValues)n.getRandomValues(e);else for(let r=0;r<e.length;r+=1)e[r]=Math.floor(Math.random()*256);e[6]=(e[6]??0)&15|64,e[8]=(e[8]??0)&63|128;let i=Array.from(e,r=>r.toString(16).padStart(2,"0")).join("");return`${t}_${i.slice(0,8)}${i.slice(8,12)}${i.slice(12,16)}${i.slice(16,20)}${i.slice(20)}`}function ge(t){let e=t.filter(i=>!!(i&&i.length>0)).join("|"),n=2166136261;for(let i=0;i<e.length;i+=1)n^=e.charCodeAt(i),n=Math.imul(n,16777619);return`fp_${(n>>>0).toString(16).padStart(8,"0")}`}function m(t,e=80){if(!t)return;let n=t.replace(/\s+/g," ").trim();if(n)return n.length>e?`${n.slice(0,e)}\u2026`:n}function ve(t){if(!t)return;let e=t.trim();if(!e||e.length>400)return;let n=e.match(/^(.*?)(?::(\d+))?(?::(\d+))?(?:#(.+))?$/);if(!n)return{file:e};let i=n[2]?Number(n[2]):void 0,r=n[3]?Number(n[3]):void 0;return{file:n[1]||void 0,line:Number.isFinite(i)?i:void 0,column:Number.isFinite(r)?r:void 0,component:n[4]||void 0}}function ye(t){if(!t)return null;try{return JSON.parse(t)}catch{return null}}function je(t){let e=t.getAttribute("aria-labelledby");if(!e||typeof document>"u")return;let n=[];for(let i of e.split(/\s+/)){let s=document.getElementById(i)?.textContent;s&&n.push(s)}return m(n.join(" "))}function be(t){let e=t.getAttribute("aria-label");if(e?.trim())return m(e);let n=je(t);if(n)return n;if(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement){let i=t.getAttribute("placeholder");if(t.labels&&t.labels.length>0)return m(t.labels[0]?.textContent??void 0);if(i)return m(i)}return t instanceof HTMLElement&&t.title?m(t.title):m(t.textContent??void 0,60)}function Ee(t){let e=t.getAttribute("role")?.trim();if(e)return e;let n=t.tagName.toLowerCase();if(n==="button")return"button";if(n==="a")return"link";if(n==="input"){let i=(t.getAttribute("type")??"text").toLowerCase();return i==="checkbox"?"checkbox":i==="radio"?"radio":i==="submit"||i==="button"?"button":"textbox"}if(n==="textarea")return"textbox";if(n==="select")return"combobox";if(t.getAttribute("contenteditable")==="true")return"textbox"}function De(t){return ge([t.qaId,t.tagName,t.role,t.name,t.type,t.href,t.testId,t.id,t.parentFingerprint])}function qe(t,e=3){let n=[],i=t.parentElement,r=e;for(;i&&r>0;){let s=i.tagName.toLowerCase();if(s==="html"||s==="body")break;let a=l(i,p),u=l(i,"role"),c=l(i,"name");n.push([s,a,u,c].filter(Boolean).join(":")),i=i.parentElement,r-=1}return n.length>0?n.reverse().join(">"):void 0}function Ae(t,e={}){let n=e.id??(t.id||void 0);return De({qaId:e.qaId??l(t,p),tagName:e.tagName??t.tagName.toLowerCase(),role:e.role??l(t,"role"),name:e.name??l(t,"name"),type:e.type??l(t,"type"),href:e.href??w(l(t,"href")),testId:e.testId??l(t,I),id:n&&!T(n)?n:void 0,parentFingerprint:qe(t)})}var ze=/^(?=.{3,80}$)[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*)+$/,Ke=/^(?:button|element|el|node|item|id)-\d+$/i,$e=/[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i;function U(t,e=!1){return e?t.trim().length>0&&t.length<=120:!ze.test(t)||$e.test(t)?!1:!t.split(".").some(n=>Ke.test(n))}var Ve=[p,A,I,"name","type","role","aria-label","title","placeholder","href","autocomplete"];function Ye(t){let e=Object.create(null);for(let n of Ve){let i=l(t,n);i&&(e[n]=n==="href"?w(i)??i:i)}return Object.keys(e).length>0?e:void 0}function Je(t){let e=t.parentElement;if(!(!e||e===document.body||e===document.documentElement))return{qaId:l(e,p),tagName:e.tagName.toLowerCase(),role:l(e,"role"),name:l(e,"name")??l(e,"aria-label")}}function M(t,e={}){let n=[],i=l(t,p),r=i&&U(i,e.allowUnstableIds)?i:void 0;i&&!r&&n.push("ignored unstable data-qa-id"),r&&n.push("explicit data-qa-id");let s=l(t,I);s&&n.push("data-testid");let a=Ee(t),u=be(t),c=ve(l(t,A)??null);c&&n.push("source metadata");let b=t.id||void 0,E=b&&!T(b)?b:void 0;E&&n.push("stable id"),a&&n.push("accessibility role"),u&&n.push("accessible name");let h=t.tagName.toLowerCase(),Y=l(t,"name"),se=l(t,"type"),ae=w(l(t,"href")),Ue=h==="input"||h==="textarea"?void 0:m(t.textContent??void 0,60),Me={qaId:r??(s&&U(s,e.allowUnstableIds)?s:void 0),tagName:h,role:a,accessibleName:u,text:Ue,name:Y,id:E,href:ae,type:se,source:c,parent:Je(t),attributes:Ye(t),fingerprint:Ae(t,{qaId:r??(s&&U(s,e.allowUnstableIds)?s:void 0),tagName:h,role:a,name:Y,type:se,href:ae,testId:s,id:E})},R="low";return r?R="exact":s||c?.file?(R="high",!n.includes("framework/source metadata")&&c?.file&&n.push("framework/source metadata")):a&&(u||Y)?(R="medium",n.push("semantic HTML")):n.push("deterministic DOM fingerprint"),{identity:Me,confidence:R,reasons:n}}var N=class{constructor(){o(this,"seen",new Map)}scan(e){this.seen.clear(),e.querySelectorAll(`[${p}]`).forEach(r=>{let s=l(r,p);if(!s)return;let a=l(r,A)??Ze(r),u=this.seen.get(s)??new Set;u.add(a),this.seen.set(s,u)});let i=[];for(let[r,s]of this.seen)(s.size>1||Ge(e,r)>1)&&i.push({qaId:r,sources:Array.from(s)});return i}};function Ge(t,e){return t.querySelectorAll(`[${p}="${We(e)}"]`).length}function We(t){return typeof CSS<"u"&&typeof CSS.escape=="function"?CSS.escape(t):t.replace(/["\\]/g,"\\$&")}function Ze(t){let e=t.tagName.toLowerCase(),n=t.getAttribute("aria-label")||t.getAttribute("name")||"";return n?`${e}[name=${n}]`:e}var H=class{constructor(e,n,i){this.config=e;this.onDuplicates=n;this.onResolved=i;o(this,"duplicates",new N);o(this,"lastScan",0);o(this,"scheduled",!1)}resolve(e){let n=P(e,this.config.interactiveSelectors)??e,i=M(n,{allowUnstableIds:this.config.allowUnstableIds});return this.onResolved?.(i),i}scheduleScan(e=document){if(this.scheduled)return;this.scheduled=!0;let n=()=>{this.scheduled=!1,this.scan(e)};typeof requestAnimationFrame=="function"?requestAnimationFrame(n):setTimeout(n,32)}scan(e=document){let n=Date.now();if(n-this.lastScan<80)return[];this.lastScan=n;let i=this.duplicates.scan(e);return i.length>0&&this.onDuplicates(i),i}};var B=class{constructor(e,n){this.config=e;this.registry=n;o(this,"observer");o(this,"timer")}start(e=document){typeof MutationObserver>"u"||(this.observer=new MutationObserver(()=>{this.schedule(e)}),e instanceof Node&&this.observer.observe(e,{subtree:!0,childList:!0,attributes:!0,attributeFilter:["data-qa-id","data-qa-source","role","name","type"]}),this.registry.scheduleScan(e))}stop(){this.observer?.disconnect(),this.observer=void 0,this.timer&&(clearTimeout(this.timer),this.timer=void 0)}schedule(e){this.timer||(this.timer=setTimeout(()=>{this.timer=void 0,this.registry.scheduleScan(e)},100))}};var Xe=new Set(["password","hidden"]),et=/^(cc-|card |current-password|new-password|one-time-code|tel|email)/i,tt=/password|passwd|secret|token|cvv|cvc|ssn|credit|card.?number|authorization|cookie|session/i;function nt(t,e){if(t.hasAttribute(le))return!0;if(t instanceof HTMLInputElement){if(Xe.has(t.type.toLowerCase()))return!0;let i=t.getAttribute("autocomplete")??"";if(i&&et.test(i))return!0}let n=t.getAttribute("name")??t.getAttribute("id")??"";if(n&&tt.test(n))return!0;for(let i of e.redactSelectors)if(Z(i))try{if(t.matches(i)||t.closest(i))return!0}catch{}return!1}function ee(t,e,n){return nt(t,n)||!n.recordInputValues?de:typeof e!="string"?e==null?void 0:String(e):e.length>200?`${e.slice(0,200)}\u2026`:e}var Ie=300,te="lc-on-autofill",ne="loopcheck-autofill-detect",it=new Set(["hidden","file","button","submit","reset","image"]);function Te(t){return t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement}function v(t){if(!(t instanceof HTMLInputElement))return!1;let e=t.type.toLowerCase();return e==="checkbox"||e==="radio"}function rt(t){if(t instanceof HTMLInputElement){let e=t.type.toLowerCase();return e==="submit"||e==="image"}return t instanceof HTMLButtonElement?t.type==="submit":!1}function we(t){return rt(t)?!0:t.closest("form")?t.matches('button, [role="button"], input[type="submit"], input[type="image"]'):!1}function S(t,e){if(t instanceof HTMLInputElement&&v(t))return{value:ee(t,t.value,e),checked:t.checked};let n=t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement?t.value:void 0;return{value:ee(t,n,e)}}function ot(t,e,n,i){let r=[];if(typeof document>"u")return r;let s=t.querySelectorAll("input, textarea, select");for(let a of s){if(!Te(a)||g(a)||a instanceof HTMLInputElement&&it.has(a.type.toLowerCase())||"disabled"in a&&a.disabled)continue;if(v(a)){if(!(a instanceof HTMLInputElement)||!a.checked)continue}else if(!a.value.trim())continue;let u=e.resolve(a).identity;if(i.has(u.fingerprint))continue;let c=S(a,n);r.push({element:a,identity:u,value:c.value,checked:c.checked,eventType:a instanceof HTMLSelectElement?"select":v(a)?"change":"input"})}return r}function st(){if(typeof document>"u"||document.getElementById(ne))return;let t=document.createElement("style");t.id=ne,t.setAttribute("data-loopcheck-recorder","1"),t.textContent=`@keyframes ${te} {
2
+ from { opacity: 0.99; }
3
+ to { opacity: 1; }
4
+ }
5
+ input:-webkit-autofill,
6
+ textarea:-webkit-autofill,
7
+ select:-webkit-autofill {
8
+ animation-name: ${te};
9
+ }`,document.documentElement.appendChild(t)}function at(){typeof document>"u"||document.getElementById(ne)?.remove()}var _=class{constructor(e,n,i){this.config=e;this.registry=n;this.onField=i;o(this,"onAnimationStart",e=>{if(e.animationName!==te)return;let n=e.target;if(!(n instanceof Element)||!Te(n)||g(n)||v(n)||!n.value.trim())return;let i=this.registry.resolve(n).identity,r=S(n,this.config);this.onField({element:n,identity:i,value:r.value,checked:r.checked,eventType:n instanceof HTMLSelectElement?"select":"input"},"autofill")})}start(){typeof document>"u"||(st(),document.addEventListener("animationstart",this.onAnimationStart,!0))}stop(){typeof document>"u"||(document.removeEventListener("animationstart",this.onAnimationStart,!0),at())}collect(e,n,i){for(let r of ot(e,this.registry,this.config,n))this.onField(r,i)}};var Se=["click","dblclick","input","change","focus","blur","submit","keydown","dragstart","drop","mouseover"],lt=new Set(["Enter","Escape"]),F=class{constructor(e,n,i,r){this.config=e;this.registry=n;this.normalizer=i;this.shouldRecord=r;o(this,"attached",!1);o(this,"onEvent");o(this,"recordedFields",new Set);o(this,"lastClickedFingerprint");o(this,"harvest",null);o(this,"onScroll",e=>{if(!this.shouldRecord())return;let n=W(e);n&&g(n)||this.throttledScroll()});o(this,"scrollTimer");o(this,"throttledScroll",()=>{this.scrollTimer||(this.scrollTimer=setTimeout(()=>{this.scrollTimer=void 0,this.normalizer.simple("scroll",void 0,this.frameMeta())},400))});this.onEvent=s=>this.handle(s)}start(){if(!(this.attached||typeof document>"u")){this.harvest=new _(this.config,this.registry,(e,n)=>{this.emitHarvested(e,n)}),this.harvest.start();for(let e of Se)document.addEventListener(e,this.onEvent,!0);window.addEventListener("scroll",this.onScroll,!0),this.attached=!0}}stop(){if(!(!this.attached||typeof document>"u")){for(let e of Se)document.removeEventListener(e,this.onEvent,!0);window.removeEventListener("scroll",this.onScroll,!0),this.harvest?.stop(),this.harvest=null,this.attached=!1}}harvestPrefills(e="prefilled",n){!this.shouldRecord()||typeof document>"u"||this.harvest?.collect(n??document,this.recordedFields,e)}frameMeta(){let e=me();if(e)return{inIframe:!0,...e.frame?{frame:e.frame}:{},...e.crossOriginFrame?{crossOriginFrame:!0}:{}}}withFrame(e){let n=this.frameMeta();if(!(!n&&!e))return{...n,...e}}emitHarvested(e,n){this.shouldRecord()&&(this.recordedFields.has(e.identity.fingerprint)||(this.recordedFields.add(e.identity.fingerprint),this.normalizer.change(e.identity,e.value,e.eventType,this.withFrame({source:n,...e.checked!==void 0?{checked:e.checked}:{}}))))}handle(e){if(!this.shouldRecord())return;let n=W(e);if(!n||g(n))return;let i=P(n,this.config.interactiveSelectors);if(!i&&e.type!=="submit"&&e.type!=="keydown")return;let r=i??n,s=this.registry.resolve(r).identity;switch(e.type){case"click":we(r)&&this.harvestPrefills("prefilled",r.closest("form")??document),this.lastClickedFingerprint=s.fingerprint,this.normalizer.click(s,"click",this.frameMeta());break;case"dblclick":this.normalizer.click(s,"dblclick",this.frameMeta());break;case"input":if(v(r)||r instanceof HTMLInputElement&&r.type==="file")break;this.recordedFields.add(s.fingerprint),this.normalizer.input(s,S(r,this.config).value,this.withFrame({source:"typed"}));break;case"change":if(this.recordedFields.add(s.fingerprint),r instanceof HTMLInputElement&&r.type==="file"){this.emitFileChange(r,s);break}this.emitControlChange(r,s,"typed");break;case"focus":this.normalizer.simple("focus",s,this.frameMeta());break;case"blur":this.normalizer.flushInput(),this.normalizer.simple("blur",s,this.frameMeta());break;case"submit":this.handleSubmit(e,r,s);break;case"keydown":this.handleKey(e,s);break;case"dragstart":this.normalizer.simple("drag",s,this.frameMeta());break;case"drop":this.normalizer.simple("drop",s,this.frameMeta());break;case"mouseover":this.config.recordHover&&this.normalizer.simple("hover",s,this.frameMeta());break;default:break}}emitFileChange(e,n){let i=Array.from(e.files??[]).map(r=>r.name);this.normalizer.change(n,void 0,"change",this.withFrame({source:"typed",files:i.length>0?i:["upload.bin"],fileName:i[0]??"upload.bin"}))}handleSubmit(e,n,i){let r=n.closest("form")??n;this.harvestPrefills("prefilled",r);let s=dt(e,r);if(s){let a=this.registry.resolve(s).identity;a.fingerprint!==this.lastClickedFingerprint&&(this.lastClickedFingerprint=a.fingerprint,this.normalizer.click(a,"click",this.frameMeta()));return}this.normalizer.simple("submit",i,this.frameMeta())}emitControlChange(e,n,i){let r=S(e,this.config);this.normalizer.change(n,r.value,ct(e),this.withFrame({source:i,...r.checked!==void 0?{checked:r.checked}:{}}))}handleKey(e,n){lt.has(e.key)&&(this.normalizer.flushInput(),this.normalizer.simple("keydown",n,this.withFrame({key:e.key})))}};function dt(t,e){let n="submitter"in t?t.submitter:null;return n instanceof Element?n:e.querySelector('button[type="submit"], input[type="submit"], button:not([type]), input[type="image"]')}function ct(t){return t instanceof HTMLSelectElement?"select":"change"}var O=class{constructor(e){this.onNavigate=e;o(this,"originalPush");o(this,"originalReplace");o(this,"lastUrl","");o(this,"check",()=>{if(typeof window>"u")return;let e=window.location.href;e!==this.lastUrl&&(this.lastUrl=e,this.onNavigate(e,window.location.pathname||"/"))})}start(){typeof window>"u"||typeof history>"u"||(this.lastUrl=window.location.href,this.originalPush=history.pushState.bind(history),this.originalReplace=history.replaceState.bind(history),history.pushState=((e,n,i)=>{this.originalPush?.(e,n,i),this.check()}),history.replaceState=((e,n,i)=>{this.originalReplace?.(e,n,i),this.check()}),window.addEventListener("popstate",this.check),window.addEventListener("hashchange",this.check))}stop(){typeof window>"u"||typeof history>"u"||(this.originalPush&&(history.pushState=this.originalPush),this.originalReplace&&(history.replaceState=this.originalReplace),window.removeEventListener("popstate",this.check),window.removeEventListener("hashchange",this.check))}};var ut=400,j=class{constructor(e){this.emit=e;o(this,"inputTimer");o(this,"pendingInput")}click(e,n="click",i){this.flushInput(),this.emit({type:n,target:e,metadata:i&&Object.keys(i).length>0?i:void 0})}input(e,n,i){this.pendingInput&&this.pendingInput.target?.fingerprint!==e.fingerprint&&this.flushInput(),this.pendingInput={target:e,value:n,extra:i},this.inputTimer&&clearTimeout(this.inputTimer),this.inputTimer=setTimeout(()=>{this.flushInput()},ut)}change(e,n,i="change",r){this.flushInput();let s={...n===void 0?{}:{value:n},...r};this.emit({type:i,target:e,metadata:Object.keys(s).length>0?s:void 0})}simple(e,n,i){e!=="input"&&this.flushInput(),this.emit({type:e,target:n,metadata:i})}flushInput(){if(this.inputTimer&&(clearTimeout(this.inputTimer),this.inputTimer=void 0),!this.pendingInput)return;let e=this.pendingInput;this.pendingInput=void 0,this.emit({type:"input",target:e.target,metadata:{value:e.value,source:"typed",...e.extra}})}dispose(){this.flushInput()}},D=class{constructor(e){o(this,"paused",!1);o(this,"recording");this.recording={id:X("rec"),schemaVersion:"1",startedAt:L(),initialUrl:e,events:[]}}get isPaused(){return this.paused}pause(){this.paused=!0}resume(){this.paused=!1}push(e){this.paused||this.recording.events.push(e)}stop(){return this.recording.endedAt=L(),this.recording}};function q(t,e={}){return{id:X("evt"),schemaVersion:"1",timestamp:L(),type:t,target:e.target,page:e.page??he(),metadata:e.metadata}}function z(t,e="QAError"){if(t&&typeof t=="object"&&"message"in t){let n=t;return{name:typeof n.name=="string"?n.name:e,message:String(n.message),cause:t}}return{name:e,message:String(t),cause:t}}function y(t,e){try{t()}catch(n){e?.(z(n))}}async function ie(t,e){try{await t()}catch(n){e?.(z(n))}}var K=class{constructor(e){o(this,"pending",[]);o(this,"options");this.options={...e}}updateSession(e){this.options={...this.options,...e}}async send(e){await this.sendBatch([e])}async sendBatch(e){if(e.length===0)return;if(!this.options.apiUrl||!this.options.sessionId){this.pending.push(...e);return}let n=`${re(this.options.apiUrl)}/v1/sdk/events`,i=JSON.stringify({projectId:this.options.projectId,sessionId:this.options.sessionId,events:e}),r={"content-type":"application/json","x-loopcheck-project":this.options.projectId};this.options.publicClientToken&&(r["x-loopcheck-client-token"]=this.options.publicClientToken);let s=await fetch(n,{method:"POST",headers:r,body:i,credentials:"omit",keepalive:!0});if(!s.ok)throw new Error(`QA transport HTTP ${s.status}`)}async flush(){if(this.pending.length===0)return;let e=this.pending.splice(0,this.pending.length);await this.sendBatch(e)}};async function Qe(t){let e=`${re(t.apiUrl)}/v1/sdk/sessions`,n=await fetch(e,{method:"POST",headers:{"content-type":"application/json","x-loopcheck-client-token":t.publishableKey},body:JSON.stringify({initialUrl:t.initialUrl,projectId:t.projectId||void 0,environmentId:t.environmentId,publishableKey:t.publishableKey}),credentials:"omit"});if(!n.ok)throw new Error(`QA session create HTTP ${n.status}`);let i=await n.json();return{sessionId:i.sessionId,projectId:i.projectId,apiUrl:i.apiUrl??t.apiUrl}}function xe(t,e,n,i,r){if(typeof navigator>"u"||typeof navigator.sendBeacon!="function"||!t)return!1;try{let s=`${re(t)}/v1/sdk/events`,a=new Blob([JSON.stringify({projectId:e,sessionId:r,publicClientToken:i,events:n})],{type:"application/json"});return navigator.sendBeacon(s,a)}catch{return!1}}function re(t){return t.replace(/\/+$/,"")}var $=class{constructor(e,n){this.storageKey=e;this.maxBytes=n}load(){if(!f())return[];try{let e=window.localStorage.getItem(this.storageKey),n=ye(e);return Array.isArray(n?.events)?n.events.slice(0,200):[]}catch{return[]}}save(e){if(f())try{let n=e.slice(-200),i=JSON.stringify({events:n});for(;i.length>this.maxBytes&&n.length>0;)n=n.slice(Math.ceil(n.length/2)),i=JSON.stringify({events:n});if(i.length>this.maxBytes){window.localStorage.removeItem(this.storageKey);return}window.localStorage.setItem(this.storageKey,i)}catch{try{window.localStorage.removeItem(this.storageKey)}catch{}}}clear(){if(f())try{window.localStorage.removeItem(this.storageKey)}catch{}}};var V=class{constructor(e){o(this,"memory",[]);o(this,"timer");o(this,"retry");o(this,"transport");o(this,"httpTransport",null);o(this,"unloading",!1);o(this,"inflight",Promise.resolve());o(this,"config");o(this,"onPageHide",()=>{if(this.unloading=!0,this.memory.length===0)return;let e=this.memory.splice(0,this.memory.length);xe(this.config.apiUrl,this.config.projectId,e,this.config.publicClientToken,this.config.sessionId)||this.retry.save(e)});o(this,"onOnline",()=>{this.unloading||this.flush()});this.config=e,e.transport?this.transport=e.transport:(this.httpTransport=new K({apiUrl:e.apiUrl,projectId:e.projectId,sessionId:e.sessionId,publicClientToken:e.publicClientToken}),this.transport=this.httpTransport),this.retry=new $(`loopcheck.qa.queue.${e.projectId||"pending"}`,e.maxStorageBytes)}updateSession(e){e.projectId&&(this.config.projectId=e.projectId),e.sessionId&&(this.config.sessionId=e.sessionId),e.apiUrl&&(this.config.apiUrl=e.apiUrl),e.publicClientToken&&(this.config.publicClientToken=e.publicClientToken),this.httpTransport?.updateSession(e)}start(){this.memory.push(...this.retry.load()),this.retry.clear(),this.timer=setInterval(()=>{this.flush()},this.config.flushIntervalMs),typeof window<"u"&&(window.addEventListener("pagehide",this.onPageHide),window.addEventListener("online",this.onOnline))}enqueue(e){this.memory.length>=this.config.maxQueueSize&&this.memory.shift(),this.memory.push(e),this.memory.length>=this.config.maxBatchSize&&this.flush()}async flush(){this.inflight=this.inflight.then(()=>this.flushOnce()),await this.inflight}async flushOnce(){if(this.memory.length===0)return;let e=this.memory.splice(0,this.config.maxBatchSize);await ie(async()=>{await this.transport.sendBatch(e)},n=>{this.memory.unshift(...e),this.retry.save(this.memory),this.config.onError?.(n)})}async shutdown(){this.timer&&(clearInterval(this.timer),this.timer=void 0),typeof window<"u"&&(window.removeEventListener("pagehide",this.onPageHide),window.removeEventListener("online",this.onOnline)),await this.flush(),await ie(()=>this.transport.flush(),this.config.onError)}};var oe="loopcheck-qa-debug-panel",Q=null;function Re(t){if(typeof document>"u"||Q)return;let e=n=>{if(!n.altKey||!n.shiftKey){Ce();return}let i=G(n.target);if(!i)return;let r=t.resolveElement(i);pt(r)};document.addEventListener("mousemove",e,!0),Q={client:t,onMove:e}}function ke(){!Q||typeof document>"u"||(document.removeEventListener("mousemove",Q.onMove,!0),Ce(),Q=null)}function pt(t){let e=t.identity,n=document.getElementById(oe)??ft();n.textContent=["QA Element","\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",`ID: ${e.qaId??"(none)"}`,`Role: ${e.role??e.tagName}`,`Name: ${e.accessibleName??e.text??""}`,`Source: ${e.source?.file?`${e.source.file}${e.source.line?`:${e.source.line}`:""}`:"(none)"}`,`Fingerprint: ${e.fingerprint}`,`Confidence: ${t.confidence}`].join(`
10
+ `)}function ft(){let t=document.createElement("pre");return t.id=oe,t.setAttribute("data-qa-sensitive",""),t.style.cssText="position:fixed;z-index:2147483647;right:12px;bottom:12px;max-width:360px;margin:0;padding:12px;background:#111;color:#f5f5f5;font:12px/1.4 ui-monospace,monospace;border-radius:8px;pointer-events:none;white-space:pre-wrap;",document.body.appendChild(t),t}function Ce(){document.getElementById(oe)?.remove()}var Pe={isRecording:!1,isPaused:!1,mode:"disabled"},x=class{constructor(e){o(this,"config");o(this,"bus",new C);o(this,"subscribers",new Set);o(this,"session",null);o(this,"registry",null);o(this,"observer",null);o(this,"delegator",null);o(this,"navigation",null);o(this,"queue",null);o(this,"normalizer",null);o(this,"started",!1);o(this,"snapshot",Pe);o(this,"prefillTimer");o(this,"prefillRaf");o(this,"sessionBootstrap",null);o(this,"queryArmed",!1);o(this,"subscribe",e=>(this.subscribers.add(e),()=>{this.subscribers.delete(e)}));o(this,"getSnapshot",()=>this.snapshot);let n=ue(e);this.config=pe(e),n.length>0&&this.report({name:"QAConfigError",message:`Ignored privileged config keys: ${n.join(", ")}`}),this.snapshot={isRecording:!1,isPaused:!1,mode:J(this.config)}}start(){y(()=>{this.started||!f()||J(this.config)==="disabled"||(this.attachInstrumentation(),this.config.mode==="record"&&this.beginRecording())},e=>this.report(e))}maybeArmFromQuery(){this.queryArmed||!fe(this.config.startOn)||(this.queryArmed=!0,this.startRecording())}async shutdown(){this.clearPrefillHarvest(),this.normalizer?.dispose(),this.delegator?.stop(),this.observer?.stop(),this.navigation?.stop(),ke(),await this.queue?.shutdown(),this.started=!1,this.registry=null,this.observer=null,this.delegator=null,this.navigation=null,this.queue=null,this.normalizer=null}async startRecording(){try{if(!f()||this.config.enabled===!1)return;await this.ensureSession(),this.beginRecording()}catch(e){this.report(z(e))}}beginRecording(){y(()=>{if(!(!f()||this.config.enabled===!1)){if(this.config.mode="record",this.config.enabled=!0,this.started||this.attachInstrumentation(),this.session&&!this.session.recording.endedAt){this.session.resume(),this.setSnapshot({isRecording:!0,isPaused:!1,mode:"record"}),this.bus.emit("recording:resume",null);return}this.session=new D(window.location.href),this.setSnapshot({isRecording:!0,isPaused:!1,mode:"record"}),this.bus.emit("recording:start",this.session.recording),this.emitEvent(q("navigation",{page:{url:window.location.href,pathname:window.location.pathname||"/",title:document.title||void 0}})),this.schedulePrefillHarvest()}},e=>this.report(e))}stopRecording(){let e=null;return y(()=>{this.clearPrefillHarvest(),this.delegator?.harvestPrefills("prefilled"),this.normalizer?.flushInput(),this.session&&(e=this.session.stop(),this.session=null,this.setSnapshot({isRecording:!1,isPaused:!1,mode:this.config.mode==="record"?"observe":this.config.mode}),this.bus.emit("recording:stop",e))},n=>this.report(n)),e}pauseRecording(){y(()=>{this.session?.pause(),this.setSnapshot({isPaused:!0}),this.bus.emit("recording:pause",null)},e=>this.report(e))}resumeRecording(){y(()=>{this.session?.resume(),this.setSnapshot({isPaused:!1}),this.bus.emit("recording:resume",null)},e=>this.report(e))}get isRecording(){return this.snapshot.isRecording}get isPaused(){return this.snapshot.isPaused}resolveElement(e){return this.registry?this.registry.resolve(e):M(e,{allowUnstableIds:this.config.allowUnstableIds})}on(e,n){return this.bus.on(e,n)}attachInstrumentation(){this.started||!f()||(this.started=!0,this.registry=new H(this.config,e=>{for(let n of e)this.bus.emit("duplicate-id",n),(this.config.debug||this.config.environment!=="production")&&console.warn(`Duplicate QA ID detected:
11
+ ${n.qaId}
12
+ Elements:
13
+ ${n.sources.map((i,r)=>`${r+1}. ${i}`).join(`
14
+ `)}`)},e=>this.bus.emit("element:resolved",e)),this.observer=new B(this.config,this.registry),this.observer.start(document),this.navigation=new O((e,n)=>{this.normalizer?.flushInput(),this.emitEvent(q("navigation",{page:{url:e,pathname:n,title:document.title||void 0}})),this.queue?.flush()}),this.navigation.start(),this.normalizer=new j(e=>{this.emitEvent(q(e.type,{target:e.target,metadata:e.metadata,page:e.page}))}),this.delegator=new F(this.config,this.registry,this.normalizer,()=>this.isRecording&&!this.isPaused),this.delegator.start(),this.queue=new V(this.config),this.queue.start(),this.config.debug&&Re(this))}async ensureSession(){if(this.config.sessionId)return;let e=this.config.publishableKey??this.config.publicClientToken;if(e){if(!this.config.apiUrl)throw new Error("apiUrl is required to create a recording session");this.sessionBootstrap||(this.sessionBootstrap=(async()=>{let n=await Qe({apiUrl:this.config.apiUrl,publishableKey:e,initialUrl:window.location.href,projectId:this.config.projectId||void 0});this.config.sessionId=n.sessionId,this.config.projectId=n.projectId,n.apiUrl&&(this.config.apiUrl=n.apiUrl),this.queue?.updateSession({sessionId:n.sessionId,projectId:n.projectId,apiUrl:this.config.apiUrl,publicClientToken:e})})());try{await this.sessionBootstrap}catch(n){throw this.sessionBootstrap=null,n}}}schedulePrefillHarvest(){this.clearPrefillHarvest();let e=()=>{this.prefillTimer=setTimeout(()=>{this.prefillTimer=void 0,this.delegator?.harvestPrefills("prefilled")},Ie)};typeof requestAnimationFrame=="function"?this.prefillRaf=requestAnimationFrame(()=>{this.prefillRaf=void 0,e()}):e()}clearPrefillHarvest(){this.prefillRaf!==void 0&&typeof cancelAnimationFrame=="function"&&(cancelAnimationFrame(this.prefillRaf),this.prefillRaf=void 0),this.prefillTimer&&(clearTimeout(this.prefillTimer),this.prefillTimer=void 0)}emitEvent(e){!this.snapshot.isRecording||this.snapshot.isPaused||(this.session?.push(e),this.bus.emit("event",e),this.queue?.enqueue(e))}setSnapshot(e){this.snapshot={...this.snapshot,...e};for(let n of this.subscribers)n()}report(e){this.config.onError?.(e),this.bus.emit("error",e),this.config.debug&&console.warn("[loopcheck-qa]",e.message)}};o(x,"getServerSnapshot",()=>Pe);var d=null,Le=!0;function mt(t){if(d){if(!!t.sessionId&&d.config.sessionId===t.sessionId&&d.config.projectId===t.projectId)return t.mode==="record"&&!d.isRecording&&d.startRecording(),d;d.shutdown(),d=null}Le=t.ui!=="none";let e=t.publishableKey??t.publicClientToken,n={projectId:t.projectId,sessionId:t.sessionId,apiUrl:t.apiUrl,publishableKey:t.publishableKey,publicClientToken:e,mode:t.mode??(t.sessionId?"record":"disabled"),startOn:t.startOn??"manual",debug:!!t.debug,enabled:!0,recordInputValues:!0,flushIntervalMs:500};if(d=new x(n),d.start(),d.maybeArmFromQuery(),(t.mode==="record"||t.sessionId)&&d.startRecording(),Le&&typeof document<"u"){document.querySelector("[data-loopcheck-recorder]")?.remove();let i=document.createElement("div");i.setAttribute("data-loopcheck-recorder","1"),i.textContent="Loopcheck recording",i.style.cssText="position:fixed;z-index:2147483647;bottom:12px;right:12px;padding:6px 10px;border-radius:8px;background:#18181b;color:#fafafa;font:12px/1.4 system-ui,sans-serif;box-shadow:0 4px 16px rgba(0,0,0,.2)",document.documentElement.appendChild(i)}return d}async function ht(){d&&(d.stopRecording(),await d.shutdown(),d=null,document.querySelector("[data-loopcheck-recorder]")?.remove())}function gt(){d?.pauseRecording()}function vt(){d?.resumeRecording()}var yt={start:mt,stop:ht,pause:gt,resume:vt,getClient:()=>d};typeof window<"u"&&(window.LoopcheckQA=yt);})();
@@ -0,0 +1,26 @@
1
+ import type { QAEvent, ResolvedQAConfig } from '../types';
2
+ export declare class BatchQueue {
3
+ private readonly memory;
4
+ private timer;
5
+ private readonly retry;
6
+ private readonly transport;
7
+ private httpTransport;
8
+ private unloading;
9
+ private inflight;
10
+ private config;
11
+ constructor(config: ResolvedQAConfig);
12
+ updateSession(patch: {
13
+ projectId?: string;
14
+ sessionId?: string;
15
+ apiUrl?: string;
16
+ publicClientToken?: string;
17
+ }): void;
18
+ start(): void;
19
+ enqueue(event: QAEvent): void;
20
+ flush(): Promise<void>;
21
+ private flushOnce;
22
+ shutdown(): Promise<void>;
23
+ private readonly onPageHide;
24
+ private readonly onOnline;
25
+ }
26
+ //# sourceMappingURL=BatchQueue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BatchQueue.d.ts","sourceRoot":"","sources":["../../src/transport/BatchQueue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAe,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAKvE,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,MAAM,CAAmB;gBAErB,MAAM,EAAE,gBAAgB;IAmBpC,aAAa,CAAC,KAAK,EAAE;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,GAAG,IAAI;IAgBR,KAAK,IAAI,IAAI;IAYb,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAUvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAKd,SAAS;IAcjB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAa/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAgBzB;IAEF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAIvB;CACH"}
@@ -0,0 +1,110 @@
1
+ import { swallowAsync } from '../utils/safe';
2
+ import { HTTPTransport, sendBeaconBatch } from './HTTPTransport';
3
+ import { RetryQueue } from './RetryQueue';
4
+ export class BatchQueue {
5
+ memory = [];
6
+ timer;
7
+ retry;
8
+ transport;
9
+ httpTransport = null;
10
+ unloading = false;
11
+ inflight = Promise.resolve();
12
+ config;
13
+ constructor(config) {
14
+ this.config = config;
15
+ if (config.transport) {
16
+ this.transport = config.transport;
17
+ }
18
+ else {
19
+ this.httpTransport = new HTTPTransport({
20
+ apiUrl: config.apiUrl,
21
+ projectId: config.projectId,
22
+ sessionId: config.sessionId,
23
+ publicClientToken: config.publicClientToken,
24
+ });
25
+ this.transport = this.httpTransport;
26
+ }
27
+ this.retry = new RetryQueue(`loopcheck.qa.queue.${config.projectId || 'pending'}`, config.maxStorageBytes);
28
+ }
29
+ updateSession(patch) {
30
+ if (patch.projectId) {
31
+ this.config.projectId = patch.projectId;
32
+ }
33
+ if (patch.sessionId) {
34
+ this.config.sessionId = patch.sessionId;
35
+ }
36
+ if (patch.apiUrl) {
37
+ this.config.apiUrl = patch.apiUrl;
38
+ }
39
+ if (patch.publicClientToken) {
40
+ this.config.publicClientToken = patch.publicClientToken;
41
+ }
42
+ this.httpTransport?.updateSession(patch);
43
+ }
44
+ start() {
45
+ this.memory.push(...this.retry.load());
46
+ this.retry.clear();
47
+ this.timer = setInterval(() => {
48
+ void this.flush();
49
+ }, this.config.flushIntervalMs);
50
+ if (typeof window !== 'undefined') {
51
+ window.addEventListener('pagehide', this.onPageHide);
52
+ window.addEventListener('online', this.onOnline);
53
+ }
54
+ }
55
+ enqueue(event) {
56
+ if (this.memory.length >= this.config.maxQueueSize) {
57
+ this.memory.shift();
58
+ }
59
+ this.memory.push(event);
60
+ if (this.memory.length >= this.config.maxBatchSize) {
61
+ void this.flush();
62
+ }
63
+ }
64
+ async flush() {
65
+ this.inflight = this.inflight.then(() => this.flushOnce());
66
+ await this.inflight;
67
+ }
68
+ async flushOnce() {
69
+ if (this.memory.length === 0) {
70
+ return;
71
+ }
72
+ const batch = this.memory.splice(0, this.config.maxBatchSize);
73
+ await swallowAsync(async () => {
74
+ await this.transport.sendBatch(batch);
75
+ }, (error) => {
76
+ this.memory.unshift(...batch);
77
+ this.retry.save(this.memory);
78
+ this.config.onError?.(error);
79
+ });
80
+ }
81
+ async shutdown() {
82
+ if (this.timer) {
83
+ clearInterval(this.timer);
84
+ this.timer = undefined;
85
+ }
86
+ if (typeof window !== 'undefined') {
87
+ window.removeEventListener('pagehide', this.onPageHide);
88
+ window.removeEventListener('online', this.onOnline);
89
+ }
90
+ await this.flush();
91
+ await swallowAsync(() => this.transport.flush(), this.config.onError);
92
+ }
93
+ onPageHide = () => {
94
+ this.unloading = true;
95
+ if (this.memory.length === 0) {
96
+ return;
97
+ }
98
+ const batch = this.memory.splice(0, this.memory.length);
99
+ const sent = sendBeaconBatch(this.config.apiUrl, this.config.projectId, batch, this.config.publicClientToken, this.config.sessionId);
100
+ if (!sent) {
101
+ this.retry.save(batch);
102
+ }
103
+ };
104
+ onOnline = () => {
105
+ if (!this.unloading) {
106
+ void this.flush();
107
+ }
108
+ };
109
+ }
110
+ //# sourceMappingURL=BatchQueue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BatchQueue.js","sourceRoot":"","sources":["../../src/transport/BatchQueue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { QAEvent, QATransport } from '../types';
2
+ export declare class MemoryTransport implements QATransport {
3
+ readonly events: QAEvent[];
4
+ failNext: boolean;
5
+ send(event: QAEvent): Promise<void>;
6
+ sendBatch(events: QAEvent[]): Promise<void>;
7
+ flush(): Promise<void>;
8
+ }
9
+ export type HTTPTransportOptions = {
10
+ apiUrl: string;
11
+ projectId: string;
12
+ sessionId?: string;
13
+ publicClientToken?: string;
14
+ };
15
+ export declare class HTTPTransport implements QATransport {
16
+ private readonly pending;
17
+ private options;
18
+ constructor(options: HTTPTransportOptions);
19
+ updateSession(patch: Partial<HTTPTransportOptions>): void;
20
+ send(event: QAEvent): Promise<void>;
21
+ sendBatch(events: QAEvent[]): Promise<void>;
22
+ flush(): Promise<void>;
23
+ }
24
+ export declare function createSdkSession(input: {
25
+ apiUrl: string;
26
+ publishableKey: string;
27
+ initialUrl: string;
28
+ projectId?: string;
29
+ environmentId?: string;
30
+ }): Promise<{
31
+ sessionId: string;
32
+ projectId: string;
33
+ apiUrl: string;
34
+ }>;
35
+ export declare function sendBeaconBatch(apiUrl: string, projectId: string, events: QAEvent[], token?: string, sessionId?: string): boolean;
36
+ //# sourceMappingURL=HTTPTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HTTPTransport.d.ts","sourceRoot":"","sources":["../../src/transport/HTTPTransport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAErD,qBAAa,eAAgB,YAAW,WAAW;IACjD,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,CAAM;IAChC,QAAQ,UAAS;IAEX,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAInC,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,qBAAa,aAAc,YAAW,WAAW;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,OAAO,CAAuB;gBAE1B,OAAO,EAAE,oBAAoB;IAIzC,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,IAAI;IAInD,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAInC,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAO7B;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA6BpE;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,OAAO,EAAE,EACjB,KAAK,CAAC,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAyBT"}