humanbehavior-js 0.4.13 → 0.4.14

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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import * as fs from 'fs';
3
3
  import * as path from 'path';
4
- import * as readline from 'readline';
4
+ import * as clack from '@clack/prompts';
5
5
 
6
6
  /******************************************************************************
7
7
  Copyright (c) Microsoft Corporation.
@@ -165,6 +165,24 @@ class AutoInstallationWizard {
165
165
  projectRoot: this.projectRoot
166
166
  };
167
167
  }
168
+ else if (dependencies.astro) {
169
+ framework = {
170
+ name: 'astro',
171
+ type: 'astro',
172
+ hasTypeScript: !!dependencies.typescript || !!dependencies['@astrojs/ts-plugin'],
173
+ hasRouter: true,
174
+ projectRoot: this.projectRoot
175
+ };
176
+ }
177
+ else if (dependencies.gatsby) {
178
+ framework = {
179
+ name: 'gatsby',
180
+ type: 'gatsby',
181
+ hasTypeScript: !!dependencies.typescript || !!dependencies['@types/react'],
182
+ hasRouter: true,
183
+ projectRoot: this.projectRoot
184
+ };
185
+ }
168
186
  // Detect bundler
169
187
  if (dependencies.vite) {
170
188
  framework.bundler = 'vite';
@@ -196,13 +214,18 @@ class AutoInstallationWizard {
196
214
  */
197
215
  installPackage() {
198
216
  return __awaiter(this, void 0, void 0, function* () {
199
- var _a, _b;
217
+ var _a, _b, _c, _d;
200
218
  const { execSync } = yield import('child_process');
201
- const command = ((_a = this.framework) === null || _a === void 0 ? void 0 : _a.packageManager) === 'yarn'
219
+ // Build base command
220
+ let command = ((_a = this.framework) === null || _a === void 0 ? void 0 : _a.packageManager) === 'yarn'
202
221
  ? 'yarn add humanbehavior-js'
203
222
  : ((_b = this.framework) === null || _b === void 0 ? void 0 : _b.packageManager) === 'pnpm'
204
223
  ? 'pnpm add humanbehavior-js'
205
224
  : 'npm install humanbehavior-js';
225
+ // Add legacy peer deps flag for npm to handle dependency conflicts
226
+ if (((_c = this.framework) === null || _c === void 0 ? void 0 : _c.packageManager) !== 'yarn' && ((_d = this.framework) === null || _d === void 0 ? void 0 : _d.packageManager) !== 'pnpm') {
227
+ command += ' --legacy-peer-deps';
228
+ }
206
229
  try {
207
230
  execSync(command, { cwd: this.projectRoot, stdio: 'inherit' });
208
231
  }
@@ -228,6 +251,12 @@ class AutoInstallationWizard {
228
251
  case 'nuxt':
229
252
  modifications.push(...yield this.generateNuxtModifications());
230
253
  break;
254
+ case 'astro':
255
+ modifications.push(...yield this.generateAstroModifications());
256
+ break;
257
+ case 'gatsby':
258
+ modifications.push(...yield this.generateGatsbyModifications());
259
+ break;
231
260
  case 'remix':
232
261
  modifications.push(...yield this.generateRemixModifications());
233
262
  break;
@@ -339,6 +368,84 @@ export function Providers({ children }: { children: React.ReactNode }) {
339
368
  return modifications;
340
369
  });
341
370
  }
371
+ /**
372
+ * Generate Astro-specific modifications
373
+ */
374
+ generateAstroModifications() {
375
+ return __awaiter(this, void 0, void 0, function* () {
376
+ const modifications = [];
377
+ // Create Astro component for HumanBehavior
378
+ const astroComponentPath = path.join(this.projectRoot, 'src', 'components', 'HumanBehavior.astro');
379
+ const astroComponentContent = `---
380
+ // This component will only run on the client side
381
+ ---
382
+
383
+ <script>
384
+ import { HumanBehaviorTracker } from 'humanbehavior-js';
385
+
386
+ // Get API key from environment variable
387
+ const apiKey = import.meta.env.PUBLIC_HUMANBEHAVIOR_API_KEY;
388
+
389
+ console.log('HumanBehavior: API key found:', apiKey ? 'Yes' : 'No');
390
+
391
+ if (apiKey) {
392
+ try {
393
+ const tracker = HumanBehaviorTracker.init(apiKey);
394
+ console.log('HumanBehavior: Tracker initialized successfully');
395
+
396
+ // Test event to verify tracking is working
397
+ setTimeout(() => {
398
+ tracker.customEvent('astro_page_view', {
399
+ page: window.location.pathname,
400
+ framework: 'astro'
401
+ }).then(() => {
402
+ console.log('HumanBehavior: Test event sent successfully');
403
+ }).catch((error) => {
404
+ console.error('HumanBehavior: Failed to send test event:', error);
405
+ });
406
+ }, 1000);
407
+
408
+ } catch (error) {
409
+ console.error('HumanBehavior: Failed to initialize tracker:', error);
410
+ }
411
+ } else {
412
+ console.error('HumanBehavior: No API key found');
413
+ }
414
+ </script>`;
415
+ modifications.push({
416
+ filePath: astroComponentPath,
417
+ action: 'create',
418
+ content: astroComponentContent,
419
+ description: 'Created Astro component for HumanBehavior SDK'
420
+ });
421
+ // Find and update layout file
422
+ const layoutFiles = [
423
+ path.join(this.projectRoot, 'src', 'layouts', 'Layout.astro'),
424
+ path.join(this.projectRoot, 'src', 'layouts', 'layout.astro'),
425
+ path.join(this.projectRoot, 'src', 'layouts', 'BaseLayout.astro')
426
+ ];
427
+ let layoutFile = null;
428
+ for (const file of layoutFiles) {
429
+ if (fs.existsSync(file)) {
430
+ layoutFile = file;
431
+ break;
432
+ }
433
+ }
434
+ if (layoutFile) {
435
+ const content = fs.readFileSync(layoutFile, 'utf8');
436
+ const modifiedContent = this.injectAstroLayout(content);
437
+ modifications.push({
438
+ filePath: layoutFile,
439
+ action: 'modify',
440
+ content: modifiedContent,
441
+ description: 'Added HumanBehavior component to Astro layout'
442
+ });
443
+ }
444
+ // Add environment variable
445
+ modifications.push(this.createEnvironmentModification(this.framework));
446
+ return modifications;
447
+ });
448
+ }
342
449
  /**
343
450
  * Generate Nuxt-specific modifications
344
451
  */
@@ -559,6 +666,50 @@ export default defineNuxtPlugin(() => {
559
666
  return modifications;
560
667
  });
561
668
  }
669
+ /**
670
+ * Generate Gatsby-specific modifications
671
+ */
672
+ generateGatsbyModifications() {
673
+ return __awaiter(this, void 0, void 0, function* () {
674
+ const modifications = [];
675
+ // Modify or create gatsby-browser.js for Gatsby
676
+ const gatsbyBrowserFile = path.join(this.projectRoot, 'gatsby-browser.js');
677
+ if (fs.existsSync(gatsbyBrowserFile)) {
678
+ const content = fs.readFileSync(gatsbyBrowserFile, 'utf8');
679
+ const modifiedContent = this.injectGatsbyBrowser(content);
680
+ modifications.push({
681
+ filePath: gatsbyBrowserFile,
682
+ action: 'modify',
683
+ content: modifiedContent,
684
+ description: 'Added HumanBehavior initialization to Gatsby browser'
685
+ });
686
+ }
687
+ else {
688
+ // Create gatsby-browser.js if it doesn't exist
689
+ modifications.push({
690
+ filePath: gatsbyBrowserFile,
691
+ action: 'create',
692
+ content: `import { HumanBehaviorTracker } from 'humanbehavior-js';
693
+
694
+ export const onClientEntry = () => {
695
+ console.log('Gatsby browser entry point loaded');
696
+ const apiKey = process.env.GATSBY_HUMANBEHAVIOR_API_KEY;
697
+ console.log('API Key found:', apiKey ? 'Yes' : 'No');
698
+ if (apiKey) {
699
+ const tracker = HumanBehaviorTracker.init(apiKey);
700
+ console.log('HumanBehavior SDK initialized for Gatsby');
701
+ } else {
702
+ console.log('No API key found in environment variables');
703
+ }
704
+ };`,
705
+ description: 'Created gatsby-browser.js with HumanBehavior initialization'
706
+ });
707
+ }
708
+ // Create or append to environment file
709
+ modifications.push(this.createEnvironmentModification(this.framework));
710
+ return modifications;
711
+ });
712
+ }
562
713
  /**
563
714
  * Apply modifications to the codebase
564
715
  */
@@ -864,6 +1015,37 @@ if (typeof window !== 'undefined') {
864
1015
  </script>`;
865
1016
  return content.replace(/<\/head>/, ` ${cdnScript}\n ${initScript}\n</head>`);
866
1017
  }
1018
+ /**
1019
+ * Inject Astro layout with HumanBehavior component
1020
+ */
1021
+ injectAstroLayout(content) {
1022
+ // Check if HumanBehavior component is already imported
1023
+ if (content.includes('HumanBehavior') || content.includes('humanbehavior-js')) {
1024
+ return content; // Already has HumanBehavior
1025
+ }
1026
+ // Add import inside frontmatter if not present
1027
+ let modifiedContent = content;
1028
+ if (!content.includes('import HumanBehavior')) {
1029
+ const importStatement = 'import HumanBehavior from \'../components/HumanBehavior.astro\';';
1030
+ const frontmatterEndIndex = content.indexOf('---', 3);
1031
+ if (frontmatterEndIndex !== -1) {
1032
+ // Insert import inside frontmatter, before the closing ---
1033
+ modifiedContent = content.slice(0, frontmatterEndIndex) + '\n' + importStatement + '\n' + content.slice(frontmatterEndIndex);
1034
+ }
1035
+ else {
1036
+ // No frontmatter, add at the very beginning
1037
+ modifiedContent = '---\n' + importStatement + '\n---\n\n' + content;
1038
+ }
1039
+ }
1040
+ // Find the closing </body> tag and add HumanBehavior component before it
1041
+ const bodyCloseIndex = modifiedContent.lastIndexOf('</body>');
1042
+ if (bodyCloseIndex === -1) {
1043
+ // No body tag found, append to end
1044
+ return modifiedContent + '\n\n<HumanBehavior />';
1045
+ }
1046
+ // Add component before closing body tag
1047
+ return modifiedContent.slice(0, bodyCloseIndex) + ' <HumanBehavior />\n' + modifiedContent.slice(bodyCloseIndex);
1048
+ }
867
1049
  injectNuxtConfig(content) {
868
1050
  if (content.includes('humanBehaviorApiKey')) {
869
1051
  return content;
@@ -876,6 +1058,45 @@ if (typeof window !== 'undefined') {
876
1058
  }
877
1059
  },`);
878
1060
  }
1061
+ injectGatsbyLayout(content) {
1062
+ if (content.includes('HumanBehavior')) {
1063
+ return content;
1064
+ }
1065
+ const importStatement = `import HumanBehavior from './HumanBehavior';`;
1066
+ const componentUsage = `<HumanBehavior apiKey={process.env.GATSBY_HUMANBEHAVIOR_API_KEY || ''} />`;
1067
+ // Add import at the top
1068
+ let modifiedContent = content.replace(/import.*from.*['"]\./, `${importStatement}\n$&`);
1069
+ // Add component before closing body tag
1070
+ modifiedContent = modifiedContent.replace(/(\s*<\/body>)/, `\n ${componentUsage}\n$1`);
1071
+ return modifiedContent;
1072
+ }
1073
+ injectGatsbyBrowser(content) {
1074
+ if (content.includes('HumanBehaviorTracker')) {
1075
+ return content;
1076
+ }
1077
+ const importStatement = `import { HumanBehaviorTracker } from 'humanbehavior-js';`;
1078
+ const initCode = `
1079
+ // Initialize HumanBehavior SDK
1080
+ export const onClientEntry = () => {
1081
+ console.log('Gatsby browser entry point loaded');
1082
+ const apiKey = process.env.GATSBY_HUMANBEHAVIOR_API_KEY;
1083
+ console.log('API Key found:', apiKey ? 'Yes' : 'No');
1084
+ if (apiKey) {
1085
+ const tracker = HumanBehaviorTracker.init(apiKey);
1086
+ console.log('HumanBehavior SDK initialized for Gatsby');
1087
+ } else {
1088
+ console.log('No API key found in environment variables');
1089
+ }
1090
+ };`;
1091
+ // If the file already has content, add the import and init code
1092
+ if (content.trim()) {
1093
+ return `${importStatement}${initCode}\n\n${content}`;
1094
+ }
1095
+ else {
1096
+ // If file is empty, just return the new content
1097
+ return `${importStatement}${initCode}`;
1098
+ }
1099
+ }
879
1100
  /**
880
1101
  * Helper method to find the best environment file for a framework
881
1102
  */
@@ -905,6 +1126,8 @@ if (typeof window !== 'undefined') {
905
1126
  nuxt: 'NUXT_PUBLIC_HUMANBEHAVIOR_API_KEY',
906
1127
  remix: 'HUMANBEHAVIOR_API_KEY',
907
1128
  vanilla: 'HUMANBEHAVIOR_API_KEY',
1129
+ astro: 'PUBLIC_HUMANBEHAVIOR_API_KEY',
1130
+ gatsby: 'GATSBY_HUMANBEHAVIOR_API_KEY',
908
1131
  node: 'HUMANBEHAVIOR_API_KEY',
909
1132
  auto: 'HUMANBEHAVIOR_API_KEY'
910
1133
  };
@@ -928,6 +1151,8 @@ if (typeof window !== 'undefined') {
928
1151
  nuxt: '.env',
929
1152
  remix: '.env.local',
930
1153
  vanilla: '.env',
1154
+ astro: '.env',
1155
+ gatsby: '.env.development',
931
1156
  node: '.env',
932
1157
  auto: '.env'
933
1158
  };
@@ -942,6 +1167,8 @@ if (typeof window !== 'undefined') {
942
1167
  */
943
1168
  createEnvironmentModification(framework) {
944
1169
  const { filePath, envVarName } = this.findBestEnvFile(framework);
1170
+ // Clean the API key to prevent formatting issues
1171
+ const cleanApiKey = this.apiKey.trim();
945
1172
  if (fs.existsSync(filePath)) {
946
1173
  // Check if the variable already exists
947
1174
  const content = fs.readFileSync(filePath, 'utf8');
@@ -959,7 +1186,7 @@ if (typeof window !== 'undefined') {
959
1186
  return {
960
1187
  filePath,
961
1188
  action: 'append',
962
- content: `\n${envVarName}=${this.apiKey}`,
1189
+ content: `\n${envVarName}=${cleanApiKey}`,
963
1190
  description: `Added API key to existing ${path.basename(filePath)}`
964
1191
  };
965
1192
  }
@@ -969,13 +1196,435 @@ if (typeof window !== 'undefined') {
969
1196
  return {
970
1197
  filePath,
971
1198
  action: 'create',
972
- content: `${envVarName}=${this.apiKey}`,
1199
+ content: `${envVarName}=${cleanApiKey}`,
973
1200
  description: `Created ${path.basename(filePath)} with API key`
974
1201
  };
975
1202
  }
976
1203
  }
977
1204
  }
978
1205
 
1206
+ /**
1207
+ * Remote AI Service Implementation
1208
+ *
1209
+ * This connects to your deployed Lambda function via API Gateway
1210
+ */
1211
+ class RemoteAIService {
1212
+ constructor(config) {
1213
+ this.config = Object.assign({ timeout: 10000 }, config);
1214
+ }
1215
+ /**
1216
+ * Analyze code patterns using your deployed AI service
1217
+ */
1218
+ analyzeCodePatterns(codeSamples) {
1219
+ return __awaiter(this, void 0, void 0, function* () {
1220
+ try {
1221
+ const response = yield fetch(`${this.config.apiEndpoint}/analyze`, {
1222
+ method: 'POST',
1223
+ headers: {
1224
+ 'Content-Type': 'application/json',
1225
+ },
1226
+ body: JSON.stringify({ codeSamples }),
1227
+ signal: AbortSignal.timeout(this.config.timeout || 10000)
1228
+ });
1229
+ if (!response.ok) {
1230
+ throw new Error(`AI service returned ${response.status}: ${response.statusText}`);
1231
+ }
1232
+ const result = yield response.json();
1233
+ return result.analysis;
1234
+ }
1235
+ catch (error) {
1236
+ console.warn('Remote AI service failed, falling back to heuristic analysis:', error);
1237
+ return this.performHeuristicAnalysis(codeSamples);
1238
+ }
1239
+ });
1240
+ }
1241
+ /**
1242
+ * Resolve conflicts using your deployed AI service
1243
+ */
1244
+ resolveConflicts(conflicts, framework) {
1245
+ return __awaiter(this, void 0, void 0, function* () {
1246
+ try {
1247
+ const response = yield fetch(`${this.config.apiEndpoint}/resolve-conflicts`, {
1248
+ method: 'POST',
1249
+ headers: {
1250
+ 'Content-Type': 'application/json',
1251
+ },
1252
+ body: JSON.stringify({ conflicts, framework }),
1253
+ signal: AbortSignal.timeout(this.config.timeout || 10000)
1254
+ });
1255
+ if (!response.ok) {
1256
+ throw new Error(`AI service returned ${response.status}: ${response.statusText}`);
1257
+ }
1258
+ const result = yield response.json();
1259
+ return result.resolutions || [];
1260
+ }
1261
+ catch (error) {
1262
+ console.warn('Remote AI conflict resolution failed, using heuristic approach:', error);
1263
+ return this.resolveConflictsHeuristic(conflicts, framework);
1264
+ }
1265
+ });
1266
+ }
1267
+ /**
1268
+ * Generate optimizations using your deployed AI service
1269
+ */
1270
+ generateOptimizations(framework, patterns) {
1271
+ return __awaiter(this, void 0, void 0, function* () {
1272
+ try {
1273
+ const response = yield fetch(`${this.config.apiEndpoint}/optimize`, {
1274
+ method: 'POST',
1275
+ headers: {
1276
+ 'Content-Type': 'application/json',
1277
+ },
1278
+ body: JSON.stringify({ framework, patterns }),
1279
+ signal: AbortSignal.timeout(this.config.timeout || 10000)
1280
+ });
1281
+ if (!response.ok) {
1282
+ throw new Error(`AI service returned ${response.status}: ${response.statusText}`);
1283
+ }
1284
+ const result = yield response.json();
1285
+ return result.optimizations || [];
1286
+ }
1287
+ catch (error) {
1288
+ console.warn('Remote AI optimization generation failed, using heuristic approach:', error);
1289
+ return this.generateOptimizationsHeuristic(framework, patterns);
1290
+ }
1291
+ });
1292
+ }
1293
+ /**
1294
+ * Heuristic analysis fallback
1295
+ */
1296
+ performHeuristicAnalysis(codeSamples) {
1297
+ const patterns = codeSamples.join(' ').toLowerCase();
1298
+ // Framework detection
1299
+ let framework = { name: 'vanilla', type: 'vanilla' };
1300
+ let confidence = 0.5;
1301
+ if (patterns.includes('nuxt') || patterns.includes('nuxtjs') || patterns.includes('defineNuxtConfig') || patterns.includes('nuxt.config') || patterns.includes('@nuxt/') || patterns.includes('useNuxtApp') || patterns.includes('useRuntimeConfig') || patterns.includes('useSeoMeta') || patterns.includes('useHead') || patterns.includes('useLazyFetch') || patterns.includes('useFetch') || patterns.includes('useAsyncData') || patterns.includes('#app')) {
1302
+ framework = { name: 'nuxt', type: 'nuxt' };
1303
+ confidence = 0.95;
1304
+ }
1305
+ else if (patterns.includes('next') || patterns.includes('nextjs') || patterns.includes('next/link') || patterns.includes('next/image') || patterns.includes('next/navigation') || patterns.includes('next/router') || patterns.includes('getserverSideProps') || patterns.includes('getstaticProps') || patterns.includes('getstaticPaths') || patterns.includes('app/layout') || patterns.includes('app/page') || patterns.includes('pages/')) {
1306
+ framework = { name: 'nextjs', type: 'nextjs' };
1307
+ confidence = 0.95;
1308
+ }
1309
+ else if (patterns.includes('gatsby') || patterns.includes('gatsby-browser') || patterns.includes('gatsby-ssr') || patterns.includes('gatsby-node') || patterns.includes('gatsby-config') || patterns.includes('useStaticQuery') || patterns.includes('graphql')) {
1310
+ framework = { name: 'gatsby', type: 'gatsby' };
1311
+ confidence = 0.95;
1312
+ }
1313
+ else if (patterns.includes('react')) {
1314
+ framework = { name: 'react', type: 'react' };
1315
+ confidence = 0.9;
1316
+ }
1317
+ else if (patterns.includes('vue')) {
1318
+ framework = { name: 'vue', type: 'vue' };
1319
+ confidence = 0.9;
1320
+ }
1321
+ else if (patterns.includes('angular')) {
1322
+ framework = { name: 'angular', type: 'angular' };
1323
+ confidence = 0.9;
1324
+ }
1325
+ else if (patterns.includes('svelte')) {
1326
+ framework = { name: 'svelte', type: 'svelte' };
1327
+ confidence = 0.9;
1328
+ }
1329
+ else if (patterns.includes('astro')) {
1330
+ framework = { name: 'astro', type: 'astro' };
1331
+ confidence = 0.9;
1332
+ }
1333
+ // Integration strategy
1334
+ let integrationStrategy = 'script';
1335
+ if (framework.type === 'react' || framework.type === 'nextjs' || framework.type === 'gatsby') {
1336
+ integrationStrategy = 'provider';
1337
+ }
1338
+ else if (framework.type === 'vue') {
1339
+ integrationStrategy = 'plugin';
1340
+ }
1341
+ else if (framework.type === 'angular') {
1342
+ integrationStrategy = 'module';
1343
+ }
1344
+ // Compatibility mode
1345
+ let compatibilityMode = 'modern';
1346
+ if (patterns.includes('require(') || patterns.includes('var ')) {
1347
+ compatibilityMode = 'legacy';
1348
+ }
1349
+ return {
1350
+ framework,
1351
+ confidence,
1352
+ patterns: codeSamples,
1353
+ conflicts: [],
1354
+ recommendations: [],
1355
+ integrationStrategy,
1356
+ compatibilityMode
1357
+ };
1358
+ }
1359
+ /**
1360
+ * Heuristic conflict resolution
1361
+ */
1362
+ resolveConflictsHeuristic(conflicts, framework) {
1363
+ const resolutions = [];
1364
+ for (const conflict of conflicts) {
1365
+ switch (conflict) {
1366
+ case 'existing_humanbehavior_code':
1367
+ resolutions.push('update_existing_integration');
1368
+ break;
1369
+ case 'existing_provider':
1370
+ resolutions.push('merge_providers');
1371
+ break;
1372
+ case 'module_system_conflict':
1373
+ resolutions.push('hybrid_module_support');
1374
+ break;
1375
+ default:
1376
+ resolutions.push('skip_conflict');
1377
+ }
1378
+ }
1379
+ return resolutions;
1380
+ }
1381
+ /**
1382
+ * Heuristic optimization generation
1383
+ */
1384
+ generateOptimizationsHeuristic(framework, patterns) {
1385
+ const optimizations = [];
1386
+ switch (framework.type) {
1387
+ case 'react':
1388
+ optimizations.push('Use React.memo for performance optimization');
1389
+ optimizations.push('Implement error boundaries for better error tracking');
1390
+ optimizations.push('Consider using React.lazy for code splitting');
1391
+ break;
1392
+ case 'vue':
1393
+ optimizations.push('Use Vue 3 Composition API for better performance');
1394
+ optimizations.push('Implement proper error handling in components');
1395
+ optimizations.push('Consider using Vue Router for navigation tracking');
1396
+ break;
1397
+ case 'angular':
1398
+ optimizations.push('Use Angular standalone components for better tree-shaking');
1399
+ optimizations.push('Implement proper error handling with ErrorHandler');
1400
+ optimizations.push('Consider using Angular signals for state management');
1401
+ break;
1402
+ default:
1403
+ optimizations.push('Enable performance tracking');
1404
+ optimizations.push('Implement error tracking');
1405
+ optimizations.push('Consider progressive enhancement');
1406
+ }
1407
+ return optimizations;
1408
+ }
1409
+ }
1410
+
1411
+ /**
1412
+ * Manual Framework Installation Wizard
1413
+ *
1414
+ * This wizard allows users to manually specify their framework instead of auto-detection.
1415
+ * Useful when auto-detection fails or users want more control.
1416
+ */
1417
+ class ManualFrameworkInstallationWizard extends AutoInstallationWizard {
1418
+ constructor(apiKey, projectRoot = process.cwd(), framework) {
1419
+ super(apiKey, projectRoot);
1420
+ this.selectedFramework = framework.toLowerCase();
1421
+ this.framework = this.createFrameworkInfo(this.selectedFramework);
1422
+ }
1423
+ /**
1424
+ * Manual installation with user-specified framework
1425
+ */
1426
+ install() {
1427
+ return __awaiter(this, void 0, void 0, function* () {
1428
+ try {
1429
+ // Step 1: Handle framework selection
1430
+ if (this.selectedFramework === 'auto') {
1431
+ // Use full AI detection for "Other" option
1432
+ this.framework = yield this.runFullDetection();
1433
+ }
1434
+ else {
1435
+ // Set framework based on user selection
1436
+ this.framework = this.createFrameworkInfo(this.selectedFramework);
1437
+ if (!this.framework) {
1438
+ this.framework = { name: 'unknown', type: 'vanilla' };
1439
+ }
1440
+ // Step 2: Run full detection logic to find entry points, file names, etc.
1441
+ const detectedFramework = yield this.runFullDetection();
1442
+ // Step 3: Merge manual framework with detected details
1443
+ this.framework = Object.assign(Object.assign({}, detectedFramework), { name: this.framework.name, type: this.framework.type });
1444
+ }
1445
+ // Step 4: Install package
1446
+ yield this.installPackage();
1447
+ // Step 5: Generate and apply code modifications
1448
+ const modifications = yield this.generateModifications();
1449
+ yield this.applyModifications(modifications);
1450
+ // Step 6: Generate next steps
1451
+ const nextSteps = this.generateManualNextSteps();
1452
+ return {
1453
+ success: true,
1454
+ framework: this.framework,
1455
+ modifications,
1456
+ errors: [],
1457
+ nextSteps,
1458
+ selectedFramework: this.selectedFramework,
1459
+ manualMode: true
1460
+ };
1461
+ }
1462
+ catch (error) {
1463
+ return {
1464
+ success: false,
1465
+ framework: this.framework || { name: 'unknown', type: 'vanilla' },
1466
+ modifications: [],
1467
+ errors: [error instanceof Error ? error.message : 'Unknown error'],
1468
+ nextSteps: [],
1469
+ selectedFramework: this.selectedFramework,
1470
+ manualMode: true
1471
+ };
1472
+ }
1473
+ });
1474
+ }
1475
+ /**
1476
+ * Run full detection logic to find entry points, file names, bundler, etc.
1477
+ */
1478
+ runFullDetection() {
1479
+ return __awaiter(this, void 0, void 0, function* () {
1480
+ if (this.selectedFramework === 'auto') {
1481
+ // Use AI service for auto-detection
1482
+ const aiService = new RemoteAIService({
1483
+ apiEndpoint: 'https://ik3zxh4790.execute-api.us-east-1.amazonaws.com/prod'
1484
+ });
1485
+ // Use AI service directly for detection
1486
+ const projectFiles = yield this.scanProjectFiles();
1487
+ const codeSamples = yield this.extractCodeSamples(projectFiles);
1488
+ const aiAnalysis = yield aiService.analyzeCodePatterns(codeSamples);
1489
+ return aiAnalysis.framework;
1490
+ }
1491
+ else {
1492
+ // Use traditional detection for manual frameworks
1493
+ const tempWizard = new AutoInstallationWizard(this.apiKey, this.projectRoot);
1494
+ const detected = yield tempWizard.detectFramework();
1495
+ return detected;
1496
+ }
1497
+ });
1498
+ }
1499
+ /**
1500
+ * Scan project files for analysis
1501
+ */
1502
+ scanProjectFiles() {
1503
+ return __awaiter(this, void 0, void 0, function* () {
1504
+ const files = [];
1505
+ const scanDir = (dir, depth = 0) => {
1506
+ if (depth > 3)
1507
+ return; // Limit depth
1508
+ try {
1509
+ const items = fs.readdirSync(dir);
1510
+ for (const item of items) {
1511
+ const fullPath = path.join(dir, item);
1512
+ const stat = fs.statSync(fullPath);
1513
+ if (stat.isDirectory() && !item.startsWith('.') && item !== 'node_modules') {
1514
+ scanDir(fullPath, depth + 1);
1515
+ }
1516
+ else if (stat.isFile() && this.isRelevantFile(item)) {
1517
+ files.push(fullPath);
1518
+ }
1519
+ }
1520
+ }
1521
+ catch (error) {
1522
+ // Skip inaccessible directories
1523
+ }
1524
+ };
1525
+ scanDir(this.projectRoot);
1526
+ return files;
1527
+ });
1528
+ }
1529
+ /**
1530
+ * Check if file is relevant for analysis
1531
+ */
1532
+ isRelevantFile(filename) {
1533
+ const relevantExtensions = [
1534
+ '.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte', '.html',
1535
+ '.json', '.config.js', '.config.ts', '.babelrc', '.eslintrc'
1536
+ ];
1537
+ const relevantNames = [
1538
+ 'package.json', 'tsconfig.json', 'vite.config', 'webpack.config',
1539
+ 'next.config', 'nuxt.config', 'angular.json', 'svelte.config'
1540
+ ];
1541
+ return relevantExtensions.some(ext => filename.endsWith(ext)) ||
1542
+ relevantNames.some(name => filename.includes(name));
1543
+ }
1544
+ /**
1545
+ * Extract code samples for AI analysis
1546
+ */
1547
+ extractCodeSamples(files) {
1548
+ return __awaiter(this, void 0, void 0, function* () {
1549
+ const samples = [];
1550
+ for (const file of files.slice(0, 20)) { // Limit to 20 files
1551
+ try {
1552
+ const content = fs.readFileSync(file, 'utf8');
1553
+ const relativePath = path.relative(this.projectRoot, file);
1554
+ samples.push(`File: ${relativePath}\n${content.substring(0, 1000)}`);
1555
+ }
1556
+ catch (error) {
1557
+ // Skip unreadable files
1558
+ }
1559
+ }
1560
+ return samples;
1561
+ });
1562
+ }
1563
+ /**
1564
+ * Create framework info based on user selection
1565
+ */
1566
+ createFrameworkInfo(framework) {
1567
+ const frameworkMap = {
1568
+ 'react': { name: 'react', type: 'react' },
1569
+ 'nextjs': { name: 'nextjs', type: 'nextjs' },
1570
+ 'next': { name: 'nextjs', type: 'nextjs' },
1571
+ 'vue': { name: 'vue', type: 'vue' },
1572
+ 'nuxt': { name: 'nuxt', type: 'nuxt' },
1573
+ 'nuxtjs': { name: 'nuxt', type: 'nuxt' },
1574
+ 'angular': { name: 'angular', type: 'angular' },
1575
+ 'svelte': { name: 'svelte', type: 'svelte' },
1576
+ 'sveltekit': { name: 'svelte', type: 'svelte' },
1577
+ 'remix': { name: 'remix', type: 'remix' },
1578
+ 'astro': { name: 'astro', type: 'astro' },
1579
+ 'gatsby': { name: 'gatsby', type: 'gatsby' },
1580
+ 'vanilla': { name: 'vanilla', type: 'vanilla' },
1581
+ 'node': { name: 'node', type: 'node' },
1582
+ 'auto': { name: 'auto-detected', type: 'auto' }
1583
+ };
1584
+ return frameworkMap[framework] || { name: framework, type: 'vanilla' };
1585
+ }
1586
+ /**
1587
+ * Override framework detection to use manual selection
1588
+ */
1589
+ detectFramework() {
1590
+ return __awaiter(this, void 0, void 0, function* () {
1591
+ return this.framework || { name: 'unknown', type: 'vanilla' };
1592
+ });
1593
+ }
1594
+ /**
1595
+ * Generate next steps with manual mode info
1596
+ */
1597
+ generateManualNextSteps() {
1598
+ var _a;
1599
+ return [
1600
+ 'āœ… Manual framework installation completed!',
1601
+ `šŸŽÆ Selected framework: ${((_a = this.framework) === null || _a === void 0 ? void 0 : _a.name) || 'unknown'}`,
1602
+ `šŸ”§ Integration strategy: ${this.getIntegrationStrategy()}`,
1603
+ 'šŸš€ Your app is now ready to track user behavior',
1604
+ 'šŸ“Š View sessions in your HumanBehavior dashboard'
1605
+ ];
1606
+ }
1607
+ /**
1608
+ * Get integration strategy based on framework
1609
+ */
1610
+ getIntegrationStrategy() {
1611
+ var _a;
1612
+ if (!((_a = this.framework) === null || _a === void 0 ? void 0 : _a.type))
1613
+ return 'script';
1614
+ switch (this.framework.type) {
1615
+ case 'react':
1616
+ case 'nextjs':
1617
+ return 'provider';
1618
+ case 'vue':
1619
+ return 'plugin';
1620
+ case 'angular':
1621
+ return 'module';
1622
+ default:
1623
+ return 'script';
1624
+ }
1625
+ }
1626
+ }
1627
+
979
1628
  /**
980
1629
  * HumanBehavior SDK Auto-Installation CLI
981
1630
  *
@@ -987,46 +1636,46 @@ if (typeof window !== 'undefined') {
987
1636
  class AutoInstallCLI {
988
1637
  constructor(options) {
989
1638
  this.options = options;
990
- this.rl = readline.createInterface({
991
- input: process.stdin,
992
- output: process.stdout
993
- });
994
1639
  }
995
1640
  run() {
996
1641
  return __awaiter(this, void 0, void 0, function* () {
997
- console.log('šŸš€ HumanBehavior SDK Auto-Installation');
998
- console.log('=====================================\n');
1642
+ clack.intro('šŸš€ HumanBehavior SDK Auto-Installation');
999
1643
  try {
1000
1644
  // Get API key
1001
1645
  const apiKey = yield this.getApiKey();
1002
1646
  if (!apiKey) {
1003
- console.error('āŒ API key is required');
1647
+ clack.cancel('API key is required');
1004
1648
  process.exit(1);
1005
1649
  }
1006
1650
  // Get project path
1007
1651
  const projectPath = this.options.projectPath || process.cwd();
1652
+ // Choose framework
1653
+ const framework = yield this.chooseFramework();
1654
+ if (!framework) {
1655
+ clack.cancel('Installation cancelled.');
1656
+ process.exit(0);
1657
+ }
1008
1658
  // Confirm installation
1009
1659
  if (!this.options.yes) {
1010
- const confirmed = yield this.confirmInstallation(projectPath);
1660
+ const confirmed = yield this.confirmInstallation(projectPath, framework);
1011
1661
  if (!confirmed) {
1012
- console.log('Installation cancelled.');
1662
+ clack.cancel('Installation cancelled.');
1013
1663
  process.exit(0);
1014
1664
  }
1015
1665
  }
1016
- // Run auto-installation
1017
- console.log('šŸ” Detecting your project setup...');
1018
- const wizard = new AutoInstallationWizard(apiKey, projectPath);
1666
+ // Run installation
1667
+ const spinner = clack.spinner();
1668
+ spinner.start('šŸ” Analyzing your project...');
1669
+ const wizard = new ManualFrameworkInstallationWizard(apiKey, projectPath, framework);
1019
1670
  const result = yield wizard.install();
1671
+ spinner.stop('Detection complete!');
1020
1672
  // Display results
1021
1673
  this.displayResults(result);
1022
1674
  }
1023
1675
  catch (error) {
1024
- console.error('āŒ Error:', error instanceof Error ? error.message : error);
1676
+ clack.cancel(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
1025
1677
  process.exit(1);
1026
1678
  }
1027
- finally {
1028
- this.rl.close();
1029
- }
1030
1679
  });
1031
1680
  }
1032
1681
  getApiKey() {
@@ -1034,139 +1683,131 @@ class AutoInstallCLI {
1034
1683
  if (this.options.apiKey) {
1035
1684
  return this.options.apiKey;
1036
1685
  }
1037
- return new Promise((resolve) => {
1038
- this.rl.question('Enter your HumanBehavior API key: ', (answer) => {
1039
- resolve(answer.trim());
1040
- });
1686
+ const apiKey = yield clack.text({
1687
+ message: 'Enter your HumanBehavior API key:',
1688
+ placeholder: 'hb_...',
1689
+ validate: (value) => {
1690
+ if (!value)
1691
+ return 'API key is required';
1692
+ if (!value.startsWith('hb_'))
1693
+ return 'API key should start with "hb_"';
1694
+ return undefined;
1695
+ }
1041
1696
  });
1697
+ return apiKey;
1042
1698
  });
1043
1699
  }
1044
- confirmInstallation(projectPath) {
1700
+ chooseFramework() {
1045
1701
  return __awaiter(this, void 0, void 0, function* () {
1046
- console.log(`šŸ“ Project path: ${projectPath}`);
1047
- console.log('āš ļø This will modify your codebase to integrate HumanBehavior SDK.');
1048
- console.log(' The following changes will be made:');
1049
- console.log(' - Install humanbehavior-js package');
1050
- console.log(' - Modify your main app file');
1051
- console.log(' - Create environment files');
1052
- console.log(' - Add SDK initialization code\n');
1053
- return new Promise((resolve) => {
1054
- this.rl.question('Continue with auto-installation? (y/n): ', (answer) => {
1055
- resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
1056
- });
1702
+ const framework = yield clack.select({
1703
+ message: 'Select your framework:',
1704
+ options: [
1705
+ { label: 'React', value: 'react' },
1706
+ { label: 'Next.js', value: 'nextjs' },
1707
+ { label: 'Vue', value: 'vue' },
1708
+ { label: 'Angular', value: 'angular' },
1709
+ { label: 'Svelte', value: 'svelte' },
1710
+ { label: 'Nuxt.js', value: 'nuxt' },
1711
+ { label: 'Remix', value: 'remix' },
1712
+ { label: 'Astro', value: 'astro' },
1713
+ { label: 'Gatsby', value: 'gatsby' },
1714
+ { label: 'Vanilla JS/TS', value: 'vanilla' }
1715
+ ]
1057
1716
  });
1717
+ return framework;
1718
+ });
1719
+ }
1720
+ confirmInstallation(projectPath, framework) {
1721
+ return __awaiter(this, void 0, void 0, function* () {
1722
+ const confirmed = yield clack.confirm({
1723
+ message: `Ready to install HumanBehavior SDK in ${projectPath} for ${framework}?`
1724
+ });
1725
+ return confirmed;
1058
1726
  });
1059
1727
  }
1060
1728
  displayResults(result) {
1061
1729
  if (result.success) {
1062
- console.log('\nāœ… Installation completed successfully!');
1063
- console.log(`šŸ“¦ Framework detected: ${result.framework.name}`);
1064
- if (result.framework.bundler) {
1065
- console.log(`šŸ”§ Bundler: ${result.framework.bundler}`);
1066
- }
1067
- if (result.framework.packageManager) {
1068
- console.log(`šŸ“‹ Package Manager: ${result.framework.packageManager}`);
1069
- }
1070
- console.log('\nšŸ“ Changes made:');
1071
- result.modifications.forEach((mod) => {
1072
- console.log(` ${mod.action === 'create' ? 'āž•' : 'āœļø'} ${mod.description}`);
1073
- console.log(` ${mod.filePath}`);
1074
- });
1075
- console.log('\nšŸŽÆ Next steps:');
1076
- result.nextSteps.forEach((step) => {
1077
- console.log(` ${step}`);
1078
- });
1079
- console.log('\nšŸš€ Your app is now ready to track user behavior!');
1080
- console.log('šŸ“Š View sessions in your HumanBehavior dashboard');
1730
+ clack.outro('šŸŽ‰ Installation completed successfully!');
1731
+ // Display framework info
1732
+ clack.note(`Framework detected: ${result.framework.name} (${result.framework.type})`, 'Framework Info');
1733
+ // Display modifications
1734
+ if (result.modifications && result.modifications.length > 0) {
1735
+ const modifications = result.modifications.map((mod) => `${mod.action}: ${mod.filePath} - ${mod.description}`);
1736
+ clack.note(modifications.join('\n'), 'Files Modified');
1737
+ }
1738
+ // Display next steps
1739
+ if (result.nextSteps && result.nextSteps.length > 0) {
1740
+ clack.note(result.nextSteps.join('\n'), 'Next Steps');
1741
+ }
1081
1742
  }
1082
1743
  else {
1083
- console.log('\nāŒ Installation failed:');
1084
- result.errors.forEach((error) => {
1085
- console.log(` ${error}`);
1086
- });
1087
- console.log('\nšŸ’” Try running with --help for more options');
1744
+ clack.cancel('Installation failed');
1745
+ if (result.errors && result.errors.length > 0) {
1746
+ clack.note(result.errors.join('\n'), 'Errors');
1747
+ }
1088
1748
  }
1089
1749
  }
1090
1750
  }
1091
- // CLI argument parsing
1092
1751
  function parseArgs() {
1093
1752
  const args = process.argv.slice(2);
1094
1753
  const options = {};
1095
1754
  for (let i = 0; i < args.length; i++) {
1096
1755
  const arg = args[i];
1097
- if (arg === '--yes' || arg === '-y') {
1098
- options.yes = true;
1099
- }
1100
- else if (arg === '--dry-run' || arg === '-d') {
1101
- options.dryRun = true;
1102
- }
1103
- else if (arg === '--project' || arg === '-p') {
1104
- options.projectPath = args[i + 1];
1105
- i++;
1106
- }
1107
- else if (arg === '--help' || arg === '-h') {
1108
- showHelp();
1109
- process.exit(0);
1110
- }
1111
- else if (!options.apiKey) {
1112
- options.apiKey = arg;
1756
+ switch (arg) {
1757
+ case '--help':
1758
+ case '-h':
1759
+ showHelp();
1760
+ process.exit(0);
1761
+ break;
1762
+ case '--yes':
1763
+ case '-y':
1764
+ options.yes = true;
1765
+ break;
1766
+ case '--dry-run':
1767
+ options.dryRun = true;
1768
+ break;
1769
+ case '--project':
1770
+ case '-p':
1771
+ options.projectPath = args[++i];
1772
+ break;
1773
+ default:
1774
+ if (!options.apiKey && !arg.startsWith('-')) {
1775
+ options.apiKey = arg;
1776
+ }
1777
+ break;
1113
1778
  }
1114
1779
  }
1115
1780
  return options;
1116
1781
  }
1117
1782
  function showHelp() {
1118
1783
  console.log(`
1119
- HumanBehavior SDK Auto-Installation CLI
1120
-
1121
- Usage: npx humanbehavior-js [api-key] [options]
1784
+ šŸš€ HumanBehavior SDK Auto-Installation
1122
1785
 
1123
- This tool automatically detects your project's framework and modifies your codebase
1124
- to integrate the HumanBehavior SDK with minimal user intervention.
1786
+ Usage: npx humanbehavior-js auto-install [api-key] [options]
1125
1787
 
1126
- Arguments:
1127
- api-key Your HumanBehavior API key
1788
+ This tool automatically detects your framework and integrates the HumanBehavior SDK.
1128
1789
 
1129
1790
  Options:
1130
- -y, --yes Skip all prompts and use defaults
1131
- -d, --dry-run Show what would be changed without making changes
1132
- -p, --project <path> Project directory (default: current directory)
1133
- -h, --help Show this help message
1791
+ -h, --help Show this help message
1792
+ -y, --yes Skip all prompts and use defaults
1793
+ --dry-run Show what would be changed without making changes
1794
+ -p, --project <path> Specify project directory
1134
1795
 
1135
1796
  Examples:
1136
- npx humanbehavior-js your-api-key
1137
- npx humanbehavior-js your-api-key --yes
1138
- npx humanbehavior-js your-api-key -p /path/to/project
1139
-
1140
- Supported Frameworks:
1141
- āœ… React (CRA, Vite, Webpack)
1142
- āœ… Next.js (App Router, Pages Router)
1143
- āœ… Vue (Vue CLI, Vite)
1144
- āœ… Angular
1145
- āœ… Svelte (SvelteKit, Vite)
1146
- āœ… Remix
1147
- āœ… Vanilla JS/TS
1148
- āœ… Node.js (CommonJS & ESM)
1149
-
1150
- The tool will:
1151
- 1. šŸ” Auto-detect your project's framework and setup
1152
- 2. šŸ“¦ Install the humanbehavior-js package
1153
- 3. āœļø Modify your codebase to integrate the SDK
1154
- 4. šŸ”§ Create environment files with your API key
1155
- 5. šŸš€ Make your app ready to track user behavior
1797
+ npx humanbehavior-js auto-install
1798
+ npx humanbehavior-js auto-install hb_your_api_key_here
1799
+ npx humanbehavior-js auto-install --project ./my-app --yes
1156
1800
  `);
1157
1801
  }
1158
1802
  // Main execution
1159
- const options = parseArgs();
1160
- // Check if we have enough arguments (api-key is required)
1161
- if (process.argv.length < 3 || process.argv.includes('--help') || process.argv.includes('-h')) {
1162
- showHelp();
1163
- process.exit(0);
1803
+ if (import.meta.url === `file://${process.argv[1]}`) {
1804
+ const options = parseArgs();
1805
+ const cli = new AutoInstallCLI(options);
1806
+ cli.run().catch((error) => {
1807
+ clack.cancel(`Unexpected error: ${error.message}`);
1808
+ process.exit(1);
1809
+ });
1164
1810
  }
1165
- const cli = new AutoInstallCLI(options);
1166
- cli.run().catch((error) => {
1167
- console.error('āŒ Fatal error:', error);
1168
- process.exit(1);
1169
- });
1170
1811
 
1171
1812
  export { AutoInstallCLI };
1172
1813
  //# sourceMappingURL=auto-install.js.map