@keak/sdk 1.0.1

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 (89) hide show
  1. package/README.md +131 -0
  2. package/dist/Conversion.d.ts +13 -0
  3. package/dist/Conversion.d.ts.map +1 -0
  4. package/dist/KeakToolbarShadow.d.ts +21 -0
  5. package/dist/KeakToolbarShadow.d.ts.map +1 -0
  6. package/dist/components/ui/card.d.ts +9 -0
  7. package/dist/components/ui/card.d.ts.map +1 -0
  8. package/dist/components/ui/html-preview.d.ts +9 -0
  9. package/dist/components/ui/html-preview.d.ts.map +1 -0
  10. package/dist/components/ui/simple-tabs.d.ts +26 -0
  11. package/dist/components/ui/simple-tabs.d.ts.map +1 -0
  12. package/dist/components/ui/spinner.d.ts +6 -0
  13. package/dist/components/ui/spinner.d.ts.map +1 -0
  14. package/dist/components/ui/tabs.d.ts +13 -0
  15. package/dist/components/ui/tabs.d.ts.map +1 -0
  16. package/dist/components/ui/textarea.d.ts +6 -0
  17. package/dist/components/ui/textarea.d.ts.map +1 -0
  18. package/dist/index.cjs.js +17407 -0
  19. package/dist/index.cjs.js.map +1 -0
  20. package/dist/index.d.ts +101 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +17395 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/runtime/sourceInjector.d.ts +2 -0
  25. package/dist/runtime/sourceInjector.d.ts.map +1 -0
  26. package/dist/scripts/sourcePathInjection.d.ts +11 -0
  27. package/dist/scripts/sourcePathInjection.d.ts.map +1 -0
  28. package/dist/services/index.d.ts +7 -0
  29. package/dist/services/index.d.ts.map +1 -0
  30. package/dist/services/telemetry/index.d.ts +20 -0
  31. package/dist/services/telemetry/index.d.ts.map +1 -0
  32. package/dist/services/telemetry/telemetryService.d.ts +66 -0
  33. package/dist/services/telemetry/telemetryService.d.ts.map +1 -0
  34. package/dist/services/telemetry/types.d.ts +64 -0
  35. package/dist/services/telemetry/types.d.ts.map +1 -0
  36. package/dist/toolbar/AIPromptPanel.d.ts +9 -0
  37. package/dist/toolbar/AIPromptPanel.d.ts.map +1 -0
  38. package/dist/toolbar/ElementSelector.d.ts +8 -0
  39. package/dist/toolbar/ElementSelector.d.ts.map +1 -0
  40. package/dist/toolbar/ExperimentPanel.d.ts +9 -0
  41. package/dist/toolbar/ExperimentPanel.d.ts.map +1 -0
  42. package/dist/toolbar/KeakToolbar.d.ts +10 -0
  43. package/dist/toolbar/KeakToolbar.d.ts.map +1 -0
  44. package/dist/toolbar/MetricsPanel.d.ts +7 -0
  45. package/dist/toolbar/MetricsPanel.d.ts.map +1 -0
  46. package/dist/toolbar/PageScanPanel.d.ts +15 -0
  47. package/dist/toolbar/PageScanPanel.d.ts.map +1 -0
  48. package/dist/toolbar/components/PrimaryButton.d.ts +12 -0
  49. package/dist/toolbar/components/PrimaryButton.d.ts.map +1 -0
  50. package/dist/toolbar/components/WarningButton.d.ts +12 -0
  51. package/dist/toolbar/components/WarningButton.d.ts.map +1 -0
  52. package/dist/toolbar/components/icons/index.d.ts +13 -0
  53. package/dist/toolbar/components/icons/index.d.ts.map +1 -0
  54. package/dist/toolbar/components/ui/Badge.d.ts +10 -0
  55. package/dist/toolbar/components/ui/Badge.d.ts.map +1 -0
  56. package/dist/toolbar/components/ui/Button.d.ts +12 -0
  57. package/dist/toolbar/components/ui/Button.d.ts.map +1 -0
  58. package/dist/toolbar/components/ui/Progress.d.ts +5 -0
  59. package/dist/toolbar/components/ui/Progress.d.ts.map +1 -0
  60. package/dist/toolbar/components/ui/Tabs.d.ts +44 -0
  61. package/dist/toolbar/components/ui/Tabs.d.ts.map +1 -0
  62. package/dist/toolbar/components/ui/dropdown-menu.d.ts +28 -0
  63. package/dist/toolbar/components/ui/dropdown-menu.d.ts.map +1 -0
  64. package/dist/toolbar/lib/utils.d.ts +3 -0
  65. package/dist/toolbar/lib/utils.d.ts.map +1 -0
  66. package/dist/toolbar/utils/fiberSource.d.ts +64 -0
  67. package/dist/toolbar/utils/fiberSource.d.ts.map +1 -0
  68. package/dist/toolbar/utils/keakCodeClient.d.ts +104 -0
  69. package/dist/toolbar/utils/keakCodeClient.d.ts.map +1 -0
  70. package/dist/toolbar.css +1 -0
  71. package/dist/toolbar.js +1257 -0
  72. package/dist/toolbar.js.map +1 -0
  73. package/dist/utils/generateElementId.d.ts +44 -0
  74. package/dist/utils/generateElementId.d.ts.map +1 -0
  75. package/dist/utils/injectDataId.d.ts +33 -0
  76. package/dist/utils/injectDataId.d.ts.map +1 -0
  77. package/package.json +152 -0
  78. package/src/cli/ai-helper.js +206 -0
  79. package/src/cli/code-transformer.js +354 -0
  80. package/src/cli/conversion-detector.js +716 -0
  81. package/src/cli/framework-config.js +477 -0
  82. package/src/cli/install.js +618 -0
  83. package/src/cli/keak-setup.js +43 -0
  84. package/src/cli/revert-conversions.js +264 -0
  85. package/src/cli/safe-transformer.js +456 -0
  86. package/src/cli/simple-transformer.js +339 -0
  87. package/src/plugins/README.md +131 -0
  88. package/src/plugins/babel-source-inject.cjs +170 -0
  89. package/src/plugins/next.cjs +145 -0
@@ -0,0 +1,456 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+
6
+ class SafeTransformer {
7
+ constructor() {
8
+ this.stats = {
9
+ filesProcessed: 0,
10
+ elementsWrapped: 0,
11
+ filesModified: 0
12
+ };
13
+ }
14
+
15
+ // Safe approach: Only transform simple, well-formed JSX elements
16
+ transformReactFile(content, filePath) {
17
+ // Skip if file already has Conversion imports
18
+ if (content.includes('import') && content.includes('Conversion') && content.includes('keak-sdk')) {
19
+ return content;
20
+ }
21
+
22
+ let modified = content;
23
+ let elementsFound = 0;
24
+
25
+ // Handle common React patterns for buttons and links
26
+ const safePatterns = [
27
+ // HTML button elements
28
+ {
29
+ regex: /(<button\s[^>]*>)([\s\S]*?)(<\/button>)/g,
30
+ type: 'button',
31
+ replacement: (match, open, content, close) => {
32
+ if (this.isWellFormedJSX(match)) {
33
+ const category = this.detectCategory(content + open);
34
+ const label = this.extractText(content).substring(0, 30);
35
+ return `<Conversion type="button" category="${category}" label="${this.escapeAttr(label)}">${open}${content}${close}</Conversion>`;
36
+ }
37
+ return match;
38
+ }
39
+ },
40
+ // React Button components (capitalized)
41
+ {
42
+ regex: /(<Button\s[^>]*>)([\s\S]*?)(<\/Button>)/g,
43
+ type: 'button',
44
+ replacement: (match, open, content, close) => {
45
+ if (this.isWellFormedJSX(match)) {
46
+ const category = this.detectCategory(content + open);
47
+ const label = this.extractText(content).substring(0, 30);
48
+ return `<Conversion type="button" category="${category}" label="${this.escapeAttr(label)}">${open}${content}${close}</Conversion>`;
49
+ }
50
+ return match;
51
+ }
52
+ },
53
+ // Self-closing Button components
54
+ {
55
+ regex: /(<Button\s[^>]*\/>)/g,
56
+ type: 'button',
57
+ replacement: (match, open) => {
58
+ if (this.isWellFormedJSX(match)) {
59
+ const category = this.detectCategory(open);
60
+ const label = this.extractTextFromProps(open);
61
+ return `<Conversion type="button" category="${category}" label="${this.escapeAttr(label)}">${open}</Conversion>`;
62
+ }
63
+ return match;
64
+ }
65
+ },
66
+ // HTML links with href
67
+ {
68
+ regex: /(<a\s+[^>]*href=["'][^"']*["'][^>]*>)([\s\S]*?)(<\/a>)/g,
69
+ type: 'link',
70
+ replacement: (match, open, content, close) => {
71
+ if (this.isWellFormedJSX(match)) {
72
+ const category = this.detectCategory(content + open);
73
+ const label = this.extractText(content).substring(0, 30);
74
+ return `<Conversion type="link" category="${category}" label="${this.escapeAttr(label)}">${open}${content}${close}</Conversion>`;
75
+ }
76
+ return match;
77
+ }
78
+ },
79
+ // Link components
80
+ {
81
+ regex: /(<Link\s[^>]*>)([\s\S]*?)(<\/Link>)/g,
82
+ type: 'link',
83
+ replacement: (match, open, content, close) => {
84
+ if (this.isWellFormedJSX(match)) {
85
+ const category = this.detectCategory(content + open);
86
+ const label = this.extractText(content).substring(0, 30);
87
+ return `<Conversion type="link" category="${category}" label="${this.escapeAttr(label)}">${open}${content}${close}</Conversion>`;
88
+ }
89
+ return match;
90
+ }
91
+ }
92
+ ];
93
+
94
+ // Only apply transformations if we can safely parse the file
95
+ if (this.isSafeToTransform(content)) {
96
+ for (const pattern of safePatterns) {
97
+ modified = modified.replace(pattern.regex, (...args) => {
98
+ const match = args[0];
99
+
100
+ // Additional safety checks
101
+ if (this.isAlreadyWrapped(match, modified) ||
102
+ this.hasComplexJSX(match) ||
103
+ this.isInJSXExpression(match, modified, args.index)) {
104
+ return match;
105
+ }
106
+
107
+ elementsFound++;
108
+ return pattern.replacement(...args);
109
+ });
110
+ }
111
+ }
112
+
113
+ if (elementsFound > 0) {
114
+ // Add import
115
+ modified = this.addImport(modified);
116
+ this.stats.elementsWrapped += elementsFound;
117
+ this.stats.filesModified++;
118
+ }
119
+
120
+ return modified;
121
+ }
122
+
123
+ // Check if content is safe to transform
124
+ isSafeToTransform(content) {
125
+ // Only reject files with patterns that would definitely break our transformation
126
+ const dangerousPatterns = [
127
+ /dangerouslySetInnerHTML/, // Dangerous HTML
128
+ /jsx`/, // Template literals with JSX
129
+ /React\.createElement\s*\(/, // React.createElement calls
130
+ /<script\s*>/i, // Script tags
131
+ /eval\s*\(/, // eval calls
132
+ ];
133
+
134
+ return !dangerousPatterns.some(pattern => pattern.test(content));
135
+ }
136
+
137
+ // Check if JSX is well-formed
138
+ isWellFormedJSX(jsx) {
139
+ // For conversion wrapping, we just need basic safety checks
140
+ // The regex patterns already ensure we have complete elements
141
+
142
+ // Check for unmatched curly braces (which could break JSX)
143
+ const openBraces = (jsx.match(/\{/g) || []).length;
144
+ const closeBraces = (jsx.match(/\}/g) || []).length;
145
+ if (openBraces !== closeBraces) return false;
146
+
147
+ // Check for unmatched quotes that could break attributes
148
+ const singleQuotes = (jsx.match(/'/g) || []).length;
149
+ const doubleQuotes = (jsx.match(/"/g) || []).length;
150
+ if (singleQuotes % 2 !== 0 || doubleQuotes % 2 !== 0) return false;
151
+
152
+ // Don't wrap elements that already have conversion tracking
153
+ if (jsx.includes('data-keak-conversion') || jsx.includes('Conversion')) return false;
154
+
155
+ return true;
156
+ }
157
+
158
+ // Check if element has complex JSX that might break
159
+ hasComplexJSX(jsx) {
160
+ return (
161
+ jsx.includes('...') || // Spread
162
+ jsx.includes('&&') || // Logical AND
163
+ jsx.includes('?') || // Ternary
164
+ jsx.includes('as ') || // TypeScript casting
165
+ jsx.includes('dangerouslySetInnerHTML') ||
166
+ jsx.match(/\{\s*\w+\.\w+/) // Complex property access
167
+ );
168
+ }
169
+
170
+ // Check if we're inside a JSX expression
171
+ isInJSXExpression(match, content, index) {
172
+ if (!index) return false;
173
+
174
+ const before = content.substring(Math.max(0, index - 100), index);
175
+ const after = content.substring(index + match.length, index + match.length + 100);
176
+
177
+ // Check if we're inside curly braces
178
+ const openBraces = (before.match(/\{/g) || []).length;
179
+ const closeBraces = (before.match(/\}/g) || []).length;
180
+
181
+ return openBraces > closeBraces;
182
+ }
183
+
184
+ // Check if already wrapped
185
+ isAlreadyWrapped(match, content) {
186
+ return content.includes('<Conversion') || match.includes('data-keak-conversion');
187
+ }
188
+
189
+ // Add import statement safely
190
+ addImport(content) {
191
+ if (content.includes("import { Conversion } from 'keak-sdk'")) {
192
+ return content;
193
+ }
194
+
195
+ const lines = content.split('\n');
196
+ let insertIndex = 0;
197
+ let lastImportIndex = -1;
198
+
199
+ // Find the best place to insert import
200
+ for (let i = 0; i < lines.length; i++) {
201
+ const line = lines[i].trim();
202
+
203
+ // Skip initial comments and directives
204
+ if (line.startsWith('//') || line.startsWith('/*') || line === '' ||
205
+ line.includes("'use client'") || line.includes('"use client"') ||
206
+ line.includes("'use server'") || line.includes('"use server"')) {
207
+ insertIndex = i + 1;
208
+ continue;
209
+ }
210
+
211
+ // Track imports
212
+ if (line.startsWith('import ')) {
213
+ lastImportIndex = i;
214
+ continue;
215
+ }
216
+
217
+ // If we found imports, insert after them
218
+ if (lastImportIndex >= 0 && !line.startsWith('import ')) {
219
+ insertIndex = lastImportIndex + 1;
220
+ break;
221
+ }
222
+
223
+ // If we find other code, insert before it
224
+ if (line !== '' && !line.startsWith('import ')) {
225
+ insertIndex = i;
226
+ break;
227
+ }
228
+ }
229
+
230
+ lines.splice(insertIndex, 0, "import { Conversion } from 'keak-sdk';");
231
+ return lines.join('\n');
232
+ }
233
+
234
+ // Detect category from content
235
+ detectCategory(text) {
236
+ const t = text.toLowerCase();
237
+
238
+ if (/buy|purchase|checkout|order|cart|pay/.test(t)) return 'purchase';
239
+ if (/sign.?up|register|join|create.?account/.test(t)) return 'signup';
240
+ if (/download|install|get.?app/.test(t)) return 'download';
241
+ if (/demo|trial|preview|try/.test(t)) return 'demo';
242
+ if (/contact|call|phone|support/.test(t)) return 'contact';
243
+ if (/learn.?more|read.?more|details/.test(t)) return 'engagement';
244
+ if (/subscribe|newsletter/.test(t)) return 'subscription';
245
+
246
+ return 'interaction';
247
+ }
248
+
249
+ // Extract text content safely
250
+ extractText(html) {
251
+ return html
252
+ .replace(/<[^]*/g, ' ')
253
+ .replace(/\{[^}]*\}/g, ' ') // Remove JSX expressions
254
+ .replace(/\s+/g, ' ')
255
+ .trim();
256
+ }
257
+
258
+ // Extract text from props (for self-closing elements)
259
+ extractTextFromProps(jsx) {
260
+ // Try to extract text from common props like children, title, aria-label
261
+ const propsText = [];
262
+
263
+ // Look for title prop
264
+ const titleMatch = jsx.match(/title=["']([^"']*)["']/);
265
+ if (titleMatch) propsText.push(titleMatch[1]);
266
+
267
+ // Look for aria-label prop
268
+ const ariaLabelMatch = jsx.match(/aria-label=["']([^"']*)["']/);
269
+ if (ariaLabelMatch) propsText.push(ariaLabelMatch[1]);
270
+
271
+ // Look for alt prop
272
+ const altMatch = jsx.match(/alt=["']([^"']*)["']/);
273
+ if (altMatch) propsText.push(altMatch[1]);
274
+
275
+ // Look for children prop
276
+ const childrenMatch = jsx.match(/children=["']([^"']*)["']/);
277
+ if (childrenMatch) propsText.push(childrenMatch[1]);
278
+
279
+ return propsText.join(' ').substring(0, 30) || 'button';
280
+ }
281
+
282
+ // Escape attribute values safely
283
+ escapeAttr(str) {
284
+ return str
285
+ .replace(/"/g, '&quot;')
286
+ .replace(/'/g, '&#39;')
287
+ .replace(/</g, '&lt;')
288
+ .replace(/>/g, '&gt;')
289
+ .replace(/\{/g, '&#123;')
290
+ .replace(/\}/g, '&#125;');
291
+ }
292
+
293
+ // Process a single file
294
+ processFile(filePath) {
295
+ try {
296
+ const content = fs.readFileSync(filePath, 'utf-8');
297
+ const transformed = this.transformReactFile(content, filePath);
298
+
299
+ if (transformed !== content) {
300
+ // Create backup
301
+ const backupPath = `${filePath}.keak-backup`;
302
+ fs.writeFileSync(backupPath, content);
303
+
304
+ // Write transformed content
305
+ fs.writeFileSync(filePath, transformed);
306
+
307
+ return { success: true, modified: true, backup: backupPath };
308
+ }
309
+
310
+ return { success: true, modified: false };
311
+ } catch (error) {
312
+ return { success: false, error: error.message };
313
+ }
314
+ }
315
+
316
+ // Find React files
317
+ findReactFiles(dir) {
318
+ const files = [];
319
+
320
+ if (!fs.existsSync(dir)) return files;
321
+
322
+ const items = fs.readdirSync(dir);
323
+
324
+ for (const item of items) {
325
+ const fullPath = path.join(dir, item);
326
+
327
+ // Skip directories we don't want to process
328
+ if (['node_modules', '.git', 'dist', 'build', '.next', 'coverage'].includes(item)) {
329
+ continue;
330
+ }
331
+
332
+ const stat = fs.statSync(fullPath);
333
+
334
+ if (stat.isDirectory()) {
335
+ files.push(...this.findReactFiles(fullPath));
336
+ } else if (this.isReactFile(item, fullPath)) {
337
+ files.push(fullPath);
338
+ }
339
+ }
340
+
341
+ return files;
342
+ }
343
+
344
+ // Check if file is a React file
345
+ isReactFile(filename, fullPath) {
346
+ // Check extension
347
+ if (!/\.(jsx|tsx|js|ts)$/.test(filename)) return false;
348
+
349
+ // Skip test files and config files
350
+ if (/\.(test|spec|config|stories)\.(jsx?|tsx?)$/.test(filename)) return false;
351
+
352
+ try {
353
+ const content = fs.readFileSync(fullPath, 'utf-8');
354
+
355
+ // Must contain JSX or React imports and be safe to transform
356
+ return (
357
+ content.includes('import') && (
358
+ content.includes('react') ||
359
+ content.includes('React') ||
360
+ /\s*<\w+/.test(content) // JSX elements
361
+ ) && this.isSafeToTransform(content)
362
+ );
363
+ } catch (error) {
364
+ return false;
365
+ }
366
+ }
367
+
368
+ // Process directory
369
+ processDirectory(rootDir) {
370
+ console.log(`šŸ” Scanning for React files in ${rootDir}...`);
371
+
372
+ const reactFiles = this.findReactFiles(rootDir);
373
+ console.log(`šŸ“ Found ${reactFiles.length} React files (safe to transform)`);
374
+
375
+ const results = [];
376
+
377
+ for (const filePath of reactFiles) {
378
+ console.log(`āš™ļø Processing ${path.relative(rootDir, filePath)}...`);
379
+
380
+ const result = this.processFile(filePath);
381
+ result.filePath = filePath;
382
+ results.push(result);
383
+
384
+ this.stats.filesProcessed++;
385
+
386
+ if (result.success && result.modified) {
387
+ console.log(`āœ… Modified ${path.relative(rootDir, filePath)}`);
388
+ if (result.backup) {
389
+ console.log(`šŸ“‹ Backup created: ${path.basename(result.backup)}`);
390
+ }
391
+ }
392
+ }
393
+
394
+ return results;
395
+ }
396
+
397
+ // Generate report
398
+ generateReport() {
399
+ return {
400
+ summary: `
401
+ šŸŽÆ Safe Conversion Tracking Integration Complete!
402
+
403
+ šŸ“Š Summary:
404
+ • Files processed: ${this.stats.filesProcessed}
405
+ • Files modified: ${this.stats.filesModified}
406
+ • Elements wrapped: ${this.stats.elementsWrapped}
407
+
408
+ āœ… Safe conversion tracking applied to compatible elements.
409
+ šŸ”„ Backup files created for all modified files (*.keak-backup)
410
+ šŸ“” Conversion telemetry is now active for tracked interactions.
411
+
412
+ āš ļø Note: Only simple, well-formed JSX elements were wrapped to avoid breaking your code.
413
+ `,
414
+ stats: this.stats
415
+ };
416
+ }
417
+ }
418
+
419
+ export default SafeTransformer;
420
+
421
+ // CLI execution
422
+ if (import.meta.url === `file://${process.argv[1]}`) {
423
+ const transformer = new SafeTransformer();
424
+ const targetDir = process.argv[2] || process.cwd();
425
+
426
+ console.log('šŸŽÆ Setting up safe conversion tracking...\n');
427
+
428
+ const results = transformer.processDirectory(targetDir);
429
+ const report = transformer.generateReport();
430
+
431
+ console.log(report.summary);
432
+
433
+ // Show any errors
434
+ const errors = results.filter(r => !r.success);
435
+ if (errors.length > 0) {
436
+ console.log('\nāš ļø Some files had issues:');
437
+ errors.forEach(error => {
438
+ console.log(`āŒ ${path.relative(targetDir, error.filePath)}: ${error.error}`);
439
+ });
440
+ }
441
+
442
+ if (report.stats.filesModified > 0) {
443
+ console.log(`
444
+ šŸ”§ Next steps:
445
+ 1. Test your application to ensure everything works
446
+ 2. The components will automatically track interactions
447
+ 3. Check your Keak dashboard for conversion analytics
448
+ 4. If issues occur, run: npx keak-setup revert-conversions
449
+
450
+ šŸ’” This safe mode only wraps simple elements to avoid breaking complex JSX
451
+ `);
452
+ } else {
453
+ console.log('\n✨ No compatible elements found for safe wrapping.');
454
+ console.log('šŸ’” Complex JSX patterns were skipped to prevent breaking your code.');
455
+ }
456
+ }