@peak-ai/canvas 1.4.21 → 1.4.22

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 (213) hide show
  1. package/.babelrc +14 -0
  2. package/.eslintcache +1 -0
  3. package/.eslintignore +5 -0
  4. package/.eslintrc.js +29 -0
  5. package/{GrapesjsCanvas.js → dist/GrapesjsCanvas.js} +3 -2
  6. package/dist/GrapesjsCanvas.js.map +1 -0
  7. package/dist/package.json +62 -0
  8. package/{plugins → dist/plugins}/helpers/render-components.js +1 -1
  9. package/dist/plugins/helpers/render-components.js.map +1 -0
  10. package/dist/shadcn/components/ui/error-wrapper.js +2 -0
  11. package/dist/shadcn/components/ui/error-wrapper.js.map +1 -0
  12. package/package.json +45 -7
  13. package/scripts/build.ts +120 -0
  14. package/src/GrapesjsCanvas.tsx +494 -0
  15. package/src/constants/index.ts +25 -0
  16. package/src/declaration.d.ts +1 -0
  17. package/src/helpers/compiled-table.css +2429 -0
  18. package/src/helpers/css.ts +2667 -0
  19. package/src/helpers/date-picker.ts +807 -0
  20. package/src/helpers/filter-placeholder.ts +18 -0
  21. package/src/helpers/index.ts +13 -0
  22. package/src/helpers/merge-json.ts +106 -0
  23. package/src/index.styles.ts +58 -0
  24. package/src/index.ts +9 -0
  25. package/src/plugins/grapejs-plugin.tsx +196 -0
  26. package/src/plugins/helpers/custom-modal.tsx +123 -0
  27. package/src/plugins/helpers/data-table.tsx +300 -0
  28. package/src/plugins/helpers/extra.tsx +164 -0
  29. package/src/plugins/helpers/query-cache-context.tsx +154 -0
  30. package/src/plugins/helpers/query-cache-singleton.ts +176 -0
  31. package/src/plugins/helpers/query-cache-utils.ts +226 -0
  32. package/src/plugins/helpers/query-details-modal.tsx +400 -0
  33. package/src/plugins/helpers/query-heading-formatter.ts +24 -0
  34. package/src/plugins/helpers/query-loading-modal.tsx +94 -0
  35. package/src/plugins/helpers/render-components.tsx +1450 -0
  36. package/src/plugins/helpers/styled-info-button.tsx +504 -0
  37. package/src/public/canvas.css +42 -0
  38. package/src/public/components-css/table/table-output.css +2436 -0
  39. package/src/public/components-css/table/table.css +30 -0
  40. package/src/public/output.css +2465 -0
  41. package/src/public/table.css +135 -0
  42. package/src/shadcn/components/icons/AiAvatarIcon.tsx +47 -0
  43. package/src/shadcn/components/icons/Co_driver Expanding button copy.svg +21 -0
  44. package/src/shadcn/components/icons/ai-avatar.svg +7 -0
  45. package/src/shadcn/components/icons/thinking.gif +0 -0
  46. package/src/shadcn/components/ui/button.tsx +132 -0
  47. package/src/shadcn/components/ui/card.tsx +92 -0
  48. package/src/shadcn/components/ui/chart.tsx +324 -0
  49. package/src/shadcn/components/ui/checkbox.tsx +27 -0
  50. package/src/shadcn/components/ui/component-wrapper.tsx +61 -0
  51. package/src/shadcn/components/ui/date-filter.tsx +816 -0
  52. package/src/shadcn/components/ui/error-container.tsx +125 -0
  53. package/src/shadcn/components/ui/error-wrapper.tsx +99 -0
  54. package/src/shadcn/components/ui/filter.tsx +368 -0
  55. package/src/shadcn/components/ui/hover-card.tsx +36 -0
  56. package/src/shadcn/components/ui/input.tsx +20 -0
  57. package/src/shadcn/components/ui/label.tsx +24 -0
  58. package/src/shadcn/components/ui/pagination.tsx +213 -0
  59. package/src/shadcn/components/ui/scroll-area.tsx +59 -0
  60. package/src/shadcn/components/ui/search.tsx +150 -0
  61. package/src/shadcn/components/ui/separator.tsx +26 -0
  62. package/src/shadcn/components/ui/skeleton.tsx +69 -0
  63. package/src/shadcn/components/ui/table.tsx +196 -0
  64. package/src/shadcn/components/ui/tabs.tsx +55 -0
  65. package/src/shadcn/components/ui/textarea.tsx +18 -0
  66. package/src/shadcn/components/ui/tooltip.tsx +87 -0
  67. package/src/shadcn/utils.ts +6 -0
  68. package/src/types/grapesjs-tailwind.d.ts +61 -0
  69. package/src/types/images.d.ts +1 -0
  70. package/tailwind.config.js +5 -0
  71. package/tooling/tailwind-compiler/index.js +99 -0
  72. package/tooling/tailwind-compiler/package.json +11 -0
  73. package/tooling/tailwind-compiler/yarn.lock +123 -0
  74. package/tsconfig.build.json +15 -0
  75. package/tsconfig.json +8 -0
  76. package/GrapesjsCanvas.js.map +0 -1
  77. package/plugins/helpers/render-components.js.map +0 -1
  78. package/shadcn/components/ui/error-wrapper.js +0 -2
  79. package/shadcn/components/ui/error-wrapper.js.map +0 -1
  80. /package/{GrapesjsCanvas.d.ts → dist/GrapesjsCanvas.d.ts} +0 -0
  81. /package/{constants → dist/constants}/index.d.ts +0 -0
  82. /package/{constants → dist/constants}/index.js +0 -0
  83. /package/{constants → dist/constants}/index.js.map +0 -0
  84. /package/{declaration.d.js → dist/declaration.d.js} +0 -0
  85. /package/{declaration.d.js.map → dist/declaration.d.js.map} +0 -0
  86. /package/{helpers → dist/helpers}/compiled-table.css +0 -0
  87. /package/{helpers → dist/helpers}/css.d.ts +0 -0
  88. /package/{helpers → dist/helpers}/css.js +0 -0
  89. /package/{helpers → dist/helpers}/css.js.map +0 -0
  90. /package/{helpers → dist/helpers}/date-picker.d.ts +0 -0
  91. /package/{helpers → dist/helpers}/date-picker.js +0 -0
  92. /package/{helpers → dist/helpers}/date-picker.js.map +0 -0
  93. /package/{helpers → dist/helpers}/filter-placeholder.d.ts +0 -0
  94. /package/{helpers → dist/helpers}/filter-placeholder.js +0 -0
  95. /package/{helpers → dist/helpers}/filter-placeholder.js.map +0 -0
  96. /package/{helpers → dist/helpers}/index.d.ts +0 -0
  97. /package/{helpers → dist/helpers}/index.js +0 -0
  98. /package/{helpers → dist/helpers}/index.js.map +0 -0
  99. /package/{helpers → dist/helpers}/merge-json.d.ts +0 -0
  100. /package/{helpers → dist/helpers}/merge-json.js +0 -0
  101. /package/{helpers → dist/helpers}/merge-json.js.map +0 -0
  102. /package/{index.d.ts → dist/index.d.ts} +0 -0
  103. /package/{index.js → dist/index.js} +0 -0
  104. /package/{index.js.map → dist/index.js.map} +0 -0
  105. /package/{index.styles.d.ts → dist/index.styles.d.ts} +0 -0
  106. /package/{index.styles.js → dist/index.styles.js} +0 -0
  107. /package/{index.styles.js.map → dist/index.styles.js.map} +0 -0
  108. /package/{plugins → dist/plugins}/grapejs-plugin.d.ts +0 -0
  109. /package/{plugins → dist/plugins}/grapejs-plugin.js +0 -0
  110. /package/{plugins → dist/plugins}/grapejs-plugin.js.map +0 -0
  111. /package/{plugins → dist/plugins}/helpers/custom-modal.d.ts +0 -0
  112. /package/{plugins → dist/plugins}/helpers/custom-modal.js +0 -0
  113. /package/{plugins → dist/plugins}/helpers/custom-modal.js.map +0 -0
  114. /package/{plugins → dist/plugins}/helpers/data-table.d.ts +0 -0
  115. /package/{plugins → dist/plugins}/helpers/data-table.js +0 -0
  116. /package/{plugins → dist/plugins}/helpers/data-table.js.map +0 -0
  117. /package/{plugins → dist/plugins}/helpers/extra.d.ts +0 -0
  118. /package/{plugins → dist/plugins}/helpers/extra.js +0 -0
  119. /package/{plugins → dist/plugins}/helpers/extra.js.map +0 -0
  120. /package/{plugins → dist/plugins}/helpers/query-cache-context.d.ts +0 -0
  121. /package/{plugins → dist/plugins}/helpers/query-cache-context.js +0 -0
  122. /package/{plugins → dist/plugins}/helpers/query-cache-context.js.map +0 -0
  123. /package/{plugins → dist/plugins}/helpers/query-cache-singleton.d.ts +0 -0
  124. /package/{plugins → dist/plugins}/helpers/query-cache-singleton.js +0 -0
  125. /package/{plugins → dist/plugins}/helpers/query-cache-singleton.js.map +0 -0
  126. /package/{plugins → dist/plugins}/helpers/query-cache-utils.d.ts +0 -0
  127. /package/{plugins → dist/plugins}/helpers/query-cache-utils.js +0 -0
  128. /package/{plugins → dist/plugins}/helpers/query-cache-utils.js.map +0 -0
  129. /package/{plugins → dist/plugins}/helpers/query-details-modal.d.ts +0 -0
  130. /package/{plugins → dist/plugins}/helpers/query-details-modal.js +0 -0
  131. /package/{plugins → dist/plugins}/helpers/query-details-modal.js.map +0 -0
  132. /package/{plugins → dist/plugins}/helpers/query-heading-formatter.d.ts +0 -0
  133. /package/{plugins → dist/plugins}/helpers/query-heading-formatter.js +0 -0
  134. /package/{plugins → dist/plugins}/helpers/query-heading-formatter.js.map +0 -0
  135. /package/{plugins → dist/plugins}/helpers/query-loading-modal.d.ts +0 -0
  136. /package/{plugins → dist/plugins}/helpers/query-loading-modal.js +0 -0
  137. /package/{plugins → dist/plugins}/helpers/query-loading-modal.js.map +0 -0
  138. /package/{plugins → dist/plugins}/helpers/render-components.d.ts +0 -0
  139. /package/{plugins → dist/plugins}/helpers/styled-info-button.d.ts +0 -0
  140. /package/{plugins → dist/plugins}/helpers/styled-info-button.js +0 -0
  141. /package/{plugins → dist/plugins}/helpers/styled-info-button.js.map +0 -0
  142. /package/{shadcn → dist/shadcn}/components/icons/AiAvatarIcon.d.ts +0 -0
  143. /package/{shadcn → dist/shadcn}/components/icons/AiAvatarIcon.js +0 -0
  144. /package/{shadcn → dist/shadcn}/components/icons/AiAvatarIcon.js.map +0 -0
  145. /package/{shadcn → dist/shadcn}/components/icons/thinking.gif +0 -0
  146. /package/{shadcn → dist/shadcn}/components/ui/button.d.ts +0 -0
  147. /package/{shadcn → dist/shadcn}/components/ui/button.js +0 -0
  148. /package/{shadcn → dist/shadcn}/components/ui/button.js.map +0 -0
  149. /package/{shadcn → dist/shadcn}/components/ui/card.d.ts +0 -0
  150. /package/{shadcn → dist/shadcn}/components/ui/card.js +0 -0
  151. /package/{shadcn → dist/shadcn}/components/ui/card.js.map +0 -0
  152. /package/{shadcn → dist/shadcn}/components/ui/chart.d.ts +0 -0
  153. /package/{shadcn → dist/shadcn}/components/ui/chart.js +0 -0
  154. /package/{shadcn → dist/shadcn}/components/ui/chart.js.map +0 -0
  155. /package/{shadcn → dist/shadcn}/components/ui/checkbox.d.ts +0 -0
  156. /package/{shadcn → dist/shadcn}/components/ui/checkbox.js +0 -0
  157. /package/{shadcn → dist/shadcn}/components/ui/checkbox.js.map +0 -0
  158. /package/{shadcn → dist/shadcn}/components/ui/component-wrapper.d.ts +0 -0
  159. /package/{shadcn → dist/shadcn}/components/ui/component-wrapper.js +0 -0
  160. /package/{shadcn → dist/shadcn}/components/ui/component-wrapper.js.map +0 -0
  161. /package/{shadcn → dist/shadcn}/components/ui/date-filter.d.ts +0 -0
  162. /package/{shadcn → dist/shadcn}/components/ui/date-filter.js +0 -0
  163. /package/{shadcn → dist/shadcn}/components/ui/date-filter.js.map +0 -0
  164. /package/{shadcn → dist/shadcn}/components/ui/error-container.d.ts +0 -0
  165. /package/{shadcn → dist/shadcn}/components/ui/error-container.js +0 -0
  166. /package/{shadcn → dist/shadcn}/components/ui/error-container.js.map +0 -0
  167. /package/{shadcn → dist/shadcn}/components/ui/error-wrapper.d.ts +0 -0
  168. /package/{shadcn → dist/shadcn}/components/ui/filter.d.ts +0 -0
  169. /package/{shadcn → dist/shadcn}/components/ui/filter.js +0 -0
  170. /package/{shadcn → dist/shadcn}/components/ui/filter.js.map +0 -0
  171. /package/{shadcn → dist/shadcn}/components/ui/hover-card.d.ts +0 -0
  172. /package/{shadcn → dist/shadcn}/components/ui/hover-card.js +0 -0
  173. /package/{shadcn → dist/shadcn}/components/ui/hover-card.js.map +0 -0
  174. /package/{shadcn → dist/shadcn}/components/ui/input.d.ts +0 -0
  175. /package/{shadcn → dist/shadcn}/components/ui/input.js +0 -0
  176. /package/{shadcn → dist/shadcn}/components/ui/input.js.map +0 -0
  177. /package/{shadcn → dist/shadcn}/components/ui/label.d.ts +0 -0
  178. /package/{shadcn → dist/shadcn}/components/ui/label.js +0 -0
  179. /package/{shadcn → dist/shadcn}/components/ui/label.js.map +0 -0
  180. /package/{shadcn → dist/shadcn}/components/ui/pagination.d.ts +0 -0
  181. /package/{shadcn → dist/shadcn}/components/ui/pagination.js +0 -0
  182. /package/{shadcn → dist/shadcn}/components/ui/pagination.js.map +0 -0
  183. /package/{shadcn → dist/shadcn}/components/ui/scroll-area.d.ts +0 -0
  184. /package/{shadcn → dist/shadcn}/components/ui/scroll-area.js +0 -0
  185. /package/{shadcn → dist/shadcn}/components/ui/scroll-area.js.map +0 -0
  186. /package/{shadcn → dist/shadcn}/components/ui/search.d.ts +0 -0
  187. /package/{shadcn → dist/shadcn}/components/ui/search.js +0 -0
  188. /package/{shadcn → dist/shadcn}/components/ui/search.js.map +0 -0
  189. /package/{shadcn → dist/shadcn}/components/ui/separator.d.ts +0 -0
  190. /package/{shadcn → dist/shadcn}/components/ui/separator.js +0 -0
  191. /package/{shadcn → dist/shadcn}/components/ui/separator.js.map +0 -0
  192. /package/{shadcn → dist/shadcn}/components/ui/skeleton.d.ts +0 -0
  193. /package/{shadcn → dist/shadcn}/components/ui/skeleton.js +0 -0
  194. /package/{shadcn → dist/shadcn}/components/ui/skeleton.js.map +0 -0
  195. /package/{shadcn → dist/shadcn}/components/ui/table.d.ts +0 -0
  196. /package/{shadcn → dist/shadcn}/components/ui/table.js +0 -0
  197. /package/{shadcn → dist/shadcn}/components/ui/table.js.map +0 -0
  198. /package/{shadcn → dist/shadcn}/components/ui/tabs.d.ts +0 -0
  199. /package/{shadcn → dist/shadcn}/components/ui/tabs.js +0 -0
  200. /package/{shadcn → dist/shadcn}/components/ui/tabs.js.map +0 -0
  201. /package/{shadcn → dist/shadcn}/components/ui/textarea.d.ts +0 -0
  202. /package/{shadcn → dist/shadcn}/components/ui/textarea.js +0 -0
  203. /package/{shadcn → dist/shadcn}/components/ui/textarea.js.map +0 -0
  204. /package/{shadcn → dist/shadcn}/components/ui/tooltip.d.ts +0 -0
  205. /package/{shadcn → dist/shadcn}/components/ui/tooltip.js +0 -0
  206. /package/{shadcn → dist/shadcn}/components/ui/tooltip.js.map +0 -0
  207. /package/{shadcn → dist/shadcn}/utils.d.ts +0 -0
  208. /package/{shadcn → dist/shadcn}/utils.js +0 -0
  209. /package/{shadcn → dist/shadcn}/utils.js.map +0 -0
  210. /package/{types → dist/types}/grapesjs-tailwind.d.js +0 -0
  211. /package/{types → dist/types}/grapesjs-tailwind.d.js.map +0 -0
  212. /package/{types → dist/types}/images.d.js +0 -0
  213. /package/{types → dist/types}/images.d.js.map +0 -0
@@ -0,0 +1,176 @@
1
+ import {
2
+ QueryCacheEntry,
3
+ InteractionType,
4
+ InteractionState,
5
+ createInteractionKey
6
+ } from './query-cache-utils';
7
+
8
+ class QueryCacheManager {
9
+ private cache: Map<string, Map<string, QueryCacheEntry>> = new Map();
10
+ private totalEntries = 0;
11
+ private maxCacheSize = 100;
12
+ private maxCacheAge: number = 5 * 60 * 1000; // 5 minutes
13
+
14
+ constructor(maxCacheSize = 100, maxCacheAge = 5 * 60 * 1000) {
15
+ this.maxCacheSize = maxCacheSize;
16
+ this.maxCacheAge = maxCacheAge;
17
+ }
18
+
19
+ getCachedQuery(
20
+ componentId: string,
21
+ interactionType: InteractionType,
22
+ state: InteractionState
23
+ ): QueryCacheEntry | null {
24
+ const componentCache = this.cache.get(componentId);
25
+
26
+ if (!componentCache) {
27
+ return null;
28
+ }
29
+
30
+ const key = createInteractionKey(interactionType, state);
31
+ const entry = componentCache.get(key);
32
+
33
+ if (!entry) {
34
+ return null;
35
+ }
36
+
37
+ const now = Date.now();
38
+
39
+ if (now - entry.timestamp > this.maxCacheAge) {
40
+ componentCache.delete(key);
41
+ this.totalEntries -= 1;
42
+
43
+ if (componentCache.size === 0) {
44
+ this.cache.delete(componentId);
45
+ }
46
+
47
+ return null;
48
+ }
49
+
50
+ return entry;
51
+ }
52
+
53
+ setCachedQuery(
54
+ componentId: string,
55
+ interactionType: InteractionType,
56
+ state: InteractionState,
57
+ entry: QueryCacheEntry
58
+ ): void {
59
+ let componentCache = this.cache.get(componentId);
60
+
61
+ if (!componentCache) {
62
+ componentCache = new Map();
63
+ this.cache.set(componentId, componentCache);
64
+ }
65
+
66
+ const key = createInteractionKey(interactionType, state);
67
+ const isNewEntry = !componentCache.has(key);
68
+
69
+ componentCache.set(key, {
70
+ ...entry,
71
+ timestamp: Date.now(),
72
+ });
73
+
74
+ if (isNewEntry) {
75
+ this.totalEntries += 1;
76
+ }
77
+
78
+ if (this.totalEntries > this.maxCacheSize) {
79
+ this.cleanupCache();
80
+ }
81
+ }
82
+
83
+ invalidateComponent(componentId: string): void {
84
+ const componentCache = this.cache.get(componentId);
85
+
86
+ if (componentCache) {
87
+ this.totalEntries -= componentCache.size;
88
+ this.cache.delete(componentId);
89
+ }
90
+ }
91
+
92
+ invalidateAll(): void {
93
+ this.cache.clear();
94
+ this.totalEntries = 0;
95
+ }
96
+
97
+ isCached(
98
+ componentId: string,
99
+ interactionType: InteractionType,
100
+ state: InteractionState
101
+ ): boolean {
102
+ return this.getCachedQuery(componentId, interactionType, state) !== null;
103
+ }
104
+
105
+ private cleanupCache(): void {
106
+ const now = Date.now();
107
+ const allEntries: Array<{ componentId: string; key: string; timestamp: number }> = [];
108
+
109
+ for (const [componentId, componentCache] of this.cache.entries()) {
110
+ for (const [key, entry] of componentCache.entries()) {
111
+ if (now - entry.timestamp > this.maxCacheAge) {
112
+ componentCache.delete(key);
113
+ this.totalEntries -= 1;
114
+ } else {
115
+ allEntries.push({ componentId, key, timestamp: entry.timestamp });
116
+ }
117
+ }
118
+
119
+ if (componentCache.size === 0) {
120
+ this.cache.delete(componentId);
121
+ }
122
+ }
123
+
124
+ if (allEntries.length > this.maxCacheSize) {
125
+ allEntries.sort((a, b) => a.timestamp - b.timestamp);
126
+
127
+ const entriesToRemove = allEntries.slice(0, allEntries.length - this.maxCacheSize);
128
+
129
+ for (const { componentId, key } of entriesToRemove) {
130
+ const componentCache = this.cache.get(componentId);
131
+
132
+ if (componentCache) {
133
+ componentCache.delete(key);
134
+ this.totalEntries -= 1;
135
+
136
+ // Remove empty component caches
137
+ if (componentCache.size === 0) {
138
+ this.cache.delete(componentId);
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ private getTotalCacheSize(): number {
146
+ return this.totalEntries;
147
+ }
148
+
149
+ getStats(): { componentCount: number; maxAge: number; maxSize: number; size: number } {
150
+ return {
151
+ componentCount: this.cache.size,
152
+ maxAge: this.maxCacheAge,
153
+ maxSize: this.maxCacheSize,
154
+ size: this.getTotalCacheSize(),
155
+ };
156
+ }
157
+ }
158
+
159
+ let globalCacheInstance: QueryCacheManager | null = null;
160
+
161
+ export function getGlobalQueryCache(): QueryCacheManager {
162
+ if (!globalCacheInstance) {
163
+ globalCacheInstance = new QueryCacheManager();
164
+ }
165
+
166
+ return globalCacheInstance;
167
+ }
168
+
169
+ export function resetGlobalQueryCache(): void {
170
+ globalCacheInstance = null;
171
+ }
172
+
173
+ export function configureGlobalQueryCache(maxCacheSize = 100, maxCacheAge = 5 * 60 * 1000): void {
174
+ globalCacheInstance = new QueryCacheManager(maxCacheSize, maxCacheAge);
175
+ }
176
+
@@ -0,0 +1,226 @@
1
+
2
+ export type InteractionType = 'semanticLayerQuery' | 'tableButton';
3
+
4
+ export type InteractionState = {
5
+ [key: string]: unknown;
6
+ bodyContent?: unknown;
7
+ chartData?: unknown;
8
+ chartDataFingerprint?: string;
9
+ chartDataLength?: number;
10
+ dataFingerprint?: string;
11
+ dataLength?: number;
12
+ error?: unknown;
13
+ filterValue?: unknown;
14
+ filters?: Record<string, unknown>;
15
+ footerContent?: unknown;
16
+ headerContent?: unknown;
17
+ isMissing?: unknown;
18
+ pageNumber?: number;
19
+ pageSize?: number;
20
+ searchQuery?: string;
21
+ searchValue?: string;
22
+ sortColumn?: string | null;
23
+ sortDirection?: 'asc' | 'desc' | null;
24
+ summaryText?: unknown;
25
+ };
26
+
27
+ export type QueryCacheEntry = {
28
+ queryHeadings: string[];
29
+ sqlQuery: string | string[];
30
+ tableButtonExplanations: Array<{
31
+ action: string;
32
+ buttonClassName?: string;
33
+ buttonStyle?: Record<string, unknown>;
34
+ explanation: string;
35
+ formFields?: Record<string, unknown>;
36
+ jsonBody?: unknown;
37
+ operationDescription?: string;
38
+ operationSummary?: string;
39
+ requestUrl?: string;
40
+ }>;
41
+ timestamp: number;
42
+ };
43
+
44
+ export type CacheKey = {
45
+ componentId: string;
46
+ interactionType: InteractionType;
47
+ state: InteractionState;
48
+ };
49
+
50
+ export function createCacheKey(
51
+ componentId: string,
52
+ interactionType: InteractionType,
53
+ interactionState: InteractionState
54
+ ): string {
55
+ const sortedState = sortObjectKeys(interactionState);
56
+
57
+ try {
58
+ const stateString = JSON.stringify(sortedState);
59
+
60
+ return `${componentId}::${interactionType}::${stateString}`;
61
+ } catch (error) {
62
+ // eslint-disable-next-line no-console
63
+ console.warn('Failed to serialize interaction state:', error);
64
+
65
+ return `${componentId}::${interactionType}::${Date.now()}::${Math.random()}`;
66
+ }
67
+ }
68
+
69
+ export function createInteractionKey(
70
+ interactionType: InteractionType,
71
+ interactionState: InteractionState
72
+ ): string {
73
+ const sortedState = sortObjectKeys(interactionState);
74
+
75
+ try {
76
+ const stateString = JSON.stringify(sortedState);
77
+
78
+ return `${interactionType}::${stateString}`;
79
+ } catch (error) {
80
+ // eslint-disable-next-line no-console
81
+ console.warn('Failed to serialize interaction state:', error);
82
+
83
+ return `${interactionType}::${Date.now()}::${Math.random()}`;
84
+ }
85
+ }
86
+
87
+ function sortObjectKeys(obj: unknown): unknown {
88
+ if (obj === null || obj === undefined) {
89
+ return obj;
90
+ }
91
+
92
+ if (Array.isArray(obj)) {
93
+ return obj.map(item => sortObjectKeys(item));
94
+ }
95
+
96
+ if (typeof obj === 'object') {
97
+ const sorted: Record<string, unknown> = {};
98
+ const keys = Object.keys(obj as Record<string, unknown>).sort();
99
+
100
+ for (const key of keys) {
101
+ sorted[key] = sortObjectKeys((obj as Record<string, unknown>)[key]);
102
+ }
103
+
104
+ return sorted;
105
+ }
106
+
107
+ return obj;
108
+ }
109
+
110
+ export function extractInteractionState(componentProps: Record<string, unknown>): InteractionState {
111
+ const state: InteractionState = {};
112
+
113
+ if ('pagination' in componentProps && typeof componentProps.pagination === 'object' && componentProps.pagination) {
114
+ const pagination = componentProps.pagination as Record<string, unknown>;
115
+
116
+ if ('pageNumber' in pagination && typeof pagination.pageNumber === 'number') {
117
+ state.pageNumber = pagination.pageNumber;
118
+ }
119
+
120
+ if ('pageSize' in pagination && typeof pagination.pageSize === 'number') {
121
+ state.pageSize = pagination.pageSize;
122
+ }
123
+ }
124
+
125
+ if ('sortColumn' in componentProps) {
126
+ state.sortColumn = componentProps.sortColumn as string | null;
127
+ }
128
+
129
+ if ('sortDirection' in componentProps) {
130
+ state.sortDirection = componentProps.sortDirection as 'asc' | 'desc' | null;
131
+ }
132
+
133
+ if ('filters' in componentProps && typeof componentProps.filters === 'object') {
134
+ state.filters = componentProps.filters as Record<string, unknown>;
135
+ }
136
+
137
+ if ('value' in componentProps) {
138
+ state.filterValue = componentProps.value as unknown;
139
+ }
140
+
141
+ if ('searchQuery' in componentProps && typeof componentProps.searchQuery === 'string') {
142
+ state.searchQuery = componentProps.searchQuery;
143
+ }
144
+
145
+ if ('searchValue' in componentProps && typeof componentProps.searchValue === 'string') {
146
+ state.searchValue = componentProps.searchValue;
147
+ }
148
+
149
+ if ('data' in componentProps) {
150
+ const data = componentProps.data;
151
+
152
+ if (Array.isArray(data) && data.length > 0) {
153
+ state.dataLength = data.length;
154
+ const firstRow = data[0];
155
+ const lastRow = data[data.length - 1];
156
+
157
+ if (firstRow && typeof firstRow === 'object') {
158
+ try {
159
+ state.dataFingerprint = JSON.stringify({
160
+ firstId: (firstRow as Record<string, unknown>).id || Object.values(firstRow)[0],
161
+ lastId: lastRow && typeof lastRow === 'object'
162
+ ? ((lastRow as Record<string, unknown>).id || Object.values(lastRow)[0])
163
+ : null,
164
+ length: data.length
165
+ });
166
+ } catch (error) {
167
+ // eslint-disable-next-line no-console
168
+ console.warn('Failed to serialize data fingerprint:', error);
169
+ state.dataFingerprint = `fallback_${data.length}_${Date.now()}`;
170
+ }
171
+ }
172
+ } else if (data && typeof data === 'object' && (data as Record<string, unknown>).name === '__peak_placeholder') {
173
+ state.dataFingerprint = '__peak_placeholder';
174
+ } else {
175
+ state.dataFingerprint = 'empty';
176
+ }
177
+ }
178
+
179
+ if ('bodyContent' in componentProps) {
180
+ state.bodyContent = componentProps.bodyContent as unknown;
181
+ }
182
+
183
+ if ('headerContent' in componentProps) {
184
+ state.headerContent = componentProps.headerContent as unknown;
185
+ }
186
+
187
+ if ('footerContent' in componentProps) {
188
+ state.footerContent = componentProps.footerContent as unknown;
189
+ }
190
+
191
+ if ('chartData' in componentProps) {
192
+ const chartData = componentProps.chartData;
193
+
194
+ if (Array.isArray(chartData)) {
195
+ state.chartDataLength = chartData.length;
196
+
197
+ try {
198
+ state.chartDataFingerprint = JSON.stringify(chartData);
199
+ } catch (error) {
200
+ // eslint-disable-next-line no-console
201
+ console.warn('Failed to serialize chart data fingerprint:', error);
202
+ state.chartDataFingerprint = `fallback_${chartData.length}_${Date.now()}`;
203
+ }
204
+ }
205
+ }
206
+
207
+ if ('summaryText' in componentProps) {
208
+ state.summaryText = componentProps.summaryText as unknown;
209
+ }
210
+
211
+ if ('error' in componentProps) {
212
+ state.error = componentProps.error as unknown;
213
+ }
214
+
215
+ if ('isMissing' in componentProps) {
216
+ state.isMissing = componentProps.isMissing as unknown;
217
+ }
218
+
219
+ return state;
220
+ }
221
+
222
+ export function areStatesEqual(state1: InteractionState, state2: InteractionState): boolean {
223
+ return createCacheKey('test', 'semanticLayerQuery', state1) ===
224
+ createCacheKey('test', 'semanticLayerQuery', state2);
225
+ }
226
+