diff --git a/AISKU/Tests/CloudFlareWorkerRepro/CloudflareWorkerReproduction.md b/AISKU/Tests/CloudFlareWorkerRepro/CloudflareWorkerReproduction.md new file mode 100644 index 000000000..528d3200f --- /dev/null +++ b/AISKU/Tests/CloudFlareWorkerRepro/CloudflareWorkerReproduction.md @@ -0,0 +1,178 @@ +# Reproducing ApplicationInsights Issues with Angular SSR in Cloudflare Workers + +This document outlines steps to reproduce the issues reported in [issue #2523](https://github.com/microsoft/ApplicationInsights-JS/issues/2523), where the ApplicationInsights SDK breaks Angular Server-Side Rendering in Cloudflare Workers environments. + +> **Note:** We've created automated reproduction scripts in this directory. These scripts set up a test environment and provide tools to analyze the issue in depth. + +## Issues Reported + +1. Property redefinition error for 'name' property: + ``` + Cannot redefine property: name + at defineProperty () + at __name (server.js:7:33) + ``` + +2. Rendering hanging or being extremely slow without error messages + +## Prerequisites + +- Node.js v16+ and npm/pnpm +- Cloudflare account (for deployment testing) +- Wrangler CLI tool (`npm i -g wrangler`) + +## Reproduction Steps + +### 1. Create Basic Angular + Cloudflare Project + +```bash +# Using the Cloudflare template for Angular +pnpm create cloudflare@latest my-angular-app --framework=angular + +# Navigate to the created project +cd my-angular-app +``` + +### 2. Add ApplicationInsights SDK + +```bash +# Install ApplicationInsights +npm install @microsoft/applicationinsights-web +``` + +### 3. Configure ApplicationInsights in Angular + +Update `app.component.ts` to initialize ApplicationInsights: + +```typescript +import { Component, OnInit } from '@angular/core'; +import { ApplicationInsights } from '@microsoft/applicationinsights-web'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent implements OnInit { + private _appInsights: ApplicationInsights; + + constructor() { + // This initialization alone will cause the issues + this._appInsights = new ApplicationInsights({ + config: { + connectionString: 'PLACEHOLDER_CONNECTION_STRING', + enableAutoRouteTracking: true, + enableCorsCorrelation: false, + enableRequestHeaderTracking: true, + enableResponseHeaderTracking: true, + } + }); + } + + ngOnInit() { + // Even without calling loadAppInsights(), the issue occurs + // this._appInsights.loadAppInsights(); + } +} +``` + +### 4. Configure Server-Side Rendering + +Ensure the application is configured for server-side rendering in `app.route.server.ts`: + +```typescript +import { RenderMode, ServerRoute } from '@angular/ssr'; + +export const serverRoutes: ServerRoute[] = [ + { + path: '**', + renderMode: RenderMode.Server + } +]; +``` + +### 5. Build and Test + +```bash +# Build the Angular application +npm run build + +# Run the application locally using Wrangler +npx wrangler dev +``` + +## Observed Errors + +When running the application, we observe: + +1. Error in the Cloudflare Worker logs: + ``` + Cannot redefine property: name + at defineProperty () + at __name (server.js:7:33) + ``` + +2. The page load hangs indefinitely or is extremely slow. + +## Testing Workarounds + +### 1. Using esbuild with preserveNames=false + +To test the workaround mentioned in the issue, we can modify the esbuild configuration: + +```bash +# Create a custom esbuild config +npx esbuild server.js --bundle --outfile=server.custom.js --platform=neutral --target=es2020 --preserve-names=false +``` + +### 2. Dynamically Importing ApplicationInsights in Client-Side Only + +Modify `app.component.ts` to conditionally import and initialize ApplicationInsights: + +```typescript +export class AppComponent implements OnInit { + private _appInsights: any; + + constructor() { + this.lazyLoadAppInsights('PLACEHOLDER_CONNECTION_STRING'); + } + + private async lazyLoadAppInsights(connectionString: string) { + try { + // Check if we're in a browser environment (not SSR) + if (typeof window !== 'undefined' && typeof document !== 'undefined') { + const appInsights = await import('@microsoft/applicationinsights-web'); + const ApplicationInsights = appInsights.ApplicationInsights; + + this._appInsights = new ApplicationInsights({ + config: { + connectionString: connectionString, + enableAutoRouteTracking: true, + enableCorsCorrelation: false, + enableRequestHeaderTracking: true, + enableResponseHeaderTracking: true, + } + }); + + this._appInsights.loadAppInsights(); + } + } catch (error) { + console.error('Failed to initialize ApplicationInsights:', error); + } + } +} +``` + +## Analyzing the Built Bundle + +To understand what causes the property redefinition error, we can examine the built bundle: + +```bash +# Find occurrences of property name definition in the bundle +grep -n "defineProperty" server.js +grep -n "__name" server.js +``` + +## Findings and Conclusions + +(This section will be updated after successfully reproducing and analyzing the issue) \ No newline at end of file diff --git a/AISKU/Tests/CloudFlareWorkerRepro/README.md b/AISKU/Tests/CloudFlareWorkerRepro/README.md new file mode 100644 index 000000000..a8f8a80f5 --- /dev/null +++ b/AISKU/Tests/CloudFlareWorkerRepro/README.md @@ -0,0 +1,93 @@ +# ApplicationInsights with Angular SSR in Cloudflare Workers - Issue Reproduction + +This repository contains scripts and tools to reproduce and analyze the issue reported in [ApplicationInsights-JS issue #2523](https://github.com/microsoft/ApplicationInsights-JS/issues/2523), where the ApplicationInsights SDK breaks Angular Server-Side Rendering in Cloudflare Workers. + +## Setup and Reproduction + +This reproducer includes several scripts to help diagnose the issue: + +### 1. Basic Setup (`setup.sh`) + +This script creates a simple Angular application with Cloudflare Worker integration and ApplicationInsights. It: + +- Creates a new Angular project +- Adds Angular Universal for SSR +- Installs ApplicationInsights SDK +- Configures server routes for SSR +- Sets up AppComponent with ApplicationInsights initialization +- Creates a Wrangler configuration for Cloudflare Workers +- Adds a bundle analysis script + +Usage: +```bash +chmod +x setup.sh +./setup.sh +``` + +### 2. Testing Workarounds (`test-workarounds.sh`) + +This script implements and tests the workarounds mentioned in the issue: + +- Using esbuild with `preserveNames=false` +- Using dynamic imports to load ApplicationInsights only in client-side context + +Usage: +```bash +chmod +x test-workarounds.sh +./test-workarounds.sh +``` + +### 3. Analyzing the ApplicationInsights SDK (`analyze-appinsights.js`) + +This script scans the ApplicationInsights SDK code to identify patterns that might cause issues with esbuild in Cloudflare Workers: + +- Looks for `Object.defineProperty` calls +- Checks for `.name` property access or assignment +- Identifies use of `__name` metadata +- Scans for dynamic function creation and property redefinition + +Usage: +```bash +node analyze-appinsights.js +``` + +### 4. Testing esbuild Configurations (`test-esbuild.js`) + +This script tests different esbuild configurations to understand how they process function names and properties: + +- Creates test code with various function types +- Builds with different esbuild configurations +- Analyzes the output for `__name` helper functions and property definitions + +Usage: +```bash +node test-esbuild.js +``` + +## Expected Results + +After running these scripts, you should be able to: + +1. Reproduce the "Cannot redefine property: name" error in Cloudflare Workers +2. Understand how esbuild processes function names and properties +3. Identify patterns in the ApplicationInsights SDK that trigger esbuild to add property redefinition code +4. Test workarounds to see if they resolve the issue + +## Key Findings + +(This section will be updated after running the reproduction scripts and analyzing the results) + +## Recommendations + +Based on the reproduction and analysis, potential solutions might include: + +1. Modifying the esbuild configuration when bundling for Cloudflare Workers +2. Adding specific detection for Cloudflare Workers environments in the SDK +3. Providing guidance to users on how to properly initialize the SDK in SSR contexts +4. Restructuring specific parts of the SDK to avoid triggering esbuild's property redefinition + +## References + +- [ApplicationInsights-JS Issue #2523](https://github.com/microsoft/ApplicationInsights-JS/issues/2523) +- [Cloudflare Worker documentation](https://developers.cloudflare.com/workers/) +- [Angular SSR with Cloudflare](https://developers.cloudflare.com/workers/frameworks/framework-guides/angular/) \ No newline at end of file diff --git a/AISKU/Tests/CloudFlareWorkerRepro/analyze-appinsights.js b/AISKU/Tests/CloudFlareWorkerRepro/analyze-appinsights.js new file mode 100644 index 000000000..48c4eadb9 --- /dev/null +++ b/AISKU/Tests/CloudFlareWorkerRepro/analyze-appinsights.js @@ -0,0 +1,124 @@ +/** + * This script analyzes the ApplicationInsights SDK to understand how it might + * interact with esbuild and cause property redefinition issues in Cloudflare Workers. + */ + +const fs = require('fs'); +const path = require('path'); + +// Directory where node_modules are located +const nodeModulesDir = path.join(__dirname, 'angular-cloudflare-repro', 'node_modules'); +const appInsightsDir = path.join(nodeModulesDir, '@microsoft', 'applicationinsights-web'); + +// Function to scan a file for potential issues +function analyzeFile(filePath) { + try { + const content = fs.readFileSync(filePath, 'utf8'); + + // Look for patterns that might cause issues with esbuild + const result = { + path: filePath, + definePropertyCount: (content.match(/Object\.defineProperty/g) || []).length, + namePropertyAccess: (content.match(/\.name\s*=/g) || []).length, + hasNameMetadata: content.includes('__name'), + functionNameUsage: (content.match(/\.name\s*\)/g) || []).length, + dynamicFunctionCreation: content.includes('new Function('), + propertyRedefinition: content.includes('Object.defineProperties'), + reflectionUsage: content.includes('Reflect.') + }; + + // Look for specific problematic patterns + if (result.definePropertyCount > 0 || result.namePropertyAccess > 0 || result.hasNameMetadata) { + return result; + } + + return null; + } catch (error) { + return { path: filePath, error: error.message }; + } +} + +// Function to recursively scan directories +async function scanDirectory(dir, fileExtensions = ['.js', '.ts']) { + const results = []; + + try { + const files = fs.readdirSync(dir); + + for (const file of files) { + const filePath = path.join(dir, file); + const stats = fs.statSync(filePath); + + if (stats.isDirectory()) { + results.push(...await scanDirectory(filePath, fileExtensions)); + } else if (fileExtensions.includes(path.extname(filePath))) { + const result = analyzeFile(filePath); + if (result) { + results.push(result); + } + } + } + } catch (error) { + console.error(`Error scanning directory ${dir}:`, error); + } + + return results; +} + +// Main function +async function main() { + console.log('Analyzing ApplicationInsights SDK...'); + + if (!fs.existsSync(appInsightsDir)) { + console.error('ApplicationInsights SDK not found. Please run setup.sh first.'); + return; + } + + const results = await scanDirectory(appInsightsDir); + + // Filter for the most suspicious files + const suspiciousFiles = results + .filter(r => r.definePropertyCount > 0 || r.namePropertyAccess > 0 || r.hasNameMetadata) + .sort((a, b) => (b.definePropertyCount + b.namePropertyAccess) - (a.definePropertyCount + a.namePropertyAccess)); + + console.log(`\nFound ${suspiciousFiles.length} suspicious files:`); + suspiciousFiles.forEach((file, i) => { + console.log(`\n${i + 1}. ${path.relative(appInsightsDir, file.path)}`); + console.log(` - Object.defineProperty calls: ${file.definePropertyCount}`); + console.log(` - .name property assignments: ${file.namePropertyAccess}`); + console.log(` - Has __name metadata: ${file.hasNameMetadata}`); + console.log(` - Function name usage: ${file.functionNameUsage}`); + if (file.dynamicFunctionCreation) console.log(' - Uses dynamic function creation (new Function())'); + if (file.propertyRedefinition) console.log(' - Uses Object.defineProperties'); + if (file.reflectionUsage) console.log(' - Uses Reflection API'); + }); + + // Check for use of DynamicProto-JS as it was mentioned in the issue comments + const dynamicProtoDir = path.join(nodeModulesDir, '@microsoft', 'dynamicproto-js'); + if (fs.existsSync(dynamicProtoDir)) { + console.log('\nAnalyzing DynamicProto-JS package...'); + const dynamicProtoResults = await scanDirectory(dynamicProtoDir); + + const suspiciousDynamicProtoFiles = dynamicProtoResults + .filter(r => r.definePropertyCount > 0 || r.namePropertyAccess > 0 || r.hasNameMetadata) + .sort((a, b) => (b.definePropertyCount + b.namePropertyAccess) - (a.definePropertyCount + a.namePropertyAccess)); + + if (suspiciousDynamicProtoFiles.length > 0) { + console.log(`\nFound ${suspiciousDynamicProtoFiles.length} suspicious files in DynamicProto-JS:`); + suspiciousDynamicProtoFiles.forEach((file, i) => { + console.log(`\n${i + 1}. ${path.relative(dynamicProtoDir, file.path)}`); + console.log(` - Object.defineProperty calls: ${file.definePropertyCount}`); + console.log(` - .name property assignments: ${file.namePropertyAccess}`); + console.log(` - Has __name metadata: ${file.hasNameMetadata}`); + }); + } else { + console.log('\nNo suspicious patterns found in DynamicProto-JS'); + } + } else { + console.log('\nDynamicProto-JS package not found'); + } + + console.log('\nAnalysis complete.'); +} + +main().catch(console.error); \ No newline at end of file diff --git a/AISKU/Tests/CloudFlareWorkerRepro/setup.sh b/AISKU/Tests/CloudFlareWorkerRepro/setup.sh new file mode 100755 index 000000000..943d19cf1 --- /dev/null +++ b/AISKU/Tests/CloudFlareWorkerRepro/setup.sh @@ -0,0 +1,153 @@ +#!/bin/bash + +# Create a new directory for the reproduction +echo "Creating directory for the reproduction..." +mkdir -p angular-cloudflare-repro +cd angular-cloudflare-repro + +# Create a package.json file to install dependencies +echo "Creating package.json..." +cat > package.json << 'EOF' +{ + "name": "angular-cloudflare-repro", + "version": "0.0.1", + "description": "Reproduction of ApplicationInsights issue with Angular and Cloudflare Worker", + "scripts": { + "start": "npx wrangler dev", + "build": "npm run build:client && npm run build:server", + "build:client": "ng build", + "build:server": "ng run angular-cloudflare-repro:server:production" + } +} +EOF + +# Install Angular CLI +echo "Installing Angular CLI..." +npm install -g @angular/cli@latest + +# Create a new Angular project +echo "Creating Angular project..." +ng new --skip-git --skip-tests --style=css --routing=true angular-cloudflare-repro + +# Navigate to the project directory +cd angular-cloudflare-repro + +# Install ApplicationInsights SDK +echo "Installing ApplicationInsights SDK..." +npm install @microsoft/applicationinsights-web + +# Install Cloudflare worker dependencies +echo "Installing Cloudflare worker dependencies..." +npm install @cloudflare/workers-types wrangler + +# Add Angular Universal SSR +echo "Adding Angular Universal SSR support..." +ng add @nguniversal/express-engine + +# Create the server routes file for Angular SSR +echo "Creating server routes file..." +mkdir -p src/app/server +cat > src/app/server/app.route.server.ts << 'EOF' +import { Routes } from '@angular/router'; + +export const serverRoutes: Routes = [ + { + path: '**', + component: null, + data: { + renderMode: 'server' + } + } +]; +EOF + +# Update AppComponent to include ApplicationInsights +echo "Updating AppComponent with ApplicationInsights..." +cat > src/app/app.component.ts << 'EOF' +import { Component, OnInit } from '@angular/core'; +import { ApplicationInsights } from '@microsoft/applicationinsights-web'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent implements OnInit { + title = 'angular-cloudflare-repro'; + private _appInsights: ApplicationInsights; + + constructor() { + // This initialization alone will cause the issues + this._appInsights = new ApplicationInsights({ + config: { + connectionString: 'PLACEHOLDER_CONNECTION_STRING', + enableAutoRouteTracking: true, + enableCorsCorrelation: false, + enableRequestHeaderTracking: true, + enableResponseHeaderTracking: true, + } + }); + + // Even commenting out loadAppInsights still produces the error + // this._appInsights.loadAppInsights(); + } + + ngOnInit() { + // Empty for now + } +} +EOF + +# Create a Cloudflare Worker configuration +echo "Creating Cloudflare Worker configuration..." +cat > wrangler.toml << 'EOF' +name = "angular-cloudflare-repro" +main = "dist/server/main.js" +compatibility_date = "2023-06-28" +workers_dev = true + +[build] +command = "npm run build" +[build.upload] +format = "modules" +EOF + +# Create a simple test script to analyze bundle output +echo "Creating bundle analysis script..." +cat > analyze-bundle.js << 'EOF' +const fs = require('fs'); +const path = require('path'); + +// Read the server bundle +const serverBundlePath = path.join(__dirname, 'dist', 'server', 'main.js'); +try { + const bundle = fs.readFileSync(serverBundlePath, 'utf8'); + + // Look for defineProperty and __name occurrences + const definePropertyMatches = bundle.match(/defineProperty/g) || []; + const nameMatches = bundle.match(/__name/g) || []; + + console.log(`Found ${definePropertyMatches.length} occurrences of defineProperty`); + console.log(`Found ${nameMatches.length} occurrences of __name`); + + // Look for lines that might be redefining the name property + const lines = bundle.split('\n'); + const suspiciousLines = lines.filter(line => + line.includes('defineProperty') && line.includes('name') + ); + + console.log('\nSuspicious lines that might redefine name property:'); + suspiciousLines.forEach((line, i) => { + console.log(`${i + 1}. ${line.trim()}`); + }); + +} catch (error) { + console.error('Failed to read server bundle:', error); +} +EOF + +echo "Setup completed! Next steps:" +echo "1. Run 'cd angular-cloudflare-repro'" +echo "2. Run 'npm run build' to build the application" +echo "3. Run 'node analyze-bundle.js' to analyze the server bundle" +echo "4. Run 'npm start' to start the application locally using Wrangler" \ No newline at end of file diff --git a/AISKU/Tests/CloudFlareWorkerRepro/test-esbuild.js b/AISKU/Tests/CloudFlareWorkerRepro/test-esbuild.js new file mode 100644 index 000000000..35b670c62 --- /dev/null +++ b/AISKU/Tests/CloudFlareWorkerRepro/test-esbuild.js @@ -0,0 +1,161 @@ +/** + * This script tests different esbuild configurations to understand + * how they process the code and potentially cause issues in Cloudflare Workers + */ + +const { build } = require('esbuild'); +const fs = require('fs'); +const path = require('path'); + +// Create a simple test file with functions +const createTestFile = () => { + const testDir = path.join(__dirname, 'esbuild-test'); + if (!fs.existsSync(testDir)) { + fs.mkdirSync(testDir, { recursive: true }); + } + + const testFile = path.join(testDir, 'test.js'); + fs.writeFileSync(testFile, ` +// Named function +function namedFunction() { + console.log("I'm a named function"); +} + +// Anonymous function assigned to variable +const anonymousFunction = function() { + console.log("I'm anonymous"); +}; + +// Arrow function +const arrowFunction = () => { + console.log("I'm an arrow function"); +}; + +// Class with methods +class TestClass { + constructor() { + this.value = 42; + } + + method1() { + console.log("Method 1"); + } + + method2() { + console.log("Method 2"); + } +} + +// Access function name +console.log(namedFunction.name); +console.log(anonymousFunction.name); +console.log(arrowFunction.name); +console.log(TestClass.name); +console.log(new TestClass().method1.name); + +// Export everything +export { + namedFunction, + anonymousFunction, + arrowFunction, + TestClass +}; + `); + + return { testDir, testFile }; +}; + +// Build with different configurations +const runBuilds = async () => { + const { testDir, testFile } = createTestFile(); + + // Test different configurations + const configs = [ + { + name: 'default', + preserveNames: undefined + }, + { + name: 'preserveNames-true', + preserveNames: true + }, + { + name: 'preserveNames-false', + preserveNames: false + }, + { + name: 'minified', + minify: true, + preserveNames: undefined + }, + { + name: 'minified-preserveNames', + minify: true, + preserveNames: true + } + ]; + + for (const config of configs) { + const outfile = path.join(testDir, `output-${config.name}.js`); + + console.log(`Building with configuration: ${config.name}`); + try { + await build({ + entryPoints: [testFile], + bundle: true, + outfile, + format: 'esm', + platform: 'neutral', + minify: config.minify || false, + preserveNames: config.preserveNames, + metafile: true + }); + + // Read output and log interesting parts + const output = fs.readFileSync(outfile, 'utf8'); + console.log(`${config.name} output size: ${output.length} bytes`); + + // Check for __name helper function + const hasNameHelper = output.includes('__name'); + console.log(`${config.name} includes __name helper: ${hasNameHelper}`); + + // Check for Object.defineProperty + const definePropertyCount = (output.match(/Object\.defineProperty/g) || []).length; + console.log(`${config.name} calls to Object.defineProperty: ${definePropertyCount}`); + + // Check for name property references + const namePropertyCount = (output.match(/\.name/g) || []).length; + console.log(`${config.name} references to .name: ${namePropertyCount}`); + + // If the __name helper is present, show its implementation + if (hasNameHelper) { + const nameHelperMatch = output.match(/function __name\(target[^{]*{[^}]*}/); + if (nameHelperMatch) { + console.log(`\n${config.name} __name helper implementation:`); + console.log(nameHelperMatch[0]); + } + } + + console.log('-----------------------------------'); + } catch (error) { + console.error(`Error building ${config.name}:`, error); + } + } +}; + +// Main function +async function main() { + try { + // Install esbuild if not already present + const { execSync } = require('child_process'); + console.log('Ensuring esbuild is installed...'); + execSync('npm install -D esbuild', { stdio: 'inherit' }); + + await runBuilds(); + console.log('\nAll builds completed!'); + } catch (error) { + console.error('Error:', error); + } +} + +main(); \ No newline at end of file diff --git a/AISKU/Tests/CloudFlareWorkerRepro/test-workarounds.sh b/AISKU/Tests/CloudFlareWorkerRepro/test-workarounds.sh new file mode 100755 index 000000000..35c4bb2bc --- /dev/null +++ b/AISKU/Tests/CloudFlareWorkerRepro/test-workarounds.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +cd angular-cloudflare-repro + +# 1. Testing esbuild with preserveNames=false +echo "Testing esbuild with preserveNames=false..." + +# Install esbuild if not already installed +npm install -D esbuild + +# Create a custom build script for esbuild +cat > custom-build.js << 'EOF' +const { build } = require('esbuild'); +const fs = require('fs'); +const path = require('path'); + +async function customBuild() { + try { + const serverBundlePath = path.join(__dirname, 'dist', 'server', 'main.js'); + const outputPath = path.join(__dirname, 'dist', 'server', 'main.custom.js'); + + if (!fs.existsSync(serverBundlePath)) { + console.error('Server bundle not found. Run "npm run build" first.'); + return; + } + + await build({ + entryPoints: [serverBundlePath], + bundle: true, + outfile: outputPath, + platform: 'neutral', + target: 'es2020', + minify: false, + preserveNames: false, // This is the key setting we're testing + format: 'esm', + }); + + console.log(`Custom bundle created at ${outputPath}`); + } catch (error) { + console.error('Error during custom build:', error); + } +} + +customBuild(); +EOF + +# Run the custom build script +echo "Running custom build with esbuild..." +node custom-build.js + +# 2. Testing dynamic import workaround +echo "Testing dynamic import workaround..." + +# Create a version of AppComponent with dynamic import +mkdir -p src/app/workaround +cat > src/app/workaround/app.component.workaround.ts << 'EOF' +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-root', + templateUrl: '../app.component.html', + styleUrls: ['../app.component.css'] +}) +export class AppComponent implements OnInit { + title = 'angular-cloudflare-repro'; + private _appInsights: any; + + constructor() { + this.lazyLoadAppInsights('PLACEHOLDER_CONNECTION_STRING'); + } + + ngOnInit() { + // Empty for now + } + + private async lazyLoadAppInsights(connectionString: string) { + try { + // Check if we're in a browser environment (not SSR) + if (typeof window !== 'undefined' && typeof document !== 'undefined') { + const appInsights = await import('@microsoft/applicationinsights-web'); + const ApplicationInsights = appInsights.ApplicationInsights; + + this._appInsights = new ApplicationInsights({ + config: { + connectionString: connectionString, + enableAutoRouteTracking: true, + enableCorsCorrelation: false, + enableRequestHeaderTracking: true, + enableResponseHeaderTracking: true, + } + }); + + this._appInsights.loadAppInsights(); + console.log('ApplicationInsights loaded in client-side context'); + } else { + console.log('Skipping ApplicationInsights initialization in server context'); + } + } catch (error) { + console.error('Failed to initialize ApplicationInsights:', error); + } + } +} +EOF + +echo "Workaround implementations are ready. You can:" +echo "1. Test the esbuild custom bundle by renaming dist/server/main.custom.js to dist/server/main.js and running 'npm start'" +echo "2. Test the dynamic import approach by replacing src/app/app.component.ts with the content from src/app/workaround/app.component.workaround.ts, then rebuilding and running" \ No newline at end of file diff --git a/AISKU/Tests/CloudFlareWorkerRepro/verify.js b/AISKU/Tests/CloudFlareWorkerRepro/verify.js new file mode 100644 index 000000000..758f12d7b --- /dev/null +++ b/AISKU/Tests/CloudFlareWorkerRepro/verify.js @@ -0,0 +1,71 @@ +/** + * This script verifies that the reproduction environment can be set up + * and that the issue can be reproduced. + */ + +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +// Check if the required files exist +const requiredFiles = [ + 'README.md', + 'setup.sh', + 'test-workarounds.sh', + 'analyze-appinsights.js', + 'test-esbuild.js' +]; + +console.log('Verifying reproduction environment...'); + +const missingFiles = requiredFiles.filter(file => !fs.existsSync(path.join(__dirname, file))); +if (missingFiles.length > 0) { + console.error('Missing required files:', missingFiles); + process.exit(1); +} + +// Check if esbuild can be installed +try { + console.log('Checking if esbuild can be installed...'); + execSync('npm install --no-save esbuild', { stdio: 'inherit' }); + console.log('✓ esbuild can be installed'); +} catch (error) { + console.error('Error installing esbuild:', error); +} + +// Check if Node.js and npm versions are compatible +console.log('Checking Node.js and npm versions...'); +const nodeVersion = process.version; +const npmVersion = execSync('npm --version').toString().trim(); + +console.log(`Node.js version: ${nodeVersion}`); +console.log(`npm version: ${npmVersion}`); + +if (parseInt(nodeVersion.substring(1).split('.')[0]) < 16) { + console.warn('⚠️ Warning: Node.js version 16 or higher recommended for this reproduction'); +} + +// Check for Angular CLI +try { + console.log('Checking for Angular CLI...'); + execSync('npm list -g @angular/cli || echo "Angular CLI not found globally"', { stdio: 'inherit' }); + console.log('Angular CLI can be installed during setup if not already present'); +} catch (error) { + console.log('Angular CLI will be installed during setup'); +} + +// Check for wrangler +try { + console.log('Checking for Wrangler CLI...'); + execSync('npm list -g wrangler || echo "Wrangler not found globally"', { stdio: 'inherit' }); + console.log('Wrangler can be installed during setup if not already present'); +} catch (error) { + console.log('Wrangler will be installed during setup'); +} + +console.log('\nVerification completed!'); +console.log('The reproduction environment is ready to use.'); +console.log('\nTo start reproduction:'); +console.log('1. Make the setup script executable: chmod +x setup.sh'); +console.log('2. Run the setup script: ./setup.sh'); +console.log('3. Follow the instructions provided after setup completes'); \ No newline at end of file diff --git a/docs/CloudflareWorkerNote.md b/docs/CloudflareWorkerNote.md new file mode 100644 index 000000000..a18967cf3 --- /dev/null +++ b/docs/CloudflareWorkerNote.md @@ -0,0 +1,58 @@ +# Note on Cloudflare Worker Compatibility with ApplicationInsights SDK + +When using the ApplicationInsights SDK in a Cloudflare Worker environment, particularly with Angular SSR, users may encounter errors related to property redefinition: + +``` +Cannot redefine property: name + at defineProperty () + at __name (server.js:7:33) +``` + +## Root Cause + +The issue appears to be related to how esbuild processes the code when bundling for Cloudflare Workers. The error occurs because esbuild adds code that attempts to redefine the "name" property, which is prohibited in Cloudflare Workers' restrictive JavaScript environment. + +## Workaround + +As noted in [issue #2523](https://github.com/microsoft/ApplicationInsights-JS/issues/2523), you can work around this by: + +1. Manually invoking esbuild with `preserveNames=false` parameter +2. Instructing wrangler to skip bundling + +You may also consider dynamically importing and initializing the SDK only in client-side rendering contexts: + +```typescript +private async lazyLoadAppInsights(connectionString: string) { + try { + // Check if we're in a browser environment (not SSR) + if (typeof window !== 'undefined' && typeof document !== 'undefined') { + // Dynamically import the module only at runtime in browser + const appInsights = await import('@microsoft/applicationinsights-web'); + const ApplicationInsights = appInsights.ApplicationInsights; + + this._appInsights = new ApplicationInsights({ + config: { + connectionString: connectionString, + // your other config options... + } + }); + + this._appInsights.loadAppInsights(); + } + } catch (error) { + console.error('Failed to initialize ApplicationInsights:', error); + } +} +``` + +This conditional initialization approach prevents the SDK from being instantiated during server-side rendering, avoiding the property redefinition issues. + +## Reproducing the Issue + +To better understand and diagnose this issue, we've created reproduction scripts in the `/AISKU/Tests/CloudFlareWorkerRepro/` directory. These scripts provide: + +1. An automated setup for an Angular + Cloudflare Worker environment +2. Analysis tools to inspect how esbuild processes the code +3. Implementation of various workarounds to test their effectiveness + +You can use these scripts to reproduce the issue and test different solutions in your environment. \ No newline at end of file