@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.
- package/README.md +131 -0
- package/dist/Conversion.d.ts +13 -0
- package/dist/Conversion.d.ts.map +1 -0
- package/dist/KeakToolbarShadow.d.ts +21 -0
- package/dist/KeakToolbarShadow.d.ts.map +1 -0
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/html-preview.d.ts +9 -0
- package/dist/components/ui/html-preview.d.ts.map +1 -0
- package/dist/components/ui/simple-tabs.d.ts +26 -0
- package/dist/components/ui/simple-tabs.d.ts.map +1 -0
- package/dist/components/ui/spinner.d.ts +6 -0
- package/dist/components/ui/spinner.d.ts.map +1 -0
- package/dist/components/ui/tabs.d.ts +13 -0
- package/dist/components/ui/tabs.d.ts.map +1 -0
- package/dist/components/ui/textarea.d.ts +6 -0
- package/dist/components/ui/textarea.d.ts.map +1 -0
- package/dist/index.cjs.js +17407 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17395 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/sourceInjector.d.ts +2 -0
- package/dist/runtime/sourceInjector.d.ts.map +1 -0
- package/dist/scripts/sourcePathInjection.d.ts +11 -0
- package/dist/scripts/sourcePathInjection.d.ts.map +1 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/telemetry/index.d.ts +20 -0
- package/dist/services/telemetry/index.d.ts.map +1 -0
- package/dist/services/telemetry/telemetryService.d.ts +66 -0
- package/dist/services/telemetry/telemetryService.d.ts.map +1 -0
- package/dist/services/telemetry/types.d.ts +64 -0
- package/dist/services/telemetry/types.d.ts.map +1 -0
- package/dist/toolbar/AIPromptPanel.d.ts +9 -0
- package/dist/toolbar/AIPromptPanel.d.ts.map +1 -0
- package/dist/toolbar/ElementSelector.d.ts +8 -0
- package/dist/toolbar/ElementSelector.d.ts.map +1 -0
- package/dist/toolbar/ExperimentPanel.d.ts +9 -0
- package/dist/toolbar/ExperimentPanel.d.ts.map +1 -0
- package/dist/toolbar/KeakToolbar.d.ts +10 -0
- package/dist/toolbar/KeakToolbar.d.ts.map +1 -0
- package/dist/toolbar/MetricsPanel.d.ts +7 -0
- package/dist/toolbar/MetricsPanel.d.ts.map +1 -0
- package/dist/toolbar/PageScanPanel.d.ts +15 -0
- package/dist/toolbar/PageScanPanel.d.ts.map +1 -0
- package/dist/toolbar/components/PrimaryButton.d.ts +12 -0
- package/dist/toolbar/components/PrimaryButton.d.ts.map +1 -0
- package/dist/toolbar/components/WarningButton.d.ts +12 -0
- package/dist/toolbar/components/WarningButton.d.ts.map +1 -0
- package/dist/toolbar/components/icons/index.d.ts +13 -0
- package/dist/toolbar/components/icons/index.d.ts.map +1 -0
- package/dist/toolbar/components/ui/Badge.d.ts +10 -0
- package/dist/toolbar/components/ui/Badge.d.ts.map +1 -0
- package/dist/toolbar/components/ui/Button.d.ts +12 -0
- package/dist/toolbar/components/ui/Button.d.ts.map +1 -0
- package/dist/toolbar/components/ui/Progress.d.ts +5 -0
- package/dist/toolbar/components/ui/Progress.d.ts.map +1 -0
- package/dist/toolbar/components/ui/Tabs.d.ts +44 -0
- package/dist/toolbar/components/ui/Tabs.d.ts.map +1 -0
- package/dist/toolbar/components/ui/dropdown-menu.d.ts +28 -0
- package/dist/toolbar/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/toolbar/lib/utils.d.ts +3 -0
- package/dist/toolbar/lib/utils.d.ts.map +1 -0
- package/dist/toolbar/utils/fiberSource.d.ts +64 -0
- package/dist/toolbar/utils/fiberSource.d.ts.map +1 -0
- package/dist/toolbar/utils/keakCodeClient.d.ts +104 -0
- package/dist/toolbar/utils/keakCodeClient.d.ts.map +1 -0
- package/dist/toolbar.css +1 -0
- package/dist/toolbar.js +1257 -0
- package/dist/toolbar.js.map +1 -0
- package/dist/utils/generateElementId.d.ts +44 -0
- package/dist/utils/generateElementId.d.ts.map +1 -0
- package/dist/utils/injectDataId.d.ts +33 -0
- package/dist/utils/injectDataId.d.ts.map +1 -0
- package/package.json +152 -0
- package/src/cli/ai-helper.js +206 -0
- package/src/cli/code-transformer.js +354 -0
- package/src/cli/conversion-detector.js +716 -0
- package/src/cli/framework-config.js +477 -0
- package/src/cli/install.js +618 -0
- package/src/cli/keak-setup.js +43 -0
- package/src/cli/revert-conversions.js +264 -0
- package/src/cli/safe-transformer.js +456 -0
- package/src/cli/simple-transformer.js +339 -0
- package/src/plugins/README.md +131 -0
- package/src/plugins/babel-source-inject.cjs +170 -0
- package/src/plugins/next.cjs +145 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
class CodeTransformer {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.importAdded = false;
|
|
9
|
+
this.stats = {
|
|
10
|
+
filesProcessed: 0,
|
|
11
|
+
elementsWrapped: 0,
|
|
12
|
+
filesModified: 0
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Transform React/JSX files to wrap clickable elements with Conversion component
|
|
17
|
+
transformReactFile(content, filePath) {
|
|
18
|
+
let modified = content;
|
|
19
|
+
this.importAdded = false;
|
|
20
|
+
|
|
21
|
+
// Skip if file already has Conversion imports
|
|
22
|
+
if (content.includes('import') && content.includes('Conversion') && content.includes('keak-sdk')) {
|
|
23
|
+
return content;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Patterns for clickable elements that should be wrapped
|
|
27
|
+
const clickablePatterns = [
|
|
28
|
+
// Button elements
|
|
29
|
+
{
|
|
30
|
+
pattern: /<button([^]*?)([\s\S]*?)<\/button/g,
|
|
31
|
+
type: 'button',
|
|
32
|
+
category: this.detectCategoryFromContent
|
|
33
|
+
},
|
|
34
|
+
// Link elements
|
|
35
|
+
{
|
|
36
|
+
pattern: /<a([^]*?)([\s\S]*?)<\/a/g,
|
|
37
|
+
type: 'link',
|
|
38
|
+
category: this.detectCategoryFromContent
|
|
39
|
+
},
|
|
40
|
+
// Input buttons (self-closing)
|
|
41
|
+
{
|
|
42
|
+
pattern: /<input([^]*?type=["'](?:button|submit)["'][^]*?)(\s*\/)/g,
|
|
43
|
+
type: 'button',
|
|
44
|
+
category: this.detectCategoryFromContent,
|
|
45
|
+
selfClosing: true
|
|
46
|
+
},
|
|
47
|
+
// Clickable divs/spans (with onClick)
|
|
48
|
+
{
|
|
49
|
+
pattern: /<(div|span)([^]*?onClick[^]*?)([\s\S]*?)<\/\1/g,
|
|
50
|
+
type: 'custom',
|
|
51
|
+
category: this.detectCategoryFromContent
|
|
52
|
+
}
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
let elementsFound = 0;
|
|
56
|
+
|
|
57
|
+
// Transform each pattern
|
|
58
|
+
clickablePatterns.forEach(({ pattern, type, category, selfClosing }) => {
|
|
59
|
+
modified = modified.replace(pattern, (match, ...args) => {
|
|
60
|
+
// Skip if already wrapped with Conversion
|
|
61
|
+
if (match.includes('data-keak-conversion') ||
|
|
62
|
+
(match.toLowerCase().includes('<conversion') && match.toLowerCase().includes('</conversion'))) {
|
|
63
|
+
return match;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let attributes, content, elementType, selfClosingPart;
|
|
67
|
+
|
|
68
|
+
if (selfClosing) {
|
|
69
|
+
// For input elements: match, attributes, selfClosingPart
|
|
70
|
+
[, attributes, selfClosingPart] = args;
|
|
71
|
+
content = '';
|
|
72
|
+
elementType = 'input';
|
|
73
|
+
} else if (type === 'custom') {
|
|
74
|
+
// For div/span with onClick: match, elementType, attributes, content
|
|
75
|
+
[, elementType, attributes, content] = args;
|
|
76
|
+
} else {
|
|
77
|
+
// For button/a elements: match, attributes, content
|
|
78
|
+
[, attributes, content] = args;
|
|
79
|
+
elementType = type === 'button' ? 'button' : 'a';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Skip form elements that might break functionality
|
|
83
|
+
if (attributes && (attributes.includes('type="hidden"') || attributes.includes('type="password"'))) {
|
|
84
|
+
return match;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
elementsFound++;
|
|
88
|
+
|
|
89
|
+
// Mark that we found elements (import will be added later)
|
|
90
|
+
this.importAdded = true;
|
|
91
|
+
|
|
92
|
+
// Detect category from content and attributes
|
|
93
|
+
const detectedCategory = category(content || '', attributes || '');
|
|
94
|
+
|
|
95
|
+
// Build Conversion wrapper
|
|
96
|
+
const conversionProps = [
|
|
97
|
+
`type="${type}"`,
|
|
98
|
+
detectedCategory ? `category="${detectedCategory}"` : '',
|
|
99
|
+
// Add label from content for better tracking
|
|
100
|
+
content && content.trim() ? `label="${this.escapeJSX(this.extractTextContent(content))}"` : ''
|
|
101
|
+
].filter(Boolean).join(' ');
|
|
102
|
+
|
|
103
|
+
if (selfClosing) {
|
|
104
|
+
// For self-closing elements like input
|
|
105
|
+
return `<input${attributes}${selfClosingPart}
|
|
106
|
+
`;
|
|
107
|
+
} else {
|
|
108
|
+
// For regular elements
|
|
109
|
+
return `<${elementType}${attributes}${content}</${elementType}`;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (elementsFound 0) {
|
|
115
|
+
// Add the import at the top
|
|
116
|
+
modified = this.addImportToContent(modified);
|
|
117
|
+
this.stats.elementsWrapped += elementsFound;
|
|
118
|
+
this.stats.filesModified++;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return modified;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Add import statement to the file content
|
|
125
|
+
addImportToContent(content) {
|
|
126
|
+
if (this.importAdded) return content;
|
|
127
|
+
|
|
128
|
+
const importStatement = "\n";
|
|
129
|
+
|
|
130
|
+
// Find the best place to add the import
|
|
131
|
+
const lines = content.split('\n');
|
|
132
|
+
let insertIndex = 0;
|
|
133
|
+
|
|
134
|
+
// Look for existing imports
|
|
135
|
+
for (let i = 0; i < lines.length; i++) {
|
|
136
|
+
const line = lines[i].trim();
|
|
137
|
+
|
|
138
|
+
// Skip comments and empty lines at the top
|
|
139
|
+
if (line.startsWith('//') || line.startsWith('/*') || line === '' || line.startsWith("'use client'") || line.startsWith('"use client"')) {
|
|
140
|
+
insertIndex = i + 1;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// If we find an import, set insert point after imports
|
|
145
|
+
if (line.startsWith('import ')) {
|
|
146
|
+
// Find the end of import block
|
|
147
|
+
while (i < lines.length && (lines[i].trim().startsWith('import ') || lines[i].trim() === '')) {
|
|
148
|
+
i++;
|
|
149
|
+
}
|
|
150
|
+
insertIndex = i;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// If we find any other code, insert before it
|
|
155
|
+
if (line !== '') {
|
|
156
|
+
insertIndex = i;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
lines.splice(insertIndex, 0, importStatement);
|
|
162
|
+
this.importAdded = true;
|
|
163
|
+
return lines.join('\n');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Detect conversion category from element content and attributes
|
|
167
|
+
detectCategoryFromContent(content, attributes) {
|
|
168
|
+
const text = (content + ' ' + attributes).toLowerCase();
|
|
169
|
+
|
|
170
|
+
if (/buy|purchase|checkout|order|cart|pay|billing/.test(text)) return 'purchase';
|
|
171
|
+
if (/sign.?up|register|join|create.?account/.test(text)) return 'signup';
|
|
172
|
+
if (/download|install|get.?app/.test(text)) return 'download';
|
|
173
|
+
if (/demo|trial|preview|try/.test(text)) return 'demo';
|
|
174
|
+
if (/contact|call|phone|support|help/.test(text)) return 'contact';
|
|
175
|
+
if (/learn.?more|read.?more|details|info/.test(text)) return 'engagement';
|
|
176
|
+
if (/subscribe|newsletter|updates/.test(text)) return 'subscription';
|
|
177
|
+
|
|
178
|
+
return 'interaction';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Extract text content from JSX, removing tags
|
|
182
|
+
extractTextContent(jsx) {
|
|
183
|
+
return jsx
|
|
184
|
+
.replace(/<[^]*/g, ' ') // Remove HTML/JSX tags
|
|
185
|
+
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
186
|
+
.trim()
|
|
187
|
+
.substring(0, 50); // Limit length
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Escape JSX string content
|
|
191
|
+
escapeJSX(str) {
|
|
192
|
+
return str
|
|
193
|
+
.replace(/"/g, '"')
|
|
194
|
+
.replace(/'/g, ''')
|
|
195
|
+
.replace(/</g, '<')
|
|
196
|
+
.replace(//g, '>');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Process a single file
|
|
200
|
+
processFile(filePath) {
|
|
201
|
+
try {
|
|
202
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
203
|
+
const transformed = this.transformReactFile(content, filePath);
|
|
204
|
+
|
|
205
|
+
if (transformed !== content) {
|
|
206
|
+
// Create backup
|
|
207
|
+
const backupPath = `${filePath}.keak-backup`;
|
|
208
|
+
fs.writeFileSync(backupPath, content);
|
|
209
|
+
|
|
210
|
+
// Write transformed content
|
|
211
|
+
fs.writeFileSync(filePath, transformed);
|
|
212
|
+
|
|
213
|
+
return { success: true, modified: true, backup: backupPath };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return { success: true, modified: false };
|
|
217
|
+
} catch (error) {
|
|
218
|
+
return { success: false, error: error.message };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Find all React/JSX files in a directory
|
|
223
|
+
findReactFiles(dir, extensions = ['.jsx', '.tsx', '.js', '.ts']) {
|
|
224
|
+
const files = [];
|
|
225
|
+
|
|
226
|
+
if (!fs.existsSync(dir)) return files;
|
|
227
|
+
|
|
228
|
+
const items = fs.readdirSync(dir);
|
|
229
|
+
|
|
230
|
+
for (const item of items) {
|
|
231
|
+
const fullPath = path.join(dir, item);
|
|
232
|
+
|
|
233
|
+
// Skip common directories
|
|
234
|
+
if (['node_modules', '.git', 'dist', 'build', '.next', 'coverage'].includes(item)) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const stat = fs.statSync(fullPath);
|
|
239
|
+
|
|
240
|
+
if (stat.isDirectory()) {
|
|
241
|
+
files.push(...this.findReactFiles(fullPath, extensions));
|
|
242
|
+
} else if (extensions.some(ext =item.endsWith(ext))) {
|
|
243
|
+
// Check if file contains JSX/React content
|
|
244
|
+
try {
|
|
245
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
246
|
+
if (this.isReactFile(content)) {
|
|
247
|
+
files.push(fullPath);
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
// Skip files that can't be read
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return files;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Check if file contains React/JSX content
|
|
259
|
+
isReactFile(content) {
|
|
260
|
+
return (
|
|
261
|
+
content.includes('import') && (
|
|
262
|
+
content.includes('react') ||
|
|
263
|
+
content.includes('React') ||
|
|
264
|
+
content.includes('<') && content.includes('') // JSX-like syntax
|
|
265
|
+
)
|
|
266
|
+
) || content.includes('jsx') || content.includes('tsx');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Process all React files in a directory
|
|
270
|
+
processDirectory(rootDir) {
|
|
271
|
+
console.log(`š Scanning for React files in ${rootDir}...`);
|
|
272
|
+
|
|
273
|
+
const reactFiles = this.findReactFiles(rootDir);
|
|
274
|
+
console.log(`š Found ${reactFiles.length} React files`);
|
|
275
|
+
|
|
276
|
+
const results = [];
|
|
277
|
+
|
|
278
|
+
for (const filePath of reactFiles) {
|
|
279
|
+
console.log(`āļø Processing ${path.relative(rootDir, filePath)}...`);
|
|
280
|
+
|
|
281
|
+
const result = this.processFile(filePath);
|
|
282
|
+
result.filePath = filePath;
|
|
283
|
+
results.push(result);
|
|
284
|
+
|
|
285
|
+
this.stats.filesProcessed++;
|
|
286
|
+
|
|
287
|
+
if (result.success && result.modified) {
|
|
288
|
+
console.log(`ā
Modified ${path.relative(rootDir, filePath)}`);
|
|
289
|
+
if (result.backup) {
|
|
290
|
+
console.log(`š Backup created: ${path.basename(result.backup)}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return results;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Generate a summary report
|
|
299
|
+
generateReport() {
|
|
300
|
+
return {
|
|
301
|
+
summary: `
|
|
302
|
+
šÆ Conversion Tracking Integration Complete!
|
|
303
|
+
|
|
304
|
+
š Summary:
|
|
305
|
+
⢠Files processed: ${this.stats.filesProcessed}
|
|
306
|
+
⢠Files modified: ${this.stats.filesModified}
|
|
307
|
+
⢠Elements wrapped: ${this.stats.elementsWrapped}
|
|
308
|
+
|
|
309
|
+
ā
All clickable elements have been automatically wrapped with components.
|
|
310
|
+
š Backup files created for all modified files (*.keak-backup)
|
|
311
|
+
š” Conversion telemetry is now active for all interactions.
|
|
312
|
+
`,
|
|
313
|
+
stats: this.stats
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export default CodeTransformer;
|
|
319
|
+
|
|
320
|
+
// CLI execution
|
|
321
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
322
|
+
const transformer = new CodeTransformer();
|
|
323
|
+
const targetDir = process.argv[2] || process.cwd();
|
|
324
|
+
|
|
325
|
+
console.log('šÆ Setting up automatic conversion tracking...\n');
|
|
326
|
+
|
|
327
|
+
const results = transformer.processDirectory(targetDir);
|
|
328
|
+
const report = transformer.generateReport();
|
|
329
|
+
|
|
330
|
+
console.log(report.summary);
|
|
331
|
+
|
|
332
|
+
// Show any errors
|
|
333
|
+
const errors = results.filter(r =!r.success);
|
|
334
|
+
if (errors.length 0) {
|
|
335
|
+
console.log('\nā ļø Some files had issues:');
|
|
336
|
+
errors.forEach(error ={
|
|
337
|
+
console.log(`ā ${path.relative(targetDir, error.filePath)}: ${error.error}`);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (report.stats.filesModified 0) {
|
|
342
|
+
console.log(`
|
|
343
|
+
š§ Next steps:
|
|
344
|
+
1. Review the modified files
|
|
345
|
+
2. Test your application to ensure everything works
|
|
346
|
+
3. The components will automatically track all interactions
|
|
347
|
+
4. Check your Keak dashboard for conversion analytics
|
|
348
|
+
|
|
349
|
+
š” To undo changes, restore from the .keak-backup files
|
|
350
|
+
`);
|
|
351
|
+
} else {
|
|
352
|
+
console.log('\n⨠No files needed modification. Your code is already optimized!');
|
|
353
|
+
}
|
|
354
|
+
}
|