humanbehavior-js 0.4.28 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/README.md +151 -0
  2. package/package.json +114 -79
  3. package/packages/angular/dist/index.d.ts +46 -0
  4. package/packages/angular/dist/index.d.ts.map +1 -0
  5. package/packages/angular/dist/index.js +2 -0
  6. package/packages/angular/dist/index.js.map +1 -0
  7. package/packages/angular/dist/index.mjs +2 -0
  8. package/packages/angular/dist/index.mjs.map +1 -0
  9. package/packages/browser/dist/index.d.ts +5 -0
  10. package/packages/browser/dist/index.d.ts.map +1 -0
  11. package/packages/browser/dist/index.iife.js +12095 -0
  12. package/packages/browser/dist/index.iife.js.map +1 -0
  13. package/packages/browser/dist/index.js +2 -0
  14. package/packages/browser/dist/index.js.map +1 -0
  15. package/packages/browser/dist/index.min.js +2 -0
  16. package/packages/browser/dist/index.min.js.map +1 -0
  17. package/packages/browser/dist/index.mjs +2 -0
  18. package/packages/browser/dist/index.mjs.map +1 -0
  19. package/packages/react/dist/browser.d.ts +2 -0
  20. package/packages/react/dist/browser.d.ts.map +1 -0
  21. package/packages/react/dist/index.d.ts +48 -0
  22. package/packages/react/dist/index.d.ts.map +1 -0
  23. package/packages/react/dist/index.js +2 -0
  24. package/packages/react/dist/index.js.map +1 -0
  25. package/packages/react/dist/index.mjs +2 -0
  26. package/packages/react/dist/index.mjs.map +1 -0
  27. package/packages/remix/dist/index.d.ts +8 -0
  28. package/packages/remix/dist/index.d.ts.map +1 -0
  29. package/packages/remix/dist/index.js +2 -0
  30. package/packages/remix/dist/index.js.map +1 -0
  31. package/packages/remix/dist/index.mjs +2 -0
  32. package/packages/remix/dist/index.mjs.map +1 -0
  33. package/packages/svelte/dist/index.d.ts +11 -0
  34. package/packages/svelte/dist/index.d.ts.map +1 -0
  35. package/packages/svelte/dist/index.js +2 -0
  36. package/packages/svelte/dist/index.js.map +1 -0
  37. package/packages/svelte/dist/index.mjs +2 -0
  38. package/packages/svelte/dist/index.mjs.map +1 -0
  39. package/{dist/types/vue → packages/vue/dist}/index.d.ts +4 -5
  40. package/packages/vue/dist/index.d.ts.map +1 -0
  41. package/packages/vue/dist/index.js +2 -0
  42. package/packages/vue/dist/index.js.map +1 -0
  43. package/packages/vue/dist/index.mjs +2 -0
  44. package/packages/vue/dist/index.mjs.map +1 -0
  45. package/packages/wizard/dist/ai/ai-install-wizard.d.ts +145 -0
  46. package/packages/wizard/dist/ai/ai-install-wizard.d.ts.map +1 -0
  47. package/packages/wizard/dist/ai/manual-framework-wizard.d.ts +52 -0
  48. package/packages/wizard/dist/ai/manual-framework-wizard.d.ts.map +1 -0
  49. package/packages/wizard/dist/cli/ai-auto-install.d.ts +27 -0
  50. package/packages/wizard/dist/cli/ai-auto-install.d.ts.map +1 -0
  51. package/{dist → packages/wizard/dist}/cli/ai-auto-install.js +821 -905
  52. package/packages/wizard/dist/cli/ai-auto-install.js.map +1 -0
  53. package/packages/wizard/dist/cli/auto-install.d.ts +26 -0
  54. package/packages/wizard/dist/cli/auto-install.d.ts.map +1 -0
  55. package/{dist → packages/wizard/dist}/cli/auto-install.js +821 -905
  56. package/packages/wizard/dist/cli/auto-install.js.map +1 -0
  57. package/{dist/types → packages/wizard/dist/core}/install-wizard.d.ts +6 -8
  58. package/packages/wizard/dist/core/install-wizard.d.ts.map +1 -0
  59. package/packages/wizard/dist/index.d.ts +18 -0
  60. package/packages/wizard/dist/index.d.ts.map +1 -0
  61. package/packages/wizard/dist/index.js +2 -0
  62. package/packages/wizard/dist/index.js.map +1 -0
  63. package/packages/wizard/dist/index.mjs +2 -0
  64. package/packages/wizard/dist/index.mjs.map +1 -0
  65. package/packages/wizard/dist/services/centralized-ai-service.d.ts +159 -0
  66. package/packages/wizard/dist/services/centralized-ai-service.d.ts.map +1 -0
  67. package/packages/wizard/dist/services/remote-ai-service.d.ts +58 -0
  68. package/packages/wizard/dist/services/remote-ai-service.d.ts.map +1 -0
  69. package/WIZARD_USAGE_GUIDE.md +0 -381
  70. package/dist/cjs/angular/index.cjs +0 -14979
  71. package/dist/cjs/angular/index.cjs.map +0 -1
  72. package/dist/cjs/index.cjs +0 -14964
  73. package/dist/cjs/index.cjs.map +0 -1
  74. package/dist/cjs/install-wizard.cjs +0 -1576
  75. package/dist/cjs/install-wizard.cjs.map +0 -1
  76. package/dist/cjs/react/index.cjs +0 -15103
  77. package/dist/cjs/react/index.cjs.map +0 -1
  78. package/dist/cjs/remix/index.cjs +0 -15077
  79. package/dist/cjs/remix/index.cjs.map +0 -1
  80. package/dist/cjs/svelte/index.cjs +0 -14933
  81. package/dist/cjs/svelte/index.cjs.map +0 -1
  82. package/dist/cjs/vue/index.cjs +0 -14942
  83. package/dist/cjs/vue/index.cjs.map +0 -1
  84. package/dist/cjs/wizard/index.cjs +0 -3490
  85. package/dist/cjs/wizard/index.cjs.map +0 -1
  86. package/dist/cli/ai-auto-install.js.map +0 -1
  87. package/dist/cli/auto-install.js.map +0 -1
  88. package/dist/esm/angular/index.js +0 -14975
  89. package/dist/esm/angular/index.js.map +0 -1
  90. package/dist/esm/index.js +0 -14941
  91. package/dist/esm/index.js.map +0 -1
  92. package/dist/esm/install-wizard.js +0 -1553
  93. package/dist/esm/install-wizard.js.map +0 -1
  94. package/dist/esm/react/index.js +0 -15097
  95. package/dist/esm/react/index.js.map +0 -1
  96. package/dist/esm/remix/index.js +0 -15073
  97. package/dist/esm/remix/index.js.map +0 -1
  98. package/dist/esm/svelte/index.js +0 -14931
  99. package/dist/esm/svelte/index.js.map +0 -1
  100. package/dist/esm/vue/index.js +0 -14940
  101. package/dist/esm/vue/index.js.map +0 -1
  102. package/dist/esm/wizard/index.js +0 -3459
  103. package/dist/esm/wizard/index.js.map +0 -1
  104. package/dist/index.min.js +0 -2
  105. package/dist/index.min.js.map +0 -1
  106. package/dist/types/angular/index.d.ts +0 -357
  107. package/dist/types/index.d.ts +0 -644
  108. package/dist/types/react/index.d.ts +0 -345
  109. package/dist/types/remix/index.d.ts +0 -336
  110. package/dist/types/svelte/index.d.ts +0 -322
  111. package/dist/types/wizard/index.d.ts +0 -523
  112. package/readme.md +0 -335
  113. package/rollup.config.js +0 -422
  114. package/simple-spa.html +0 -1000
  115. package/src/angular/index.ts +0 -79
  116. package/src/api.ts +0 -416
  117. package/src/index.ts +0 -35
  118. package/src/react/AutoInstallWizard.tsx +0 -557
  119. package/src/react/browser.ts +0 -8
  120. package/src/react/index.tsx +0 -308
  121. package/src/redact.ts +0 -327
  122. package/src/remix/index.ts +0 -16
  123. package/src/svelte/index.ts +0 -14
  124. package/src/tracker.ts +0 -1587
  125. package/src/types/clack.d.ts +0 -31
  126. package/src/utils/ip-detector.ts +0 -158
  127. package/src/utils/logger.ts +0 -144
  128. package/src/utils/property-detector.ts +0 -345
  129. package/src/utils/property-manager.ts +0 -274
  130. package/src/vue/index.ts +0 -29
  131. package/src/wizard/README.md +0 -114
  132. package/src/wizard/ai/ai-install-wizard.ts +0 -897
  133. package/src/wizard/ai/manual-framework-wizard.ts +0 -238
  134. package/src/wizard/cli/ai-auto-install.ts +0 -241
  135. package/src/wizard/cli/auto-install.ts +0 -224
  136. package/src/wizard/core/install-wizard.ts +0 -1794
  137. package/src/wizard/index.ts +0 -23
  138. package/src/wizard/services/centralized-ai-service.ts +0 -668
  139. package/src/wizard/services/remote-ai-service.ts +0 -240
  140. package/tsconfig.json +0 -24
@@ -1,1794 +0,0 @@
1
- /**
2
- * HumanBehavior SDK Auto-Installation Wizard
3
- *
4
- * This wizard automatically detects the user's framework and modifies their codebase
5
- * to integrate the SDK with minimal user intervention.
6
- */
7
-
8
- import * as fs from 'fs';
9
- import * as path from 'path';
10
-
11
- export interface FrameworkInfo {
12
- name: string;
13
- type: 'react' | 'vue' | 'angular' | 'svelte' | 'nextjs' | 'nuxt' | 'remix' | 'vanilla' | 'astro' | 'gatsby' | 'node' | 'auto';
14
- bundler?: 'vite' | 'webpack' | 'esbuild' | 'rollup';
15
- packageManager?: 'npm' | 'yarn' | 'pnpm';
16
- hasTypeScript?: boolean;
17
- hasRouter?: boolean;
18
- projectRoot?: string;
19
- version?: string;
20
- majorVersion?: number;
21
- features?: {
22
- hasReact18?: boolean;
23
- hasVue3?: boolean;
24
- hasNuxt3?: boolean;
25
- hasAngularStandalone?: boolean;
26
- hasNextAppRouter?: boolean;
27
- hasSvelteKit?: boolean;
28
- };
29
- }
30
-
31
- export interface CodeModification {
32
- filePath: string;
33
- action: 'create' | 'modify' | 'append';
34
- content: string;
35
- description: string;
36
- }
37
-
38
- export interface InstallationResult {
39
- success: boolean;
40
- framework: FrameworkInfo;
41
- modifications: CodeModification[];
42
- errors: string[];
43
- nextSteps: string[];
44
- }
45
-
46
- export class AutoInstallationWizard {
47
- protected apiKey: string;
48
- protected projectRoot: string;
49
- protected framework: FrameworkInfo | null = null;
50
-
51
- constructor(apiKey: string, projectRoot: string = process.cwd()) {
52
- this.apiKey = apiKey;
53
- this.projectRoot = projectRoot;
54
- }
55
-
56
- /**
57
- * Simple version comparison utility
58
- */
59
- private compareVersions(version1: string, version2: string): number {
60
- const v1Parts = version1.split('.').map(Number);
61
- const v2Parts = version2.split('.').map(Number);
62
-
63
- for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
64
- const v1 = v1Parts[i] || 0;
65
- const v2 = v2Parts[i] || 0;
66
- if (v1 > v2) return 1;
67
- if (v1 < v2) return -1;
68
- }
69
- return 0;
70
- }
71
-
72
- private isVersionGte(version: string, target: string): boolean {
73
- return this.compareVersions(version, target) >= 0;
74
- }
75
-
76
- private getMajorVersion(version: string): number {
77
- return parseInt(version.split('.')[0]) || 0;
78
- }
79
-
80
- /**
81
- * Main installation method - detects framework and auto-installs
82
- */
83
- async install(): Promise<InstallationResult> {
84
- try {
85
- // Step 1: Detect framework
86
- this.framework = await this.detectFramework();
87
-
88
- // Step 2: Install package
89
- await this.installPackage();
90
-
91
- // Step 3: Generate and apply code modifications
92
- const modifications = await this.generateModifications();
93
- await this.applyModifications(modifications);
94
-
95
- // Step 4: Generate next steps
96
- const nextSteps = this.generateNextSteps();
97
-
98
- return {
99
- success: true,
100
- framework: this.framework,
101
- modifications,
102
- errors: [],
103
- nextSteps
104
- };
105
- } catch (error) {
106
- return {
107
- success: false,
108
- framework: this.framework || { name: 'unknown', type: 'vanilla' },
109
- modifications: [],
110
- errors: [error instanceof Error ? error.message : 'Unknown error'],
111
- nextSteps: []
112
- };
113
- }
114
- }
115
-
116
- /**
117
- * Detect the current framework and project setup
118
- */
119
- public async detectFramework(): Promise<FrameworkInfo> {
120
- const packageJsonPath = path.join(this.projectRoot, 'package.json');
121
-
122
- if (!fs.existsSync(packageJsonPath)) {
123
- return {
124
- name: 'vanilla',
125
- type: 'vanilla',
126
- projectRoot: this.projectRoot
127
- };
128
- }
129
-
130
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
131
- const dependencies = {
132
- ...packageJson.dependencies,
133
- ...packageJson.devDependencies
134
- };
135
-
136
- // Detect framework with version information
137
- let framework: FrameworkInfo = {
138
- name: 'vanilla',
139
- type: 'vanilla',
140
- projectRoot: this.projectRoot,
141
- features: {}
142
- };
143
-
144
- if (dependencies.nuxt) {
145
- const nuxtVersion = dependencies.nuxt;
146
- const isNuxt3 = this.isVersionGte(nuxtVersion, '3.0.0');
147
-
148
- framework = {
149
- name: 'nuxt',
150
- type: 'nuxt',
151
- version: nuxtVersion,
152
- majorVersion: this.getMajorVersion(nuxtVersion),
153
- hasTypeScript: !!dependencies.typescript,
154
- hasRouter: true,
155
- projectRoot: this.projectRoot,
156
- features: {
157
- hasNuxt3: isNuxt3
158
- }
159
- };
160
- } else if (dependencies.next) {
161
- const nextVersion = dependencies.next;
162
- const isNext13 = this.isVersionGte(nextVersion, '13.0.0');
163
-
164
- framework = {
165
- name: 'nextjs',
166
- type: 'nextjs',
167
- version: nextVersion,
168
- majorVersion: this.getMajorVersion(nextVersion),
169
- hasTypeScript: !!dependencies.typescript || !!dependencies['@types/node'],
170
- hasRouter: true,
171
- projectRoot: this.projectRoot,
172
- features: {
173
- hasNextAppRouter: isNext13
174
- }
175
- };
176
- } else if (dependencies['@remix-run/react'] || dependencies['@remix-run/dev']) {
177
- const remixVersion = dependencies['@remix-run/react'] || dependencies['@remix-run/dev'];
178
- framework = {
179
- name: 'remix',
180
- type: 'remix',
181
- version: remixVersion,
182
- majorVersion: this.getMajorVersion(remixVersion),
183
- hasTypeScript: !!dependencies.typescript || !!dependencies['@types/react'],
184
- hasRouter: true,
185
- projectRoot: this.projectRoot,
186
- features: {}
187
- };
188
- } else if (dependencies.react) {
189
- const reactVersion = dependencies.react;
190
- const isReact18 = this.isVersionGte(reactVersion, '18.0.0');
191
-
192
- framework = {
193
- name: 'react',
194
- type: 'react',
195
- version: reactVersion,
196
- majorVersion: this.getMajorVersion(reactVersion),
197
- hasTypeScript: !!dependencies.typescript || !!dependencies['@types/react'],
198
- hasRouter: !!dependencies['react-router-dom'] || !!dependencies['react-router'],
199
- projectRoot: this.projectRoot,
200
- features: {
201
- hasReact18: isReact18
202
- }
203
- };
204
- } else if (dependencies.vue) {
205
- const vueVersion = dependencies.vue;
206
- const isVue3 = this.isVersionGte(vueVersion, '3.0.0');
207
-
208
- framework = {
209
- name: 'vue',
210
- type: 'vue',
211
- version: vueVersion,
212
- majorVersion: this.getMajorVersion(vueVersion),
213
- hasTypeScript: !!dependencies.typescript || !!dependencies['@vue/cli-service'],
214
- hasRouter: !!dependencies['vue-router'],
215
- projectRoot: this.projectRoot,
216
- features: {
217
- hasVue3: isVue3
218
- }
219
- };
220
- } else if (dependencies['@angular/core']) {
221
- const angularVersion = dependencies['@angular/core'];
222
- const isAngular17 = this.isVersionGte(angularVersion, '17.0.0');
223
-
224
- framework = {
225
- name: 'angular',
226
- type: 'angular',
227
- version: angularVersion,
228
- majorVersion: this.getMajorVersion(angularVersion),
229
- hasTypeScript: true,
230
- hasRouter: true,
231
- projectRoot: this.projectRoot,
232
- features: {
233
- hasAngularStandalone: isAngular17
234
- }
235
- };
236
- } else if (dependencies.svelte) {
237
- const svelteVersion = dependencies.svelte;
238
- const isSvelteKit = !!dependencies['@sveltejs/kit'];
239
-
240
- framework = {
241
- name: 'svelte',
242
- type: 'svelte',
243
- version: svelteVersion,
244
- majorVersion: this.getMajorVersion(svelteVersion),
245
- hasTypeScript: !!dependencies.typescript || !!dependencies['svelte-check'],
246
- hasRouter: !!dependencies['svelte-routing'] || !!dependencies['@sveltejs/kit'],
247
- projectRoot: this.projectRoot,
248
- features: {
249
- hasSvelteKit: isSvelteKit
250
- }
251
- };
252
- } else if (dependencies.astro) {
253
- const astroVersion = dependencies.astro;
254
- framework = {
255
- name: 'astro',
256
- type: 'astro',
257
- version: astroVersion,
258
- majorVersion: this.getMajorVersion(astroVersion),
259
- hasTypeScript: !!dependencies.typescript || !!dependencies['@astrojs/ts-plugin'],
260
- hasRouter: true,
261
- projectRoot: this.projectRoot,
262
- features: {}
263
- };
264
- } else if (dependencies.gatsby) {
265
- const gatsbyVersion = dependencies.gatsby;
266
- framework = {
267
- name: 'gatsby',
268
- type: 'gatsby',
269
- version: gatsbyVersion,
270
- majorVersion: this.getMajorVersion(gatsbyVersion),
271
- hasTypeScript: !!dependencies.typescript || !!dependencies['@types/react'],
272
- hasRouter: true,
273
- projectRoot: this.projectRoot,
274
- features: {}
275
- };
276
- }
277
-
278
- // Detect bundler
279
- if (dependencies.vite) {
280
- framework.bundler = 'vite';
281
- } else if (dependencies.webpack) {
282
- framework.bundler = 'webpack';
283
- } else if (dependencies.esbuild) {
284
- framework.bundler = 'esbuild';
285
- } else if (dependencies.rollup) {
286
- framework.bundler = 'rollup';
287
- }
288
-
289
- // Detect package manager
290
- if (fs.existsSync(path.join(this.projectRoot, 'yarn.lock'))) {
291
- framework.packageManager = 'yarn';
292
- } else if (fs.existsSync(path.join(this.projectRoot, 'pnpm-lock.yaml'))) {
293
- framework.packageManager = 'pnpm';
294
- } else {
295
- framework.packageManager = 'npm';
296
- }
297
-
298
- return framework;
299
- }
300
-
301
- /**
302
- * Install the SDK package with latest version range
303
- */
304
- protected async installPackage(): Promise<void> {
305
- const { execSync } = await import('child_process');
306
-
307
- // Build base command with latest version range
308
- let command = this.framework?.packageManager === 'yarn'
309
- ? 'yarn add humanbehavior-js@latest'
310
- : this.framework?.packageManager === 'pnpm'
311
- ? 'pnpm add humanbehavior-js@latest'
312
- : 'npm install humanbehavior-js@latest';
313
-
314
- // Add legacy peer deps flag for npm to handle dependency conflicts
315
- if (this.framework?.packageManager !== 'yarn' && this.framework?.packageManager !== 'pnpm') {
316
- command += ' --legacy-peer-deps';
317
- }
318
-
319
- try {
320
- execSync(command, { cwd: this.projectRoot, stdio: 'inherit' });
321
- } catch (error) {
322
- throw new Error(`Failed to install humanbehavior-js: ${error}`);
323
- }
324
- }
325
-
326
- /**
327
- * Generate code modifications based on framework
328
- */
329
- protected async generateModifications(): Promise<CodeModification[]> {
330
- const modifications: CodeModification[] = [];
331
-
332
- switch (this.framework?.type) {
333
- case 'react':
334
- modifications.push(...await this.generateReactModifications());
335
- break;
336
- case 'nextjs':
337
- modifications.push(...await this.generateNextJSModifications());
338
- break;
339
- case 'nuxt':
340
- modifications.push(...await this.generateNuxtModifications());
341
- break;
342
- case 'astro':
343
- modifications.push(...await this.generateAstroModifications());
344
- break;
345
- case 'gatsby':
346
- modifications.push(...await this.generateGatsbyModifications());
347
- break;
348
- case 'remix':
349
- modifications.push(...await this.generateRemixModifications());
350
- break;
351
- case 'vue':
352
- modifications.push(...await this.generateVueModifications());
353
- break;
354
- case 'angular':
355
- modifications.push(...await this.generateAngularModifications());
356
- break;
357
- case 'svelte':
358
- modifications.push(...await this.generateSvelteModifications());
359
- break;
360
- default:
361
- modifications.push(...await this.generateVanillaModifications());
362
- }
363
-
364
- return modifications;
365
- }
366
-
367
- /**
368
- * Generate React-specific modifications
369
- */
370
- private async generateReactModifications(): Promise<CodeModification[]> {
371
- const modifications: CodeModification[] = [];
372
-
373
- // Find main App component or index file
374
- const appFile = this.findReactAppFile();
375
- if (appFile) {
376
- const content = fs.readFileSync(appFile, 'utf8');
377
- const modifiedContent = this.injectReactProvider(content, appFile);
378
-
379
- modifications.push({
380
- filePath: appFile,
381
- action: 'modify',
382
- content: modifiedContent,
383
- description: 'Added HumanBehaviorProvider to React app'
384
- });
385
- }
386
-
387
- // Create or append to environment file
388
- modifications.push(this.createEnvironmentModification(this.framework!));
389
-
390
- return modifications;
391
- }
392
-
393
- /**
394
- * Generate Next.js-specific modifications
395
- */
396
- private async generateNextJSModifications(): Promise<CodeModification[]> {
397
- const modifications: CodeModification[] = [];
398
-
399
- // Check for App Router
400
- const appLayoutFile = path.join(this.projectRoot, 'src', 'app', 'layout.tsx');
401
- const pagesLayoutFile = path.join(this.projectRoot, 'src', 'pages', '_app.tsx');
402
-
403
- if (fs.existsSync(appLayoutFile)) {
404
- // Create providers.tsx file for App Router
405
- modifications.push({
406
- filePath: path.join(this.projectRoot, 'src', 'app', 'providers.tsx'),
407
- action: 'create',
408
- content: `'use client';
409
-
410
- import { HumanBehaviorProvider } from 'humanbehavior-js/react';
411
-
412
- export function Providers({ children }: { children: React.ReactNode }) {
413
- return (
414
- <HumanBehaviorProvider apiKey={process.env.NEXT_PUBLIC_HUMANBEHAVIOR_API_KEY}>
415
- {children}
416
- </HumanBehaviorProvider>
417
- );
418
- }`,
419
- description: 'Created providers.tsx file for Next.js App Router'
420
- });
421
-
422
- // Modify layout.tsx to use the provider
423
- const content = fs.readFileSync(appLayoutFile, 'utf8');
424
- const modifiedContent = this.injectNextJSAppRouter(content);
425
-
426
- modifications.push({
427
- filePath: appLayoutFile,
428
- action: 'modify',
429
- content: modifiedContent,
430
- description: 'Added Providers wrapper to Next.js App Router layout'
431
- });
432
- } else if (fs.existsSync(pagesLayoutFile)) {
433
- // Create providers.tsx file for Pages Router
434
- modifications.push({
435
- filePath: path.join(this.projectRoot, 'src', 'components', 'providers.tsx'),
436
- action: 'create',
437
- content: `'use client';
438
-
439
- import { HumanBehaviorProvider } from 'humanbehavior-js/react';
440
-
441
- export function Providers({ children }: { children: React.ReactNode }) {
442
- return (
443
- <HumanBehaviorProvider apiKey={process.env.NEXT_PUBLIC_HUMANBEHAVIOR_API_KEY}>
444
- {children}
445
- </HumanBehaviorProvider>
446
- );
447
- }`,
448
- description: 'Created providers.tsx file for Pages Router'
449
- });
450
-
451
- // Modify _app.tsx to use the provider
452
- const content = fs.readFileSync(pagesLayoutFile, 'utf8');
453
- const modifiedContent = this.injectNextJSPagesRouter(content);
454
-
455
- modifications.push({
456
- filePath: pagesLayoutFile,
457
- action: 'modify',
458
- content: modifiedContent,
459
- description: 'Added Providers wrapper to Next.js Pages Router'
460
- });
461
- }
462
-
463
- // Create or append to environment file
464
- modifications.push(this.createEnvironmentModification(this.framework!));
465
-
466
- return modifications;
467
- }
468
-
469
- /**
470
- * Generate Astro-specific modifications
471
- */
472
- private async generateAstroModifications(): Promise<CodeModification[]> {
473
- const modifications: CodeModification[] = [];
474
-
475
- // Create Astro component for HumanBehavior
476
- const astroComponentPath = path.join(this.projectRoot, 'src', 'components', 'HumanBehavior.astro');
477
- const astroComponentContent = `---
478
- // This component will only run on the client side
479
- ---
480
-
481
- <script>
482
- import { HumanBehaviorTracker } from 'humanbehavior-js';
483
-
484
- // Get API key from environment variable
485
- const apiKey = import.meta.env.PUBLIC_HUMANBEHAVIOR_API_KEY;
486
-
487
- console.log('HumanBehavior: API key found:', apiKey ? 'Yes' : 'No');
488
-
489
- if (apiKey) {
490
- try {
491
- const tracker = HumanBehaviorTracker.init(apiKey);
492
- console.log('HumanBehavior: Tracker initialized successfully');
493
-
494
- // Test event to verify tracking is working
495
- setTimeout(() => {
496
- tracker.customEvent('astro_page_view', {
497
- page: window.location.pathname,
498
- framework: 'astro'
499
- }).then(() => {
500
- console.log('HumanBehavior: Test event sent successfully');
501
- }).catch((error) => {
502
- console.error('HumanBehavior: Failed to send test event:', error);
503
- });
504
- }, 1000);
505
-
506
- } catch (error) {
507
- console.error('HumanBehavior: Failed to initialize tracker:', error);
508
- }
509
- } else {
510
- console.error('HumanBehavior: No API key found');
511
- }
512
- </script>`;
513
-
514
- modifications.push({
515
- filePath: astroComponentPath,
516
- action: 'create',
517
- content: astroComponentContent,
518
- description: 'Created Astro component for HumanBehavior SDK'
519
- });
520
-
521
- // Find and update layout file
522
- const layoutFiles = [
523
- path.join(this.projectRoot, 'src', 'layouts', 'Layout.astro'),
524
- path.join(this.projectRoot, 'src', 'layouts', 'layout.astro'),
525
- path.join(this.projectRoot, 'src', 'layouts', 'BaseLayout.astro')
526
- ];
527
-
528
- let layoutFile = null;
529
- for (const file of layoutFiles) {
530
- if (fs.existsSync(file)) {
531
- layoutFile = file;
532
- break;
533
- }
534
- }
535
-
536
- if (layoutFile) {
537
- const content = fs.readFileSync(layoutFile, 'utf8');
538
- const modifiedContent = this.injectAstroLayout(content);
539
-
540
- modifications.push({
541
- filePath: layoutFile,
542
- action: 'modify',
543
- content: modifiedContent,
544
- description: 'Added HumanBehavior component to Astro layout'
545
- });
546
- }
547
-
548
- // Add environment variable
549
- modifications.push(this.createEnvironmentModification(this.framework!));
550
-
551
- return modifications;
552
- }
553
-
554
- /**
555
- * Generate Nuxt-specific modifications
556
- */
557
- private async generateNuxtModifications(): Promise<CodeModification[]> {
558
- const modifications: CodeModification[] = [];
559
-
560
- // Create plugin file for Nuxt (in app directory)
561
- const pluginFile = path.join(this.projectRoot, 'app', 'plugins', 'humanbehavior.client.ts');
562
- modifications.push({
563
- filePath: pluginFile,
564
- action: 'create',
565
- content: `import { HumanBehaviorTracker } from 'humanbehavior-js';
566
-
567
- export default defineNuxtPlugin(() => {
568
- const config = useRuntimeConfig();
569
-
570
- // Initialize HumanBehavior SDK (client-side only)
571
- if (typeof window !== 'undefined') {
572
- const apiKey = config.public.humanBehaviorApiKey;
573
- console.log('HumanBehavior: API key:', apiKey ? 'present' : 'missing');
574
-
575
- if (apiKey) {
576
- try {
577
- const tracker = HumanBehaviorTracker.init(apiKey);
578
- console.log('HumanBehavior: Tracker initialized successfully');
579
- } catch (error) {
580
- console.error('HumanBehavior: Failed to initialize tracker:', error);
581
- }
582
- } else {
583
- console.error('HumanBehavior: No API key found in runtime config');
584
- }
585
- }
586
- });`,
587
- description: 'Created Nuxt plugin for HumanBehavior SDK in app directory'
588
- });
589
-
590
- // Create environment configuration
591
- const nuxtConfigFile = path.join(this.projectRoot, 'nuxt.config.ts');
592
- if (fs.existsSync(nuxtConfigFile)) {
593
- const content = fs.readFileSync(nuxtConfigFile, 'utf8');
594
- const modifiedContent = this.injectNuxtConfig(content);
595
-
596
- modifications.push({
597
- filePath: nuxtConfigFile,
598
- action: 'modify',
599
- content: modifiedContent,
600
- description: 'Added HumanBehavior runtime config to Nuxt config'
601
- });
602
- }
603
-
604
- // Create or append to environment file
605
- modifications.push(this.createEnvironmentModification(this.framework!));
606
-
607
- return modifications;
608
- }
609
-
610
- /**
611
- * Generate Remix-specific modifications
612
- */
613
- private async generateRemixModifications(): Promise<CodeModification[]> {
614
- const modifications: CodeModification[] = [];
615
-
616
- // Find root.tsx file
617
- const rootFile = path.join(this.projectRoot, 'app', 'root.tsx');
618
- if (fs.existsSync(rootFile)) {
619
- const content = fs.readFileSync(rootFile, 'utf8');
620
- const modifiedContent = this.injectRemixProvider(content);
621
-
622
- modifications.push({
623
- filePath: rootFile,
624
- action: 'modify',
625
- content: modifiedContent,
626
- description: 'Added HumanBehaviorProvider to Remix root component'
627
- });
628
- }
629
-
630
- // Create or append to environment file
631
- modifications.push(this.createEnvironmentModification(this.framework!));
632
-
633
- return modifications;
634
- }
635
-
636
- /**
637
- * Generate Vue-specific modifications
638
- */
639
- private async generateVueModifications(): Promise<CodeModification[]> {
640
- const modifications: CodeModification[] = [];
641
-
642
- // Find main.js or main.ts
643
- const mainFile = this.findVueMainFile();
644
- if (mainFile) {
645
- const content = fs.readFileSync(mainFile, 'utf8');
646
- const modifiedContent = this.injectVuePlugin(content);
647
-
648
- modifications.push({
649
- filePath: mainFile,
650
- action: 'modify',
651
- content: modifiedContent,
652
- description: 'Added HumanBehaviorPlugin to Vue app'
653
- });
654
- }
655
-
656
- // Create or append to environment file
657
- modifications.push(this.createEnvironmentModification(this.framework!));
658
-
659
- return modifications;
660
- }
661
-
662
- /**
663
- * Generate Angular-specific modifications
664
- */
665
- private async generateAngularModifications(): Promise<CodeModification[]> {
666
- const modifications: CodeModification[] = [];
667
-
668
- // Check for modern Angular (standalone components) vs legacy (NgModule)
669
- const appModuleFile = path.join(this.projectRoot, 'src', 'app', 'app.module.ts');
670
- const appComponentFile = path.join(this.projectRoot, 'src', 'app', 'app.ts');
671
- const mainFile = path.join(this.projectRoot, 'src', 'main.ts');
672
-
673
- const isModernAngular = fs.existsSync(appComponentFile) && !fs.existsSync(appModuleFile);
674
-
675
- if (isModernAngular) {
676
- // Modern Angular 17+ with standalone components
677
- if (fs.existsSync(mainFile)) {
678
- const content = fs.readFileSync(mainFile, 'utf8');
679
- const modifiedContent = this.injectAngularStandaloneInit(content);
680
-
681
- modifications.push({
682
- filePath: mainFile,
683
- action: 'modify',
684
- content: modifiedContent,
685
- description: 'Added HumanBehavior initialization to Angular main.ts'
686
- });
687
- }
688
- } else if (fs.existsSync(appModuleFile)) {
689
- // Legacy Angular with NgModule
690
- const content = fs.readFileSync(appModuleFile, 'utf8');
691
- const modifiedContent = this.injectAngularModule(content);
692
-
693
- modifications.push({
694
- filePath: appModuleFile,
695
- action: 'modify',
696
- content: modifiedContent,
697
- description: 'Added HumanBehaviorModule to Angular app'
698
- });
699
- }
700
-
701
- // Handle Angular environment files (proper Angular way)
702
- const envFile = path.join(this.projectRoot, 'src', 'environments', 'environment.ts');
703
- const envProdFile = path.join(this.projectRoot, 'src', 'environments', 'environment.prod.ts');
704
-
705
- // Create environments directory if it doesn't exist
706
- const envDir = path.dirname(envFile);
707
- if (!fs.existsSync(envDir)) {
708
- fs.mkdirSync(envDir, { recursive: true });
709
- }
710
-
711
- // Create or update development environment
712
- if (fs.existsSync(envFile)) {
713
- const content = fs.readFileSync(envFile, 'utf8');
714
- if (!content.includes('humanBehaviorApiKey')) {
715
- const modifiedContent = content.replace(
716
- /export const environment = {([\s\S]*?)};/,
717
- `export const environment = {
718
- $1,
719
- humanBehaviorApiKey: '${this.apiKey}'
720
- };`
721
- );
722
- modifications.push({
723
- filePath: envFile,
724
- action: 'modify',
725
- content: modifiedContent,
726
- description: 'Added API key to Angular development environment'
727
- });
728
- }
729
- } else {
730
- // Create new development environment file
731
- modifications.push({
732
- filePath: envFile,
733
- action: 'create',
734
- content: `export const environment = {
735
- production: false,
736
- humanBehaviorApiKey: '${this.apiKey}'
737
- };`,
738
- description: 'Created Angular development environment file'
739
- });
740
- }
741
-
742
- // Create or update production environment
743
- if (fs.existsSync(envProdFile)) {
744
- const content = fs.readFileSync(envProdFile, 'utf8');
745
- if (!content.includes('humanBehaviorApiKey')) {
746
- const modifiedContent = content.replace(
747
- /export const environment = {([\s\S]*?)};/,
748
- `export const environment = {
749
- $1,
750
- humanBehaviorApiKey: '${this.apiKey}'
751
- };`
752
- );
753
- modifications.push({
754
- filePath: envProdFile,
755
- action: 'modify',
756
- content: modifiedContent,
757
- description: 'Added API key to Angular production environment'
758
- });
759
- }
760
- } else {
761
- // Create new production environment file
762
- modifications.push({
763
- filePath: envProdFile,
764
- action: 'create',
765
- content: `export const environment = {
766
- production: true,
767
- humanBehaviorApiKey: '${this.apiKey}'
768
- };`,
769
- description: 'Created Angular production environment file'
770
- });
771
- }
772
-
773
- // For Angular, we don't need .env files since we use environment.ts
774
- // The environment files are already created above
775
-
776
- return modifications;
777
- }
778
-
779
- /**
780
- * Generate Svelte-specific modifications
781
- */
782
- private async generateSvelteModifications(): Promise<CodeModification[]> {
783
- const modifications: CodeModification[] = [];
784
-
785
- // Check for SvelteKit
786
- const svelteConfigFile = path.join(this.projectRoot, 'svelte.config.js');
787
- const isSvelteKit = fs.existsSync(svelteConfigFile);
788
-
789
- if (isSvelteKit) {
790
- // SvelteKit - create layout file
791
- const layoutFile = path.join(this.projectRoot, 'src', 'routes', '+layout.svelte');
792
- if (fs.existsSync(layoutFile)) {
793
- const content = fs.readFileSync(layoutFile, 'utf8');
794
- const modifiedContent = this.injectSvelteKitLayout(content);
795
-
796
- modifications.push({
797
- filePath: layoutFile,
798
- action: 'modify',
799
- content: modifiedContent,
800
- description: 'Added HumanBehavior store to SvelteKit layout'
801
- });
802
- }
803
- } else {
804
- // Regular Svelte - modify main file
805
- const mainFile = this.findSvelteMainFile();
806
- if (mainFile) {
807
- const content = fs.readFileSync(mainFile, 'utf8');
808
- const modifiedContent = this.injectSvelteStore(content);
809
-
810
- modifications.push({
811
- filePath: mainFile,
812
- action: 'modify',
813
- content: modifiedContent,
814
- description: 'Added HumanBehavior store to Svelte app'
815
- });
816
- }
817
- }
818
-
819
- // Create or append to environment file
820
- modifications.push(this.createEnvironmentModification(this.framework!));
821
-
822
- return modifications;
823
- }
824
-
825
- /**
826
- * Generate vanilla JS/TS modifications
827
- */
828
- private async generateVanillaModifications(): Promise<CodeModification[]> {
829
- const modifications: CodeModification[] = [];
830
-
831
- // Find HTML file to inject script
832
- const htmlFile = this.findHTMLFile();
833
- if (htmlFile) {
834
- const content = fs.readFileSync(htmlFile, 'utf8');
835
- const modifiedContent = this.injectVanillaScript(content);
836
-
837
- modifications.push({
838
- filePath: htmlFile,
839
- action: 'modify',
840
- content: modifiedContent,
841
- description: 'Added HumanBehavior CDN script to HTML file'
842
- });
843
- }
844
-
845
- // Create or append to environment file
846
- modifications.push(this.createEnvironmentModification(this.framework!));
847
-
848
- return modifications;
849
- }
850
-
851
- /**
852
- * Generate Gatsby-specific modifications
853
- */
854
- private async generateGatsbyModifications(): Promise<CodeModification[]> {
855
- const modifications: CodeModification[] = [];
856
-
857
- // Modify or create gatsby-browser.js for Gatsby
858
- const gatsbyBrowserFile = path.join(this.projectRoot, 'gatsby-browser.js');
859
-
860
- if (fs.existsSync(gatsbyBrowserFile)) {
861
- const content = fs.readFileSync(gatsbyBrowserFile, 'utf8');
862
- const modifiedContent = this.injectGatsbyBrowser(content);
863
-
864
- modifications.push({
865
- filePath: gatsbyBrowserFile,
866
- action: 'modify',
867
- content: modifiedContent,
868
- description: 'Added HumanBehavior initialization to Gatsby browser'
869
- });
870
- } else {
871
- // Create gatsby-browser.js if it doesn't exist
872
- modifications.push({
873
- filePath: gatsbyBrowserFile,
874
- action: 'create',
875
- content: `import { HumanBehaviorTracker } from 'humanbehavior-js';
876
-
877
- export const onClientEntry = () => {
878
- console.log('Gatsby browser entry point loaded');
879
- const apiKey = process.env.GATSBY_HUMANBEHAVIOR_API_KEY;
880
- console.log('API Key found:', apiKey ? 'Yes' : 'No');
881
- if (apiKey) {
882
- const tracker = HumanBehaviorTracker.init(apiKey);
883
- console.log('HumanBehavior SDK initialized for Gatsby');
884
- } else {
885
- console.log('No API key found in environment variables');
886
- }
887
- };`,
888
- description: 'Created gatsby-browser.js with HumanBehavior initialization'
889
- });
890
- }
891
-
892
- // Create or append to environment file
893
- modifications.push(this.createEnvironmentModification(this.framework!));
894
-
895
- return modifications;
896
- }
897
-
898
-
899
-
900
- /**
901
- * Apply modifications to the codebase
902
- */
903
- protected async applyModifications(modifications: CodeModification[]): Promise<void> {
904
- for (const modification of modifications) {
905
- try {
906
- const dir = path.dirname(modification.filePath);
907
- if (!fs.existsSync(dir)) {
908
- fs.mkdirSync(dir, { recursive: true });
909
- }
910
-
911
- switch (modification.action) {
912
- case 'create':
913
- fs.writeFileSync(modification.filePath, modification.content);
914
- break;
915
- case 'modify':
916
- fs.writeFileSync(modification.filePath, modification.content);
917
- break;
918
- case 'append':
919
- fs.appendFileSync(modification.filePath, '\n' + modification.content);
920
- break;
921
- }
922
- } catch (error) {
923
- throw new Error(`Failed to apply modification to ${modification.filePath}: ${error}`);
924
- }
925
- }
926
- }
927
-
928
- /**
929
- * Generate next steps for the user
930
- */
931
- private generateNextSteps(): string[] {
932
- const steps = [
933
- '✅ SDK installed and configured automatically!',
934
- '🚀 Your app is now tracking user behavior',
935
- '📊 View sessions in your HumanBehavior dashboard',
936
- '🔧 Customize tracking in your code as needed'
937
- ];
938
-
939
- if (this.framework?.type === 'react' || this.framework?.type === 'nextjs') {
940
- steps.push('💡 Use the useHumanBehavior() hook to track custom events');
941
- }
942
-
943
- return steps;
944
- }
945
-
946
- // Helper methods for file detection and content injection
947
- private findReactAppFile(): string | null {
948
- const possibleFiles = [
949
- 'src/App.jsx', 'src/App.js', 'src/App.tsx', 'src/App.ts',
950
- 'src/index.js', 'src/index.tsx', 'src/main.js', 'src/main.tsx'
951
- ];
952
-
953
- for (const file of possibleFiles) {
954
- const fullPath = path.join(this.projectRoot, file);
955
- if (fs.existsSync(fullPath)) {
956
- return fullPath;
957
- }
958
- }
959
- return null;
960
- }
961
-
962
- private findVueMainFile(): string | null {
963
- const possibleFiles = [
964
- 'src/main.js', 'src/main.ts', 'src/main.jsx', 'src/main.tsx'
965
- ];
966
-
967
- for (const file of possibleFiles) {
968
- const fullPath = path.join(this.projectRoot, file);
969
- if (fs.existsSync(fullPath)) {
970
- return fullPath;
971
- }
972
- }
973
- return null;
974
- }
975
-
976
- private findSvelteMainFile(): string | null {
977
- const possibleFiles = [
978
- 'src/main.js', 'src/main.ts', 'src/main.svelte'
979
- ];
980
-
981
- for (const file of possibleFiles) {
982
- const fullPath = path.join(this.projectRoot, file);
983
- if (fs.existsSync(fullPath)) {
984
- return fullPath;
985
- }
986
- }
987
- return null;
988
- }
989
-
990
- private findHTMLFile(): string | null {
991
- const possibleFiles = ['index.html', 'public/index.html', 'dist/index.html'];
992
-
993
- for (const file of possibleFiles) {
994
- const fullPath = path.join(this.projectRoot, file);
995
- if (fs.existsSync(fullPath)) {
996
- return fullPath;
997
- }
998
- }
999
- return null;
1000
- }
1001
-
1002
- private injectReactProvider(content: string, filePath: string): string {
1003
- const isTypeScript = filePath.endsWith('.tsx') || filePath.endsWith('.ts');
1004
-
1005
- // Check if already has HumanBehaviorProvider
1006
- if (content.includes('HumanBehaviorProvider')) {
1007
- return content;
1008
- }
1009
-
1010
- // Determine the correct environment variable syntax based on bundler
1011
- const isVite = this.framework?.bundler === 'vite';
1012
- const envVar = isVite
1013
- ? 'import.meta.env.VITE_HUMANBEHAVIOR_API_KEY!'
1014
- : 'process.env.HUMANBEHAVIOR_API_KEY!';
1015
-
1016
- const importStatement = `import { HumanBehaviorProvider } from 'humanbehavior-js/react';`;
1017
-
1018
- // Enhanced parsing for React 18+ features
1019
- const hasReact18 = this.framework?.features?.hasReact18;
1020
-
1021
- // Handle different React patterns
1022
- if (content.includes('function App()') || content.includes('const App =')) {
1023
- // Add import statement
1024
- let modifiedContent = content.replace(
1025
- /(import.*?from.*?['"]react['"];?)/,
1026
- `$1\n${importStatement}`
1027
- );
1028
-
1029
- // If no React import found, add it at the top
1030
- if (!modifiedContent.includes(importStatement)) {
1031
- modifiedContent = `${importStatement}\n\n${modifiedContent}`;
1032
- }
1033
-
1034
- // Wrap the App component return with HumanBehaviorProvider
1035
- modifiedContent = modifiedContent.replace(
1036
- /(return\s*\([\s\S]*?\)\s*;)/,
1037
- `return (
1038
- <HumanBehaviorProvider apiKey={${envVar}}>
1039
- $1
1040
- </HumanBehaviorProvider>
1041
- );`
1042
- );
1043
-
1044
- return modifiedContent;
1045
- }
1046
-
1047
- // Handle React 18+ createRoot pattern
1048
- if (hasReact18 && content.includes('createRoot')) {
1049
- let modifiedContent = content.replace(
1050
- /(import.*?from.*?['"]react['"];?)/,
1051
- `$1\n${importStatement}`
1052
- );
1053
-
1054
- if (!modifiedContent.includes(importStatement)) {
1055
- modifiedContent = `${importStatement}\n\n${modifiedContent}`;
1056
- }
1057
-
1058
- // Wrap the root render with HumanBehaviorProvider
1059
- modifiedContent = modifiedContent.replace(
1060
- /(root\.render\s*\([\s\S]*?\)\s*;)/,
1061
- `root.render(
1062
- <HumanBehaviorProvider apiKey={${envVar}}>
1063
- $1
1064
- </HumanBehaviorProvider>
1065
- );`
1066
- );
1067
-
1068
- return modifiedContent;
1069
- }
1070
-
1071
- // Fallback: simple injection
1072
- return `${importStatement}\n\n${content}`;
1073
- }
1074
-
1075
- private injectNextJSAppRouter(content: string): string {
1076
- if (content.includes('Providers')) {
1077
- return content;
1078
- }
1079
-
1080
- const importStatement = `import { Providers } from './providers';`;
1081
-
1082
- // First, add the import statement
1083
- let modifiedContent = content.replace(
1084
- /export default function RootLayout/,
1085
- `${importStatement}\n\nexport default function RootLayout`
1086
- );
1087
-
1088
- // Then wrap the body content with Providers
1089
- // Use a more specific approach to handle the body content
1090
- modifiedContent = modifiedContent.replace(
1091
- /<body([^>]*)>([\s\S]*?)<\/body>/,
1092
- (match, bodyAttrs, bodyContent) => {
1093
- // Trim whitespace and newlines from bodyContent
1094
- const trimmedContent = bodyContent.trim();
1095
- return `<body${bodyAttrs}>
1096
- <Providers>
1097
- ${trimmedContent}
1098
- </Providers>
1099
- </body>`;
1100
- }
1101
- );
1102
-
1103
- return modifiedContent;
1104
- }
1105
-
1106
- private injectNextJSPagesRouter(content: string): string {
1107
- if (content.includes('Providers')) {
1108
- return content;
1109
- }
1110
-
1111
- const importStatement = `import { Providers } from '../components/providers';`;
1112
-
1113
- return content.replace(
1114
- /function MyApp/,
1115
- `${importStatement}\n\nfunction MyApp`
1116
- ).replace(
1117
- /return \(([\s\S]*?)\);/,
1118
- `return (
1119
- <Providers>
1120
- $1
1121
- </Providers>
1122
- );`
1123
- );
1124
- }
1125
-
1126
- private injectRemixProvider(content: string): string {
1127
- if (content.includes('HumanBehaviorProvider')) {
1128
- return content;
1129
- }
1130
-
1131
- const importStatement = `import { HumanBehaviorProvider, createHumanBehaviorLoader } from 'humanbehavior-js/remix';`;
1132
- const useLoaderDataImport = `import { useLoaderData } from "@remix-run/react";`;
1133
-
1134
- // Add imports more robustly
1135
- let modifiedContent = content;
1136
-
1137
- // Add HumanBehaviorProvider import - find the last import and add after it
1138
- if (!content.includes('HumanBehaviorProvider')) {
1139
- const lastImportIndex = modifiedContent.lastIndexOf('import');
1140
- if (lastImportIndex !== -1) {
1141
- const nextLineIndex = modifiedContent.indexOf('\n', lastImportIndex);
1142
- if (nextLineIndex !== -1) {
1143
- modifiedContent = modifiedContent.slice(0, nextLineIndex + 1) +
1144
- importStatement + '\n' +
1145
- modifiedContent.slice(nextLineIndex + 1);
1146
- } else {
1147
- modifiedContent = modifiedContent + '\n' + importStatement;
1148
- }
1149
- } else {
1150
- modifiedContent = importStatement + '\n' + modifiedContent;
1151
- }
1152
- }
1153
-
1154
- // Add useLoaderData import - find the last import and add after it
1155
- if (!content.includes('useLoaderData')) {
1156
- const lastImportIndex = modifiedContent.lastIndexOf('import');
1157
- if (lastImportIndex !== -1) {
1158
- const nextLineIndex = modifiedContent.indexOf('\n', lastImportIndex);
1159
- if (nextLineIndex !== -1) {
1160
- modifiedContent = modifiedContent.slice(0, nextLineIndex + 1) +
1161
- useLoaderDataImport + '\n' +
1162
- modifiedContent.slice(nextLineIndex + 1);
1163
- } else {
1164
- modifiedContent = modifiedContent + '\n' + useLoaderDataImport;
1165
- }
1166
- } else {
1167
- modifiedContent = useLoaderDataImport + '\n' + modifiedContent;
1168
- }
1169
- }
1170
-
1171
-
1172
-
1173
- // Add loader function before the App component
1174
- if (!content.includes('export const loader')) {
1175
- modifiedContent = modifiedContent.replace(
1176
- /export default function App\(\)/,
1177
- `export const loader = createHumanBehaviorLoader();
1178
-
1179
- export default function App()`
1180
- );
1181
- }
1182
-
1183
- // Wrap the App component content with HumanBehaviorProvider
1184
- modifiedContent = modifiedContent.replace(
1185
- /export default function App\(\) \{[\s\S]*?return \(([\s\S]*?)\);[\s\S]*?\}/,
1186
- `export default function App() {
1187
- const data = useLoaderData<typeof loader>();
1188
-
1189
- return (
1190
- <HumanBehaviorProvider apiKey={data.ENV.HUMANBEHAVIOR_API_KEY}>
1191
- $1
1192
- </HumanBehaviorProvider>
1193
- );
1194
- }`
1195
- );
1196
-
1197
- return modifiedContent;
1198
- }
1199
-
1200
- private injectVuePlugin(content: string): string {
1201
- if (content.includes('HumanBehaviorPlugin')) {
1202
- return content;
1203
- }
1204
-
1205
- const importStatement = `import { HumanBehaviorPlugin } from 'humanbehavior-js/vue';`;
1206
-
1207
- // Enhanced Vue 3 support with version detection
1208
- const hasVue3 = this.framework?.features?.hasVue3;
1209
-
1210
- if (hasVue3) {
1211
- // Vue 3 with Composition API
1212
- const pluginUsage = `app.use(HumanBehaviorPlugin, {
1213
- apiKey: import.meta.env.VITE_HUMANBEHAVIOR_API_KEY
1214
- });`;
1215
-
1216
- let modifiedContent = content;
1217
-
1218
- // Add import statement
1219
- if (!content.includes(importStatement)) {
1220
- modifiedContent = content.replace(
1221
- /(import.*?from.*?['"]vue['"];?)/,
1222
- `$1\n${importStatement}`
1223
- );
1224
-
1225
- // If no Vue import found, add it at the top
1226
- if (!modifiedContent.includes(importStatement)) {
1227
- modifiedContent = `${importStatement}\n\n${modifiedContent}`;
1228
- }
1229
- }
1230
-
1231
- // Handle createApp pattern
1232
- if (content.includes('createApp')) {
1233
- modifiedContent = modifiedContent.replace(
1234
- /(app\.mount\(.*?\))/,
1235
- `${pluginUsage}\n\n$1`
1236
- );
1237
- }
1238
-
1239
- return modifiedContent;
1240
- } else {
1241
- // Vue 2 with Options API
1242
- const pluginUsage = `Vue.use(HumanBehaviorPlugin, {
1243
- apiKey: process.env.VUE_APP_HUMANBEHAVIOR_API_KEY
1244
- });`;
1245
-
1246
- let modifiedContent = content;
1247
-
1248
- // Add import statement
1249
- if (!content.includes(importStatement)) {
1250
- modifiedContent = content.replace(
1251
- /(import.*?from.*?['"]vue['"];?)/,
1252
- `$1\n${importStatement}`
1253
- );
1254
-
1255
- if (!modifiedContent.includes(importStatement)) {
1256
- modifiedContent = `${importStatement}\n\n${modifiedContent}`;
1257
- }
1258
- }
1259
-
1260
- // Handle new Vue pattern
1261
- if (content.includes('new Vue')) {
1262
- modifiedContent = modifiedContent.replace(
1263
- /(new Vue\(.*?\))/,
1264
- `${pluginUsage}\n\n$1`
1265
- );
1266
- }
1267
-
1268
- return modifiedContent;
1269
- }
1270
- }
1271
-
1272
- private injectAngularModule(content: string): string {
1273
- if (content.includes('HumanBehaviorModule')) {
1274
- return content;
1275
- }
1276
-
1277
- const importStatement = `import { HumanBehaviorModule } from 'humanbehavior-js/angular';`;
1278
- const environmentImport = `import { environment } from '../environments/environment';`;
1279
-
1280
- // Add environment import if not present
1281
- let modifiedContent = content;
1282
- if (!content.includes('environment')) {
1283
- modifiedContent = content.replace(
1284
- /import.*from.*['"]@angular/,
1285
- `${environmentImport}\n$&`
1286
- );
1287
- }
1288
-
1289
- return modifiedContent.replace(
1290
- /imports:\s*\[([\s\S]*?)\]/,
1291
- `imports: [
1292
- $1,
1293
- HumanBehaviorModule.forRoot({
1294
- apiKey: environment.humanBehaviorApiKey
1295
- })
1296
- ]`
1297
- ).replace(
1298
- /import.*from.*['"]@angular/,
1299
- `$&\n${importStatement}`
1300
- );
1301
- }
1302
-
1303
- private injectAngularStandaloneInit(content: string): string {
1304
- if (content.includes('initializeHumanBehavior')) {
1305
- return content;
1306
- }
1307
-
1308
- const importStatement = `import { initializeHumanBehavior } from 'humanbehavior-js/angular';`;
1309
- const environmentImport = `import { environment } from './environments/environment';`;
1310
-
1311
- // Add imports at the top
1312
- let modifiedContent = content.replace(
1313
- /import.*from.*['"]@angular/,
1314
- `${importStatement}\n${environmentImport}\n$&`
1315
- );
1316
-
1317
- // Add initialization after bootstrapApplication
1318
- modifiedContent = modifiedContent.replace(
1319
- /(bootstrapApplication\([^}]+\}?\)(?:\s*\.catch[^;]+;)?)/,
1320
- `$1
1321
-
1322
- // Initialize HumanBehavior SDK (client-side only)
1323
- if (typeof window !== 'undefined') {
1324
- const tracker = initializeHumanBehavior(environment.humanBehaviorApiKey);
1325
- }`
1326
- );
1327
-
1328
- return modifiedContent;
1329
- }
1330
-
1331
- private injectSvelteStore(content: string): string {
1332
- if (content.includes('humanBehaviorStore')) {
1333
- return content;
1334
- }
1335
-
1336
- const importStatement = `import { humanBehaviorStore } from 'humanbehavior-js/svelte';`;
1337
- const initCode = `humanBehaviorStore.init(process.env.PUBLIC_HUMANBEHAVIOR_API_KEY || '');`;
1338
-
1339
- return `${importStatement}\n${initCode}\n\n${content}`;
1340
- }
1341
-
1342
- private injectSvelteKitLayout(content: string): string {
1343
- if (content.includes('humanBehaviorStore')) {
1344
- return content;
1345
- }
1346
-
1347
- const importStatement = `import { humanBehaviorStore } from 'humanbehavior-js/svelte';`;
1348
- const envImport = `import { PUBLIC_HUMANBEHAVIOR_API_KEY } from '$env/static/public';`;
1349
- const initCode = `humanBehaviorStore.init(PUBLIC_HUMANBEHAVIOR_API_KEY || '');`;
1350
-
1351
- // Add to script section - handle different script tag patterns
1352
- if (content.includes('<script lang="ts">')) {
1353
- return content.replace(
1354
- /<script lang="ts">/,
1355
- `<script lang="ts">\n\t${envImport}\n\t${importStatement}\n\t${initCode}`
1356
- );
1357
- } else if (content.includes('<script>')) {
1358
- return content.replace(
1359
- /<script>/,
1360
- `<script>\n\t${envImport}\n\t${importStatement}\n\t${initCode}`
1361
- );
1362
- } else if (content.includes('<script context="module">')) {
1363
- return content.replace(
1364
- /<script\s+context="module">/,
1365
- `<script context="module">\n\t${envImport}\n\t${importStatement}\n\t${initCode}`
1366
- );
1367
- } else {
1368
- // If no script tag found, add one at the beginning
1369
- return content.replace(
1370
- /<svelte:head>/,
1371
- `<script lang="ts">\n\t${envImport}\n\t${importStatement}\n\t${initCode}\n</script>\n\n<svelte:head>`
1372
- );
1373
- }
1374
- }
1375
-
1376
- private injectVanillaScript(content: string): string {
1377
- if (content.includes('humanbehavior-js')) {
1378
- return content;
1379
- }
1380
-
1381
- const cdnScript = `<script src="https://unpkg.com/humanbehavior-js@latest/dist/index.min.js"></script>`;
1382
- const initScript = `<script>
1383
- // Initialize HumanBehavior SDK
1384
- // Note: For vanilla HTML, the API key must be hardcoded since env vars aren't available
1385
- const tracker = HumanBehaviorTracker.init('${this.apiKey}');
1386
- </script>`;
1387
-
1388
- return content.replace(
1389
- /<\/head>/,
1390
- ` ${cdnScript}\n ${initScript}\n</head>`
1391
- );
1392
- }
1393
-
1394
- /**
1395
- * Inject Astro layout with HumanBehavior component
1396
- */
1397
- private injectAstroLayout(content: string): string {
1398
- // Check if HumanBehavior component is already imported
1399
- if (content.includes('HumanBehavior') || content.includes('humanbehavior-js')) {
1400
- return content; // Already has HumanBehavior
1401
- }
1402
-
1403
- // Add import inside frontmatter if not present
1404
- let modifiedContent = content;
1405
- if (!content.includes('import HumanBehavior')) {
1406
- const importStatement = 'import HumanBehavior from \'../components/HumanBehavior.astro\';';
1407
- const frontmatterEndIndex = content.indexOf('---', 3);
1408
- if (frontmatterEndIndex !== -1) {
1409
- // Insert import inside frontmatter, before the closing ---
1410
- modifiedContent = content.slice(0, frontmatterEndIndex) + '\n' + importStatement + '\n' + content.slice(frontmatterEndIndex);
1411
- } else {
1412
- // No frontmatter, add at the very beginning
1413
- modifiedContent = '---\n' + importStatement + '\n---\n\n' + content;
1414
- }
1415
- }
1416
-
1417
- // Find the closing </body> tag and add HumanBehavior component before it
1418
- const bodyCloseIndex = modifiedContent.lastIndexOf('</body>');
1419
- if (bodyCloseIndex === -1) {
1420
- // No body tag found, append to end
1421
- return modifiedContent + '\n\n<HumanBehavior />';
1422
- }
1423
-
1424
- // Add component before closing body tag
1425
- return modifiedContent.slice(0, bodyCloseIndex) + ' <HumanBehavior />\n' + modifiedContent.slice(bodyCloseIndex);
1426
- }
1427
-
1428
- private injectNuxtConfig(content: string): string {
1429
- if (content.includes('humanBehaviorApiKey')) {
1430
- return content;
1431
- }
1432
-
1433
- // Enhanced Nuxt 3 support with version detection
1434
- const hasNuxt3 = this.framework?.features?.hasNuxt3;
1435
-
1436
- if (hasNuxt3) {
1437
- // Nuxt 3 with runtime config
1438
- return content.replace(
1439
- /export default defineNuxtConfig\(\{/,
1440
- `export default defineNuxtConfig({
1441
- runtimeConfig: {
1442
- public: {
1443
- humanBehaviorApiKey: process.env.NUXT_PUBLIC_HUMANBEHAVIOR_API_KEY
1444
- }
1445
- },`
1446
- );
1447
- } else {
1448
- // Nuxt 2 with env config
1449
- return content.replace(
1450
- /export default \{/,
1451
- `export default {
1452
- env: {
1453
- humanBehaviorApiKey: process.env.HUMANBEHAVIOR_API_KEY
1454
- },`
1455
- );
1456
- }
1457
- }
1458
-
1459
- private injectGatsbyLayout(content: string): string {
1460
- if (content.includes('HumanBehavior')) {
1461
- return content;
1462
- }
1463
-
1464
- const importStatement = `import HumanBehavior from './HumanBehavior';`;
1465
- const componentUsage = `<HumanBehavior apiKey={process.env.GATSBY_HUMANBEHAVIOR_API_KEY || ''} />`;
1466
-
1467
- // Add import at the top
1468
- let modifiedContent = content.replace(
1469
- /import.*from.*['"]\./,
1470
- `${importStatement}\n$&`
1471
- );
1472
-
1473
- // Add component before closing body tag
1474
- modifiedContent = modifiedContent.replace(
1475
- /(\s*<\/body>)/,
1476
- `\n ${componentUsage}\n$1`
1477
- );
1478
-
1479
- return modifiedContent;
1480
- }
1481
-
1482
- private injectGatsbyBrowser(content: string): string {
1483
- if (content.includes('HumanBehaviorTracker')) {
1484
- return content;
1485
- }
1486
-
1487
- const importStatement = `import { HumanBehaviorTracker } from 'humanbehavior-js';`;
1488
- const initCode = `
1489
- // Initialize HumanBehavior SDK
1490
- export const onClientEntry = () => {
1491
- console.log('Gatsby browser entry point loaded');
1492
- const apiKey = process.env.GATSBY_HUMANBEHAVIOR_API_KEY;
1493
- console.log('API Key found:', apiKey ? 'Yes' : 'No');
1494
- if (apiKey) {
1495
- const tracker = HumanBehaviorTracker.init(apiKey);
1496
- console.log('HumanBehavior SDK initialized for Gatsby');
1497
- } else {
1498
- console.log('No API key found in environment variables');
1499
- }
1500
- };`;
1501
-
1502
- // If the file already has content, add the import and init code
1503
- if (content.trim()) {
1504
- return `${importStatement}${initCode}\n\n${content}`;
1505
- } else {
1506
- // If file is empty, just return the new content
1507
- return `${importStatement}${initCode}`;
1508
- }
1509
- }
1510
-
1511
-
1512
-
1513
- /**
1514
- * Helper method to find the best environment file for a framework
1515
- */
1516
- private findBestEnvFile(framework: FrameworkInfo): { filePath: string; envVarName: string } {
1517
- const possibleEnvFiles = [
1518
- '.env.local',
1519
- '.env.development.local',
1520
- '.env.development',
1521
- '.env.local.development',
1522
- '.env',
1523
- '.env.production',
1524
- '.env.staging'
1525
- ];
1526
-
1527
- // Framework-specific environment variable names
1528
- const getEnvVarName = (framework: FrameworkInfo) => {
1529
- // Handle React+Vite specifically
1530
- if (framework.type === 'react' && framework.bundler === 'vite') {
1531
- return 'VITE_HUMANBEHAVIOR_API_KEY';
1532
- }
1533
-
1534
- // Framework-specific mappings
1535
- const envVarNames = {
1536
- react: 'HUMANBEHAVIOR_API_KEY',
1537
- nextjs: 'NEXT_PUBLIC_HUMANBEHAVIOR_API_KEY',
1538
- vue: 'VITE_HUMANBEHAVIOR_API_KEY',
1539
- svelte: 'PUBLIC_HUMANBEHAVIOR_API_KEY',
1540
- angular: 'HUMANBEHAVIOR_API_KEY',
1541
- nuxt: 'NUXT_PUBLIC_HUMANBEHAVIOR_API_KEY',
1542
- remix: 'HUMANBEHAVIOR_API_KEY',
1543
- vanilla: 'HUMANBEHAVIOR_API_KEY',
1544
- astro: 'PUBLIC_HUMANBEHAVIOR_API_KEY',
1545
- gatsby: 'GATSBY_HUMANBEHAVIOR_API_KEY',
1546
- node: 'HUMANBEHAVIOR_API_KEY',
1547
- auto: 'HUMANBEHAVIOR_API_KEY'
1548
- };
1549
-
1550
- return envVarNames[framework.type] || 'HUMANBEHAVIOR_API_KEY';
1551
- };
1552
-
1553
- const envVarName = getEnvVarName(framework);
1554
-
1555
- // Check for existing files
1556
- for (const envFile of possibleEnvFiles) {
1557
- const fullPath = path.join(this.projectRoot, envFile);
1558
- if (fs.existsSync(fullPath)) {
1559
- return { filePath: fullPath, envVarName };
1560
- }
1561
- }
1562
-
1563
- // Framework-specific default file creation
1564
- const defaultFiles = {
1565
- react: '.env.local',
1566
- nextjs: '.env.local',
1567
- vue: '.env.local',
1568
- svelte: '.env',
1569
- angular: '.env',
1570
- nuxt: '.env',
1571
- remix: '.env.local',
1572
- vanilla: '.env',
1573
- astro: '.env',
1574
- gatsby: '.env.development',
1575
- node: '.env',
1576
- auto: '.env'
1577
- };
1578
-
1579
- const defaultFile = defaultFiles[framework.type] || '.env';
1580
- return {
1581
- filePath: path.join(this.projectRoot, defaultFile),
1582
- envVarName
1583
- };
1584
- }
1585
-
1586
- /**
1587
- * Helper method to create or append to environment files
1588
- */
1589
- private createEnvironmentModification(framework: FrameworkInfo): CodeModification {
1590
- const { filePath, envVarName } = this.findBestEnvFile(framework);
1591
-
1592
- // Clean the API key to prevent formatting issues
1593
- const cleanApiKey = this.apiKey.trim();
1594
-
1595
- if (fs.existsSync(filePath)) {
1596
- // Check if the variable already exists
1597
- const content = fs.readFileSync(filePath, 'utf8');
1598
- if (content.includes(envVarName)) {
1599
- // Variable exists, don't modify
1600
- return {
1601
- filePath,
1602
- action: 'modify',
1603
- content: content, // No change
1604
- description: `API key already exists in ${path.basename(filePath)}`
1605
- };
1606
- } else {
1607
- // Append to existing file
1608
- return {
1609
- filePath,
1610
- action: 'append',
1611
- content: `\n${envVarName}=${cleanApiKey}`,
1612
- description: `Added API key to existing ${path.basename(filePath)}`
1613
- };
1614
- }
1615
- } else {
1616
- // Create new file
1617
- return {
1618
- filePath,
1619
- action: 'create',
1620
- content: `${envVarName}=${cleanApiKey}`,
1621
- description: `Created ${path.basename(filePath)} with API key`
1622
- };
1623
- }
1624
- }
1625
- }
1626
-
1627
- /**
1628
- * Browser-based auto-installation wizard
1629
- */
1630
- export class BrowserAutoInstallationWizard {
1631
- private apiKey: string;
1632
-
1633
- constructor(apiKey: string) {
1634
- this.apiKey = apiKey;
1635
- }
1636
-
1637
- async install(): Promise<InstallationResult> {
1638
- try {
1639
- // Detect framework in browser
1640
- const framework = this.detectFramework();
1641
-
1642
- // Generate installation instructions
1643
- const modifications = this.generateBrowserModifications(framework);
1644
-
1645
- return {
1646
- success: true,
1647
- framework,
1648
- modifications,
1649
- errors: [],
1650
- nextSteps: [
1651
- '✅ Framework detected automatically',
1652
- '📋 Copy the generated code to your project',
1653
- '🚀 Your app will be ready to track user behavior'
1654
- ]
1655
- };
1656
- } catch (error) {
1657
- return {
1658
- success: false,
1659
- framework: { name: 'unknown', type: 'vanilla' },
1660
- modifications: [],
1661
- errors: [error instanceof Error ? error.message : 'Unknown error'],
1662
- nextSteps: []
1663
- };
1664
- }
1665
- }
1666
-
1667
- private detectFramework(): FrameworkInfo {
1668
- if (typeof window !== 'undefined') {
1669
- if ((window as any).React) {
1670
- return { name: 'react', type: 'react' };
1671
- }
1672
- if ((window as any).Vue) {
1673
- return { name: 'vue', type: 'vue' };
1674
- }
1675
- if ((window as any).angular) {
1676
- return { name: 'angular', type: 'angular' };
1677
- }
1678
- }
1679
-
1680
- return { name: 'vanilla', type: 'vanilla' };
1681
- }
1682
-
1683
- private generateBrowserModifications(framework: FrameworkInfo): CodeModification[] {
1684
- // Return code snippets for browser environment
1685
- const modifications: CodeModification[] = [];
1686
-
1687
- switch (framework.type) {
1688
- case 'react':
1689
- modifications.push({
1690
- filePath: 'App.jsx',
1691
- action: 'create',
1692
- content: `import { HumanBehaviorProvider } from 'humanbehavior-js/react';
1693
-
1694
- function App() {
1695
- return (
1696
- <HumanBehaviorProvider apiKey="${this.apiKey}">
1697
- {/* Your app components */}
1698
- </HumanBehaviorProvider>
1699
- );
1700
- }
1701
-
1702
- export default App;`,
1703
- description: 'React component with HumanBehaviorProvider'
1704
- });
1705
- break;
1706
-
1707
- case 'remix':
1708
- modifications.push({
1709
- filePath: 'app/root.tsx',
1710
- action: 'create',
1711
- content: `import {
1712
- Links,
1713
- Meta,
1714
- Outlet,
1715
- Scripts,
1716
- ScrollRestoration,
1717
- useLoaderData,
1718
- } from "@remix-run/react";
1719
- import { HumanBehaviorProvider } from 'humanbehavior-js/react';
1720
- import type { LoaderFunctionArgs } from "@remix-run/node";
1721
-
1722
- export async function loader({ request }: LoaderFunctionArgs) {
1723
- return {
1724
- ENV: {
1725
- HUMANBEHAVIOR_API_KEY: process.env.HUMANBEHAVIOR_API_KEY,
1726
- },
1727
- };
1728
- }
1729
-
1730
- export function Layout({ children }: { children: React.ReactNode }) {
1731
- return (
1732
- <html lang="en">
1733
- <head>
1734
- <meta charSet="utf-8" />
1735
- <meta name="viewport" content="width=device-width, initial-scale=1" />
1736
- <Meta />
1737
- <Links />
1738
- </head>
1739
- <body>
1740
- {children}
1741
- <ScrollRestoration />
1742
- <Scripts />
1743
- </body>
1744
- </html>
1745
- );
1746
- }
1747
-
1748
- export default function App() {
1749
- const data = useLoaderData<typeof loader>();
1750
-
1751
- return (
1752
- <HumanBehaviorProvider apiKey={data.ENV.HUMANBEHAVIOR_API_KEY}>
1753
- <div className="min-h-screen bg-gray-50">
1754
- {/* Your app content */}
1755
- <Outlet />
1756
- </div>
1757
- </HumanBehaviorProvider>
1758
- );
1759
- }`,
1760
- description: 'Remix root component with HumanBehaviorProvider'
1761
- });
1762
- break;
1763
-
1764
- case 'vue':
1765
- modifications.push({
1766
- filePath: 'main.js',
1767
- action: 'create',
1768
- content: `import { createApp } from 'vue';
1769
- import { HumanBehaviorPlugin } from 'humanbehavior-js/vue';
1770
- import App from './App.vue';
1771
-
1772
- const app = createApp(App);
1773
- app.use(HumanBehaviorPlugin, {
1774
- apiKey: '${this.apiKey}'
1775
- });
1776
- app.mount('#app');`,
1777
- description: 'Vue app with HumanBehaviorPlugin'
1778
- });
1779
- break;
1780
-
1781
- default:
1782
- modifications.push({
1783
- filePath: 'humanbehavior-init.js',
1784
- action: 'create',
1785
- content: `import { HumanBehaviorTracker } from 'humanbehavior-js';
1786
-
1787
- const tracker = HumanBehaviorTracker.init('${this.apiKey}');`,
1788
- description: 'Vanilla JS initialization'
1789
- });
1790
- }
1791
-
1792
- return modifications;
1793
- }
1794
- }