humanbehavior-js
Version:
SDK for HumanBehavior session and event recording
1,160 lines (1,139 loc) • 1.77 MB
JavaScript
#!/usr/bin/env node
'use strict';
var fs = require('fs');
var path = require('path');
var require$$2$2 = require('events');
var require$$0$4 = require('net');
var require$$0$3 = require('url');
var require$$1$1 = require('util');
var require$$0$6 = require('crypto');
var require$$0$5 = require('buffer');
var require$$2$1 = require('http');
var require$$4$1 = require('https');
var require$$1$3 = require('stream');
var require$$5$1 = require('zlib');
var require$$1$2 = require('querystring');
var require$$0$7 = require('assert');
var require$$3$2 = require('tls');
var require$$0$8 = require('tty');
var require$$2$3 = require('os');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
/**
* HumanBehavior SDK Auto-Installation Wizard
*
* This wizard automatically detects the user's framework and modifies their codebase
* to integrate the SDK with minimal user intervention.
*/
class AutoInstallationWizard {
constructor(apiKey, projectRoot = process.cwd()) {
this.framework = null;
this.apiKey = apiKey;
this.projectRoot = projectRoot;
}
/**
* Main installation method - detects framework and auto-installs
*/
install() {
return __awaiter(this, void 0, void 0, function* () {
try {
// Step 1: Detect framework
this.framework = yield this.detectFramework();
// Step 2: Install package
yield this.installPackage();
// Step 3: Generate and apply code modifications
const modifications = yield this.generateModifications();
yield this.applyModifications(modifications);
// Step 4: Generate next steps
const nextSteps = this.generateNextSteps();
return {
success: true,
framework: this.framework,
modifications,
errors: [],
nextSteps
};
}
catch (error) {
return {
success: false,
framework: this.framework || { name: 'unknown', type: 'vanilla' },
modifications: [],
errors: [error instanceof Error ? error.message : 'Unknown error'],
nextSteps: []
};
}
});
}
/**
* Detect the current framework and project setup
*/
detectFramework() {
return __awaiter(this, void 0, void 0, function* () {
const packageJsonPath = path__namespace.join(this.projectRoot, 'package.json');
if (!fs__namespace.existsSync(packageJsonPath)) {
return {
name: 'vanilla',
type: 'vanilla',
projectRoot: this.projectRoot
};
}
const packageJson = JSON.parse(fs__namespace.readFileSync(packageJsonPath, 'utf8'));
const dependencies = Object.assign(Object.assign({}, packageJson.dependencies), packageJson.devDependencies);
// Detect framework
let framework = {
name: 'vanilla',
type: 'vanilla',
projectRoot: this.projectRoot
};
if (dependencies.nuxt) {
framework = {
name: 'nuxt',
type: 'nuxt',
hasTypeScript: !!dependencies.typescript,
hasRouter: true,
projectRoot: this.projectRoot
};
}
else if (dependencies.next) {
framework = {
name: 'nextjs',
type: 'nextjs',
hasTypeScript: !!dependencies.typescript || !!dependencies['@types/node'],
hasRouter: true,
projectRoot: this.projectRoot
};
}
else if (dependencies['@remix-run/react'] || dependencies['@remix-run/dev']) {
framework = {
name: 'remix',
type: 'remix',
hasTypeScript: !!dependencies.typescript || !!dependencies['@types/react'],
hasRouter: true,
projectRoot: this.projectRoot
};
}
else if (dependencies.react) {
framework = {
name: 'react',
type: 'react',
hasTypeScript: !!dependencies.typescript || !!dependencies['@types/react'],
hasRouter: !!dependencies['react-router-dom'] || !!dependencies['react-router'],
projectRoot: this.projectRoot
};
}
else if (dependencies.vue) {
framework = {
name: 'vue',
type: 'vue',
hasTypeScript: !!dependencies.typescript || !!dependencies['@vue/cli-service'],
hasRouter: !!dependencies['vue-router'],
projectRoot: this.projectRoot
};
}
else if (dependencies['@angular/core']) {
framework = {
name: 'angular',
type: 'angular',
hasTypeScript: true,
hasRouter: true,
projectRoot: this.projectRoot
};
}
else if (dependencies.svelte) {
framework = {
name: 'svelte',
type: 'svelte',
hasTypeScript: !!dependencies.typescript || !!dependencies['svelte-check'],
hasRouter: !!dependencies['svelte-routing'] || !!dependencies['@sveltejs/kit'],
projectRoot: this.projectRoot
};
}
// Detect bundler
if (dependencies.vite) {
framework.bundler = 'vite';
}
else if (dependencies.webpack) {
framework.bundler = 'webpack';
}
else if (dependencies.esbuild) {
framework.bundler = 'esbuild';
}
else if (dependencies.rollup) {
framework.bundler = 'rollup';
}
// Detect package manager
if (fs__namespace.existsSync(path__namespace.join(this.projectRoot, 'yarn.lock'))) {
framework.packageManager = 'yarn';
}
else if (fs__namespace.existsSync(path__namespace.join(this.projectRoot, 'pnpm-lock.yaml'))) {
framework.packageManager = 'pnpm';
}
else {
framework.packageManager = 'npm';
}
return framework;
});
}
/**
* Install the SDK package
*/
installPackage() {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const { execSync } = yield import('child_process');
const command = ((_a = this.framework) === null || _a === void 0 ? void 0 : _a.packageManager) === 'yarn'
? 'yarn add humanbehavior-js'
: ((_b = this.framework) === null || _b === void 0 ? void 0 : _b.packageManager) === 'pnpm'
? 'pnpm add humanbehavior-js'
: 'npm install humanbehavior-js';
try {
execSync(command, { cwd: this.projectRoot, stdio: 'inherit' });
}
catch (error) {
throw new Error(`Failed to install humanbehavior-js: ${error}`);
}
});
}
/**
* Generate code modifications based on framework
*/
generateModifications() {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const modifications = [];
switch ((_a = this.framework) === null || _a === void 0 ? void 0 : _a.type) {
case 'react':
modifications.push(...yield this.generateReactModifications());
break;
case 'nextjs':
modifications.push(...yield this.generateNextJSModifications());
break;
case 'nuxt':
modifications.push(...yield this.generateNuxtModifications());
break;
case 'remix':
modifications.push(...yield this.generateRemixModifications());
break;
case 'vue':
modifications.push(...yield this.generateVueModifications());
break;
case 'angular':
modifications.push(...yield this.generateAngularModifications());
break;
case 'svelte':
modifications.push(...yield this.generateSvelteModifications());
break;
default:
modifications.push(...yield this.generateVanillaModifications());
}
return modifications;
});
}
/**
* Generate React-specific modifications
*/
generateReactModifications() {
return __awaiter(this, void 0, void 0, function* () {
const modifications = [];
// Find main App component or index file
const appFile = this.findReactAppFile();
if (appFile) {
const content = fs__namespace.readFileSync(appFile, 'utf8');
const modifiedContent = this.injectReactProvider(content, appFile);
modifications.push({
filePath: appFile,
action: 'modify',
content: modifiedContent,
description: 'Added HumanBehaviorProvider to React app'
});
}
// Create or append to environment file
modifications.push(this.createEnvironmentModification(this.framework));
return modifications;
});
}
/**
* Generate Next.js-specific modifications
*/
generateNextJSModifications() {
return __awaiter(this, void 0, void 0, function* () {
const modifications = [];
// Check for App Router
const appLayoutFile = path__namespace.join(this.projectRoot, 'src', 'app', 'layout.tsx');
const pagesLayoutFile = path__namespace.join(this.projectRoot, 'src', 'pages', '_app.tsx');
if (fs__namespace.existsSync(appLayoutFile)) {
// Create providers.tsx file for App Router
modifications.push({
filePath: path__namespace.join(this.projectRoot, 'src', 'app', 'providers.tsx'),
action: 'create',
content: `'use client';
import { HumanBehaviorProvider } from 'humanbehavior-js/react';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<HumanBehaviorProvider apiKey={process.env.NEXT_PUBLIC_HUMANBEHAVIOR_API_KEY}>
{children}
</HumanBehaviorProvider>
);
}`,
description: 'Created providers.tsx file for Next.js App Router'
});
// Modify layout.tsx to use the provider
const content = fs__namespace.readFileSync(appLayoutFile, 'utf8');
const modifiedContent = this.injectNextJSAppRouter(content);
modifications.push({
filePath: appLayoutFile,
action: 'modify',
content: modifiedContent,
description: 'Added Providers wrapper to Next.js App Router layout'
});
}
else if (fs__namespace.existsSync(pagesLayoutFile)) {
// Create providers.tsx file for Pages Router
modifications.push({
filePath: path__namespace.join(this.projectRoot, 'src', 'components', 'providers.tsx'),
action: 'create',
content: `'use client';
import { HumanBehaviorProvider } from 'humanbehavior-js/react';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<HumanBehaviorProvider apiKey={process.env.NEXT_PUBLIC_HUMANBEHAVIOR_API_KEY}>
{children}
</HumanBehaviorProvider>
);
}`,
description: 'Created providers.tsx file for Pages Router'
});
// Modify _app.tsx to use the provider
const content = fs__namespace.readFileSync(pagesLayoutFile, 'utf8');
const modifiedContent = this.injectNextJSPagesRouter(content);
modifications.push({
filePath: pagesLayoutFile,
action: 'modify',
content: modifiedContent,
description: 'Added Providers wrapper to Next.js Pages Router'
});
}
// Create or append to environment file
modifications.push(this.createEnvironmentModification(this.framework));
return modifications;
});
}
/**
* Generate Nuxt-specific modifications
*/
generateNuxtModifications() {
return __awaiter(this, void 0, void 0, function* () {
const modifications = [];
// Create plugin file for Nuxt (in app directory)
const pluginFile = path__namespace.join(this.projectRoot, 'app', 'plugins', 'humanbehavior.client.ts');
modifications.push({
filePath: pluginFile,
action: 'create',
content: `import { HumanBehaviorTracker } from 'humanbehavior-js';
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig();
// Initialize HumanBehavior SDK (client-side only)
if (typeof window !== 'undefined') {
const apiKey = config.public.humanBehaviorApiKey;
console.log('HumanBehavior: API key:', apiKey ? 'present' : 'missing');
if (apiKey) {
try {
const tracker = HumanBehaviorTracker.init(apiKey);
console.log('HumanBehavior: Tracker initialized successfully');
} catch (error) {
console.error('HumanBehavior: Failed to initialize tracker:', error);
}
} else {
console.error('HumanBehavior: No API key found in runtime config');
}
}
});`,
description: 'Created Nuxt plugin for HumanBehavior SDK in app directory'
});
// Create environment configuration
const nuxtConfigFile = path__namespace.join(this.projectRoot, 'nuxt.config.ts');
if (fs__namespace.existsSync(nuxtConfigFile)) {
const content = fs__namespace.readFileSync(nuxtConfigFile, 'utf8');
const modifiedContent = this.injectNuxtConfig(content);
modifications.push({
filePath: nuxtConfigFile,
action: 'modify',
content: modifiedContent,
description: 'Added HumanBehavior runtime config to Nuxt config'
});
}
// Create or append to environment file
modifications.push(this.createEnvironmentModification(this.framework));
return modifications;
});
}
/**
* Generate Remix-specific modifications
*/
generateRemixModifications() {
return __awaiter(this, void 0, void 0, function* () {
const modifications = [];
// Find root.tsx file
const rootFile = path__namespace.join(this.projectRoot, 'app', 'root.tsx');
if (fs__namespace.existsSync(rootFile)) {
const content = fs__namespace.readFileSync(rootFile, 'utf8');
const modifiedContent = this.injectRemixProvider(content);
modifications.push({
filePath: rootFile,
action: 'modify',
content: modifiedContent,
description: 'Added HumanBehaviorProvider to Remix root component'
});
}
// Create or append to environment file
modifications.push(this.createEnvironmentModification(this.framework));
return modifications;
});
}
/**
* Generate Vue-specific modifications
*/
generateVueModifications() {
return __awaiter(this, void 0, void 0, function* () {
const modifications = [];
// Find main.js or main.ts
const mainFile = this.findVueMainFile();
if (mainFile) {
const content = fs__namespace.readFileSync(mainFile, 'utf8');
const modifiedContent = this.injectVuePlugin(content);
modifications.push({
filePath: mainFile,
action: 'modify',
content: modifiedContent,
description: 'Added HumanBehaviorPlugin to Vue app'
});
}
// Create or append to environment file
modifications.push(this.createEnvironmentModification(this.framework));
return modifications;
});
}
/**
* Generate Angular-specific modifications
*/
generateAngularModifications() {
return __awaiter(this, void 0, void 0, function* () {
const modifications = [];
// Check for modern Angular (standalone components) vs legacy (NgModule)
const appModuleFile = path__namespace.join(this.projectRoot, 'src', 'app', 'app.module.ts');
const appComponentFile = path__namespace.join(this.projectRoot, 'src', 'app', 'app.ts');
const mainFile = path__namespace.join(this.projectRoot, 'src', 'main.ts');
const isModernAngular = fs__namespace.existsSync(appComponentFile) && !fs__namespace.existsSync(appModuleFile);
if (isModernAngular) {
// Modern Angular 17+ with standalone components
if (fs__namespace.existsSync(mainFile)) {
const content = fs__namespace.readFileSync(mainFile, 'utf8');
const modifiedContent = this.injectAngularStandaloneInit(content);
modifications.push({
filePath: mainFile,
action: 'modify',
content: modifiedContent,
description: 'Added HumanBehavior initialization to Angular main.ts'
});
}
}
else if (fs__namespace.existsSync(appModuleFile)) {
// Legacy Angular with NgModule
const content = fs__namespace.readFileSync(appModuleFile, 'utf8');
const modifiedContent = this.injectAngularModule(content);
modifications.push({
filePath: appModuleFile,
action: 'modify',
content: modifiedContent,
description: 'Added HumanBehaviorModule to Angular app'
});
}
// Handle Angular environment file (legacy structure)
const envFile = path__namespace.join(this.projectRoot, 'src', 'environments', 'environment.ts');
if (fs__namespace.existsSync(envFile)) {
const content = fs__namespace.readFileSync(envFile, 'utf8');
if (!content.includes('humanBehaviorApiKey')) {
const modifiedContent = content.replace(/export const environment = {([\s\S]*?)};/, `export const environment = {
$1,
humanBehaviorApiKey: process.env['HUMANBEHAVIOR_API_KEY'] || ''
};`);
modifications.push({
filePath: envFile,
action: 'modify',
content: modifiedContent,
description: 'Added API key to Angular environment'
});
}
}
// Create or append to environment file
modifications.push(this.createEnvironmentModification(this.framework));
return modifications;
});
}
/**
* Generate Svelte-specific modifications
*/
generateSvelteModifications() {
return __awaiter(this, void 0, void 0, function* () {
const modifications = [];
// Check for SvelteKit
const svelteConfigFile = path__namespace.join(this.projectRoot, 'svelte.config.js');
const isSvelteKit = fs__namespace.existsSync(svelteConfigFile);
if (isSvelteKit) {
// SvelteKit - create layout file
const layoutFile = path__namespace.join(this.projectRoot, 'src', 'routes', '+layout.svelte');
if (fs__namespace.existsSync(layoutFile)) {
const content = fs__namespace.readFileSync(layoutFile, 'utf8');
const modifiedContent = this.injectSvelteKitLayout(content);
modifications.push({
filePath: layoutFile,
action: 'modify',
content: modifiedContent,
description: 'Added HumanBehavior store to SvelteKit layout'
});
}
}
else {
// Regular Svelte - modify main file
const mainFile = this.findSvelteMainFile();
if (mainFile) {
const content = fs__namespace.readFileSync(mainFile, 'utf8');
const modifiedContent = this.injectSvelteStore(content);
modifications.push({
filePath: mainFile,
action: 'modify',
content: modifiedContent,
description: 'Added HumanBehavior store to Svelte app'
});
}
}
// Create or append to environment file
modifications.push(this.createEnvironmentModification(this.framework));
return modifications;
});
}
/**
* Generate vanilla JS/TS modifications
*/
generateVanillaModifications() {
return __awaiter(this, void 0, void 0, function* () {
const modifications = [];
// Find HTML file to inject script
const htmlFile = this.findHTMLFile();
if (htmlFile) {
const content = fs__namespace.readFileSync(htmlFile, 'utf8');
const modifiedContent = this.injectVanillaScript(content);
modifications.push({
filePath: htmlFile,
action: 'modify',
content: modifiedContent,
description: 'Added HumanBehavior CDN script to HTML file'
});
}
// Create or append to environment file
modifications.push(this.createEnvironmentModification(this.framework));
return modifications;
});
}
/**
* Apply modifications to the codebase
*/
applyModifications(modifications) {
return __awaiter(this, void 0, void 0, function* () {
for (const modification of modifications) {
try {
const dir = path__namespace.dirname(modification.filePath);
if (!fs__namespace.existsSync(dir)) {
fs__namespace.mkdirSync(dir, { recursive: true });
}
switch (modification.action) {
case 'create':
fs__namespace.writeFileSync(modification.filePath, modification.content);
break;
case 'modify':
fs__namespace.writeFileSync(modification.filePath, modification.content);
break;
case 'append':
fs__namespace.appendFileSync(modification.filePath, '\n' + modification.content);
break;
}
}
catch (error) {
throw new Error(`Failed to apply modification to ${modification.filePath}: ${error}`);
}
}
});
}
/**
* Generate next steps for the user
*/
generateNextSteps() {
var _a, _b;
const steps = [
'✅ SDK installed and configured automatically!',
'🚀 Your app is now tracking user behavior',
'📊 View sessions in your HumanBehavior dashboard',
'🔧 Customize tracking in your code as needed'
];
if (((_a = this.framework) === null || _a === void 0 ? void 0 : _a.type) === 'react' || ((_b = this.framework) === null || _b === void 0 ? void 0 : _b.type) === 'nextjs') {
steps.push('💡 Use the useHumanBehavior() hook to track custom events');
}
return steps;
}
// Helper methods for file detection and content injection
findReactAppFile() {
const possibleFiles = [
'src/App.jsx', 'src/App.js', 'src/App.tsx', 'src/App.ts',
'src/index.js', 'src/index.tsx', 'src/main.js', 'src/main.tsx'
];
for (const file of possibleFiles) {
const fullPath = path__namespace.join(this.projectRoot, file);
if (fs__namespace.existsSync(fullPath)) {
return fullPath;
}
}
return null;
}
findVueMainFile() {
const possibleFiles = [
'src/main.js', 'src/main.ts', 'src/main.jsx', 'src/main.tsx'
];
for (const file of possibleFiles) {
const fullPath = path__namespace.join(this.projectRoot, file);
if (fs__namespace.existsSync(fullPath)) {
return fullPath;
}
}
return null;
}
findSvelteMainFile() {
const possibleFiles = [
'src/main.js', 'src/main.ts', 'src/main.svelte'
];
for (const file of possibleFiles) {
const fullPath = path__namespace.join(this.projectRoot, file);
if (fs__namespace.existsSync(fullPath)) {
return fullPath;
}
}
return null;
}
findHTMLFile() {
const possibleFiles = ['index.html', 'public/index.html', 'dist/index.html'];
for (const file of possibleFiles) {
const fullPath = path__namespace.join(this.projectRoot, file);
if (fs__namespace.existsSync(fullPath)) {
return fullPath;
}
}
return null;
}
injectReactProvider(content, filePath) {
var _a;
filePath.endsWith('.tsx') || filePath.endsWith('.ts');
// Check if already has HumanBehaviorProvider
if (content.includes('HumanBehaviorProvider')) {
return content;
}
// Determine the correct environment variable syntax based on bundler
const isVite = ((_a = this.framework) === null || _a === void 0 ? void 0 : _a.bundler) === 'vite';
const envVar = isVite
? 'import.meta.env.VITE_HUMANBEHAVIOR_API_KEY!'
: 'process.env.HUMANBEHAVIOR_API_KEY!';
const importStatement = `import { HumanBehaviorProvider } from 'humanbehavior-js/react';`;
// Simple injection - in production, you'd want more sophisticated parsing
if (content.includes('function App()') || content.includes('const App =')) {
return content.replace(/(function App\(\)|const App =)/, `${importStatement}\n\n$1`).replace(/return \(([\s\S]*?)\);/, `return (
<HumanBehaviorProvider apiKey={${envVar}}>
$1
</HumanBehaviorProvider>
);`);
}
return `${importStatement}\n\n${content}`;
}
injectNextJSAppRouter(content) {
if (content.includes('Providers')) {
return content;
}
const importStatement = `import { Providers } from './providers';`;
// First, add the import statement
let modifiedContent = content.replace(/export default function RootLayout/, `${importStatement}\n\nexport default function RootLayout`);
// Then wrap the body content with Providers
// Use a more specific approach to handle the body content
modifiedContent = modifiedContent.replace(/<body([^>]*)>([\s\S]*?)<\/body>/, (match, bodyAttrs, bodyContent) => {
// Trim whitespace and newlines from bodyContent
const trimmedContent = bodyContent.trim();
return `<body${bodyAttrs}>
<Providers>
${trimmedContent}
</Providers>
</body>`;
});
return modifiedContent;
}
injectNextJSPagesRouter(content) {
if (content.includes('Providers')) {
return content;
}
const importStatement = `import { Providers } from '../components/providers';`;
return content.replace(/function MyApp/, `${importStatement}\n\nfunction MyApp`).replace(/return \(([\s\S]*?)\);/, `return (
<Providers>
$1
</Providers>
);`);
}
injectRemixProvider(content) {
if (content.includes('HumanBehaviorProvider')) {
return content;
}
const importStatement = `import { HumanBehaviorProvider, createHumanBehaviorLoader } from 'humanbehavior-js/remix';`;
const useLoaderDataImport = `import { useLoaderData } from "@remix-run/react";`;
// Add imports more robustly
let modifiedContent = content;
// Add HumanBehaviorProvider import - find the last import and add after it
if (!content.includes('HumanBehaviorProvider')) {
const lastImportIndex = modifiedContent.lastIndexOf('import');
if (lastImportIndex !== -1) {
const nextLineIndex = modifiedContent.indexOf('\n', lastImportIndex);
if (nextLineIndex !== -1) {
modifiedContent = modifiedContent.slice(0, nextLineIndex + 1) +
importStatement + '\n' +
modifiedContent.slice(nextLineIndex + 1);
}
else {
modifiedContent = modifiedContent + '\n' + importStatement;
}
}
else {
modifiedContent = importStatement + '\n' + modifiedContent;
}
}
// Add useLoaderData import - find the last import and add after it
if (!content.includes('useLoaderData')) {
const lastImportIndex = modifiedContent.lastIndexOf('import');
if (lastImportIndex !== -1) {
const nextLineIndex = modifiedContent.indexOf('\n', lastImportIndex);
if (nextLineIndex !== -1) {
modifiedContent = modifiedContent.slice(0, nextLineIndex + 1) +
useLoaderDataImport + '\n' +
modifiedContent.slice(nextLineIndex + 1);
}
else {
modifiedContent = modifiedContent + '\n' + useLoaderDataImport;
}
}
else {
modifiedContent = useLoaderDataImport + '\n' + modifiedContent;
}
}
// Add loader function before the App component
if (!content.includes('export const loader')) {
modifiedContent = modifiedContent.replace(/export default function App\(\)/, `export const loader = createHumanBehaviorLoader();
export default function App()`);
}
// Wrap the App component content with HumanBehaviorProvider
modifiedContent = modifiedContent.replace(/export default function App\(\) \{[\s\S]*?return \(([\s\S]*?)\);[\s\S]*?\}/, `export default function App() {
const data = useLoaderData<typeof loader>();
return (
<HumanBehaviorProvider apiKey={data.ENV.HUMANBEHAVIOR_API_KEY}>
$1
</HumanBehaviorProvider>
);
}`);
return modifiedContent;
}
injectVuePlugin(content) {
if (content.includes('HumanBehaviorPlugin')) {
return content;
}
const importStatement = `import { HumanBehaviorPlugin } from 'humanbehavior-js/vue';`;
const pluginUsage = `app.use(HumanBehaviorPlugin, {
apiKey: import.meta.env.VITE_HUMANBEHAVIOR_API_KEY
});`;
// Handle both Vue 2 and Vue 3 patterns
if (content.includes('createApp')) {
// Vue 3
return content.replace(/import.*from.*['"]vue['"]/, `$&\n${importStatement}`).replace(/app\.mount\(/, `${pluginUsage}\n\napp.mount(`);
}
else {
// Vue 2
return content.replace(/import.*from.*['"]vue['"]/, `$&\n${importStatement}`).replace(/new Vue\(/, `${pluginUsage}\n\nnew Vue(`);
}
}
injectAngularModule(content) {
if (content.includes('HumanBehaviorModule')) {
return content;
}
const importStatement = `import { HumanBehaviorModule } from 'humanbehavior-js/angular';`;
const environmentImport = `import { environment } from '../environments/environment';`;
// Add environment import if not present
let modifiedContent = content;
if (!content.includes('environment')) {
modifiedContent = content.replace(/import.*from.*['"]@angular/, `${environmentImport}\n$&`);
}
return modifiedContent.replace(/imports:\s*\[([\s\S]*?)\]/, `imports: [
$1,
HumanBehaviorModule.forRoot({
apiKey: environment.humanBehaviorApiKey
})
]`).replace(/import.*from.*['"]@angular/, `$&\n${importStatement}`);
}
injectAngularStandaloneInit(content) {
if (content.includes('initializeHumanBehavior')) {
return content;
}
const importStatement = `import { initializeHumanBehavior } from 'humanbehavior-js/angular';`;
// Add import at the top
let modifiedContent = content.replace(/import.*from.*['"]@angular/, `${importStatement}\n$&`);
// Add initialization after bootstrapApplication
modifiedContent = modifiedContent.replace(/(bootstrapApplication\([^}]+\}?\)(?:\s*\.catch[^;]+;)?)/, `$1
// Initialize HumanBehavior SDK (client-side only)
if (typeof window !== 'undefined') {
const tracker = initializeHumanBehavior(
'${this.apiKey}'
);
}`);
return modifiedContent;
}
injectSvelteStore(content) {
if (content.includes('humanBehaviorStore')) {
return content;
}
const importStatement = `import { humanBehaviorStore } from 'humanbehavior-js/svelte';`;
const initCode = `humanBehaviorStore.init(process.env.PUBLIC_HUMANBEHAVIOR_API_KEY || '');`;
return `${importStatement}\n${initCode}\n\n${content}`;
}
injectSvelteKitLayout(content) {
if (content.includes('humanBehaviorStore')) {
return content;
}
const importStatement = `import { humanBehaviorStore } from 'humanbehavior-js/svelte';`;
const envImport = `import { PUBLIC_HUMANBEHAVIOR_API_KEY } from '$env/static/public';`;
const initCode = `humanBehaviorStore.init(PUBLIC_HUMANBEHAVIOR_API_KEY || '');`;
// Add to script section - handle different script tag patterns
if (content.includes('<script lang="ts">')) {
return content.replace(/<script lang="ts">/, `<script lang="ts">\n\t${envImport}\n\t${importStatement}\n\t${initCode}`);
}
else if (content.includes('<script>')) {
return content.replace(/<script>/, `<script>\n\t${envImport}\n\t${importStatement}\n\t${initCode}`);
}
else if (content.includes('<script context="module">')) {
return content.replace(/<script\s+context="module">/, `<script context="module">\n\t${envImport}\n\t${importStatement}\n\t${initCode}`);
}
else {
// If no script tag found, add one at the beginning
return content.replace(/<svelte:head>/, `<script lang="ts">\n\t${envImport}\n\t${importStatement}\n\t${initCode}\n</script>\n\n<svelte:head>`);
}
}
injectVanillaScript(content) {
if (content.includes('humanbehavior-js')) {
return content;
}
const cdnScript = `<script src="https://unpkg.com/humanbehavior-js@latest/dist/index.min.js"></script>`;
const initScript = `<script>
// Initialize HumanBehavior SDK
// Note: For vanilla HTML, the API key must be hardcoded since env vars aren't available
const tracker = HumanBehaviorTracker.init('${this.apiKey}');
</script>`;
return content.replace(/<\/head>/, ` ${cdnScript}\n ${initScript}\n</head>`);
}
injectNuxtConfig(content) {
if (content.includes('humanBehaviorApiKey')) {
return content;
}
// Add runtime config by inserting it after the opening brace
return content.replace(/export default defineNuxtConfig\(\{/, `export default defineNuxtConfig({
runtimeConfig: {
public: {
humanBehaviorApiKey: process.env.NUXT_PUBLIC_HUMANBEHAVIOR_API_KEY
}
},`);
}
/**
* Helper method to find the best environment file for a framework
*/
findBestEnvFile(framework) {
const possibleEnvFiles = [
'.env.local',
'.env.development.local',
'.env.development',
'.env.local.development',
'.env',
'.env.production',
'.env.staging'
];
// Framework-specific environment variable names
const getEnvVarName = (framework) => {
// Handle React+Vite specifically
if (framework.type === 'react' && framework.bundler === 'vite') {
return 'VITE_HUMANBEHAVIOR_API_KEY';
}
// Framework-specific mappings
const envVarNames = {
react: 'HUMANBEHAVIOR_API_KEY',
nextjs: 'NEXT_PUBLIC_HUMANBEHAVIOR_API_KEY',
vue: 'VITE_HUMANBEHAVIOR_API_KEY',
svelte: 'PUBLIC_HUMANBEHAVIOR_API_KEY',
angular: 'HUMANBEHAVIOR_API_KEY',
nuxt: 'NUXT_PUBLIC_HUMANBEHAVIOR_API_KEY',
remix: 'HUMANBEHAVIOR_API_KEY',
vanilla: 'HUMANBEHAVIOR_API_KEY',
node: 'HUMANBEHAVIOR_API_KEY',
auto: 'HUMANBEHAVIOR_API_KEY'
};
return envVarNames[framework.type] || 'HUMANBEHAVIOR_API_KEY';
};
const envVarName = getEnvVarName(framework);
// Check for existing files
for (const envFile of possibleEnvFiles) {
const fullPath = path__namespace.join(this.projectRoot, envFile);
if (fs__namespace.existsSync(fullPath)) {
return { filePath: fullPath, envVarName };
}
}
// Framework-specific default file creation
const defaultFiles = {
react: '.env.local',
nextjs: '.env.local',
vue: '.env.local',
svelte: '.env',
angular: '.env',
nuxt: '.env',
remix: '.env.local',
vanilla: '.env',
node: '.env',
auto: '.env'
};
const defaultFile = defaultFiles[framework.type] || '.env';
return {
filePath: path__namespace.join(this.projectRoot, defaultFile),
envVarName
};
}
/**
* Helper method to create or append to environment files
*/
createEnvironmentModification(framework) {
const { filePath, envVarName } = this.findBestEnvFile(framework);
if (fs__namespace.existsSync(filePath)) {
// Check if the variable already exists
const content = fs__namespace.readFileSync(filePath, 'utf8');
if (content.includes(envVarName)) {
// Variable exists, don't modify
return {
filePath,
action: 'modify',
content: content, // No change
description: `API key already exists in ${path__namespace.basename(filePath)}`
};
}
else {
// Append to existing file
return {
filePath,
action: 'append',
content: `\n${envVarName}=${this.apiKey}`,
description: `Added API key to existing ${path__namespace.basename(filePath)}`
};
}
}
else {
// Create new file
return {
filePath,
action: 'create',
content: `${envVarName}=${this.apiKey}`,
description: `Created ${path__namespace.basename(filePath)} with API key`
};
}
}
}
/**
* AI-Enhanced HumanBehavior SDK Auto-Installation Wizard
*
* This wizard uses AI to intelligently detect frameworks, analyze code patterns,
* and generate optimal integration code that's both future-proof and backward-compatible.
*
* 🚀 KEY FEATURES:
* - AI-powered framework detection beyond package.json
* - Intelligent code pattern analysis
* - Future-proof integration strategies
* - Backward compatibility with legacy frameworks
* - Adaptive code generation for new frameworks
* - Smart conflict resolution
* - Learning from user feedback
* - Centralized AI service (no user API keys required)
*/
/**
* Default AI Service Implementation
* Uses heuristic analysis when centralized service is not available
*/
class DefaultAIService {
analyzeCodePatterns(codeSamples) {
return __awaiter(this, void 0, void 0, function* () {
return this.analyzeWithHeuristics(codeSamples);
});
}
resolveConflicts(conflicts, framework) {
return __awaiter(this, void 0, void 0, function* () {
// Default conflict resolution strategies
const resolutions = [];
for (const conflict of conflicts) {
switch (conflict) {
case 'existing_humanbehavior_code':
resolutions.push('update_existing_integration');
break;
case 'existing_provider':
resolutions.push('merge_providers');
break;
case 'module_system_conflict':
resolutions.push('hybrid_module_support');
break;
default:
resolutions.push('skip_conflict');
}
}
return resolutions;
});
}
generateOptimizations(framework, patterns) {
return __awaiter(this, void 0, void 0, function* () {
const optimizations = [];
// Framework-specific optimizations
switch (framework.type) {
case 'react':
optimizations.push('Use React.memo for performance optimization');
optimizations.push('Implement error boundaries for better error tracking');
optimizations.push('Consider using React.lazy for code splitting');
break;
case 'vue':
optimizations.push('Use Vue 3 Composition API for better performance');
optimizations.push('Implement proper error handling in components');
optimizations.push('Consider using Vue Router for navigation tracking');
break;
case 'angular':
optimizations.push('Use Angular standalone components for better tree-shaking');
optimizations.push('Implement proper error handling with ErrorHandler');
optimizations.push('Consider using Angular signals for state management');
break;
default:
optimizations.push('Enable performance tracking');
optimizations.push('Implement error tracking');
optimizations.push('Consider progressive enhancement');
}
return optimizations;
});
}
analyzeWithHeuristics(codeSamples) {
const patterns = codeSamples.join(' ').toLowerCase();
// Framework detection
let framework = { name: 'vanilla', type: 'vanilla' };
let confidence = 0.5;
if (patterns.includes('nuxt') || patterns.includes('nuxtjs') || patterns.includes('defineNuxtConfig') || patterns.includes('nuxt.config') || patterns.includes('@nuxt/') || patterns.includes('useNuxtApp') || patterns.includes('useRuntimeConfig') || patterns.includes('useSeoMeta') || patterns.includes('useHead') || patterns.includes('useLazyFetch') || patterns.includes('useFetch') || patterns.includes('useAsyncData') || patterns.includes('#app')) {
framework = { name: 'nuxt', type: 'nuxt' };
confidence = 0.95;
}
else if (patterns.includes('next') || patterns.includes('nextjs') || patterns.includes('next/link') || patterns.includes('next/image') || patterns.includes('next/navigation') || patterns.includes('next/router') || patterns.includes('getserverSideProps') || patterns.includes('getstaticProps') || patterns.includes('getstaticPaths') || patterns.includes('app/layout') || patterns.includes('app/page') || patterns.includes('pages/')) {
framework = { name: 'nextjs', type: 'nextjs' };
confidence = 0.95;
}
else if (patterns.includes('react')) {
framework = { name: 'react', type: 'react' };
confidence = 0.9;
}
else if (patterns.includes('vue')) {
framework = { name: 'vue', type: 'vue' };
confidence = 0.9;
}
else if (patterns.includes('angular')) {
framework = { name: 'angular', type: 'angular' };
confidence = 0.9;
}
else if (patterns.includes('svelte')) {
framework = { name: 'svelte', type: 'svelte' };
confidence = 0.9;
}
// Integration strategy
let integrationStrategy = 'script';
if (framework.type === 'react' || framework.type === 'nextjs') {
integrationStrategy = 'provider';
}
else if (framework.type === 'vue') {
integrationStrategy = 'plugin';
}
else if (framework.type === 'angular') {
integrationStrategy = 'module';
}
// Compatibility mode
let compatibilityMode = 'modern';
if (patterns.includes('require(') || patterns.includes('var ')) {
compatibilityMode = 'legacy';
}
return {
framework,
confidence,
patterns: codeSamples,
conflicts: [],
recommendations: [],
integrationStrategy,
compatibilityMode
};
}
}
/**
* Browser-based AI installation wizard
*/
class AIBrowserInstallationWizard {
constructor(apiKey, aiService) {
this.apiKey = apiKey;
this.aiService = aiService || new DefaultAIService();
}
install() {
return __awaiter(this, void 0, void 0, function* () {
try {
// AI-powered browser detection
const aiAnalysis = yield this.performBrowserAIAnalysis();
// Generate AI-optimi