@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,121 @@
1
+ export class MemoryTransport {
2
+ events = [];
3
+ failNext = false;
4
+ async send(event) {
5
+ await this.sendBatch([event]);
6
+ }
7
+ async sendBatch(events) {
8
+ if (this.failNext) {
9
+ this.failNext = false;
10
+ throw new Error('network unavailable');
11
+ }
12
+ this.events.push(...events);
13
+ }
14
+ async flush() {
15
+ return;
16
+ }
17
+ }
18
+ export class HTTPTransport {
19
+ pending = [];
20
+ options;
21
+ constructor(options) {
22
+ this.options = { ...options };
23
+ }
24
+ updateSession(patch) {
25
+ this.options = { ...this.options, ...patch };
26
+ }
27
+ async send(event) {
28
+ await this.sendBatch([event]);
29
+ }
30
+ async sendBatch(events) {
31
+ if (events.length === 0) {
32
+ return;
33
+ }
34
+ if (!this.options.apiUrl || !this.options.sessionId) {
35
+ this.pending.push(...events);
36
+ return;
37
+ }
38
+ const url = `${trimSlash(this.options.apiUrl)}/v1/sdk/events`;
39
+ const body = JSON.stringify({
40
+ projectId: this.options.projectId,
41
+ sessionId: this.options.sessionId,
42
+ events,
43
+ });
44
+ const headers = {
45
+ 'content-type': 'application/json',
46
+ 'x-loopcheck-project': this.options.projectId,
47
+ };
48
+ if (this.options.publicClientToken) {
49
+ headers['x-loopcheck-client-token'] = this.options.publicClientToken;
50
+ }
51
+ const response = await fetch(url, {
52
+ method: 'POST',
53
+ headers,
54
+ body,
55
+ credentials: 'omit',
56
+ keepalive: true,
57
+ });
58
+ if (!response.ok) {
59
+ throw new Error(`QA transport HTTP ${response.status}`);
60
+ }
61
+ }
62
+ async flush() {
63
+ if (this.pending.length === 0) {
64
+ return;
65
+ }
66
+ const batch = this.pending.splice(0, this.pending.length);
67
+ await this.sendBatch(batch);
68
+ }
69
+ }
70
+ export async function createSdkSession(input) {
71
+ const url = `${trimSlash(input.apiUrl)}/v1/sdk/sessions`;
72
+ const response = await fetch(url, {
73
+ method: 'POST',
74
+ headers: {
75
+ 'content-type': 'application/json',
76
+ 'x-loopcheck-client-token': input.publishableKey,
77
+ },
78
+ body: JSON.stringify({
79
+ initialUrl: input.initialUrl,
80
+ projectId: input.projectId || undefined,
81
+ environmentId: input.environmentId,
82
+ publishableKey: input.publishableKey,
83
+ }),
84
+ credentials: 'omit',
85
+ });
86
+ if (!response.ok) {
87
+ throw new Error(`QA session create HTTP ${response.status}`);
88
+ }
89
+ const data = (await response.json());
90
+ return {
91
+ sessionId: data.sessionId,
92
+ projectId: data.projectId,
93
+ apiUrl: data.apiUrl ?? input.apiUrl,
94
+ };
95
+ }
96
+ export function sendBeaconBatch(apiUrl, projectId, events, token, sessionId) {
97
+ if (typeof navigator === 'undefined' ||
98
+ typeof navigator.sendBeacon !== 'function' ||
99
+ !apiUrl) {
100
+ return false;
101
+ }
102
+ try {
103
+ const url = `${trimSlash(apiUrl)}/v1/sdk/events`;
104
+ const blob = new Blob([
105
+ JSON.stringify({
106
+ projectId,
107
+ sessionId,
108
+ publicClientToken: token,
109
+ events,
110
+ }),
111
+ ], { type: 'application/json' });
112
+ return navigator.sendBeacon(url, blob);
113
+ }
114
+ catch {
115
+ return false;
116
+ }
117
+ }
118
+ function trimSlash(value) {
119
+ return value.replace(/\/+$/, '');
120
+ }
121
+ //# sourceMappingURL=HTTPTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HTTPTransport.js","sourceRoot":"","sources":["../../src/transport/HTTPTransport.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,eAAe;IACjB,MAAM,GAAc,EAAE,CAAC;IAChC,QAAQ,GAAG,KAAK,CAAC;IAEjB,KAAK,CAAC,IAAI,CAAC,KAAc;QACvB,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,MAAiB;QAC/B,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,OAAO;IACT,CAAC;CACF;AASD,MAAM,OAAO,aAAa;IACP,OAAO,GAAc,EAAE,CAAC;IACjC,OAAO,CAAuB;IAEtC,YAAY,OAA6B;QACvC,IAAI,CAAC,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;IAChC,CAAC;IAED,aAAa,CAAC,KAAoC;QAChD,IAAI,CAAC,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,KAAc;QACvB,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,MAAiB;QAC/B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACpD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;YAC7B,OAAO;QACT,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC;QAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;YAC1B,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;YACjC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;YACjC,MAAM;SACP,CAAC,CAAC;QACH,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,qBAAqB,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;SAC9C,CAAC;QACF,IAAI,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC;YACnC,OAAO,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC;QACvE,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,MAAM;YACd,OAAO;YACP,IAAI;YACJ,WAAW,EAAE,MAAM;YACnB,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1D,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAMtC;IACC,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,kBAAkB,CAAC;IACzD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,0BAA0B,EAAE,KAAK,CAAC,cAAc;SACjD;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,SAAS;YACvC,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,cAAc,EAAE,KAAK,CAAC,cAAc;SACrC,CAAC;QACF,WAAW,EAAE,MAAM;KACpB,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAIlC,CAAC;IACF,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,MAAc,EACd,SAAiB,EACjB,MAAiB,EACjB,KAAc,EACd,SAAkB;IAElB,IACE,OAAO,SAAS,KAAK,WAAW;QAChC,OAAO,SAAS,CAAC,UAAU,KAAK,UAAU;QAC1C,CAAC,MAAM,EACP,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,gBAAgB,CAAC;QACjD,MAAM,IAAI,GAAG,IAAI,IAAI,CACnB;YACE,IAAI,CAAC,SAAS,CAAC;gBACb,SAAS;gBACT,SAAS;gBACT,iBAAiB,EAAE,KAAK;gBACxB,MAAM;aACP,CAAC;SACH,EACD,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAC7B,CAAC;QACF,OAAO,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { QAEvent } from '../types';
2
+ export declare class RetryQueue {
3
+ private readonly storageKey;
4
+ private readonly maxBytes;
5
+ constructor(storageKey: string, maxBytes: number);
6
+ load(): QAEvent[];
7
+ save(events: QAEvent[]): void;
8
+ clear(): void;
9
+ }
10
+ //# sourceMappingURL=RetryQueue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RetryQueue.d.ts","sourceRoot":"","sources":["../../src/transport/RetryQueue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAOxC,qBAAa,UAAU;IAEnB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM;IAGnC,IAAI,IAAI,OAAO,EAAE;IAajB,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI;IAyB7B,KAAK,IAAI,IAAI;CAUd"}
@@ -0,0 +1,60 @@
1
+ import { isBrowser, safeJsonParse } from '../utils/id';
2
+ export class RetryQueue {
3
+ storageKey;
4
+ maxBytes;
5
+ constructor(storageKey, maxBytes) {
6
+ this.storageKey = storageKey;
7
+ this.maxBytes = maxBytes;
8
+ }
9
+ load() {
10
+ if (!isBrowser()) {
11
+ return [];
12
+ }
13
+ try {
14
+ const raw = window.localStorage.getItem(this.storageKey);
15
+ const parsed = safeJsonParse(raw);
16
+ return Array.isArray(parsed?.events) ? parsed.events.slice(0, 200) : [];
17
+ }
18
+ catch {
19
+ return [];
20
+ }
21
+ }
22
+ save(events) {
23
+ if (!isBrowser()) {
24
+ return;
25
+ }
26
+ try {
27
+ let next = events.slice(-200);
28
+ let serialized = JSON.stringify({ events: next });
29
+ while (serialized.length > this.maxBytes && next.length > 0) {
30
+ next = next.slice(Math.ceil(next.length / 2));
31
+ serialized = JSON.stringify({ events: next });
32
+ }
33
+ if (serialized.length > this.maxBytes) {
34
+ window.localStorage.removeItem(this.storageKey);
35
+ return;
36
+ }
37
+ window.localStorage.setItem(this.storageKey, serialized);
38
+ }
39
+ catch {
40
+ try {
41
+ window.localStorage.removeItem(this.storageKey);
42
+ }
43
+ catch {
44
+ // ignore quota
45
+ }
46
+ }
47
+ }
48
+ clear() {
49
+ if (!isBrowser()) {
50
+ return;
51
+ }
52
+ try {
53
+ window.localStorage.removeItem(this.storageKey);
54
+ }
55
+ catch {
56
+ // ignore
57
+ }
58
+ }
59
+ }
60
+ //# sourceMappingURL=RetryQueue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RetryQueue.js","sourceRoot":"","sources":["../../src/transport/RetryQueue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMvD,MAAM,OAAO,UAAU;IAEF;IACA;IAFnB,YACmB,UAAkB,EAClB,QAAgB;QADhB,eAAU,GAAV,UAAU,CAAQ;QAClB,aAAQ,GAAR,QAAQ,CAAQ;IAChC,CAAC;IAEJ,IAAI;QACF,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACjB,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACzD,MAAM,MAAM,GAAG,aAAa,CAAiB,GAAG,CAAC,CAAC;YAClD,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,IAAI,CAAC,MAAiB;QACpB,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,IAAI,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;YAC9B,IAAI,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,OAAO,UAAU,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5D,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC9C,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAChD,CAAC;YACD,IAAI,UAAU,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACtC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAChD,OAAO;YACT,CAAC;YACD,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC;gBACH,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAClD,CAAC;YAAC,MAAM,CAAC;gBACP,eAAe;YACjB,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClD,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,147 @@
1
+ export declare const QA_SCHEMA_VERSION: "1";
2
+ export type QASchemaVersion = typeof QA_SCHEMA_VERSION;
3
+ export declare const QA_ID_ATTRIBUTE = "data-qa-id";
4
+ export declare const QA_SOURCE_ATTRIBUTE = "data-qa-source";
5
+ export declare const QA_SENSITIVE_ATTRIBUTE = "data-qa-sensitive";
6
+ export declare const QA_TESTID_ATTRIBUTE = "data-testid";
7
+ export declare const REDACTED_VALUE = "[REDACTED]";
8
+ export type QAMode = 'disabled' | 'observe' | 'record';
9
+ export type QAConfidence = 'exact' | 'high' | 'medium' | 'low';
10
+ export type QAEventType = 'navigation' | 'click' | 'dblclick' | 'input' | 'change' | 'submit' | 'select' | 'focus' | 'blur' | 'scroll' | 'drag' | 'drop' | 'hover' | 'keydown' | 'assertion';
11
+ export interface QASourceMeta {
12
+ file?: string;
13
+ line?: number;
14
+ column?: number;
15
+ component?: string;
16
+ }
17
+ export interface QAElementIdentity {
18
+ qaId?: string;
19
+ tagName: string;
20
+ role?: string;
21
+ accessibleName?: string;
22
+ text?: string;
23
+ name?: string;
24
+ id?: string;
25
+ href?: string;
26
+ type?: string;
27
+ source?: QASourceMeta;
28
+ parent?: Pick<QAElementIdentity, 'qaId' | 'tagName' | 'role' | 'name'>;
29
+ attributes?: Record<string, string>;
30
+ fingerprint: string;
31
+ }
32
+ export interface QAElementResolution {
33
+ identity: QAElementIdentity;
34
+ confidence: QAConfidence;
35
+ reasons: string[];
36
+ }
37
+ export interface QAPageContext {
38
+ url: string;
39
+ pathname: string;
40
+ title?: string;
41
+ }
42
+ export interface QAEvent {
43
+ id: string;
44
+ schemaVersion: QASchemaVersion;
45
+ timestamp: number;
46
+ type: QAEventType;
47
+ target?: QAElementIdentity;
48
+ page: QAPageContext;
49
+ metadata?: Record<string, unknown>;
50
+ }
51
+ export interface QARecording {
52
+ id: string;
53
+ schemaVersion: QASchemaVersion;
54
+ startedAt: number;
55
+ endedAt?: number;
56
+ initialUrl: string;
57
+ events: QAEvent[];
58
+ }
59
+ export interface QAError {
60
+ name: string;
61
+ message: string;
62
+ cause?: unknown;
63
+ }
64
+ export interface QATransport {
65
+ send(event: QAEvent): Promise<void>;
66
+ sendBatch(events: QAEvent[]): Promise<void>;
67
+ flush(): Promise<void>;
68
+ }
69
+ export type QAListener<T> = (payload: T) => void;
70
+ export interface QAClientEventMap {
71
+ 'recording:start': QARecording;
72
+ 'recording:stop': QARecording;
73
+ 'recording:pause': null;
74
+ 'recording:resume': null;
75
+ event: QAEvent;
76
+ 'element:resolved': QAElementResolution;
77
+ 'duplicate-id': {
78
+ qaId: string;
79
+ sources: string[];
80
+ };
81
+ error: QAError;
82
+ }
83
+ export type QAStartOn = 'manual' | 'query';
84
+ export interface QAConfig {
85
+ /** Project UUID. Optional when using a publishable key (resolved on session create). */
86
+ projectId?: string;
87
+ sessionId?: string;
88
+ environment?: string;
89
+ apiUrl?: string;
90
+ /** Durable browser key (`lc_pk_…`). Preferred for customer installs. */
91
+ publishableKey?: string;
92
+ /** @deprecated Prefer `publishableKey`. Still used by the extension session flow. */
93
+ publicClientToken?: string;
94
+ enabled?: boolean;
95
+ mode?: QAMode;
96
+ /**
97
+ * When to arm recording.
98
+ * - `manual` (default): stay inert until `startRecording()`.
99
+ * - `query`: auto-start when the URL has `?lc_record=1`.
100
+ */
101
+ startOn?: QAStartOn;
102
+ debug?: boolean;
103
+ interactiveSelectors?: string[];
104
+ redactSelectors?: string[];
105
+ allowUnstableIds?: boolean;
106
+ recordHover?: boolean;
107
+ recordInputValues?: boolean;
108
+ flushIntervalMs?: number;
109
+ maxBatchSize?: number;
110
+ maxQueueSize?: number;
111
+ maxStorageBytes?: number;
112
+ transport?: QATransport;
113
+ onError?: (error: QAError) => void;
114
+ }
115
+ export interface ResolvedQAConfig {
116
+ projectId: string;
117
+ sessionId?: string;
118
+ environment: string;
119
+ apiUrl: string;
120
+ publishableKey?: string;
121
+ publicClientToken?: string;
122
+ enabled: boolean;
123
+ mode: QAMode;
124
+ startOn: QAStartOn;
125
+ debug: boolean;
126
+ interactiveSelectors: string[];
127
+ redactSelectors: string[];
128
+ allowUnstableIds: boolean;
129
+ recordHover: boolean;
130
+ recordInputValues: boolean;
131
+ flushIntervalMs: number;
132
+ maxBatchSize: number;
133
+ maxQueueSize: number;
134
+ maxStorageBytes: number;
135
+ transport?: QATransport;
136
+ onError?: (error: QAError) => void;
137
+ }
138
+ export interface QARecordingControls {
139
+ startRecording: () => void;
140
+ stopRecording: () => QARecording | null;
141
+ pauseRecording: () => void;
142
+ resumeRecording: () => void;
143
+ isRecording: boolean;
144
+ isPaused: boolean;
145
+ }
146
+ export declare const DEFAULT_INTERACTIVE_SELECTORS: readonly ["button", "a", "input", "textarea", "select", "option", "label", "[role=\"button\"]", "[role=\"link\"]", "[role=\"checkbox\"]", "[role=\"radio\"]", "[role=\"tab\"]", "[role=\"menuitem\"]", "[contenteditable=\"true\"]", "[contenteditable=\"\"]"];
147
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export const QA_SCHEMA_VERSION = '1';
2
+ export const QA_ID_ATTRIBUTE = 'data-qa-id';
3
+ export const QA_SOURCE_ATTRIBUTE = 'data-qa-source';
4
+ export const QA_SENSITIVE_ATTRIBUTE = 'data-qa-sensitive';
5
+ export const QA_TESTID_ATTRIBUTE = 'data-testid';
6
+ export const REDACTED_VALUE = '[REDACTED]';
7
+ export const DEFAULT_INTERACTIVE_SELECTORS = [
8
+ 'button',
9
+ 'a',
10
+ 'input',
11
+ 'textarea',
12
+ 'select',
13
+ 'option',
14
+ 'label',
15
+ '[role="button"]',
16
+ '[role="link"]',
17
+ '[role="checkbox"]',
18
+ '[role="radio"]',
19
+ '[role="tab"]',
20
+ '[role="menuitem"]',
21
+ '[contenteditable="true"]',
22
+ '[contenteditable=""]',
23
+ ];
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAY,CAAC;AAG9C,MAAM,CAAC,MAAM,eAAe,GAAG,YAAY,CAAC;AAC5C,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AACpD,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,aAAa,CAAC;AAEjD,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAC;AAwK3C,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,QAAQ;IACR,GAAG;IACH,OAAO;IACP,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,iBAAiB;IACjB,eAAe;IACf,mBAAmB;IACnB,gBAAgB;IAChB,cAAc;IACd,mBAAmB;IACnB,0BAA0B;IAC1B,sBAAsB;CACd,CAAC"}
@@ -0,0 +1,24 @@
1
+ export declare function isUnstableDomId(id: string | undefined): boolean;
2
+ export declare function sanitizeClassName(className: string | undefined): string | undefined;
3
+ export declare function pathnameFromHref(href: string | undefined): string | undefined;
4
+ export declare function readAttr(element: Element, name: string): string | undefined;
5
+ export declare function closestElement(target: EventTarget | null): Element | null;
6
+ /** Prefer composedPath so events inside open shadow roots still resolve. */
7
+ export declare function eventTargetElement(event: Event): Element | null;
8
+ /** Same-origin iframe selector for frameLocator, or null when top-level / cross-origin. */
9
+ export declare function frameContext(): {
10
+ inIframe: boolean;
11
+ frame?: string;
12
+ crossOriginFrame?: boolean;
13
+ } | null;
14
+ export declare function isSafeSelector(selector: string): boolean;
15
+ export declare function matchesAny(element: Element, selectors: string[]): boolean;
16
+ export declare function closestInteractive(element: Element, selectors: string[]): Element | null;
17
+ export declare function getPageContext(): {
18
+ url: string;
19
+ pathname: string;
20
+ title?: string;
21
+ };
22
+ /** The recorder's own UI (extension HUD, hosted badge) is never a test step. */
23
+ export declare function isRecorderUi(element: Element): boolean;
24
+ //# sourceMappingURL=dom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/utils/dom.ts"],"names":[],"mappings":"AAKA,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAK/D;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAQnF;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAa7E;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG3E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,GAAG,IAAI,CAWzE;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,GAAG,IAAI,CAS/D;AAED,2FAA2F;AAC3F,wBAAgB,YAAY,IAAI;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,GAAG,IAAI,CAiCP;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAaxD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAczE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,GAAG,IAAI,CASxF;AAED,wBAAgB,cAAc,IAAI;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CASlF;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAItD"}
@@ -0,0 +1,152 @@
1
+ const UNSTABLE_ID = /^(?:react-aria|ember\d+|:r[\w-]+:)/i;
2
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
3
+ const CSS_MODULE = /_[A-Z0-9]{5,}$/;
4
+ const HASH_CLASS = /(?:^| )(?:css-|jsx-|sc-)[a-z0-9_-]+/i;
5
+ export function isUnstableDomId(id) {
6
+ if (!id) {
7
+ return true;
8
+ }
9
+ return UNSTABLE_ID.test(id) || UUID.test(id);
10
+ }
11
+ export function sanitizeClassName(className) {
12
+ if (!className) {
13
+ return undefined;
14
+ }
15
+ const tokens = className
16
+ .split(/\s+/)
17
+ .filter((token) => token && !CSS_MODULE.test(token) && !HASH_CLASS.test(token) && token.length < 40);
18
+ return tokens.length > 0 ? tokens.slice(0, 3).join(' ') : undefined;
19
+ }
20
+ export function pathnameFromHref(href) {
21
+ if (!href) {
22
+ return undefined;
23
+ }
24
+ try {
25
+ if (href.startsWith('/') || href.startsWith('#') || href.startsWith('?')) {
26
+ return href.split('?')[0]?.split('#')[0];
27
+ }
28
+ const url = new URL(href, 'https://loopcheck.invalid');
29
+ return `${url.pathname}`;
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ }
35
+ export function readAttr(element, name) {
36
+ const value = element.getAttribute(name);
37
+ return value && value.trim() ? value.trim() : undefined;
38
+ }
39
+ export function closestElement(target) {
40
+ if (!target) {
41
+ return null;
42
+ }
43
+ if (target instanceof Element) {
44
+ return target;
45
+ }
46
+ if (target instanceof Text) {
47
+ return target.parentElement;
48
+ }
49
+ return null;
50
+ }
51
+ /** Prefer composedPath so events inside open shadow roots still resolve. */
52
+ export function eventTargetElement(event) {
53
+ if (typeof event.composedPath === 'function') {
54
+ for (const node of event.composedPath()) {
55
+ if (node instanceof Element) {
56
+ return node;
57
+ }
58
+ }
59
+ }
60
+ return closestElement(event.target);
61
+ }
62
+ /** Same-origin iframe selector for frameLocator, or null when top-level / cross-origin. */
63
+ export function frameContext() {
64
+ if (typeof window === 'undefined') {
65
+ return null;
66
+ }
67
+ if (window === window.top) {
68
+ return null;
69
+ }
70
+ try {
71
+ const frameEl = window.frameElement;
72
+ if (!(frameEl instanceof HTMLIFrameElement)) {
73
+ return { inIframe: true };
74
+ }
75
+ if (frameEl.name) {
76
+ return { inIframe: true, frame: `iframe[name="${frameEl.name}"]` };
77
+ }
78
+ if (frameEl.id && !isUnstableDomId(frameEl.id)) {
79
+ const escaped = typeof CSS !== 'undefined' && typeof CSS.escape === 'function'
80
+ ? CSS.escape(frameEl.id)
81
+ : frameEl.id.replace(/([^\w-])/g, '\\$1');
82
+ return { inIframe: true, frame: `iframe#${escaped}` };
83
+ }
84
+ const title = frameEl.getAttribute('title');
85
+ if (title) {
86
+ return {
87
+ inIframe: true,
88
+ frame: `iframe[title="${title.replace(/"/g, '\\"')}"]`,
89
+ };
90
+ }
91
+ return { inIframe: true, frame: 'iframe' };
92
+ }
93
+ catch {
94
+ return { inIframe: true, crossOriginFrame: true };
95
+ }
96
+ }
97
+ export function isSafeSelector(selector) {
98
+ if (!selector || selector.length > 200) {
99
+ return false;
100
+ }
101
+ if (/javascript:|expression\(|@import/i.test(selector)) {
102
+ return false;
103
+ }
104
+ try {
105
+ document.createDocumentFragment().querySelector(selector);
106
+ return true;
107
+ }
108
+ catch {
109
+ return false;
110
+ }
111
+ }
112
+ export function matchesAny(element, selectors) {
113
+ for (const selector of selectors) {
114
+ if (!isSafeSelector(selector)) {
115
+ continue;
116
+ }
117
+ try {
118
+ if (element.matches(selector)) {
119
+ return true;
120
+ }
121
+ }
122
+ catch {
123
+ // Invalid selector in this document — skip.
124
+ }
125
+ }
126
+ return false;
127
+ }
128
+ export function closestInteractive(element, selectors) {
129
+ let current = element;
130
+ while (current) {
131
+ if (matchesAny(current, selectors)) {
132
+ return current;
133
+ }
134
+ current = current.parentElement;
135
+ }
136
+ return null;
137
+ }
138
+ export function getPageContext() {
139
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
140
+ return { url: '', pathname: '/' };
141
+ }
142
+ return {
143
+ url: window.location.href,
144
+ pathname: window.location.pathname || '/',
145
+ title: document.title || undefined,
146
+ };
147
+ }
148
+ /** The recorder's own UI (extension HUD, hosted badge) is never a test step. */
149
+ export function isRecorderUi(element) {
150
+ return Boolean(element.closest('[data-loopcheck-ui],[data-loopcheck-recorder]'));
151
+ }
152
+ //# sourceMappingURL=dom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom.js","sourceRoot":"","sources":["../../src/utils/dom.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function isNonProduction(): boolean;
2
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/utils/env.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,IAAI,OAAO,CAIzC"}
@@ -0,0 +1,6 @@
1
+ export function isNonProduction() {
2
+ const env = globalThis.process
3
+ ?.env?.NODE_ENV;
4
+ return env === 'development' || env === 'test';
5
+ }
6
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/utils/env.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,eAAe;IAC7B,MAAM,GAAG,GAAI,UAA4D,CAAC,OAAO;QAC/E,EAAE,GAAG,EAAE,QAAQ,CAAC;IAClB,OAAO,GAAG,KAAK,aAAa,IAAI,GAAG,KAAK,MAAM,CAAC;AACjD,CAAC"}
@@ -0,0 +1,13 @@
1
+ export declare function isBrowser(): boolean;
2
+ export declare function safeNow(): number;
3
+ export declare function createId(prefix?: string): string;
4
+ export declare function fingerprintHash(parts: Array<string | undefined | null>): string;
5
+ export declare function clampText(value: string | undefined, max?: number): string | undefined;
6
+ export declare function parseSourceMeta(raw: string | null): {
7
+ file?: string;
8
+ line?: number;
9
+ column?: number;
10
+ component?: string;
11
+ } | undefined;
12
+ export declare function safeJsonParse<T>(value: string | null): T | null;
13
+ //# sourceMappingURL=id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,wBAAgB,QAAQ,CAAC,MAAM,SAAO,GAAG,MAAM,CAc9C;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,GAAG,MAAM,CAQ/E;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,SAAK,GAAG,MAAM,GAAG,SAAS,CASjF;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,SAAS,CAoBZ;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,CAS/D"}