humanbehavior-js 0.5.25 → 0.5.26

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "humanbehavior-js",
3
- "version": "0.5.25",
3
+ "version": "0.5.26",
4
4
  "description": "SDK for HumanBehavior session and event recording",
5
5
  "private": false,
6
6
  "packageManager": "npm@10.0.0",
@@ -122,6 +122,6 @@
122
122
  "access": "public"
123
123
  },
124
124
  "bin": {
125
- "humanbehavior-js": "packages/wizard/dist/cli/ai-auto-install.js"
125
+ "humanbehavior-js": "packages/wizard/dist/cli/auto-install.js"
126
126
  }
127
127
  }
@@ -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
- try {
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
- try {
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
- if (fs.existsSync(nuxtConfigFile)) {
518
- const content = fs.readFileSync(nuxtConfigFile, 'utf8');
519
- const modifiedContent = this.injectNuxtConfig(content);
520
- modifications.push({
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
- // Check for modern Angular (standalone components) vs legacy (NgModule)
579
- const appModuleFile = path.join(this.projectRoot, 'src', 'app', 'app.module.ts');
580
- const appComponentFile = path.join(this.projectRoot, 'src', 'app', 'app.ts');
581
- const mainFile = path.join(this.projectRoot, 'src', 'main.ts');
582
- const isModernAngular = fs.existsSync(appComponentFile) && !fs.existsSync(appModuleFile);
583
- if (isModernAngular) {
584
- // Modern Angular 17+ with standalone components
585
- if (fs.existsSync(mainFile)) {
586
- const content = fs.readFileSync(mainFile, 'utf8');
587
- const modifiedContent = this.injectAngularStandaloneInit(content);
588
- modifications.push({
589
- filePath: mainFile,
590
- action: 'modify',
591
- content: modifiedContent,
592
- description: 'Added HumanBehavior initialization to Angular main.ts'
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
- else if (fs.existsSync(appModuleFile)) {
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: appModuleFile,
602
- action: 'modify',
603
- content: modifiedContent,
604
- description: 'Added HumanBehaviorModule to Angular app'
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 store to SvelteKit layout'
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 store to Svelte app'
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
- const tracker = HumanBehaviorTracker.init(apiKey);
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.HUMANBEHAVIOR_API_KEY!';
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(/(return\s*\([\s\S]*?\)\s*;)/, `return (
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
- // Add HumanBehaviorProvider import - find the last import and add after it
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
- const lastImportIndex = modifiedContent.lastIndexOf('import');
957
- if (lastImportIndex !== -1) {
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
- // Add useLoaderData import - find the last import and add after it
973
- if (!content.includes('useLoaderData')) {
974
- const lastImportIndex = modifiedContent.lastIndexOf('import');
975
- if (lastImportIndex !== -1) {
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 the App component
1012
+ // Step 4: Add loader function before Layout function
991
1013
  if (!content.includes('export const loader')) {
992
- modifiedContent = modifiedContent.replace(/export default function App\(\)/, `export const loader = createHumanBehaviorLoader();
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
- export default function App()`);
1022
+ $1`);
995
1023
  }
996
- // Wrap the App component content with HumanBehaviorProvider
997
- modifiedContent = modifiedContent.replace(/export default function App\(\) \{[\s\S]*?return \(([\s\S]*?)\);[\s\S]*?\}/, `export default function App() {
998
- const data = useLoaderData<typeof loader>();
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
- $1
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
- if (content.includes('HumanBehaviorPlugin')) {
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
- if (hasVue3) {
1016
- // Vue 3 with Composition API
1017
- const pluginUsage = `app.use(HumanBehaviorPlugin, {
1018
- apiKey: import.meta.env.VITE_HUMANBEHAVIOR_API_KEY
1019
- });`;
1020
- let modifiedContent = content;
1021
- // Add import statement
1022
- if (!content.includes(importStatement)) {
1023
- modifiedContent = content.replace(/(import.*?from.*?['"]vue['"];?)/, `$1\n${importStatement}`);
1024
- // If no Vue import found, add it at the top
1025
- if (!modifiedContent.includes(importStatement)) {
1026
- modifiedContent = `${importStatement}\n\n${modifiedContent}`;
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
- // Handle createApp pattern
1030
- if (content.includes('createApp')) {
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
- // Vue 2 with Options API
1037
- const pluginUsage = `Vue.use(HumanBehaviorPlugin, {
1038
- apiKey: process.env.VUE_APP_HUMANBEHAVIOR_API_KEY
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
- // Handle new Vue pattern
1049
- if (content.includes('new Vue')) {
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
- if (content.includes('humanBehaviorStore')) {
1119
+ // Direct tracker init for non-SSR Svelte
1120
+ if (content.includes('HumanBehaviorTracker.init')) {
1092
1121
  return content;
1093
1122
  }
1094
- const importStatement = `import { humanBehaviorStore } from 'humanbehavior-js/svelte';`;
1095
- const initCode = `humanBehaviorStore.init(process.env.PUBLIC_HUMANBEHAVIOR_API_KEY || '');`;
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
- if (content.includes('humanBehaviorStore')) {
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 initCode = `humanBehaviorStore.init(PUBLIC_HUMANBEHAVIOR_API_KEY || '');`;
1105
- // Add to script section - handle different script tag patterns
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${importStatement}\n\t${initCode}`);
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${importStatement}\n\t${initCode}`);
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
- // If no script tag found, add one at the beginning
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
- return content.replace(/export default defineNuxtConfig\(\{/, `export default defineNuxtConfig({
1172
- runtimeConfig: {
1173
- public: {
1174
- humanBehaviorApiKey: process.env.NUXT_PUBLIC_HUMANBEHAVIOR_API_KEY
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
- const initCode = `
1204
- // Initialize HumanBehavior SDK
1205
- export const onClientEntry = () => {
1206
- console.log('Gatsby browser entry point loaded');
1207
- const apiKey = process.env.GATSBY_HUMANBEHAVIOR_API_KEY;
1208
- console.log('API Key found:', apiKey ? 'Yes' : 'No');
1209
- if (apiKey) {
1210
- const tracker = HumanBehaviorTracker.init(apiKey);
1211
- console.log('HumanBehavior SDK initialized for Gatsby');
1212
- } else {
1213
- console.log('No API key found in environment variables');
1214
- }
1215
- };`;
1216
- // If the file already has content, add the import and init code
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: 'HUMANBEHAVIOR_API_KEY',
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',