humanbehavior-js 0.5.25 → 0.5.27
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/package.json +2 -2
- package/packages/browser/dist/cjs/index.js +1 -1
- package/packages/browser/dist/cjs/index.js.map +1 -1
- package/packages/browser/dist/esm/index.js +1 -1
- package/packages/browser/dist/esm/index.js.map +1 -1
- package/packages/browser/dist/index.min.js +1 -1
- package/packages/browser/dist/index.min.js.map +1 -1
- package/packages/core/dist/index.js +1 -1
- package/packages/core/dist/index.js.map +1 -1
- package/packages/core/dist/index.mjs +1 -1
- package/packages/core/dist/index.mjs.map +1 -1
- package/packages/core/dist/tracker.d.ts.map +1 -1
- package/packages/react/dist/index.js +1 -1
- package/packages/react/dist/index.js.map +1 -1
- package/packages/react/dist/index.mjs +1 -1
- package/packages/react/dist/index.mjs.map +1 -1
- package/packages/wizard/dist/cli/ai-auto-install.js +236 -203
- package/packages/wizard/dist/cli/ai-auto-install.js.map +1 -1
- package/packages/wizard/dist/cli/auto-install.d.ts +0 -1
- package/packages/wizard/dist/cli/auto-install.d.ts.map +1 -1
- package/packages/wizard/dist/cli/auto-install.js +247 -646
- package/packages/wizard/dist/cli/auto-install.js.map +1 -1
- package/packages/wizard/dist/core/install-wizard.d.ts +5 -0
- package/packages/wizard/dist/core/install-wizard.d.ts.map +1 -1
- package/packages/wizard/dist/index.js +1 -1
- package/packages/wizard/dist/index.js.map +1 -1
- package/packages/wizard/dist/index.mjs +1 -1
- package/packages/wizard/dist/index.mjs.map +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
4
5
|
import * as clack from '@clack/prompts';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -12,6 +13,7 @@ import * as clack from '@clack/prompts';
|
|
|
12
13
|
class AutoInstallationWizard {
|
|
13
14
|
constructor(apiKey, projectRoot = process.cwd()) {
|
|
14
15
|
this.framework = null;
|
|
16
|
+
this.manualNotes = [];
|
|
15
17
|
this.apiKey = apiKey;
|
|
16
18
|
this.projectRoot = projectRoot;
|
|
17
19
|
}
|
|
@@ -257,7 +259,6 @@ class AutoInstallationWizard {
|
|
|
257
259
|
* Install the SDK package with latest version range
|
|
258
260
|
*/
|
|
259
261
|
async installPackage() {
|
|
260
|
-
const { execSync } = await import('child_process');
|
|
261
262
|
// Build base command with latest version range
|
|
262
263
|
let command = this.framework?.packageManager === 'yarn'
|
|
263
264
|
? 'yarn add humanbehavior-js@latest'
|
|
@@ -415,34 +416,9 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
415
416
|
|
|
416
417
|
<script>
|
|
417
418
|
import { HumanBehaviorTracker } from 'humanbehavior-js';
|
|
418
|
-
|
|
419
|
-
// Get API key from environment variable
|
|
420
419
|
const apiKey = import.meta.env.PUBLIC_HUMANBEHAVIOR_API_KEY;
|
|
421
|
-
|
|
422
|
-
console.log('HumanBehavior: API key found:', apiKey ? 'Yes' : 'No');
|
|
423
|
-
|
|
424
420
|
if (apiKey) {
|
|
425
|
-
|
|
426
|
-
const tracker = HumanBehaviorTracker.init(apiKey);
|
|
427
|
-
console.log('HumanBehavior: Tracker initialized successfully');
|
|
428
|
-
|
|
429
|
-
// Test event to verify tracking is working
|
|
430
|
-
setTimeout(() => {
|
|
431
|
-
tracker.customEvent('astro_page_view', {
|
|
432
|
-
page: window.location.pathname,
|
|
433
|
-
framework: 'astro'
|
|
434
|
-
}).then(() => {
|
|
435
|
-
console.log('HumanBehavior: Test event sent successfully');
|
|
436
|
-
}).catch((error) => {
|
|
437
|
-
console.error('HumanBehavior: Failed to send test event:', error);
|
|
438
|
-
});
|
|
439
|
-
}, 1000);
|
|
440
|
-
|
|
441
|
-
} catch (error) {
|
|
442
|
-
console.error('HumanBehavior: Failed to initialize tracker:', error);
|
|
443
|
-
}
|
|
444
|
-
} else {
|
|
445
|
-
console.error('HumanBehavior: No API key found');
|
|
421
|
+
HumanBehaviorTracker.init(apiKey);
|
|
446
422
|
}
|
|
447
423
|
</script>`;
|
|
448
424
|
modifications.push({
|
|
@@ -492,21 +468,10 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
492
468
|
|
|
493
469
|
export default defineNuxtPlugin(() => {
|
|
494
470
|
const config = useRuntimeConfig();
|
|
495
|
-
|
|
496
|
-
// Initialize HumanBehavior SDK (client-side only)
|
|
497
471
|
if (typeof window !== 'undefined') {
|
|
498
472
|
const apiKey = config.public.humanBehaviorApiKey;
|
|
499
|
-
console.log('HumanBehavior: API key:', apiKey ? 'present' : 'missing');
|
|
500
|
-
|
|
501
473
|
if (apiKey) {
|
|
502
|
-
|
|
503
|
-
const tracker = HumanBehaviorTracker.init(apiKey);
|
|
504
|
-
console.log('HumanBehavior: Tracker initialized successfully');
|
|
505
|
-
} catch (error) {
|
|
506
|
-
console.error('HumanBehavior: Failed to initialize tracker:', error);
|
|
507
|
-
}
|
|
508
|
-
} else {
|
|
509
|
-
console.error('HumanBehavior: No API key found in runtime config');
|
|
474
|
+
HumanBehaviorTracker.init(apiKey);
|
|
510
475
|
}
|
|
511
476
|
}
|
|
512
477
|
});`,
|
|
@@ -514,15 +479,10 @@ export default defineNuxtPlugin(() => {
|
|
|
514
479
|
});
|
|
515
480
|
// Create environment configuration
|
|
516
481
|
const nuxtConfigFile = path.join(this.projectRoot, 'nuxt.config.ts');
|
|
517
|
-
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
filePath: nuxtConfigFile,
|
|
522
|
-
action: 'modify',
|
|
523
|
-
content: modifiedContent,
|
|
524
|
-
description: 'Added HumanBehavior runtime config to Nuxt config'
|
|
525
|
-
});
|
|
482
|
+
{
|
|
483
|
+
const mod = this.applyOrNotify(nuxtConfigFile, (c) => this.injectNuxtConfig(c), 'Added HumanBehavior runtime config to Nuxt config', 'Nuxt: Add inside defineNuxtConfig({ … }):\nruntimeConfig: { public: { humanBehaviorApiKey: process.env.NUXT_PUBLIC_HUMANBEHAVIOR_API_KEY } },');
|
|
484
|
+
if (mod)
|
|
485
|
+
modifications.push(mod);
|
|
526
486
|
}
|
|
527
487
|
// Create or append to environment file
|
|
528
488
|
modifications.push(this.createEnvironmentModification(this.framework));
|
|
@@ -556,6 +516,37 @@ export default defineNuxtPlugin(() => {
|
|
|
556
516
|
const modifications = [];
|
|
557
517
|
// Find main.js or main.ts
|
|
558
518
|
const mainFile = this.findVueMainFile();
|
|
519
|
+
// Create Vue composable per docs (idempotent)
|
|
520
|
+
const composableDir = path.join(this.projectRoot, 'src', 'composables');
|
|
521
|
+
const composablePath = path.join(composableDir, 'useHumanBehavior.ts');
|
|
522
|
+
const composableContent = `import { HumanBehaviorTracker } from 'humanbehavior-js'
|
|
523
|
+
|
|
524
|
+
export function useHumanBehavior() {
|
|
525
|
+
const apiKey = import.meta.env.VITE_HUMANBEHAVIOR_API_KEY
|
|
526
|
+
|
|
527
|
+
if (apiKey) {
|
|
528
|
+
const tracker = HumanBehaviorTracker.init(apiKey);
|
|
529
|
+
|
|
530
|
+
return { tracker }
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return { tracker: null }
|
|
534
|
+
}
|
|
535
|
+
`;
|
|
536
|
+
try {
|
|
537
|
+
if (!fs.existsSync(composableDir)) {
|
|
538
|
+
fs.mkdirSync(composableDir, { recursive: true });
|
|
539
|
+
}
|
|
540
|
+
if (!fs.existsSync(composablePath)) {
|
|
541
|
+
modifications.push({
|
|
542
|
+
filePath: composablePath,
|
|
543
|
+
action: 'create',
|
|
544
|
+
content: composableContent,
|
|
545
|
+
description: 'Created Vue composable useHumanBehavior'
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
catch { }
|
|
559
550
|
if (mainFile) {
|
|
560
551
|
const content = fs.readFileSync(mainFile, 'utf8');
|
|
561
552
|
const modifiedContent = this.injectVuePlugin(content);
|
|
@@ -575,33 +566,48 @@ export default defineNuxtPlugin(() => {
|
|
|
575
566
|
*/
|
|
576
567
|
async generateAngularModifications() {
|
|
577
568
|
const modifications = [];
|
|
578
|
-
//
|
|
579
|
-
const
|
|
580
|
-
const
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
569
|
+
// Create Angular service (docs pattern)
|
|
570
|
+
const serviceDir = path.join(this.projectRoot, 'src', 'app', 'services');
|
|
571
|
+
const servicePath = path.join(serviceDir, 'hb.service.ts');
|
|
572
|
+
const serviceContent = `import { Injectable, NgZone, Inject, PLATFORM_ID } from '@angular/core';
|
|
573
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
574
|
+
import { HumanBehaviorTracker } from 'humanbehavior-js';
|
|
575
|
+
import { environment } from '../../environments/environment';
|
|
576
|
+
|
|
577
|
+
@Injectable({ providedIn: 'root' })
|
|
578
|
+
export class HumanBehavior {
|
|
579
|
+
private tracker: ReturnType<typeof HumanBehaviorTracker.init> | null = null;
|
|
580
|
+
|
|
581
|
+
constructor(private ngZone: NgZone, @Inject(PLATFORM_ID) private platformId: Object) {
|
|
582
|
+
if (isPlatformBrowser(this.platformId)) {
|
|
583
|
+
this.ngZone.runOutsideAngular(() => {
|
|
584
|
+
this.tracker = HumanBehaviorTracker.init(environment.humanBehaviorApiKey);
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
capture(event: string, props?: Record<string, any>) {
|
|
590
|
+
this.tracker?.customEvent(event, props);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
identify(user: Record<string, any>) {
|
|
594
|
+
this.tracker?.identifyUser({ userProperties: user });
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
trackPageView(path?: string) {
|
|
598
|
+
this.tracker?.trackPageView(path);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
`;
|
|
602
|
+
if (!fs.existsSync(serviceDir)) {
|
|
603
|
+
fs.mkdirSync(serviceDir, { recursive: true });
|
|
595
604
|
}
|
|
596
|
-
|
|
597
|
-
// Legacy Angular with NgModule
|
|
598
|
-
const content = fs.readFileSync(appModuleFile, 'utf8');
|
|
599
|
-
const modifiedContent = this.injectAngularModule(content);
|
|
605
|
+
if (!fs.existsSync(servicePath)) {
|
|
600
606
|
modifications.push({
|
|
601
|
-
filePath:
|
|
602
|
-
action: '
|
|
603
|
-
content:
|
|
604
|
-
description: '
|
|
607
|
+
filePath: servicePath,
|
|
608
|
+
action: 'create',
|
|
609
|
+
content: serviceContent,
|
|
610
|
+
description: 'Created Angular HumanBehavior service (singleton)'
|
|
605
611
|
});
|
|
606
612
|
}
|
|
607
613
|
// Handle Angular environment files (proper Angular way)
|
|
@@ -670,6 +676,26 @@ export default defineNuxtPlugin(() => {
|
|
|
670
676
|
}
|
|
671
677
|
// For Angular, we don't need .env files since we use environment.ts
|
|
672
678
|
// The environment files are already created above
|
|
679
|
+
// Inject service into app component
|
|
680
|
+
const appComponentPath = path.join(this.projectRoot, 'src', 'app', 'app.ts');
|
|
681
|
+
if (fs.existsSync(appComponentPath)) {
|
|
682
|
+
const appContent = fs.readFileSync(appComponentPath, 'utf8');
|
|
683
|
+
// Check if already has HumanBehavior service
|
|
684
|
+
if (!appContent.includes('HumanBehavior')) {
|
|
685
|
+
let modifiedAppContent = appContent
|
|
686
|
+
.replace(/import { Component } from '@angular\/core';/, `import { Component } from '@angular/core';
|
|
687
|
+
import { HumanBehavior } from './services/hb.service';`)
|
|
688
|
+
.replace(/export class App {/, `export class App {
|
|
689
|
+
constructor(private readonly humanBehavior: HumanBehavior) {}`);
|
|
690
|
+
// Do not modify standalone setting; leave component decorator unchanged
|
|
691
|
+
modifications.push({
|
|
692
|
+
action: 'modify',
|
|
693
|
+
filePath: appComponentPath,
|
|
694
|
+
content: modifiedAppContent,
|
|
695
|
+
description: 'Injected HumanBehavior service into Angular app component'
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
}
|
|
673
699
|
return modifications;
|
|
674
700
|
}
|
|
675
701
|
/**
|
|
@@ -690,7 +716,7 @@ export default defineNuxtPlugin(() => {
|
|
|
690
716
|
filePath: layoutFile,
|
|
691
717
|
action: 'modify',
|
|
692
718
|
content: modifiedContent,
|
|
693
|
-
description: 'Added HumanBehavior
|
|
719
|
+
description: 'Added HumanBehavior tracker init to SvelteKit layout'
|
|
694
720
|
});
|
|
695
721
|
}
|
|
696
722
|
}
|
|
@@ -704,7 +730,7 @@ export default defineNuxtPlugin(() => {
|
|
|
704
730
|
filePath: mainFile,
|
|
705
731
|
action: 'modify',
|
|
706
732
|
content: modifiedContent,
|
|
707
|
-
description: 'Added HumanBehavior
|
|
733
|
+
description: 'Added HumanBehavior tracker init to Svelte app'
|
|
708
734
|
});
|
|
709
735
|
}
|
|
710
736
|
}
|
|
@@ -758,14 +784,9 @@ export default defineNuxtPlugin(() => {
|
|
|
758
784
|
content: `import { HumanBehaviorTracker } from 'humanbehavior-js';
|
|
759
785
|
|
|
760
786
|
export const onClientEntry = () => {
|
|
761
|
-
console.log('Gatsby browser entry point loaded');
|
|
762
787
|
const apiKey = process.env.GATSBY_HUMANBEHAVIOR_API_KEY;
|
|
763
|
-
console.log('API Key found:', apiKey ? 'Yes' : 'No');
|
|
764
788
|
if (apiKey) {
|
|
765
|
-
|
|
766
|
-
console.log('HumanBehavior SDK initialized for Gatsby');
|
|
767
|
-
} else {
|
|
768
|
-
console.log('No API key found in environment variables');
|
|
789
|
+
HumanBehaviorTracker.init(apiKey);
|
|
769
790
|
}
|
|
770
791
|
};`,
|
|
771
792
|
description: 'Created gatsby-browser.js with HumanBehavior initialization'
|
|
@@ -815,8 +836,33 @@ export const onClientEntry = () => {
|
|
|
815
836
|
if (this.framework?.type === 'react' || this.framework?.type === 'nextjs') {
|
|
816
837
|
steps.push('💡 Use the useHumanBehavior() hook to track custom events');
|
|
817
838
|
}
|
|
839
|
+
// Append any manual notes gathered during transformation
|
|
840
|
+
if (this.manualNotes.length) {
|
|
841
|
+
steps.push(...this.manualNotes.map((n) => `⚠️ ${n}`));
|
|
842
|
+
}
|
|
818
843
|
return steps;
|
|
819
844
|
}
|
|
845
|
+
/**
|
|
846
|
+
* Helper: apply a file transform or record a manual instruction if unchanged/missing
|
|
847
|
+
*/
|
|
848
|
+
applyOrNotify(filePath, transform, description, manualNote) {
|
|
849
|
+
if (!fs.existsSync(filePath)) {
|
|
850
|
+
this.manualNotes.push(`${manualNote} (file missing: ${path.relative(this.projectRoot, filePath)})`);
|
|
851
|
+
return null;
|
|
852
|
+
}
|
|
853
|
+
const original = fs.readFileSync(filePath, 'utf8');
|
|
854
|
+
const updated = transform(original);
|
|
855
|
+
if (updated !== original) {
|
|
856
|
+
return {
|
|
857
|
+
filePath,
|
|
858
|
+
action: 'modify',
|
|
859
|
+
content: updated,
|
|
860
|
+
description
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
this.manualNotes.push(manualNote);
|
|
864
|
+
return null;
|
|
865
|
+
}
|
|
820
866
|
// Helper methods for file detection and content injection
|
|
821
867
|
findReactAppFile() {
|
|
822
868
|
const possibleFiles = [
|
|
@@ -875,7 +921,7 @@ export const onClientEntry = () => {
|
|
|
875
921
|
const isVite = this.framework?.bundler === 'vite';
|
|
876
922
|
const envVar = isVite
|
|
877
923
|
? 'import.meta.env.VITE_HUMANBEHAVIOR_API_KEY!'
|
|
878
|
-
: 'process.env.
|
|
924
|
+
: 'process.env.REACT_APP_HUMANBEHAVIOR_API_KEY!';
|
|
879
925
|
const importStatement = `import { HumanBehaviorProvider } from 'humanbehavior-js/react';`;
|
|
880
926
|
// Enhanced parsing for React 18+ features
|
|
881
927
|
const hasReact18 = this.framework?.features?.hasReact18;
|
|
@@ -888,7 +934,7 @@ export const onClientEntry = () => {
|
|
|
888
934
|
modifiedContent = `${importStatement}\n\n${modifiedContent}`;
|
|
889
935
|
}
|
|
890
936
|
// Wrap the App component return with HumanBehaviorProvider
|
|
891
|
-
modifiedContent = modifiedContent.replace(/
|
|
937
|
+
modifiedContent = modifiedContent.replace(/return\s*\(([\s\S]*?)\)\s*;/, `return (
|
|
892
938
|
<HumanBehaviorProvider apiKey={${envVar}}>
|
|
893
939
|
$1
|
|
894
940
|
</HumanBehaviorProvider>
|
|
@@ -947,107 +993,89 @@ export const onClientEntry = () => {
|
|
|
947
993
|
if (content.includes('HumanBehaviorProvider')) {
|
|
948
994
|
return content;
|
|
949
995
|
}
|
|
950
|
-
const importStatement = `import { HumanBehaviorProvider, createHumanBehaviorLoader } from 'humanbehavior-js/remix';`;
|
|
951
|
-
const useLoaderDataImport = `import { useLoaderData } from "@remix-run/react";`;
|
|
952
|
-
// Add imports more robustly
|
|
953
996
|
let modifiedContent = content;
|
|
954
|
-
//
|
|
997
|
+
// Step 1: Add useLoaderData import
|
|
998
|
+
if (!content.includes('useLoaderData')) {
|
|
999
|
+
modifiedContent = modifiedContent.replace(/(} from ['"]@remix-run\/react['"];?\s*)/, `$1import { useLoaderData } from '@remix-run/react';
|
|
1000
|
+
`);
|
|
1001
|
+
}
|
|
1002
|
+
// Step 2: Add HumanBehaviorProvider import
|
|
955
1003
|
if (!content.includes('HumanBehaviorProvider')) {
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
const nextLineIndex = modifiedContent.indexOf('\n', lastImportIndex);
|
|
959
|
-
if (nextLineIndex !== -1) {
|
|
960
|
-
modifiedContent = modifiedContent.slice(0, nextLineIndex + 1) +
|
|
961
|
-
importStatement + '\n' +
|
|
962
|
-
modifiedContent.slice(nextLineIndex + 1);
|
|
963
|
-
}
|
|
964
|
-
else {
|
|
965
|
-
modifiedContent = modifiedContent + '\n' + importStatement;
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
else {
|
|
969
|
-
modifiedContent = importStatement + '\n' + modifiedContent;
|
|
970
|
-
}
|
|
1004
|
+
modifiedContent = modifiedContent.replace(/(} from ['"]@remix-run\/react['"];?\s*)/, `$1import { HumanBehaviorProvider } from 'humanbehavior-js/react';
|
|
1005
|
+
`);
|
|
971
1006
|
}
|
|
972
|
-
//
|
|
973
|
-
if (!content.includes('
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
const nextLineIndex = modifiedContent.indexOf('\n', lastImportIndex);
|
|
977
|
-
if (nextLineIndex !== -1) {
|
|
978
|
-
modifiedContent = modifiedContent.slice(0, nextLineIndex + 1) +
|
|
979
|
-
useLoaderDataImport + '\n' +
|
|
980
|
-
modifiedContent.slice(nextLineIndex + 1);
|
|
981
|
-
}
|
|
982
|
-
else {
|
|
983
|
-
modifiedContent = modifiedContent + '\n' + useLoaderDataImport;
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
else {
|
|
987
|
-
modifiedContent = useLoaderDataImport + '\n' + modifiedContent;
|
|
988
|
-
}
|
|
1007
|
+
// Step 3: Add LoaderFunctionArgs import
|
|
1008
|
+
if (!content.includes('LoaderFunctionArgs')) {
|
|
1009
|
+
modifiedContent = modifiedContent.replace(/(} from ['"]@remix-run\/node['"];?\s*)/, `$1import type { LoaderFunctionArgs } from '@remix-run/node';
|
|
1010
|
+
`);
|
|
989
1011
|
}
|
|
990
|
-
// Add loader function before
|
|
1012
|
+
// Step 4: Add loader function before Layout function
|
|
991
1013
|
if (!content.includes('export const loader')) {
|
|
992
|
-
modifiedContent = modifiedContent.replace(/export
|
|
1014
|
+
modifiedContent = modifiedContent.replace(/(export function Layout)/, `export const loader = async ({ request }: LoaderFunctionArgs) => {
|
|
1015
|
+
return {
|
|
1016
|
+
ENV: {
|
|
1017
|
+
HUMANBEHAVIOR_API_KEY: process.env.HUMANBEHAVIOR_API_KEY,
|
|
1018
|
+
},
|
|
1019
|
+
};
|
|
1020
|
+
};
|
|
993
1021
|
|
|
994
|
-
|
|
1022
|
+
$1`);
|
|
995
1023
|
}
|
|
996
|
-
//
|
|
997
|
-
|
|
998
|
-
|
|
1024
|
+
// Step 5: Add useLoaderData call and wrap App function's return content with HumanBehaviorProvider
|
|
1025
|
+
if (!content.includes('const data = useLoaderData')) {
|
|
1026
|
+
modifiedContent = modifiedContent.replace(/(export default function App\(\) \{\s*)(return \(\s*<div[^>]*>[\s\S]*?<\/div>\s*\);\s*\})/, `$1const data = useLoaderData<typeof loader>();
|
|
999
1027
|
|
|
1000
1028
|
return (
|
|
1001
1029
|
<HumanBehaviorProvider apiKey={data.ENV.HUMANBEHAVIOR_API_KEY}>
|
|
1002
|
-
|
|
1030
|
+
<div className="min-h-screen bg-gray-50">
|
|
1031
|
+
<Navigation />
|
|
1032
|
+
<Outlet />
|
|
1033
|
+
</div>
|
|
1003
1034
|
</HumanBehaviorProvider>
|
|
1004
1035
|
);
|
|
1005
1036
|
}`);
|
|
1037
|
+
}
|
|
1006
1038
|
return modifiedContent;
|
|
1007
1039
|
}
|
|
1008
1040
|
injectVuePlugin(content) {
|
|
1009
|
-
|
|
1041
|
+
// New: use composable/tracker pattern per docs; idempotent and migrates from plugin
|
|
1042
|
+
if (content.includes('useHumanBehavior')) {
|
|
1010
1043
|
return content;
|
|
1011
1044
|
}
|
|
1012
|
-
const importStatement = `import { HumanBehaviorPlugin } from 'humanbehavior-js/vue';`;
|
|
1013
|
-
// Enhanced Vue 3 support with version detection
|
|
1014
1045
|
const hasVue3 = this.framework?.features?.hasVue3;
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1046
|
+
const isVue3ByContent = content.includes('createApp') || content.includes('import { createApp }');
|
|
1047
|
+
let modifiedContent = content
|
|
1048
|
+
.replace(/import\s*\{\s*HumanBehaviorPlugin\s*\}\s*from\s*['\"]humanbehavior-js\/vue['\"];?/g, '')
|
|
1049
|
+
.replace(/app\.use\(\s*HumanBehaviorPlugin[\s\S]*?\);?/g, '');
|
|
1050
|
+
if (hasVue3 || isVue3ByContent) {
|
|
1051
|
+
const importComposable = `import { useHumanBehavior } from './composables/useHumanBehavior';`;
|
|
1052
|
+
if (!modifiedContent.includes(importComposable)) {
|
|
1053
|
+
const lastImportIndex = modifiedContent.lastIndexOf('import');
|
|
1054
|
+
if (lastImportIndex !== -1) {
|
|
1055
|
+
const nextLineIndex = modifiedContent.indexOf('\n', lastImportIndex);
|
|
1056
|
+
if (nextLineIndex !== -1) {
|
|
1057
|
+
modifiedContent = modifiedContent.slice(0, nextLineIndex + 1) + importComposable + '\n' + modifiedContent.slice(nextLineIndex + 1);
|
|
1058
|
+
}
|
|
1059
|
+
else {
|
|
1060
|
+
modifiedContent = modifiedContent + '\n' + importComposable;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
else {
|
|
1064
|
+
modifiedContent = importComposable + '\n' + modifiedContent;
|
|
1027
1065
|
}
|
|
1028
1066
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
modifiedContent = modifiedContent.replace(/(app\.mount\(.*?\))/, `${pluginUsage}\n\n$1`);
|
|
1067
|
+
if (modifiedContent.includes('createApp')) {
|
|
1068
|
+
modifiedContent = modifiedContent.replace(/(const\s+app\s*=\s*createApp\([^)]*\))/, `$1\nconst { tracker } = useHumanBehavior();`);
|
|
1032
1069
|
}
|
|
1033
1070
|
return modifiedContent;
|
|
1034
1071
|
}
|
|
1035
1072
|
else {
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
});`;
|
|
1040
|
-
let modifiedContent = content;
|
|
1041
|
-
// Add import statement
|
|
1042
|
-
if (!content.includes(importStatement)) {
|
|
1043
|
-
modifiedContent = content.replace(/(import.*?from.*?['"]vue['"];?)/, `$1\n${importStatement}`);
|
|
1044
|
-
if (!modifiedContent.includes(importStatement)) {
|
|
1045
|
-
modifiedContent = `${importStatement}\n\n${modifiedContent}`;
|
|
1046
|
-
}
|
|
1073
|
+
const trackerImport = `import { HumanBehaviorTracker } from 'humanbehavior-js';`;
|
|
1074
|
+
if (!modifiedContent.includes(trackerImport)) {
|
|
1075
|
+
modifiedContent = `${trackerImport}\n${modifiedContent}`;
|
|
1047
1076
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
modifiedContent = modifiedContent.replace(/(new Vue\(.*?\))/, `${pluginUsage}\n\n$1`);
|
|
1077
|
+
if (modifiedContent.includes('new Vue')) {
|
|
1078
|
+
modifiedContent = modifiedContent.replace(/(new\s+Vue\s*\()/, `HumanBehaviorTracker.init(process.env.VUE_APP_HUMANBEHAVIOR_API_KEY || import.meta?.env?.VITE_HUMANBEHAVIOR_API_KEY);\n$1`);
|
|
1051
1079
|
}
|
|
1052
1080
|
return modifiedContent;
|
|
1053
1081
|
}
|
|
@@ -1088,33 +1116,31 @@ if (typeof window !== 'undefined') {
|
|
|
1088
1116
|
return modifiedContent;
|
|
1089
1117
|
}
|
|
1090
1118
|
injectSvelteStore(content) {
|
|
1091
|
-
|
|
1119
|
+
// Direct tracker init for non-SSR Svelte
|
|
1120
|
+
if (content.includes('HumanBehaviorTracker.init')) {
|
|
1092
1121
|
return content;
|
|
1093
1122
|
}
|
|
1094
|
-
const importStatement = `import {
|
|
1095
|
-
const initCode =
|
|
1123
|
+
const importStatement = `import { HumanBehaviorTracker } from 'humanbehavior-js';`;
|
|
1124
|
+
const initCode = `// Initialize HumanBehavior SDK\nHumanBehaviorTracker.init(import.meta.env?.VITE_HUMANBEHAVIOR_API_KEY || process.env.PUBLIC_HUMANBEHAVIOR_API_KEY || '');`;
|
|
1096
1125
|
return `${importStatement}\n${initCode}\n\n${content}`;
|
|
1097
1126
|
}
|
|
1098
1127
|
injectSvelteKitLayout(content) {
|
|
1099
|
-
|
|
1128
|
+
// Direct tracker init with browser guard for SvelteKit
|
|
1129
|
+
if (content.includes('HumanBehaviorTracker.init')) {
|
|
1100
1130
|
return content;
|
|
1101
1131
|
}
|
|
1102
|
-
const importStatement = `import { humanBehaviorStore } from 'humanbehavior-js/svelte';`;
|
|
1103
1132
|
const envImport = `import { PUBLIC_HUMANBEHAVIOR_API_KEY } from '$env/static/public';`;
|
|
1104
|
-
const
|
|
1105
|
-
|
|
1133
|
+
const hbImport = `import { HumanBehaviorTracker } from 'humanbehavior-js';`;
|
|
1134
|
+
const browserImport = `import { browser } from '$app/environment';`;
|
|
1135
|
+
const initCode = `if (browser) {\n const apiKey = PUBLIC_HUMANBEHAVIOR_API_KEY || import.meta.env.VITE_HUMANBEHAVIOR_API_KEY;\n if (apiKey) {\n HumanBehaviorTracker.init(apiKey);\n }\n}`;
|
|
1106
1136
|
if (content.includes('<script lang="ts">')) {
|
|
1107
|
-
return content.replace(/<script lang="ts">/, `<script lang="ts">\n\t${envImport}\n\t${
|
|
1137
|
+
return content.replace(/<script lang="ts">/, `<script lang="ts">\n\t${browserImport}\n\t${envImport}\n\t${hbImport}\n\t${initCode}`);
|
|
1108
1138
|
}
|
|
1109
1139
|
else if (content.includes('<script>')) {
|
|
1110
|
-
return content.replace(/<script>/, `<script>\n\t${envImport}\n\t${
|
|
1111
|
-
}
|
|
1112
|
-
else if (content.includes('<script context="module">')) {
|
|
1113
|
-
return content.replace(/<script\s+context="module">/, `<script context="module">\n\t${envImport}\n\t${importStatement}\n\t${initCode}`);
|
|
1140
|
+
return content.replace(/<script>/, `<script>\n\t${browserImport}\n\t${envImport}\n\t${hbImport}\n\t${initCode}`);
|
|
1114
1141
|
}
|
|
1115
1142
|
else {
|
|
1116
|
-
|
|
1117
|
-
return content.replace(/<svelte:head>/, `<script lang="ts">\n\t${envImport}\n\t${importStatement}\n\t${initCode}\n</script>\n\n<svelte:head>`);
|
|
1143
|
+
return `<script lang="ts">\n${browserImport}\n${envImport}\n${hbImport}\n${initCode}\n</script>\n\n${content}`;
|
|
1118
1144
|
}
|
|
1119
1145
|
}
|
|
1120
1146
|
injectVanillaScript(content) {
|
|
@@ -1167,13 +1193,25 @@ if (typeof window !== 'undefined') {
|
|
|
1167
1193
|
// Enhanced Nuxt 3 support with version detection
|
|
1168
1194
|
const hasNuxt3 = this.framework?.features?.hasNuxt3;
|
|
1169
1195
|
if (hasNuxt3) {
|
|
1170
|
-
// Nuxt 3 with runtime config
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
public: {
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1196
|
+
// Nuxt 3 with runtime config (robust match for opening object)
|
|
1197
|
+
const pattern = /export\s+default\s+defineNuxtConfig\s*\(\s*\{/;
|
|
1198
|
+
if (pattern.test(content)) {
|
|
1199
|
+
const replaced = content.replace(pattern, `export default defineNuxtConfig({\n runtimeConfig: {\n public: {\n humanBehaviorApiKey: process.env.NUXT_PUBLIC_HUMANBEHAVIOR_API_KEY\n }\n },`);
|
|
1200
|
+
if (replaced !== content)
|
|
1201
|
+
return replaced;
|
|
1202
|
+
}
|
|
1203
|
+
// Fallback: insert runtimeConfig after opening brace of defineNuxtConfig
|
|
1204
|
+
const startIdx = content.indexOf('defineNuxtConfig(');
|
|
1205
|
+
if (startIdx !== -1) {
|
|
1206
|
+
const braceIdx = content.indexOf('{', startIdx);
|
|
1207
|
+
if (braceIdx !== -1) {
|
|
1208
|
+
const insertion = `\n runtimeConfig: {\n public: {\n humanBehaviorApiKey: process.env.NUXT_PUBLIC_HUMANBEHAVIOR_API_KEY\n }\n },`;
|
|
1209
|
+
const before = content.slice(0, braceIdx + 1);
|
|
1210
|
+
const after = content.slice(braceIdx + 1);
|
|
1211
|
+
return `${before}${insertion}${after}`;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
return content;
|
|
1177
1215
|
}
|
|
1178
1216
|
else {
|
|
1179
1217
|
// Nuxt 2 with env config
|
|
@@ -1196,31 +1234,26 @@ if (typeof window !== 'undefined') {
|
|
|
1196
1234
|
return modifiedContent;
|
|
1197
1235
|
}
|
|
1198
1236
|
injectGatsbyBrowser(content) {
|
|
1199
|
-
if (content.includes('HumanBehaviorTracker')) {
|
|
1200
|
-
return content;
|
|
1201
|
-
}
|
|
1202
1237
|
const importStatement = `import { HumanBehaviorTracker } from 'humanbehavior-js';`;
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
if (content.trim()) {
|
|
1218
|
-
return `${importStatement}${initCode}\n\n${content}`;
|
|
1219
|
-
}
|
|
1220
|
-
else {
|
|
1221
|
-
// If file is empty, just return the new content
|
|
1222
|
-
return `${importStatement}${initCode}`;
|
|
1238
|
+
// If an onClientEntry already exists, merge init into it idempotently
|
|
1239
|
+
if (/export\s+const\s+onClientEntry\s*=\s*\(/.test(content)) {
|
|
1240
|
+
let modified = content;
|
|
1241
|
+
// Ensure import exists
|
|
1242
|
+
if (!modified.includes("from 'humanbehavior-js'")) {
|
|
1243
|
+
modified = `${importStatement}\n${modified}`;
|
|
1244
|
+
}
|
|
1245
|
+
// If init already present, return as-is
|
|
1246
|
+
if (modified.includes('HumanBehaviorTracker.init(')) {
|
|
1247
|
+
return modified;
|
|
1248
|
+
}
|
|
1249
|
+
// Inject minimal init at start of onClientEntry body
|
|
1250
|
+
modified = modified.replace(/(export\s+const\s+onClientEntry\s*=\s*\([^)]*\)\s*=>\s*\{)/, `$1\n const apiKey = process.env.GATSBY_HUMANBEHAVIOR_API_KEY;\n if (apiKey) {\n HumanBehaviorTracker.init(apiKey);\n }\n`);
|
|
1251
|
+
return modified;
|
|
1223
1252
|
}
|
|
1253
|
+
// No existing onClientEntry: create minimal file content or prepend to existing
|
|
1254
|
+
const block = `export const onClientEntry = () => {\n const apiKey = process.env.GATSBY_HUMANBEHAVIOR_API_KEY;\n if (apiKey) {\n HumanBehaviorTracker.init(apiKey);\n }\n};`;
|
|
1255
|
+
const header = content.trim() ? `${importStatement}\n` : `${importStatement}\n`;
|
|
1256
|
+
return `${header}${block}${content.trim() ? `\n\n${content}` : ''}`;
|
|
1224
1257
|
}
|
|
1225
1258
|
/**
|
|
1226
1259
|
* Helper method to find the best environment file for a framework
|
|
@@ -1243,7 +1276,7 @@ export const onClientEntry = () => {
|
|
|
1243
1276
|
}
|
|
1244
1277
|
// Framework-specific mappings
|
|
1245
1278
|
const envVarNames = {
|
|
1246
|
-
react: '
|
|
1279
|
+
react: 'REACT_APP_HUMANBEHAVIOR_API_KEY',
|
|
1247
1280
|
nextjs: 'NEXT_PUBLIC_HUMANBEHAVIOR_API_KEY',
|
|
1248
1281
|
vue: 'VITE_HUMANBEHAVIOR_API_KEY',
|
|
1249
1282
|
svelte: 'PUBLIC_HUMANBEHAVIOR_API_KEY',
|
|
@@ -1328,417 +1361,6 @@ export const onClientEntry = () => {
|
|
|
1328
1361
|
}
|
|
1329
1362
|
}
|
|
1330
1363
|
|
|
1331
|
-
/**
|
|
1332
|
-
* Remote AI Service Implementation
|
|
1333
|
-
*
|
|
1334
|
-
* This connects to your deployed Lambda function via API Gateway
|
|
1335
|
-
*/
|
|
1336
|
-
class RemoteAIService {
|
|
1337
|
-
constructor(config) {
|
|
1338
|
-
this.config = {
|
|
1339
|
-
timeout: 10000, // 10 seconds
|
|
1340
|
-
...config
|
|
1341
|
-
};
|
|
1342
|
-
}
|
|
1343
|
-
/**
|
|
1344
|
-
* Analyze code patterns using your deployed AI service
|
|
1345
|
-
*/
|
|
1346
|
-
async analyzeCodePatterns(codeSamples) {
|
|
1347
|
-
try {
|
|
1348
|
-
const response = await fetch(`${this.config.apiEndpoint}/analyze`, {
|
|
1349
|
-
method: 'POST',
|
|
1350
|
-
headers: {
|
|
1351
|
-
'Content-Type': 'application/json',
|
|
1352
|
-
},
|
|
1353
|
-
body: JSON.stringify({ codeSamples }),
|
|
1354
|
-
signal: AbortSignal.timeout(this.config.timeout || 10000)
|
|
1355
|
-
});
|
|
1356
|
-
if (!response.ok) {
|
|
1357
|
-
throw new Error(`AI service returned ${response.status}: ${response.statusText}`);
|
|
1358
|
-
}
|
|
1359
|
-
const result = await response.json();
|
|
1360
|
-
return result.analysis;
|
|
1361
|
-
}
|
|
1362
|
-
catch (error) {
|
|
1363
|
-
console.warn('Remote AI service failed, falling back to heuristic analysis:', error);
|
|
1364
|
-
return this.performHeuristicAnalysis(codeSamples);
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
/**
|
|
1368
|
-
* Resolve conflicts using your deployed AI service
|
|
1369
|
-
*/
|
|
1370
|
-
async resolveConflicts(conflicts, framework) {
|
|
1371
|
-
try {
|
|
1372
|
-
const response = await fetch(`${this.config.apiEndpoint}/resolve-conflicts`, {
|
|
1373
|
-
method: 'POST',
|
|
1374
|
-
headers: {
|
|
1375
|
-
'Content-Type': 'application/json',
|
|
1376
|
-
},
|
|
1377
|
-
body: JSON.stringify({ conflicts, framework }),
|
|
1378
|
-
signal: AbortSignal.timeout(this.config.timeout || 10000)
|
|
1379
|
-
});
|
|
1380
|
-
if (!response.ok) {
|
|
1381
|
-
throw new Error(`AI service returned ${response.status}: ${response.statusText}`);
|
|
1382
|
-
}
|
|
1383
|
-
const result = await response.json();
|
|
1384
|
-
return result.resolutions || [];
|
|
1385
|
-
}
|
|
1386
|
-
catch (error) {
|
|
1387
|
-
console.warn('Remote AI conflict resolution failed, using heuristic approach:', error);
|
|
1388
|
-
return this.resolveConflictsHeuristic(conflicts, framework);
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
/**
|
|
1392
|
-
* Generate optimizations using your deployed AI service
|
|
1393
|
-
*/
|
|
1394
|
-
async generateOptimizations(framework, patterns) {
|
|
1395
|
-
try {
|
|
1396
|
-
const response = await fetch(`${this.config.apiEndpoint}/optimize`, {
|
|
1397
|
-
method: 'POST',
|
|
1398
|
-
headers: {
|
|
1399
|
-
'Content-Type': 'application/json',
|
|
1400
|
-
},
|
|
1401
|
-
body: JSON.stringify({ framework, patterns }),
|
|
1402
|
-
signal: AbortSignal.timeout(this.config.timeout || 10000)
|
|
1403
|
-
});
|
|
1404
|
-
if (!response.ok) {
|
|
1405
|
-
throw new Error(`AI service returned ${response.status}: ${response.statusText}`);
|
|
1406
|
-
}
|
|
1407
|
-
const result = await response.json();
|
|
1408
|
-
return result.optimizations || [];
|
|
1409
|
-
}
|
|
1410
|
-
catch (error) {
|
|
1411
|
-
console.warn('Remote AI optimization generation failed, using heuristic approach:', error);
|
|
1412
|
-
return this.generateOptimizationsHeuristic(framework, patterns);
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
/**
|
|
1416
|
-
* Heuristic analysis fallback
|
|
1417
|
-
*/
|
|
1418
|
-
performHeuristicAnalysis(codeSamples) {
|
|
1419
|
-
const patterns = codeSamples.join(' ').toLowerCase();
|
|
1420
|
-
// Framework detection
|
|
1421
|
-
let framework = { name: 'vanilla', type: 'vanilla' };
|
|
1422
|
-
let confidence = 0.5;
|
|
1423
|
-
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')) {
|
|
1424
|
-
framework = { name: 'nuxt', type: 'nuxt' };
|
|
1425
|
-
confidence = 0.95;
|
|
1426
|
-
}
|
|
1427
|
-
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/')) {
|
|
1428
|
-
framework = { name: 'nextjs', type: 'nextjs' };
|
|
1429
|
-
confidence = 0.95;
|
|
1430
|
-
}
|
|
1431
|
-
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')) {
|
|
1432
|
-
framework = { name: 'gatsby', type: 'gatsby' };
|
|
1433
|
-
confidence = 0.95;
|
|
1434
|
-
}
|
|
1435
|
-
else if (patterns.includes('react')) {
|
|
1436
|
-
framework = { name: 'react', type: 'react' };
|
|
1437
|
-
confidence = 0.9;
|
|
1438
|
-
}
|
|
1439
|
-
else if (patterns.includes('vue')) {
|
|
1440
|
-
framework = { name: 'vue', type: 'vue' };
|
|
1441
|
-
confidence = 0.9;
|
|
1442
|
-
}
|
|
1443
|
-
else if (patterns.includes('angular')) {
|
|
1444
|
-
framework = { name: 'angular', type: 'angular' };
|
|
1445
|
-
confidence = 0.9;
|
|
1446
|
-
}
|
|
1447
|
-
else if (patterns.includes('svelte')) {
|
|
1448
|
-
framework = { name: 'svelte', type: 'svelte' };
|
|
1449
|
-
confidence = 0.9;
|
|
1450
|
-
}
|
|
1451
|
-
else if (patterns.includes('astro')) {
|
|
1452
|
-
framework = { name: 'astro', type: 'astro' };
|
|
1453
|
-
confidence = 0.9;
|
|
1454
|
-
}
|
|
1455
|
-
// Integration strategy
|
|
1456
|
-
let integrationStrategy = 'script';
|
|
1457
|
-
if (framework.type === 'react' || framework.type === 'nextjs' || framework.type === 'gatsby') {
|
|
1458
|
-
integrationStrategy = 'provider';
|
|
1459
|
-
}
|
|
1460
|
-
else if (framework.type === 'vue') {
|
|
1461
|
-
integrationStrategy = 'plugin';
|
|
1462
|
-
}
|
|
1463
|
-
else if (framework.type === 'angular') {
|
|
1464
|
-
integrationStrategy = 'module';
|
|
1465
|
-
}
|
|
1466
|
-
// Compatibility mode
|
|
1467
|
-
let compatibilityMode = 'modern';
|
|
1468
|
-
if (patterns.includes('require(') || patterns.includes('var ')) {
|
|
1469
|
-
compatibilityMode = 'legacy';
|
|
1470
|
-
}
|
|
1471
|
-
return {
|
|
1472
|
-
framework,
|
|
1473
|
-
confidence,
|
|
1474
|
-
patterns: codeSamples,
|
|
1475
|
-
conflicts: [],
|
|
1476
|
-
recommendations: [],
|
|
1477
|
-
integrationStrategy,
|
|
1478
|
-
compatibilityMode
|
|
1479
|
-
};
|
|
1480
|
-
}
|
|
1481
|
-
/**
|
|
1482
|
-
* Heuristic conflict resolution
|
|
1483
|
-
*/
|
|
1484
|
-
resolveConflictsHeuristic(conflicts, framework) {
|
|
1485
|
-
const resolutions = [];
|
|
1486
|
-
for (const conflict of conflicts) {
|
|
1487
|
-
switch (conflict) {
|
|
1488
|
-
case 'existing_humanbehavior_code':
|
|
1489
|
-
resolutions.push('update_existing_integration');
|
|
1490
|
-
break;
|
|
1491
|
-
case 'existing_provider':
|
|
1492
|
-
resolutions.push('merge_providers');
|
|
1493
|
-
break;
|
|
1494
|
-
case 'module_system_conflict':
|
|
1495
|
-
resolutions.push('hybrid_module_support');
|
|
1496
|
-
break;
|
|
1497
|
-
default:
|
|
1498
|
-
resolutions.push('skip_conflict');
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
return resolutions;
|
|
1502
|
-
}
|
|
1503
|
-
/**
|
|
1504
|
-
* Heuristic optimization generation
|
|
1505
|
-
*/
|
|
1506
|
-
generateOptimizationsHeuristic(framework, patterns) {
|
|
1507
|
-
const optimizations = [];
|
|
1508
|
-
switch (framework.type) {
|
|
1509
|
-
case 'react':
|
|
1510
|
-
optimizations.push('Use React.memo for performance optimization');
|
|
1511
|
-
optimizations.push('Implement error boundaries for better error tracking');
|
|
1512
|
-
optimizations.push('Consider using React.lazy for code splitting');
|
|
1513
|
-
break;
|
|
1514
|
-
case 'vue':
|
|
1515
|
-
optimizations.push('Use Vue 3 Composition API for better performance');
|
|
1516
|
-
optimizations.push('Implement proper error handling in components');
|
|
1517
|
-
optimizations.push('Consider using Vue Router for navigation tracking');
|
|
1518
|
-
break;
|
|
1519
|
-
case 'angular':
|
|
1520
|
-
optimizations.push('Use Angular standalone components for better tree-shaking');
|
|
1521
|
-
optimizations.push('Implement proper error handling with ErrorHandler');
|
|
1522
|
-
optimizations.push('Consider using Angular signals for state management');
|
|
1523
|
-
break;
|
|
1524
|
-
default:
|
|
1525
|
-
optimizations.push('Enable performance tracking');
|
|
1526
|
-
optimizations.push('Implement error tracking');
|
|
1527
|
-
optimizations.push('Consider progressive enhancement');
|
|
1528
|
-
}
|
|
1529
|
-
return optimizations;
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
|
|
1533
|
-
/**
|
|
1534
|
-
* Manual Framework Installation Wizard
|
|
1535
|
-
*
|
|
1536
|
-
* This wizard allows users to manually specify their framework instead of auto-detection.
|
|
1537
|
-
* Useful when auto-detection fails or users want more control.
|
|
1538
|
-
*/
|
|
1539
|
-
class ManualFrameworkInstallationWizard extends AutoInstallationWizard {
|
|
1540
|
-
constructor(apiKey, projectRoot = process.cwd(), framework) {
|
|
1541
|
-
super(apiKey, projectRoot);
|
|
1542
|
-
this.selectedFramework = framework.toLowerCase();
|
|
1543
|
-
this.framework = this.createFrameworkInfo(this.selectedFramework);
|
|
1544
|
-
}
|
|
1545
|
-
/**
|
|
1546
|
-
* Manual installation with user-specified framework
|
|
1547
|
-
*/
|
|
1548
|
-
async install() {
|
|
1549
|
-
try {
|
|
1550
|
-
// Step 1: Handle framework selection
|
|
1551
|
-
if (this.selectedFramework === 'auto') {
|
|
1552
|
-
// Use full AI detection for "Other" option
|
|
1553
|
-
this.framework = await this.runFullDetection();
|
|
1554
|
-
}
|
|
1555
|
-
else {
|
|
1556
|
-
// Set framework based on user selection
|
|
1557
|
-
this.framework = this.createFrameworkInfo(this.selectedFramework);
|
|
1558
|
-
if (!this.framework) {
|
|
1559
|
-
this.framework = { name: 'unknown', type: 'vanilla' };
|
|
1560
|
-
}
|
|
1561
|
-
// Step 2: Run full detection logic to find entry points, file names, etc.
|
|
1562
|
-
const detectedFramework = await this.runFullDetection();
|
|
1563
|
-
// Step 3: Merge manual framework with detected details
|
|
1564
|
-
this.framework = {
|
|
1565
|
-
...detectedFramework,
|
|
1566
|
-
name: this.framework.name,
|
|
1567
|
-
type: this.framework.type
|
|
1568
|
-
};
|
|
1569
|
-
}
|
|
1570
|
-
// Step 4: Install package
|
|
1571
|
-
await this.installPackage();
|
|
1572
|
-
// Step 5: Generate and apply code modifications
|
|
1573
|
-
const modifications = await this.generateModifications();
|
|
1574
|
-
await this.applyModifications(modifications);
|
|
1575
|
-
// Step 6: Generate next steps
|
|
1576
|
-
const nextSteps = this.generateManualNextSteps();
|
|
1577
|
-
return {
|
|
1578
|
-
success: true,
|
|
1579
|
-
framework: this.framework,
|
|
1580
|
-
modifications,
|
|
1581
|
-
errors: [],
|
|
1582
|
-
nextSteps,
|
|
1583
|
-
selectedFramework: this.selectedFramework,
|
|
1584
|
-
manualMode: true
|
|
1585
|
-
};
|
|
1586
|
-
}
|
|
1587
|
-
catch (error) {
|
|
1588
|
-
return {
|
|
1589
|
-
success: false,
|
|
1590
|
-
framework: this.framework || { name: 'unknown', type: 'vanilla' },
|
|
1591
|
-
modifications: [],
|
|
1592
|
-
errors: [error instanceof Error ? error.message : 'Unknown error'],
|
|
1593
|
-
nextSteps: [],
|
|
1594
|
-
selectedFramework: this.selectedFramework,
|
|
1595
|
-
manualMode: true
|
|
1596
|
-
};
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
/**
|
|
1600
|
-
* Run full detection logic to find entry points, file names, bundler, etc.
|
|
1601
|
-
*/
|
|
1602
|
-
async runFullDetection() {
|
|
1603
|
-
if (this.selectedFramework === 'auto') {
|
|
1604
|
-
// Use AI service for auto-detection
|
|
1605
|
-
const aiService = new RemoteAIService({
|
|
1606
|
-
apiEndpoint: 'https://ik3zxh4790.execute-api.us-east-1.amazonaws.com/prod'
|
|
1607
|
-
});
|
|
1608
|
-
// Use AI service directly for detection
|
|
1609
|
-
const projectFiles = await this.scanProjectFiles();
|
|
1610
|
-
const codeSamples = await this.extractCodeSamples(projectFiles);
|
|
1611
|
-
const aiAnalysis = await aiService.analyzeCodePatterns(codeSamples);
|
|
1612
|
-
return aiAnalysis.framework;
|
|
1613
|
-
}
|
|
1614
|
-
else {
|
|
1615
|
-
// Use traditional detection for manual frameworks
|
|
1616
|
-
const tempWizard = new AutoInstallationWizard(this.apiKey, this.projectRoot);
|
|
1617
|
-
const detected = await tempWizard.detectFramework();
|
|
1618
|
-
return detected;
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
/**
|
|
1622
|
-
* Scan project files for analysis
|
|
1623
|
-
*/
|
|
1624
|
-
async scanProjectFiles() {
|
|
1625
|
-
const files = [];
|
|
1626
|
-
const scanDir = (dir, depth = 0) => {
|
|
1627
|
-
if (depth > 3)
|
|
1628
|
-
return; // Limit depth
|
|
1629
|
-
try {
|
|
1630
|
-
const items = fs.readdirSync(dir);
|
|
1631
|
-
for (const item of items) {
|
|
1632
|
-
const fullPath = path.join(dir, item);
|
|
1633
|
-
const stat = fs.statSync(fullPath);
|
|
1634
|
-
if (stat.isDirectory() && !item.startsWith('.') && item !== 'node_modules') {
|
|
1635
|
-
scanDir(fullPath, depth + 1);
|
|
1636
|
-
}
|
|
1637
|
-
else if (stat.isFile() && this.isRelevantFile(item)) {
|
|
1638
|
-
files.push(fullPath);
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
catch (error) {
|
|
1643
|
-
// Skip inaccessible directories
|
|
1644
|
-
}
|
|
1645
|
-
};
|
|
1646
|
-
scanDir(this.projectRoot);
|
|
1647
|
-
return files;
|
|
1648
|
-
}
|
|
1649
|
-
/**
|
|
1650
|
-
* Check if file is relevant for analysis
|
|
1651
|
-
*/
|
|
1652
|
-
isRelevantFile(filename) {
|
|
1653
|
-
const relevantExtensions = [
|
|
1654
|
-
'.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte', '.html',
|
|
1655
|
-
'.json', '.config.js', '.config.ts', '.babelrc', '.eslintrc'
|
|
1656
|
-
];
|
|
1657
|
-
const relevantNames = [
|
|
1658
|
-
'package.json', 'tsconfig.json', 'vite.config', 'webpack.config',
|
|
1659
|
-
'next.config', 'nuxt.config', 'angular.json', 'svelte.config'
|
|
1660
|
-
];
|
|
1661
|
-
return relevantExtensions.some(ext => filename.endsWith(ext)) ||
|
|
1662
|
-
relevantNames.some(name => filename.includes(name));
|
|
1663
|
-
}
|
|
1664
|
-
/**
|
|
1665
|
-
* Extract code samples for AI analysis
|
|
1666
|
-
*/
|
|
1667
|
-
async extractCodeSamples(files) {
|
|
1668
|
-
const samples = [];
|
|
1669
|
-
for (const file of files.slice(0, 20)) { // Limit to 20 files
|
|
1670
|
-
try {
|
|
1671
|
-
const content = fs.readFileSync(file, 'utf8');
|
|
1672
|
-
const relativePath = path.relative(this.projectRoot, file);
|
|
1673
|
-
samples.push(`File: ${relativePath}\n${content.substring(0, 1000)}`);
|
|
1674
|
-
}
|
|
1675
|
-
catch (error) {
|
|
1676
|
-
// Skip unreadable files
|
|
1677
|
-
}
|
|
1678
|
-
}
|
|
1679
|
-
return samples;
|
|
1680
|
-
}
|
|
1681
|
-
/**
|
|
1682
|
-
* Create framework info based on user selection
|
|
1683
|
-
*/
|
|
1684
|
-
createFrameworkInfo(framework) {
|
|
1685
|
-
const frameworkMap = {
|
|
1686
|
-
'react': { name: 'react', type: 'react' },
|
|
1687
|
-
'nextjs': { name: 'nextjs', type: 'nextjs' },
|
|
1688
|
-
'next': { name: 'nextjs', type: 'nextjs' },
|
|
1689
|
-
'vue': { name: 'vue', type: 'vue' },
|
|
1690
|
-
'nuxt': { name: 'nuxt', type: 'nuxt' },
|
|
1691
|
-
'nuxtjs': { name: 'nuxt', type: 'nuxt' },
|
|
1692
|
-
'angular': { name: 'angular', type: 'angular' },
|
|
1693
|
-
'svelte': { name: 'svelte', type: 'svelte' },
|
|
1694
|
-
'sveltekit': { name: 'svelte', type: 'svelte' },
|
|
1695
|
-
'remix': { name: 'remix', type: 'remix' },
|
|
1696
|
-
'astro': { name: 'astro', type: 'astro' },
|
|
1697
|
-
'gatsby': { name: 'gatsby', type: 'gatsby' },
|
|
1698
|
-
'vanilla': { name: 'vanilla', type: 'vanilla' },
|
|
1699
|
-
'node': { name: 'node', type: 'node' },
|
|
1700
|
-
'auto': { name: 'auto-detected', type: 'auto' }
|
|
1701
|
-
};
|
|
1702
|
-
return frameworkMap[framework] || { name: framework, type: 'vanilla' };
|
|
1703
|
-
}
|
|
1704
|
-
/**
|
|
1705
|
-
* Override framework detection to use manual selection
|
|
1706
|
-
*/
|
|
1707
|
-
async detectFramework() {
|
|
1708
|
-
return this.framework || { name: 'unknown', type: 'vanilla' };
|
|
1709
|
-
}
|
|
1710
|
-
/**
|
|
1711
|
-
* Generate next steps with manual mode info
|
|
1712
|
-
*/
|
|
1713
|
-
generateManualNextSteps() {
|
|
1714
|
-
return [
|
|
1715
|
-
'✅ Manual framework installation completed!',
|
|
1716
|
-
`🎯 Selected framework: ${this.framework?.name || 'unknown'}`,
|
|
1717
|
-
`🔧 Integration strategy: ${this.getIntegrationStrategy()}`,
|
|
1718
|
-
'🚀 Your app is now ready to track user behavior',
|
|
1719
|
-
'📊 View sessions in your HumanBehavior dashboard'
|
|
1720
|
-
];
|
|
1721
|
-
}
|
|
1722
|
-
/**
|
|
1723
|
-
* Get integration strategy based on framework
|
|
1724
|
-
*/
|
|
1725
|
-
getIntegrationStrategy() {
|
|
1726
|
-
if (!this.framework?.type)
|
|
1727
|
-
return 'script';
|
|
1728
|
-
switch (this.framework.type) {
|
|
1729
|
-
case 'react':
|
|
1730
|
-
case 'nextjs':
|
|
1731
|
-
return 'provider';
|
|
1732
|
-
case 'vue':
|
|
1733
|
-
return 'plugin';
|
|
1734
|
-
case 'angular':
|
|
1735
|
-
return 'module';
|
|
1736
|
-
default:
|
|
1737
|
-
return 'script';
|
|
1738
|
-
}
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
1364
|
/**
|
|
1743
1365
|
* HumanBehavior SDK Auto-Installation CLI
|
|
1744
1366
|
*
|
|
@@ -1762,15 +1384,9 @@ class AutoInstallCLI {
|
|
|
1762
1384
|
}
|
|
1763
1385
|
// Get project path
|
|
1764
1386
|
const projectPath = this.options.projectPath || process.cwd();
|
|
1765
|
-
// Choose framework
|
|
1766
|
-
const framework = await this.chooseFramework();
|
|
1767
|
-
if (!framework) {
|
|
1768
|
-
clack.cancel('Installation cancelled.');
|
|
1769
|
-
process.exit(0);
|
|
1770
|
-
}
|
|
1771
1387
|
// Confirm installation
|
|
1772
1388
|
if (!this.options.yes) {
|
|
1773
|
-
const confirmed = await this.confirmInstallation(projectPath
|
|
1389
|
+
const confirmed = await this.confirmInstallation(projectPath);
|
|
1774
1390
|
if (!confirmed) {
|
|
1775
1391
|
clack.cancel('Installation cancelled.');
|
|
1776
1392
|
process.exit(0);
|
|
@@ -1778,10 +1394,10 @@ class AutoInstallCLI {
|
|
|
1778
1394
|
}
|
|
1779
1395
|
// Run installation
|
|
1780
1396
|
const spinner = clack.spinner();
|
|
1781
|
-
spinner.start('🔍
|
|
1782
|
-
const wizard = new
|
|
1397
|
+
spinner.start('🔍 Detecting framework and applying integration...');
|
|
1398
|
+
const wizard = new AutoInstallationWizard(apiKey, projectPath);
|
|
1783
1399
|
const result = await wizard.install();
|
|
1784
|
-
spinner.stop('
|
|
1400
|
+
spinner.stop('Installation complete!');
|
|
1785
1401
|
// Display results
|
|
1786
1402
|
this.displayResults(result);
|
|
1787
1403
|
}
|
|
@@ -1807,27 +1423,10 @@ class AutoInstallCLI {
|
|
|
1807
1423
|
});
|
|
1808
1424
|
return apiKey;
|
|
1809
1425
|
}
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
message: 'Select your framework:',
|
|
1813
|
-
options: [
|
|
1814
|
-
{ label: 'React', value: 'react' },
|
|
1815
|
-
{ label: 'Next.js', value: 'nextjs' },
|
|
1816
|
-
{ label: 'Vue', value: 'vue' },
|
|
1817
|
-
{ label: 'Angular', value: 'angular' },
|
|
1818
|
-
{ label: 'Svelte', value: 'svelte' },
|
|
1819
|
-
{ label: 'Nuxt.js', value: 'nuxt' },
|
|
1820
|
-
{ label: 'Remix', value: 'remix' },
|
|
1821
|
-
{ label: 'Astro', value: 'astro' },
|
|
1822
|
-
{ label: 'Gatsby', value: 'gatsby' },
|
|
1823
|
-
{ label: 'Vanilla JS/TS', value: 'vanilla' }
|
|
1824
|
-
]
|
|
1825
|
-
});
|
|
1826
|
-
return framework;
|
|
1827
|
-
}
|
|
1828
|
-
async confirmInstallation(projectPath, framework) {
|
|
1426
|
+
// Framework selection no longer needed; AutoInstallationWizard auto-detects
|
|
1427
|
+
async confirmInstallation(projectPath) {
|
|
1829
1428
|
const confirmed = await clack.confirm({
|
|
1830
|
-
message: `Ready to install HumanBehavior SDK in ${projectPath}
|
|
1429
|
+
message: `Ready to install HumanBehavior SDK in ${projectPath}?`
|
|
1831
1430
|
});
|
|
1832
1431
|
return confirmed;
|
|
1833
1432
|
}
|
|
@@ -1905,8 +1504,10 @@ Examples:
|
|
|
1905
1504
|
npx humanbehavior-js auto-install --project ./my-app --yes
|
|
1906
1505
|
`);
|
|
1907
1506
|
}
|
|
1908
|
-
// Main execution
|
|
1909
|
-
|
|
1507
|
+
// Main execution (CommonJS compatible)
|
|
1508
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1509
|
+
const isMain = typeof require !== 'undefined' && require.main === module;
|
|
1510
|
+
if (isMain) {
|
|
1910
1511
|
const options = parseArgs();
|
|
1911
1512
|
const cli = new AutoInstallCLI(options);
|
|
1912
1513
|
cli.run().catch((error) => {
|