From bfcb365c4c9f271e8944d66f4a95e3b8431a3157 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Mon, 24 Nov 2025 18:16:09 +0600 Subject: [PATCH 01/40] init --- docs/PLATFORM_ISOLATION.md | 150 ++++++++++++++ package.json | 3 +- scripts/validate-platform-isolation.js | 265 +++++++++++++++++++++++++ vitest.config.mts | 10 + 4 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 docs/PLATFORM_ISOLATION.md create mode 100644 scripts/validate-platform-isolation.js diff --git a/docs/PLATFORM_ISOLATION.md b/docs/PLATFORM_ISOLATION.md new file mode 100644 index 000000000..e4a3ecb25 --- /dev/null +++ b/docs/PLATFORM_ISOLATION.md @@ -0,0 +1,150 @@ +# Platform Isolation + +## Overview + +This project supports multiple runtime platforms (Browser, Node.js, React Native, and Universal), with separate entry points for each. To ensure the build artifacts work correctly, platform-specific code must not be mixed. + +## Naming Convention + +Platform-specific files use a suffix pattern: +- `.browser.ts` - Browser-specific implementation +- `.node.ts` - Node.js-specific implementation +- `.react_native.ts` - React Native-specific implementation +- `.ts` (no suffix) - Universal code (works across all platforms) + +## Import Rules + +Each platform-specific file can **only** import from: + +1. **Universal files** (no platform suffix) +2. **Same-platform files** (matching platform suffix) +3. **External packages** (node_modules) + +### Examples + +✅ **Valid Imports** + +```typescript +// In lib/index.browser.ts +import { Config } from './shared_types'; // ✅ Universal file +import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; // ✅ Same platform +import { uuid } from 'uuid'; // ✅ External package +``` + +```typescript +// In lib/index.node.ts +import { Config } from './shared_types'; // ✅ Universal file +import { NodeRequestHandler } from './utils/http_request_handler/request_handler.node'; // ✅ Same platform +``` + +❌ **Invalid Imports** + +```typescript +// In lib/index.browser.ts +import { NodeRequestHandler } from './utils/http_request_handler/request_handler.node'; // ❌ Different platform +``` + +```typescript +// In lib/index.node.ts +import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; // ❌ Different platform +``` + +## Automatic Validation + +Platform isolation is enforced automatically during the build process. + +### Running Validation + +```bash +# Run validation manually +npm run validate-platform-isolation + +# Validation runs automatically before build +npm run build +``` + +### How It Works + +The validation script (`scripts/validate-platform-isolation.js`): + +1. Scans all source files in the `lib/` directory +2. Identifies platform-specific files by their suffix +3. Parses import statements (ES6 imports, require, dynamic imports) +4. Checks that each import follows the platform isolation rules +5. Fails the build if violations are found + +### Build Integration + +The validation is integrated into the build process: + +```json +{ + "scripts": { + "build": "npm run validate-platform-isolation && tsc --noEmit && ..." + } +} +``` + +If platform isolation is violated, the build will fail with a detailed error message showing: +- Which files have violations +- The line numbers of problematic imports +- What platform the file belongs to +- What platform it's incorrectly importing from + +## Creating New Platform-Specific Code + +When creating new platform-specific implementations: + +1. Name the file with the appropriate platform suffix (e.g., `my-feature.browser.ts`) +2. Only import from universal or same-platform files +3. Create a universal factory or interface if multiple platforms need different implementations + +### Example: Creating a Platform-Specific Feature + +```typescript +// lib/features/my-feature.ts (universal interface) +export interface MyFeature { + doSomething(): void; +} + +// lib/features/my-feature.browser.ts +export class BrowserMyFeature implements MyFeature { + doSomething(): void { + // Browser-specific implementation + } +} + +// lib/features/my-feature.node.ts +export class NodeMyFeature implements MyFeature { + doSomething(): void { + // Node.js-specific implementation + } +} + +// lib/features/factory.browser.ts +import { BrowserMyFeature } from './my-feature.browser'; +export const createMyFeature = () => new BrowserMyFeature(); + +// lib/features/factory.node.ts +import { NodeMyFeature } from './my-feature.node'; +export const createMyFeature = () => new NodeMyFeature(); +``` + +## Troubleshooting + +If you encounter a platform isolation error: + +1. **Check the error message** - It will tell you which file and line has the violation +2. **Identify the issue** - Look at the import statement on that line +3. **Fix the import**: + - If the code should be universal, remove the platform suffix from the imported file + - If the code must be platform-specific, create separate implementations for each platform + - Use factory patterns to abstract platform-specific instantiation + +## Benefits + +- ✅ Prevents runtime errors from platform-incompatible code +- ✅ Catches issues at build time, not in production +- ✅ Makes platform boundaries explicit and maintainable +- ✅ Ensures each bundle only includes relevant code +- ✅ Works independently of linting tools (ESLint, Biome, etc.) diff --git a/package.json b/package.json index cc543dd1b..e52856f45 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "clean": "rm -rf dist", "clean:win": "(if exist dist rd /s/q dist)", "lint": "tsc --noEmit && eslint 'lib/**/*.js' 'lib/**/*.ts'", + "validate-platform-isolation": "node scripts/validate-platform-isolation.js", "test-vitest": "vitest run", "test-mocha": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register -r tsconfig-paths/register -r lib/tests/exit_on_unhandled_rejection.js 'lib/**/*.tests.ts' 'lib/**/*.tests.js'", "test": "npm run test-mocha && npm run test-vitest", @@ -66,7 +67,7 @@ "test-umdbrowser": "npm run build-browser-umd && karma start karma.umd.conf.js --single-run", "test-karma-local": "karma start karma.local_chrome.bs.conf.js && npm run build-browser-umd && karma start karma.local_chrome.umd.conf.js", "prebuild": "npm run clean", - "build": "tsc --noEmit && npm run genmsg && rollup -c && cp dist/index.browser.d.ts dist/index.d.ts", + "build": "npm run validate-platform-isolation && tsc --noEmit && npm run genmsg && rollup -c && cp dist/index.browser.d.ts dist/index.d.ts", "build:win": "tsc --noEmit && npm run genmsg && rollup -c && type nul > dist/optimizely.lite.es.d.ts && type nul > dist/optimizely.lite.es.min.d.ts && type nul > dist/optimizely.lite.min.d.ts", "build-browser-umd": "rollup -c --config-umd", "coveralls": "nyc --reporter=lcov npm test", diff --git a/scripts/validate-platform-isolation.js b/scripts/validate-platform-isolation.js new file mode 100644 index 000000000..47c0f8f20 --- /dev/null +++ b/scripts/validate-platform-isolation.js @@ -0,0 +1,265 @@ +#!/usr/bin/env node + +/** + * Platform Isolation Validator + * + * This script ensures that platform-specific entry points only import + * from universal or same-platform files. + * + * Rules: + * - Files ending with .browser.ts can only import from: + * - Universal files (no platform suffix) + * - Other .browser.ts files + * - External packages (node_modules) + * - Files ending with .node.ts can only import from: + * - Universal files (no platform suffix) + * - Other .node.ts files + * - External packages (node_modules) + * - Files ending with .react_native.ts can only import from: + * - Universal files (no platform suffix) + * - Other .react_native.ts files + * - External packages (node_modules) + * + * Usage: node scripts/validate-platform-isolation.js + */ + +const fs = require('fs'); +const path = require('path'); + +const PLATFORMS = ['browser', 'node', 'react_native']; +const LIB_DIR = path.join(__dirname, '..', 'lib'); + +/** + * Extracts the platform from a filename + */ +function getPlatform(filename) { + for (const platform of PLATFORMS) { + if (filename.includes(`.${platform}.`)) { + return platform; + } + } + return null; +} + +/** + * Gets a human-readable platform name + */ +function getPlatformName(platform) { + const names = { + 'browser': 'Browser', + 'node': 'Node.js', + 'react_native': 'React Native' + }; + return names[platform] || platform; +} + +/** + * Extract import statements from a TypeScript/JavaScript file + */ +function extractImports(content) { + const imports = []; + + // Match: import ... from '...' + const importRegex = /import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?['"]([^'"]+)['"]/g; + let match; + while ((match = importRegex.exec(content)) !== null) { + imports.push({ type: 'import', path: match[1], line: content.substring(0, match.index).split('\n').length }); + } + + // Match: require('...') + const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g; + while ((match = requireRegex.exec(content)) !== null) { + imports.push({ type: 'require', path: match[1], line: content.substring(0, match.index).split('\n').length }); + } + + // Match: import('...') - dynamic imports + const dynamicImportRegex = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g; + while ((match = dynamicImportRegex.exec(content)) !== null) { + imports.push({ type: 'dynamic-import', path: match[1], line: content.substring(0, match.index).split('\n').length }); + } + + return imports; +} + +/** + * Resolve import path relative to current file + */ +function resolveImportPath(importPath, currentFilePath) { + // External imports (node_modules) - return as-is + if (!importPath.startsWith('.') && !importPath.startsWith('/')) { + return { isExternal: true, resolved: importPath }; + } + + const currentDir = path.dirname(currentFilePath); + let resolved = path.resolve(currentDir, importPath); + + // Try different extensions if no extension provided + if (!path.extname(resolved)) { + const extensions = ['.ts', '.js', '.tsx', '.jsx']; + for (const ext of extensions) { + const withExt = resolved + ext; + if (fs.existsSync(withExt)) { + resolved = withExt; + break; + } + } + + // Try index files + if (!fs.existsSync(resolved)) { + for (const ext of extensions) { + const indexFile = path.join(resolved, `index${ext}`); + if (fs.existsSync(indexFile)) { + resolved = indexFile; + break; + } + } + } + } + + return { isExternal: false, resolved }; +} + +/** + * Validate a single file + */ +function validateFile(filePath) { + const filePlatform = getPlatform(filePath); + + // Skip if not a platform-specific file + if (!filePlatform) { + return { valid: true, errors: [] }; + } + + const content = fs.readFileSync(filePath, 'utf-8'); + const imports = extractImports(content); + const errors = []; + + for (const importInfo of imports) { + const { isExternal, resolved } = resolveImportPath(importInfo.path, filePath); + + // External imports are always allowed + if (isExternal) { + continue; + } + + const importPlatform = getPlatform(resolved); + + // Universal files are always allowed + if (!importPlatform) { + continue; + } + + // Same platform is allowed + if (importPlatform === filePlatform) { + continue; + } + + // Different platform - ERROR + errors.push({ + line: importInfo.line, + importPath: importInfo.path, + filePlatform, + importPlatform, + message: `${getPlatformName(filePlatform)} file cannot import from ${getPlatformName(importPlatform)} file: "${importInfo.path}"` + }); + } + + return { valid: errors.length === 0, errors }; +} + +/** + * Recursively find all TypeScript/JavaScript files in a directory + */ +function findSourceFiles(dir, files = []) { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + + if (entry.isDirectory()) { + // Skip test directories and node_modules + if (!entry.name.startsWith('.') && + entry.name !== 'node_modules' && + entry.name !== 'dist' && + entry.name !== 'coverage' && + entry.name !== 'tests') { + findSourceFiles(fullPath, files); + } + } else if (entry.isFile()) { + // Only include TypeScript and JavaScript files, skip test files + if ((entry.name.endsWith('.ts') || entry.name.endsWith('.js')) && + !entry.name.endsWith('.spec.ts') && + !entry.name.endsWith('.test.ts') && + !entry.name.endsWith('.tests.ts') && + !entry.name.endsWith('.tests.js') && + !entry.name.endsWith('.test-d.ts') && + !entry.name.endsWith('.d.ts')) { + files.push(fullPath); + } + } + } + + return files; +} + +/** + * Main validation function + */ +function main() { + console.log('🔍 Validating platform isolation...\n'); + + const files = findSourceFiles(LIB_DIR); + const platformFiles = files.filter(f => getPlatform(f) !== null); + + console.log(`Found ${files.length} source files (${platformFiles.length} platform-specific)\n`); + + let totalErrors = 0; + const filesWithErrors = []; + + for (const file of platformFiles) { + const result = validateFile(file); + + if (!result.valid) { + totalErrors += result.errors.length; + filesWithErrors.push({ file, errors: result.errors }); + } + } + + if (totalErrors === 0) { + console.log('✅ All platform-specific files are properly isolated!\n'); + process.exit(0); + } else { + console.error(`❌ Found ${totalErrors} platform isolation violation(s) in ${filesWithErrors.length} file(s):\n`); + + for (const { file, errors } of filesWithErrors) { + const relativePath = path.relative(process.cwd(), file); + console.error(`\n📄 ${relativePath}`); + + for (const error of errors) { + console.error(` Line ${error.line}: ${error.message}`); + } + } + + console.error('\n'); + console.error('Platform isolation rules:'); + console.error(' - Browser files (.browser.ts) can only import from universal or other browser files'); + console.error(' - Node.js files (.node.ts) can only import from universal or other Node.js files'); + console.error(' - React Native files (.react_native.ts) can only import from universal or other React Native files'); + console.error(' - Universal files (no platform suffix) can be imported by any platform\n'); + + process.exit(1); + } +} + +// Run the validator +if (require.main === module) { + try { + main(); + } catch (error) { + console.error('❌ Validation failed with error:', error.message); + console.error(error.stack); + process.exit(1); + } +} + +module.exports = { validateFile, getPlatform, extractImports }; diff --git a/vitest.config.mts b/vitest.config.mts index 1bce36eb0..cc25cd3c2 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -31,5 +31,15 @@ export default defineConfig({ enabled: true, tsconfig: 'tsconfig.spec.json', }, + coverage: { + provider: 'istanbul', + include: ['lib/**/*.ts'], + exclude: [ + '**/tests/**', + '**/*.spec.ts', + '**/*.gen.ts', + '**/*d.ts', + ], + }, }, }); From 96e1b80c22d28b325fd99f5d3cf4b02b9fbac8ff Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Mon, 24 Nov 2025 18:37:29 +0600 Subject: [PATCH 02/40] multi platform --- docs/PLATFORM_ISOLATION.md | 126 +++++++++- .../default_dispatcher.browser.ts | 3 + .../request_handler.browser.ts | 3 + package.json | 1 + scripts/README.md | 61 +++++ scripts/test-validator.js | 89 +++++++ scripts/validate-platform-isolation.js | 225 +++++++++++++----- 7 files changed, 436 insertions(+), 72 deletions(-) create mode 100644 scripts/README.md create mode 100644 scripts/test-validator.js diff --git a/docs/PLATFORM_ISOLATION.md b/docs/PLATFORM_ISOLATION.md index e4a3ecb25..1ecb4fcaa 100644 --- a/docs/PLATFORM_ISOLATION.md +++ b/docs/PLATFORM_ISOLATION.md @@ -6,47 +6,116 @@ This project supports multiple runtime platforms (Browser, Node.js, React Native ## Naming Convention -Platform-specific files use a suffix pattern: +Platform-specific files can be identified in two ways: + +### 1. File Naming Convention (Single Platform) + +For files specific to a single platform, use a suffix pattern: - `.browser.ts` - Browser-specific implementation - `.node.ts` - Node.js-specific implementation - `.react_native.ts` - React Native-specific implementation - `.ts` (no suffix) - Universal code (works across all platforms) +### 2. Export Declaration (Multiple Platforms) + +For files that support multiple platforms but not all (e.g., Browser + React Native, but not Node.js), export a `__supportedPlatforms` array: + +```typescript +// lib/utils/web-features.ts +export const __supportedPlatforms = ['browser', 'react_native']; + +// Your code that works on both browser and react_native +export function getWindowSize() { + // Implementation that works on both platforms +} +``` + +Valid platform identifiers: `'browser'`, `'node'`, `'react_native'` + +### Priority + +If a file has both a platform suffix in its name AND a `__supportedPlatforms` export, the `__supportedPlatforms` export **takes priority**. This allows you to keep the `.browser.ts` naming convention while expanding support to additional platforms like React Native. + ## Import Rules Each platform-specific file can **only** import from: -1. **Universal files** (no platform suffix) -2. **Same-platform files** (matching platform suffix) +1. **Universal files** (no platform restrictions) +2. **Compatible platform files** (files that support ALL the required platforms) 3. **External packages** (node_modules) +A file is compatible if: +- It's universal (no platform restrictions) +- For single-platform files: The import supports at least that platform +- For multi-platform files: The import supports ALL of those platforms + +### Compatibility Examples + +**Single Platform File (`.browser.ts` or `__supportedPlatforms = ['browser']`)** +- ✅ Can import from: universal files, `.browser.ts` files, files with `['browser']` or `['browser', 'react_native']` +- ❌ Cannot import from: `.node.ts` files, files with `['node']` or `['react_native']` only + +**Multi-Platform File (`__supportedPlatforms = ['browser', 'react_native']`)** +- ✅ Can import from: universal files, files with exactly `['browser', 'react_native']` +- ❌ Cannot import from: `.browser.ts` (browser only), `.react_native.ts` (react_native only), `.node.ts` +- **Why?** A file supporting both platforms needs imports that work in BOTH environments + ### Examples ✅ **Valid Imports** ```typescript -// In lib/index.browser.ts +// In lib/index.browser.ts (Browser platform only) import { Config } from './shared_types'; // ✅ Universal file -import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; // ✅ Same platform +import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; // ✅ browser + react_native (supports browser) import { uuid } from 'uuid'; // ✅ External package ``` ```typescript -// In lib/index.node.ts +// In lib/index.node.ts (Node platform only) import { Config } from './shared_types'; // ✅ Universal file import { NodeRequestHandler } from './utils/http_request_handler/request_handler.node'; // ✅ Same platform ``` +```typescript +// In lib/index.react_native.ts (React Native platform only) +import { Config } from './shared_types'; // ✅ Universal file + +// If web-features.ts has: __supportedPlatforms = ['browser', 'react_native'] +import { getWindowSize } from './utils/web-features'; // ✅ Compatible (supports react_native) +``` + +```typescript +// In lib/utils/web-api.ts +// export const __supportedPlatforms = ['browser', 'react_native']; + +import { Config } from './shared_types'; // ✅ Universal file + +// If dom-helpers.ts has: __supportedPlatforms = ['browser', 'react_native'] +import { helpers } from './dom-helpers'; // ✅ Compatible (supports BOTH browser and react_native) +``` + ❌ **Invalid Imports** ```typescript -// In lib/index.browser.ts -import { NodeRequestHandler } from './utils/http_request_handler/request_handler.node'; // ❌ Different platform +// In lib/index.browser.ts (Browser platform only) +import { NodeRequestHandler } from './utils/http_request_handler/request_handler.node'; // ❌ Node-only file +``` + +```typescript +// In lib/index.node.ts (Node platform only) +// If web-features.ts has: __supportedPlatforms = ['browser', 'react_native'] +import { getWindowSize } from './utils/web-features'; // ❌ Not compatible with Node ``` ```typescript -// In lib/index.node.ts -import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; // ❌ Different platform +// In lib/utils/web-api.ts +// export const __supportedPlatforms = ['browser', 'react_native']; + +// If helper.browser.ts is browser-only (no __supportedPlatforms export) +import { helper } from './helper.browser'; // ❌ Browser-only, doesn't support react_native + +// This file needs imports that work in BOTH browser AND react_native ``` ## Automatic Validation @@ -95,11 +164,13 @@ If platform isolation is violated, the build will fail with a detailed error mes When creating new platform-specific implementations: +### Single Platform + 1. Name the file with the appropriate platform suffix (e.g., `my-feature.browser.ts`) 2. Only import from universal or same-platform files 3. Create a universal factory or interface if multiple platforms need different implementations -### Example: Creating a Platform-Specific Feature +**Example:** ```typescript // lib/features/my-feature.ts (universal interface) @@ -130,6 +201,39 @@ import { NodeMyFeature } from './my-feature.node'; export const createMyFeature = () => new NodeMyFeature(); ``` +### Multiple Platforms (But Not All) + +For code that works on multiple platforms but not all, use the `__supportedPlatforms` export: + +**Example: Browser + React Native only** + +```typescript +// lib/utils/dom-helpers.ts +export const __supportedPlatforms = ['browser', 'react_native']; + +// This code works on both browser and react_native, but not node +export function getElementById(id: string): Element | null { + if (typeof document !== 'undefined') { + return document.getElementById(id); + } + // React Native polyfill or alternative + return null; +} +``` + +**Example: Node + React Native only** + +```typescript +// lib/utils/native-crypto.ts +export const __supportedPlatforms = ['node', 'react_native']; + +import crypto from 'crypto'; // Available in both Node and React Native + +export function generateHash(data: string): string { + return crypto.createHash('sha256').update(data).digest('hex'); +} +``` + ## Troubleshooting If you encounter a platform isolation error: diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts index d38d266aa..10b7fc1c6 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +// This implementation works in both browser and react_native environments +export const __supportedPlatforms = ['browser', 'react_native']; + import { BrowserRequestHandler } from "../../utils/http_request_handler/request_handler.browser"; import { EventDispatcher } from './event_dispatcher'; import { DefaultEventDispatcher } from './default_dispatcher'; diff --git a/lib/utils/http_request_handler/request_handler.browser.ts b/lib/utils/http_request_handler/request_handler.browser.ts index 340dcca33..784245b3e 100644 --- a/lib/utils/http_request_handler/request_handler.browser.ts +++ b/lib/utils/http_request_handler/request_handler.browser.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +// This implementation works in both browser and react_native environments +export const __supportedPlatforms = ['browser', 'react_native']; + import { AbortableRequest, Headers, RequestHandler, Response } from './http'; import { LoggerFacade, LogLevel } from '../../logging/logger'; import { REQUEST_TIMEOUT_MS } from '../enums'; diff --git a/package.json b/package.json index e52856f45..d1e50ca13 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "clean:win": "(if exist dist rd /s/q dist)", "lint": "tsc --noEmit && eslint 'lib/**/*.js' 'lib/**/*.ts'", "validate-platform-isolation": "node scripts/validate-platform-isolation.js", + "test-platform-isolation": "node scripts/test-validator.js", "test-vitest": "vitest run", "test-mocha": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register -r tsconfig-paths/register -r lib/tests/exit_on_unhandled_rejection.js 'lib/**/*.tests.ts' 'lib/**/*.tests.js'", "test": "npm run test-mocha && npm run test-vitest", diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 000000000..b8132e953 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,61 @@ +# Scripts + +This directory contains build and validation scripts for the JavaScript SDK. + +## validate-platform-isolation.js + +Validates that platform-specific code is properly isolated to prevent runtime errors when building for different platforms (Browser, Node.js, React Native). + +### Usage + +```bash +# Run manually +node scripts/validate-platform-isolation.js + +# Run via npm script +npm run validate-platform-isolation + +# Runs automatically during build +npm run build +``` + +### How It Works + +The script: +1. Scans all TypeScript/JavaScript files in the `lib/` directory +2. Identifies platform-specific files by: + - Naming convention (`.browser.ts`, `.node.ts`, `.react_native.ts`) + - `__supportedPlatforms` export for multi-platform files +3. Parses import statements (ES6 imports, require(), dynamic imports) +4. Validates that each import is compatible with the file's platform +5. Fails with exit code 1 if any violations are found + +### Exit Codes + +- `0`: All platform-specific files are properly isolated +- `1`: Violations found or script error + +## test-validator.js + +Comprehensive test suite for the platform isolation validator. Documents and validates all compatibility rules. + +### Usage + +```bash +# Run via npm script +npm run test-platform-isolation + +# Or run directly +node scripts/test-validator.js +``` + +Tests cover: +- Universal imports (always compatible) +- Single platform file imports +- Single platform importing from multi-platform files +- Multi-platform file imports (strictest rules) +- `__supportedPlatforms` extraction + +--- + +See [../docs/PLATFORM_ISOLATION.md](../docs/PLATFORM_ISOLATION.md) for detailed documentation on platform isolation rules. diff --git a/scripts/test-validator.js b/scripts/test-validator.js new file mode 100644 index 000000000..32cef60b2 --- /dev/null +++ b/scripts/test-validator.js @@ -0,0 +1,89 @@ +#!/usr/bin/env node + +/** + * Comprehensive test suite for platform isolation validator + * + * This test documents and validates all the compatibility rules + */ + +const assert = require('assert'); +const validator = require('./validate-platform-isolation.js'); + +let passed = 0; +let failed = 0; + +function test(description, actual, expected) { + try { + assert.strictEqual(actual, expected); + console.log(`✅ ${description}`); + passed++; + } catch (e) { + console.log(`❌ ${description}`); + console.log(` Expected: ${expected}, Got: ${actual}`); + failed++; + } +} + +console.log('Platform Isolation Validator - Comprehensive Test Suite\n'); +console.log('=' .repeat(70)); + +console.log('\n1. UNIVERSAL IMPORTS (always compatible)'); +console.log('-'.repeat(70)); +test('Browser file can import universal', + validator.isPlatformCompatible('browser', null), true); +test('Node file can import universal', + validator.isPlatformCompatible('node', null), true); +test('Multi-platform file can import universal', + validator.isPlatformCompatible(['browser', 'react_native'], null), true); + +console.log('\n2. SINGLE PLATFORM FILES'); +console.log('-'.repeat(70)); +test('Browser file can import from browser file', + validator.isPlatformCompatible('browser', 'browser'), true); +test('Browser file CANNOT import from node file', + validator.isPlatformCompatible('browser', 'node'), false); +test('Node file can import from node file', + validator.isPlatformCompatible('node', 'node'), true); +test('React Native file can import from react_native file', + validator.isPlatformCompatible('react_native', 'react_native'), true); + +console.log('\n3. SINGLE PLATFORM IMPORTING FROM MULTI-PLATFORM'); +console.log('-'.repeat(70)); +test('Browser file CAN import from [browser, react_native] file', + validator.isPlatformCompatible('browser', ['browser', 'react_native']), true); +test('React Native file CAN import from [browser, react_native] file', + validator.isPlatformCompatible('react_native', ['browser', 'react_native']), true); +test('Node file CANNOT import from [browser, react_native] file', + validator.isPlatformCompatible('node', ['browser', 'react_native']), false); + +console.log('\n4. MULTI-PLATFORM FILES (strictest rules)'); +console.log('-'.repeat(70)); +test('[browser, react_native] file CAN import from [browser, react_native] file', + validator.isPlatformCompatible(['browser', 'react_native'], ['browser', 'react_native']), true); +test('[browser, react_native] file CANNOT import from browser-only file', + validator.isPlatformCompatible(['browser', 'react_native'], 'browser'), false); +test('[browser, react_native] file CANNOT import from react_native-only file', + validator.isPlatformCompatible(['browser', 'react_native'], 'react_native'), false); +test('[browser, react_native] file CANNOT import from node file', + validator.isPlatformCompatible(['browser', 'react_native'], 'node'), false); + +console.log('\n5. SUPPORTED PLATFORMS EXTRACTION'); +console.log('-'.repeat(70)); +const testExport1 = `export const __supportedPlatforms = ['browser', 'react_native'];`; +const platforms1 = validator.extractSupportedPlatforms(testExport1); +test('Extract __supportedPlatforms array', + JSON.stringify(platforms1), JSON.stringify(['browser', 'react_native'])); + +const testExport2 = `export const __supportedPlatforms: string[] = ["browser", "node"];`; +const platforms2 = validator.extractSupportedPlatforms(testExport2); +test('Extract __supportedPlatforms with type annotation', + JSON.stringify(platforms2), JSON.stringify(['browser', 'node'])); + +console.log('\n' + '='.repeat(70)); +console.log(`\nResults: ${passed} passed, ${failed} failed`); + +if (failed > 0) { + process.exit(1); +} + +console.log('\n✅ All tests passed!'); diff --git a/scripts/validate-platform-isolation.js b/scripts/validate-platform-isolation.js index 47c0f8f20..4fe6b0aa4 100644 --- a/scripts/validate-platform-isolation.js +++ b/scripts/validate-platform-isolation.js @@ -4,20 +4,16 @@ * Platform Isolation Validator * * This script ensures that platform-specific entry points only import - * from universal or same-platform files. + * from universal or compatible platform files. + * + * Platform Detection: + * 1. Files with naming convention: .browser.ts, .node.ts, .react_native.ts + * 2. Files exporting __supportedPlatforms array (for multi-platform support) * * Rules: - * - Files ending with .browser.ts can only import from: - * - Universal files (no platform suffix) - * - Other .browser.ts files - * - External packages (node_modules) - * - Files ending with .node.ts can only import from: - * - Universal files (no platform suffix) - * - Other .node.ts files - * - External packages (node_modules) - * - Files ending with .react_native.ts can only import from: - * - Universal files (no platform suffix) - * - Other .react_native.ts files + * - Platform-specific files can only import from: + * - Universal files (no platform restrictions) + * - Files supporting the same platform * - External packages (node_modules) * * Usage: node scripts/validate-platform-isolation.js @@ -29,10 +25,13 @@ const path = require('path'); const PLATFORMS = ['browser', 'node', 'react_native']; const LIB_DIR = path.join(__dirname, '..', 'lib'); +// Cache for __supportedPlatforms exports +const platformCache = new Map(); + /** - * Extracts the platform from a filename + * Extracts the platform from a filename using naming convention */ -function getPlatform(filename) { +function getPlatformFromFilename(filename) { for (const platform of PLATFORMS) { if (filename.includes(`.${platform}.`)) { return platform; @@ -41,6 +40,75 @@ function getPlatform(filename) { return null; } +/** + * Extracts __supportedPlatforms array from file content + */ +function extractSupportedPlatforms(content) { + // Match: export const __supportedPlatforms = ['browser', 'react_native']; + // or: export const __supportedPlatforms: string[] = ['browser', 'react_native']; + const regex = /export\s+(?:const|let|var)\s+__supportedPlatforms\s*(?::\s*[^=]+)?\s*=\s*\[([^\]]+)\]/; + const match = content.match(regex); + + if (!match) { + return null; + } + + // Extract platform names from the array + const platformsStr = match[1]; + const platforms = []; + + for (const platform of PLATFORMS) { + if (platformsStr.includes(`'${platform}'`) || platformsStr.includes(`"${platform}"`)) { + platforms.push(platform); + } + } + + return platforms.length > 0 ? platforms : null; +} + +/** + * Gets the supported platforms for a file (with caching) + * Returns: + * - string (single platform from filename) + * - string[] (multiple platforms from __supportedPlatforms) + * - null (universal, no restrictions) + */ +function getSupportedPlatforms(filePath) { + // Check cache first + if (platformCache.has(filePath)) { + return platformCache.get(filePath); + } + + let result; + + // Check for __supportedPlatforms export first (takes priority) + try { + const content = fs.readFileSync(filePath, 'utf-8'); + const supportedPlatforms = extractSupportedPlatforms(content); + + if (supportedPlatforms) { + result = supportedPlatforms; + platformCache.set(filePath, result); + return result; + } + } catch (error) { + // If file doesn't exist or can't be read, try filename convention + } + + // Check filename convention + const platformFromFilename = getPlatformFromFilename(filePath); + if (platformFromFilename) { + result = platformFromFilename; + platformCache.set(filePath, result); + return result; + } + + // Universal file + result = null; + platformCache.set(filePath, result); + return result; +} + /** * Gets a human-readable platform name */ @@ -53,6 +121,38 @@ function getPlatformName(platform) { return names[platform] || platform; } +/** + * Formats platform info for display + */ +function formatPlatforms(platforms) { + if (!platforms) return 'Universal'; + if (typeof platforms === 'string') return getPlatformName(platforms); + return platforms.map(p => getPlatformName(p)).join(' + '); +} + +/** + * Checks if a platform is compatible with target platforms + * + * Rules: + * - Universal imports (no platform restrictions) are always compatible + * - If the file has multiple platforms, the import must support ALL of them + * - If the file has a single platform, the import must support at least that one + */ +function isPlatformCompatible(filePlatforms, importPlatforms) { + // Universal imports are always compatible + if (!importPlatforms) { + return true; + } + + // Convert to arrays for consistent handling + const fileArray = Array.isArray(filePlatforms) ? filePlatforms : [filePlatforms]; + const importArray = Array.isArray(importPlatforms) ? importPlatforms : [importPlatforms]; + + // The import must support ALL platforms that the file supports + // Check if every platform in fileArray is present in importArray + return fileArray.every(fp => importArray.includes(fp)); +} + /** * Extract import statements from a TypeScript/JavaScript file */ @@ -93,29 +193,32 @@ function resolveImportPath(importPath, currentFilePath) { const currentDir = path.dirname(currentFilePath); let resolved = path.resolve(currentDir, importPath); - // Try different extensions if no extension provided - if (!path.extname(resolved)) { - const extensions = ['.ts', '.js', '.tsx', '.jsx']; - for (const ext of extensions) { - const withExt = resolved + ext; - if (fs.existsSync(withExt)) { - resolved = withExt; - break; - } + // Check if file exists as-is + if (fs.existsSync(resolved)) { + return { isExternal: false, resolved }; + } + + // Try different extensions + const extensions = ['.ts', '.js', '.tsx', '.jsx']; + for (const ext of extensions) { + const withExt = resolved + ext; + if (fs.existsSync(withExt)) { + resolved = withExt; + return { isExternal: false, resolved }; } - - // Try index files - if (!fs.existsSync(resolved)) { - for (const ext of extensions) { - const indexFile = path.join(resolved, `index${ext}`); - if (fs.existsSync(indexFile)) { - resolved = indexFile; - break; - } - } + } + + // Try index files + for (const ext of extensions) { + const indexFile = path.join(resolved, `index${ext}`); + if (fs.existsSync(indexFile)) { + resolved = indexFile; + return { isExternal: false, resolved }; } } + // Return the resolved path even if it doesn't exist + // (getSupportedPlatforms will handle it) return { isExternal: false, resolved }; } @@ -123,10 +226,10 @@ function resolveImportPath(importPath, currentFilePath) { * Validate a single file */ function validateFile(filePath) { - const filePlatform = getPlatform(filePath); + const filePlatforms = getSupportedPlatforms(filePath); - // Skip if not a platform-specific file - if (!filePlatform) { + // Skip if universal file + if (!filePlatforms) { return { valid: true, errors: [] }; } @@ -142,26 +245,18 @@ function validateFile(filePath) { continue; } - const importPlatform = getPlatform(resolved); + const importPlatforms = getSupportedPlatforms(resolved); - // Universal files are always allowed - if (!importPlatform) { - continue; + // Check compatibility + if (!isPlatformCompatible(filePlatforms, importPlatforms)) { + errors.push({ + line: importInfo.line, + importPath: importInfo.path, + filePlatforms, + importPlatforms, + message: `${formatPlatforms(filePlatforms)} file cannot import from ${formatPlatforms(importPlatforms)}-only file: "${importInfo.path}"` + }); } - - // Same platform is allowed - if (importPlatform === filePlatform) { - continue; - } - - // Different platform - ERROR - errors.push({ - line: importInfo.line, - importPath: importInfo.path, - filePlatform, - importPlatform, - message: `${getPlatformName(filePlatform)} file cannot import from ${getPlatformName(importPlatform)} file: "${importInfo.path}"` - }); } return { valid: errors.length === 0, errors }; @@ -209,7 +304,7 @@ function main() { console.log('🔍 Validating platform isolation...\n'); const files = findSourceFiles(LIB_DIR); - const platformFiles = files.filter(f => getPlatform(f) !== null); + const platformFiles = files.filter(f => getSupportedPlatforms(f) !== null); console.log(`Found ${files.length} source files (${platformFiles.length} platform-specific)\n`); @@ -233,7 +328,8 @@ function main() { for (const { file, errors } of filesWithErrors) { const relativePath = path.relative(process.cwd(), file); - console.error(`\n📄 ${relativePath}`); + const filePlatforms = getSupportedPlatforms(file); + console.error(`\n📄 ${relativePath} [${formatPlatforms(filePlatforms)}]`); for (const error of errors) { console.error(` Line ${error.line}: ${error.message}`); @@ -242,10 +338,11 @@ function main() { console.error('\n'); console.error('Platform isolation rules:'); - console.error(' - Browser files (.browser.ts) can only import from universal or other browser files'); - console.error(' - Node.js files (.node.ts) can only import from universal or other Node.js files'); - console.error(' - React Native files (.react_native.ts) can only import from universal or other React Native files'); - console.error(' - Universal files (no platform suffix) can be imported by any platform\n'); + console.error(' - Platform-specific files can only import from universal or compatible platform files'); + console.error(' - Specify platforms using:'); + console.error(' 1. Naming convention: .browser.ts, .node.ts, .react_native.ts'); + console.error(' 2. Export __supportedPlatforms array: export const __supportedPlatforms = [\'browser\', \'react_native\'];'); + console.error(' - Universal files (no platform restrictions) can be imported by any platform\n'); process.exit(1); } @@ -262,4 +359,10 @@ if (require.main === module) { } } -module.exports = { validateFile, getPlatform, extractImports }; +module.exports = { + validateFile, + getSupportedPlatforms, + extractImports, + extractSupportedPlatforms, + isPlatformCompatible +}; From 3759938a466b3563e0f28b243978e2c9489e63da Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Mon, 24 Nov 2025 19:02:20 +0600 Subject: [PATCH 03/40] mandatory export --- lib/client_factory.ts | 2 + lib/common_exports.ts | 2 + lib/core/audience_evaluator/index.ts | 2 + .../odp_segment_condition_evaluator/index.ts | 2 + lib/core/bucketer/bucket_value_generator.ts | 2 + lib/core/bucketer/index.ts | 3 + lib/core/condition_tree_evaluator/index.ts | 2 + .../index.ts | 3 + lib/core/decision/index.ts | 3 + lib/core/decision_service/cmab/cmab_client.ts | 2 + .../decision_service/cmab/cmab_service.ts | 3 + lib/core/decision_service/index.ts | 3 + lib/error/error_handler.ts | 2 + lib/error/error_notifier.ts | 2 + lib/error/error_notifier_factory.ts | 2 + lib/error/error_reporter.ts | 2 + lib/error/optimizly_error.ts | 2 + .../batch_event_processor.react_native.ts | 2 + lib/event_processor/batch_event_processor.ts | 2 + .../event_builder/log_event.ts | 2 + .../event_builder/user_event.ts | 3 + .../default_dispatcher.node.ts | 2 + .../event_dispatcher/default_dispatcher.ts | 2 + .../event_dispatcher/event_dispatcher.ts | 2 + .../event_dispatcher_factory.ts | 2 + .../send_beacon_dispatcher.browser.ts | 2 + lib/event_processor/event_processor.ts | 2 + .../event_processor_factory.browser.ts | 3 + .../event_processor_factory.node.ts | 3 + .../event_processor_factory.react_native.ts | 3 + .../event_processor_factory.ts | 2 + .../event_processor_factory.universal.ts | 3 + lib/event_processor/event_store.ts | 3 + .../forwarding_event_processor.ts | 2 + lib/export_types.ts | 2 + lib/feature_toggle.ts | 2 + lib/index.browser.ts | 3 + lib/index.browser.umdtests.js | 2 + lib/index.node.ts | 3 + lib/index.react_native.ts | 3 + lib/index.universal.ts | 2 + lib/logging/logger.ts | 2 + lib/logging/logger_factory.ts | 2 + lib/message/error_message.ts | 2 + lib/message/log_message.ts | 2 + lib/message/message_resolver.ts | 2 + lib/notification_center/index.ts | 2 + lib/notification_center/type.ts | 3 + lib/odp/constant.ts | 2 + lib/odp/event_manager/odp_event.ts | 2 + .../event_manager/odp_event_api_manager.ts | 2 + lib/odp/event_manager/odp_event_manager.ts | 3 + lib/odp/odp_config.ts | 2 + lib/odp/odp_manager.ts | 2 + lib/odp/odp_manager_factory.browser.ts | 2 + lib/odp/odp_manager_factory.node.ts | 2 + lib/odp/odp_manager_factory.react_native.ts | 2 + lib/odp/odp_manager_factory.ts | 2 + lib/odp/odp_manager_factory.universal.ts | 2 + lib/odp/odp_types.ts | 2 + .../segment_manager/odp_response_schema.ts | 3 + .../odp_segment_api_manager.ts | 3 + .../segment_manager/odp_segment_manager.ts | 2 + .../optimizely_segment_option.ts | 2 + lib/odp/ua_parser/ua_parser.ts | 2 + lib/odp/ua_parser/user_agent_info.ts | 2 + lib/odp/ua_parser/user_agent_parser.ts | 2 + lib/optimizely/index.ts | 3 + lib/optimizely_decision/index.ts | 2 + lib/optimizely_user_context/index.ts | 3 + lib/platform_support.ts | 56 +++++ .../config_manager_factory.browser.ts | 2 + .../config_manager_factory.node.ts | 2 + .../config_manager_factory.react_native.ts | 2 + lib/project_config/config_manager_factory.ts | 2 + .../config_manager_factory.universal.ts | 2 + lib/project_config/constant.ts | 2 + lib/project_config/datafile_manager.ts | 2 + lib/project_config/optimizely_config.ts | 3 + .../polling_datafile_manager.ts | 3 + lib/project_config/project_config.ts | 3 + lib/project_config/project_config_manager.ts | 3 + lib/project_config/project_config_schema.ts | 2 + lib/service.ts | 2 + lib/shared_types.ts | 2 + lib/utils/attributes_validator/index.ts | 2 + .../cache/async_storage_cache.react_native.ts | 2 + lib/utils/cache/cache.ts | 3 + lib/utils/cache/in_memory_lru_cache.ts | 2 + .../cache/local_storage_cache.browser.ts | 2 + lib/utils/cache/store.ts | 2 + lib/utils/cache/store_validator.ts | 2 + lib/utils/config_validator/index.ts | 3 + lib/utils/enums/index.ts | 2 + lib/utils/event_emitter/event_emitter.ts | 2 + lib/utils/event_tag_utils/index.ts | 3 + lib/utils/event_tags_validator/index.ts | 3 + lib/utils/executor/backoff_retry_runner.ts | 2 + lib/utils/executor/serial_runner.ts | 2 + lib/utils/fns/index.ts | 2 + lib/utils/http_request_handler/http.ts | 2 + lib/utils/http_request_handler/http_util.ts | 2 + .../request_handler.node.ts | 3 + .../request_handler_validator.ts | 2 + lib/utils/id_generator/index.ts | 2 + .../async-storage.ts | 2 + lib/utils/json_schema_validator/index.ts | 3 + lib/utils/microtask/index.ts | 2 + lib/utils/promise/operation_value.ts | 2 + lib/utils/promise/resolvablePromise.ts | 2 + lib/utils/repeater/repeater.ts | 3 + lib/utils/semantic_version/index.ts | 3 + lib/utils/string_value_validator/index.ts | 2 + lib/utils/type.ts | 2 + .../user_profile_service_validator/index.ts | 2 + lib/vuid/vuid.ts | 2 + lib/vuid/vuid_manager.ts | 2 + lib/vuid/vuid_manager_factory.browser.ts | 2 + lib/vuid/vuid_manager_factory.node.ts | 2 + lib/vuid/vuid_manager_factory.react_native.ts | 2 + lib/vuid/vuid_manager_factory.ts | 2 + package.json | 1 + scripts/add-platform-exports.js | 162 +++++++++++++ scripts/test-validator.js | 25 +- scripts/validate-platform-isolation.js | 224 +++++++++++++----- 125 files changed, 666 insertions(+), 74 deletions(-) create mode 100644 lib/platform_support.ts create mode 100644 scripts/add-platform-exports.js diff --git a/lib/client_factory.ts b/lib/client_factory.ts index 3be99b554..1ae8d9112 100644 --- a/lib/client_factory.ts +++ b/lib/client_factory.ts @@ -29,6 +29,8 @@ import { InMemoryLruCache } from "./utils/cache/in_memory_lru_cache"; import { transformCache, CacheWithRemove } from "./utils/cache/cache"; import { ConstantBackoff } from "./utils/repeater/repeater"; +export const __supportedPlatforms = ['__universal__'] as const; + export type OptimizelyFactoryConfig = Config & { requestHandler: RequestHandler; } diff --git a/lib/common_exports.ts b/lib/common_exports.ts index 801fb7728..8ce7af0e7 100644 --- a/lib/common_exports.ts +++ b/lib/common_exports.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export const __supportedPlatforms = ['__universal__'] as const; + export { createStaticProjectConfigManager } from './project_config/config_manager_factory'; export { LogLevel } from './logging/logger'; diff --git a/lib/core/audience_evaluator/index.ts b/lib/core/audience_evaluator/index.ts index e2b3bce0a..71d54b9c3 100644 --- a/lib/core/audience_evaluator/index.ts +++ b/lib/core/audience_evaluator/index.ts @@ -21,6 +21,8 @@ import { CONDITION_EVALUATOR_ERROR, UNKNOWN_CONDITION_TYPE } from 'error_message import { AUDIENCE_EVALUATION_RESULT, EVALUATING_AUDIENCE} from 'log_message'; import { LoggerFacade } from '../../logging/logger'; +export const __supportedPlatforms = ['__universal__'] as const; + export class AudienceEvaluator { private logger?: LoggerFacade; diff --git a/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts b/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts index 7380c9269..1bd08ee17 100644 --- a/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts +++ b/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts @@ -17,6 +17,8 @@ import { UNKNOWN_MATCH_TYPE } from 'error_message'; import { LoggerFacade } from '../../../logging/logger'; import { Condition, OptimizelyUserContext } from '../../../shared_types'; +export const __supportedPlatforms = ['__universal__'] as const; + const QUALIFIED_MATCH_TYPE = 'qualified'; const MATCH_TYPES = [ diff --git a/lib/core/bucketer/bucket_value_generator.ts b/lib/core/bucketer/bucket_value_generator.ts index c5f85303b..05f2f19b2 100644 --- a/lib/core/bucketer/bucket_value_generator.ts +++ b/lib/core/bucketer/bucket_value_generator.ts @@ -17,6 +17,8 @@ import murmurhash from 'murmurhash'; import { INVALID_BUCKETING_ID } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; +export const __supportedPlatforms = ['__universal__'] as const; + const HASH_SEED = 1; const MAX_HASH_VALUE = Math.pow(2, 32); const MAX_TRAFFIC_VALUE = 10000; diff --git a/lib/core/bucketer/index.ts b/lib/core/bucketer/index.ts index e31c8df4b..610d868a2 100644 --- a/lib/core/bucketer/index.ts +++ b/lib/core/bucketer/index.ts @@ -19,6 +19,9 @@ */ import { LoggerFacade } from '../../logging/logger'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + DecisionResponse, BucketerParams, TrafficAllocation, diff --git a/lib/core/condition_tree_evaluator/index.ts b/lib/core/condition_tree_evaluator/index.ts index 7b0c8df9d..2f73231e5 100644 --- a/lib/core/condition_tree_evaluator/index.ts +++ b/lib/core/condition_tree_evaluator/index.ts @@ -14,6 +14,8 @@ * limitations under the License. * ***************************************************************************/ +export const __supportedPlatforms = ['__universal__'] as const; + const AND_CONDITION = 'and'; const OR_CONDITION = 'or'; const NOT_CONDITION = 'not'; diff --git a/lib/core/custom_attribute_condition_evaluator/index.ts b/lib/core/custom_attribute_condition_evaluator/index.ts index 797a7d4e0..cbb3606de 100644 --- a/lib/core/custom_attribute_condition_evaluator/index.ts +++ b/lib/core/custom_attribute_condition_evaluator/index.ts @@ -18,6 +18,9 @@ import { Condition, OptimizelyUserContext } from '../../shared_types'; import fns from '../../utils/fns'; import { compareVersion } from '../../utils/semantic_version'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + MISSING_ATTRIBUTE_VALUE, UNEXPECTED_TYPE_NULL, } from 'log_message'; diff --git a/lib/core/decision/index.ts b/lib/core/decision/index.ts index 27fd1c734..5db330468 100644 --- a/lib/core/decision/index.ts +++ b/lib/core/decision/index.ts @@ -21,6 +21,9 @@ import { DecisionObj } from '../decision_service'; * @param {DecisionObj} decisionObj Object representing decision * @returns {string} Experiment key or empty string if experiment is null */ +export const __supportedPlatforms = ['__universal__'] as const; + + export function getExperimentKey(decisionObj: DecisionObj): string { return decisionObj.experiment?.key ?? ''; } diff --git a/lib/core/decision_service/cmab/cmab_client.ts b/lib/core/decision_service/cmab/cmab_client.ts index a6925713a..a9a8c576f 100644 --- a/lib/core/decision_service/cmab/cmab_client.ts +++ b/lib/core/decision_service/cmab/cmab_client.ts @@ -24,6 +24,8 @@ import { isSuccessStatusCode } from "../../../utils/http_request_handler/http_ut import { BackoffController } from "../../../utils/repeater/repeater"; import { Producer } from "../../../utils/type"; +export const __supportedPlatforms = ['__universal__'] as const; + export interface CmabClient { fetchDecision( ruleId: string, diff --git a/lib/core/decision_service/cmab/cmab_service.ts b/lib/core/decision_service/cmab/cmab_service.ts index 1963df613..66f0ec0bf 100644 --- a/lib/core/decision_service/cmab/cmab_service.ts +++ b/lib/core/decision_service/cmab/cmab_service.ts @@ -25,6 +25,9 @@ import murmurhash from "murmurhash"; import { DecideOptionsMap } from ".."; import { SerialRunner } from "../../../utils/executor/serial_runner"; import { +export const __supportedPlatforms = ['__universal__'] as const; + + CMAB_CACHE_ATTRIBUTES_MISMATCH, CMAB_CACHE_HIT, CMAB_CACHE_MISS, diff --git a/lib/core/decision_service/index.ts b/lib/core/decision_service/index.ts index 33fd85eb1..a807b3c1f 100644 --- a/lib/core/decision_service/index.ts +++ b/lib/core/decision_service/index.ts @@ -16,6 +16,9 @@ import { LoggerFacade } from '../../logging/logger' import { bucket } from '../bucketer'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + AUDIENCE_EVALUATION_TYPES, CONTROL_ATTRIBUTES, DECISION_SOURCES, diff --git a/lib/error/error_handler.ts b/lib/error/error_handler.ts index 4a772c71c..7ad1402e8 100644 --- a/lib/error/error_handler.ts +++ b/lib/error/error_handler.ts @@ -17,6 +17,8 @@ * @export * @interface ErrorHandler */ +export const __supportedPlatforms = ['__universal__'] as const; + export interface ErrorHandler { /** * @param {Error} exception diff --git a/lib/error/error_notifier.ts b/lib/error/error_notifier.ts index 174c163e2..30ca8ec0e 100644 --- a/lib/error/error_notifier.ts +++ b/lib/error/error_notifier.ts @@ -17,6 +17,8 @@ import { MessageResolver } from "../message/message_resolver"; import { ErrorHandler } from "./error_handler"; import { OptimizelyError } from "./optimizly_error"; +export const __supportedPlatforms = ['__universal__'] as const; + export interface ErrorNotifier { notify(error: Error): void; child(name: string): ErrorNotifier; diff --git a/lib/error/error_notifier_factory.ts b/lib/error/error_notifier_factory.ts index 994564f1a..fac284880 100644 --- a/lib/error/error_notifier_factory.ts +++ b/lib/error/error_notifier_factory.ts @@ -18,6 +18,8 @@ import { Maybe } from "../utils/type"; import { ErrorHandler } from "./error_handler"; import { DefaultErrorNotifier } from "./error_notifier"; +export const __supportedPlatforms = ['__universal__'] as const; + export const INVALID_ERROR_HANDLER = 'Invalid error handler'; const errorNotifierSymbol = Symbol(); diff --git a/lib/error/error_reporter.ts b/lib/error/error_reporter.ts index 130527928..9be3873c9 100644 --- a/lib/error/error_reporter.ts +++ b/lib/error/error_reporter.ts @@ -17,6 +17,8 @@ import { LoggerFacade } from "../logging/logger"; import { ErrorNotifier } from "./error_notifier"; import { OptimizelyError } from "./optimizly_error"; +export const __supportedPlatforms = ['__universal__'] as const; + export class ErrorReporter { private logger?: LoggerFacade; private errorNotifier?: ErrorNotifier; diff --git a/lib/error/optimizly_error.ts b/lib/error/optimizly_error.ts index 76a07511a..841b9567e 100644 --- a/lib/error/optimizly_error.ts +++ b/lib/error/optimizly_error.ts @@ -16,6 +16,8 @@ import { MessageResolver } from "../message/message_resolver"; import { sprintf } from "../utils/fns"; +export const __supportedPlatforms = ['__universal__'] as const; + export class OptimizelyError extends Error { baseMessage: string; params: any[]; diff --git a/lib/event_processor/batch_event_processor.react_native.ts b/lib/event_processor/batch_event_processor.react_native.ts index 28741380a..5fedae039 100644 --- a/lib/event_processor/batch_event_processor.react_native.ts +++ b/lib/event_processor/batch_event_processor.react_native.ts @@ -19,6 +19,8 @@ import { NetInfoState, addEventListener } from '@react-native-community/netinfo' import { BatchEventProcessor, BatchEventProcessorConfig } from './batch_event_processor'; import { Fn } from '../utils/type'; +export const __supportedPlatforms = ['react_native'] as const; + export class ReactNativeNetInfoEventProcessor extends BatchEventProcessor { private isInternetReachable = true; private unsubscribeNetInfo?: Fn; diff --git a/lib/event_processor/batch_event_processor.ts b/lib/event_processor/batch_event_processor.ts index ba9931f06..4e17fbac9 100644 --- a/lib/event_processor/batch_event_processor.ts +++ b/lib/event_processor/batch_event_processor.ts @@ -32,6 +32,8 @@ import { OptimizelyError } from "../error/optimizly_error"; import { sprintf } from "../utils/fns"; import { SERVICE_STOPPED_BEFORE_RUNNING } from "../service"; +export const __supportedPlatforms = ['__universal__'] as const; + export const DEFAULT_MIN_BACKOFF = 1000; export const DEFAULT_MAX_BACKOFF = 32000; export const MAX_EVENTS_IN_STORE = 500; diff --git a/lib/event_processor/event_builder/log_event.ts b/lib/event_processor/event_builder/log_event.ts index 4d4048950..48051dd19 100644 --- a/lib/event_processor/event_builder/log_event.ts +++ b/lib/event_processor/event_builder/log_event.ts @@ -21,6 +21,8 @@ import { LogEvent } from '../event_dispatcher/event_dispatcher'; import { EventTags } from '../../shared_types'; import { Region } from '../../project_config/project_config'; +export const __supportedPlatforms = ['__universal__'] as const; + const ACTIVATE_EVENT_KEY = 'campaign_activated' const CUSTOM_ATTRIBUTE_FEATURE_TYPE = 'custom' diff --git a/lib/event_processor/event_builder/user_event.ts b/lib/event_processor/event_builder/user_event.ts index ae33d65da..9b10311b0 100644 --- a/lib/event_processor/event_builder/user_event.ts +++ b/lib/event_processor/event_builder/user_event.ts @@ -19,6 +19,9 @@ import { isAttributeValid } from '../../utils/attributes_validator'; import * as eventTagUtils from '../../utils/event_tag_utils'; import fns from '../../utils/fns'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + getAttributeId, getEventId, getLayerId, diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.node.ts b/lib/event_processor/event_dispatcher/default_dispatcher.node.ts index 65dc115af..a66f08d78 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.node.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.node.ts @@ -17,6 +17,8 @@ import { EventDispatcher } from './event_dispatcher'; import { NodeRequestHandler } from '../../utils/http_request_handler/request_handler.node'; import { DefaultEventDispatcher } from './default_dispatcher'; +export const __supportedPlatforms = ['node'] as const; + const eventDispatcher: EventDispatcher = new DefaultEventDispatcher(new NodeRequestHandler()); export default eventDispatcher; diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.ts b/lib/event_processor/event_dispatcher/default_dispatcher.ts index b786ffda2..63a1bf2bb 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.ts @@ -18,6 +18,8 @@ import { ONLY_POST_REQUESTS_ARE_SUPPORTED } from 'error_message'; import { RequestHandler } from '../../utils/http_request_handler/http'; import { EventDispatcher, EventDispatcherResponse, LogEvent } from './event_dispatcher'; +export const __supportedPlatforms = ['__universal__'] as const; + export class DefaultEventDispatcher implements EventDispatcher { private requestHandler: RequestHandler; diff --git a/lib/event_processor/event_dispatcher/event_dispatcher.ts b/lib/event_processor/event_dispatcher/event_dispatcher.ts index 4dfda8f30..ea1a3e3e8 100644 --- a/lib/event_processor/event_dispatcher/event_dispatcher.ts +++ b/lib/event_processor/event_dispatcher/event_dispatcher.ts @@ -15,6 +15,8 @@ */ import { EventBatch } from "../event_builder/log_event"; +export const __supportedPlatforms = ['__universal__'] as const; + export type EventDispatcherResponse = { statusCode?: number } diff --git a/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts b/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts index 383ad8380..a1cb12c94 100644 --- a/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts +++ b/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts @@ -20,6 +20,8 @@ import { EventDispatcher } from './event_dispatcher'; import { validateRequestHandler } from '../../utils/http_request_handler/request_handler_validator'; +export const __supportedPlatforms = ['__universal__'] as const; + export const createEventDispatcher = (requestHander: RequestHandler): EventDispatcher => { validateRequestHandler(requestHander); return new DefaultEventDispatcher(requestHander); diff --git a/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts index 006adedd6..bfd748c89 100644 --- a/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts @@ -18,6 +18,8 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { SEND_BEACON_FAILED } from 'error_message'; import { EventDispatcher, EventDispatcherResponse } from './event_dispatcher'; +export const __supportedPlatforms = ['browser'] as const; + export type Event = { url: string; httpVerb: 'POST'; diff --git a/lib/event_processor/event_processor.ts b/lib/event_processor/event_processor.ts index 585c71f68..079cbdd42 100644 --- a/lib/event_processor/event_processor.ts +++ b/lib/event_processor/event_processor.ts @@ -19,6 +19,8 @@ import { Service } from '../service' import { Consumer, Fn } from '../utils/type'; import { LoggerFacade } from '../logging/logger'; +export const __supportedPlatforms = ['__universal__'] as const; + export const DEFAULT_FLUSH_INTERVAL = 30000 // Unit is ms - default flush interval is 30s export const DEFAULT_BATCH_SIZE = 10 diff --git a/lib/event_processor/event_processor_factory.browser.ts b/lib/event_processor/event_processor_factory.browser.ts index 1e8b251ef..21b035288 100644 --- a/lib/event_processor/event_processor_factory.browser.ts +++ b/lib/event_processor/event_processor_factory.browser.ts @@ -17,6 +17,9 @@ import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import { EventProcessor } from './event_processor'; import { EventWithId } from './batch_event_processor'; import { +export const __supportedPlatforms = ['browser'] as const; + + getOpaqueBatchEventProcessor, BatchEventProcessorOptions, OpaqueEventProcessor, diff --git a/lib/event_processor/event_processor_factory.node.ts b/lib/event_processor/event_processor_factory.node.ts index cfa10feae..a9d4c935e 100644 --- a/lib/event_processor/event_processor_factory.node.ts +++ b/lib/event_processor/event_processor_factory.node.ts @@ -16,6 +16,9 @@ import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import defaultEventDispatcher from './event_dispatcher/default_dispatcher.node'; import { +export const __supportedPlatforms = ['node'] as const; + + BatchEventProcessorOptions, FAILED_EVENT_RETRY_INTERVAL, getOpaqueBatchEventProcessor, diff --git a/lib/event_processor/event_processor_factory.react_native.ts b/lib/event_processor/event_processor_factory.react_native.ts index 0d2f00971..b56a807ad 100644 --- a/lib/event_processor/event_processor_factory.react_native.ts +++ b/lib/event_processor/event_processor_factory.react_native.ts @@ -16,6 +16,9 @@ import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import defaultEventDispatcher from './event_dispatcher/default_dispatcher.browser'; import { +export const __supportedPlatforms = ['react_native'] as const; + + BatchEventProcessorOptions, getOpaqueBatchEventProcessor, getPrefixEventStore, diff --git a/lib/event_processor/event_processor_factory.ts b/lib/event_processor/event_processor_factory.ts index 7c7fda93d..e7b419be5 100644 --- a/lib/event_processor/event_processor_factory.ts +++ b/lib/event_processor/event_processor_factory.ts @@ -26,6 +26,8 @@ import { EventProcessor } from "./event_processor"; import { EVENT_STORE_PREFIX } from "./event_store"; import { ForwardingEventProcessor } from "./forwarding_event_processor"; +export const __supportedPlatforms = ['__universal__'] as const; + export const INVALID_EVENT_DISPATCHER = 'Invalid event dispatcher'; export const FAILED_EVENT_RETRY_INTERVAL = 20 * 1000; diff --git a/lib/event_processor/event_processor_factory.universal.ts b/lib/event_processor/event_processor_factory.universal.ts index 0a3b2ec56..9f423d131 100644 --- a/lib/event_processor/event_processor_factory.universal.ts +++ b/lib/event_processor/event_processor_factory.universal.ts @@ -18,6 +18,9 @@ import { getForwardingEventProcessor } from './event_processor_factory'; import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + getOpaqueBatchEventProcessor, BatchEventProcessorOptions, OpaqueEventProcessor, diff --git a/lib/event_processor/event_store.ts b/lib/event_processor/event_store.ts index b55520a19..d807afb27 100644 --- a/lib/event_processor/event_store.ts +++ b/lib/event_processor/event_store.ts @@ -2,6 +2,9 @@ import { OptimizelyError } from "../error/optimizly_error"; import { LoggerFacade } from "../logging/logger"; import { EVENT_STORE_FULL } from "error_message"; import { +export const __supportedPlatforms = ['__universal__'] as const; + + AsyncPrefixStore, AsyncStore, AsyncStoreWithBatchedGet, diff --git a/lib/event_processor/forwarding_event_processor.ts b/lib/event_processor/forwarding_event_processor.ts index f578992c7..c33b86b7b 100644 --- a/lib/event_processor/forwarding_event_processor.ts +++ b/lib/event_processor/forwarding_event_processor.ts @@ -26,6 +26,8 @@ import { Consumer, Fn } from '../utils/type'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; import { sprintf } from '../utils/fns'; +export const __supportedPlatforms = ['__universal__'] as const; + export class ForwardingEventProcessor extends BaseService implements EventProcessor { private dispatcher: EventDispatcher; private eventEmitter: EventEmitter<{ dispatch: LogEvent }>; diff --git a/lib/export_types.ts b/lib/export_types.ts index b620fbb8e..1236bca7c 100644 --- a/lib/export_types.ts +++ b/lib/export_types.ts @@ -15,6 +15,8 @@ */ // config manager related types +export const __supportedPlatforms = ['__universal__'] as const; + export type { StaticConfigManagerConfig, PollingConfigManagerConfig, diff --git a/lib/feature_toggle.ts b/lib/feature_toggle.ts index 67ccb3d83..df42b3460 100644 --- a/lib/feature_toggle.ts +++ b/lib/feature_toggle.ts @@ -34,4 +34,6 @@ // example feature flag definition // export const wipFeat = () => false as const; +export const __supportedPlatforms = ['__universal__'] as const; + export type IfActive boolean, Y, N = unknown> = ReturnType extends true ? Y : N; diff --git a/lib/index.browser.ts b/lib/index.browser.ts index 0f644a844..60993e640 100644 --- a/lib/index.browser.ts +++ b/lib/index.browser.ts @@ -26,6 +26,9 @@ import { BrowserRequestHandler } from './utils/http_request_handler/request_hand * @return {Client|null} the Optimizely client object * null on error */ +export const __supportedPlatforms = ['browser'] as const; + + export const createInstance = function(config: Config): Client { const client = getOptimizelyInstance({ ...config, diff --git a/lib/index.browser.umdtests.js b/lib/index.browser.umdtests.js index a13f5046b..a57f1193c 100644 --- a/lib/index.browser.umdtests.js +++ b/lib/index.browser.umdtests.js @@ -25,6 +25,8 @@ import packageJSON from '../package.json'; import eventDispatcher from './plugins/event_dispatcher/index.browser'; import { INVALID_CONFIG_OR_SOMETHING } from './exception_messages'; +export const __supportedPlatforms = ['browser'] as const; + describe('javascript-sdk', function() { describe('APIs', function() { describe('createInstance', function() { diff --git a/lib/index.node.ts b/lib/index.node.ts index 02d162ed6..dad0d692c 100644 --- a/lib/index.node.ts +++ b/lib/index.node.ts @@ -25,6 +25,9 @@ import { NodeRequestHandler } from './utils/http_request_handler/request_handler * @return {Client|null} the Optimizely client object * null on error */ +export const __supportedPlatforms = ['node'] as const; + + export const createInstance = function(config: Config): Client { const nodeConfig: OptimizelyFactoryConfig = { ...config, diff --git a/lib/index.react_native.ts b/lib/index.react_native.ts index c393261b7..85bd079e6 100644 --- a/lib/index.react_native.ts +++ b/lib/index.react_native.ts @@ -28,6 +28,9 @@ import { BrowserRequestHandler } from './utils/http_request_handler/request_hand * @return {Client|null} the Optimizely client object * null on error */ +export const __supportedPlatforms = ['react_native'] as const; + + export const createInstance = function(config: Config): Client { const rnConfig: OptimizelyFactoryConfig = { ...config, diff --git a/lib/index.universal.ts b/lib/index.universal.ts index 11c39c1d1..fffc46a16 100644 --- a/lib/index.universal.ts +++ b/lib/index.universal.ts @@ -19,6 +19,8 @@ import { JAVASCRIPT_CLIENT_ENGINE } from './utils/enums'; import { RequestHandler } from './utils/http_request_handler/http'; +export const __supportedPlatforms = ['__universal__'] as const; + export type UniversalConfig = Config & { requestHandler: RequestHandler; } diff --git a/lib/logging/logger.ts b/lib/logging/logger.ts index 8414d544a..14b51b299 100644 --- a/lib/logging/logger.ts +++ b/lib/logging/logger.ts @@ -17,6 +17,8 @@ import { OptimizelyError } from '../error/optimizly_error'; import { MessageResolver } from '../message/message_resolver'; import { sprintf } from '../utils/fns' +export const __supportedPlatforms = ['__universal__'] as const; + export enum LogLevel { Debug, Info, diff --git a/lib/logging/logger_factory.ts b/lib/logging/logger_factory.ts index 2aee1b535..7307c6ee9 100644 --- a/lib/logging/logger_factory.ts +++ b/lib/logging/logger_factory.ts @@ -17,6 +17,8 @@ import { ConsoleLogHandler, LogHandler, LogLevel, OptimizelyLogger } from './log import { errorResolver, infoResolver, MessageResolver } from '../message/message_resolver'; import { Maybe } from '../utils/type'; +export const __supportedPlatforms = ['__universal__'] as const; + export const INVALID_LOG_HANDLER = 'Invalid log handler'; export const INVALID_LEVEL_PRESET = 'Invalid level preset'; diff --git a/lib/message/error_message.ts b/lib/message/error_message.ts index 61f876f4a..72639676f 100644 --- a/lib/message/error_message.ts +++ b/lib/message/error_message.ts @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +export const __supportedPlatforms = ['__universal__'] as const; + export const NOTIFICATION_LISTENER_EXCEPTION = 'Notification listener for (%s) threw exception: %s'; export const CONDITION_EVALUATOR_ERROR = 'Error evaluating audience condition of type %s: %s'; export const EXPERIMENT_KEY_NOT_IN_DATAFILE = 'Experiment key %s is not in datafile.'; diff --git a/lib/message/log_message.ts b/lib/message/log_message.ts index b4757e2d3..4e32036d6 100644 --- a/lib/message/log_message.ts +++ b/lib/message/log_message.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export const __supportedPlatforms = ['__universal__'] as const; + export const FEATURE_ENABLED_FOR_USER = 'Feature %s is enabled for user %s.'; export const FEATURE_NOT_ENABLED_FOR_USER = 'Feature %s is not enabled for user %s.'; export const FAILED_TO_PARSE_VALUE = 'Failed to parse event value "%s" from event tags.'; diff --git a/lib/message/message_resolver.ts b/lib/message/message_resolver.ts index 07a0cefdf..8e38f0a40 100644 --- a/lib/message/message_resolver.ts +++ b/lib/message/message_resolver.ts @@ -1,6 +1,8 @@ import { messages as infoMessages } from 'log_message'; import { messages as errorMessages } from 'error_message'; +export const __supportedPlatforms = ['__universal__'] as const; + export interface MessageResolver { resolve(baseMessage: string): string; } diff --git a/lib/notification_center/index.ts b/lib/notification_center/index.ts index 7b17ba658..8f0f43634 100644 --- a/lib/notification_center/index.ts +++ b/lib/notification_center/index.ts @@ -24,6 +24,8 @@ import { NOTIFICATION_LISTENER_EXCEPTION } from 'error_message'; import { ErrorReporter } from '../error/error_reporter'; import { ErrorNotifier } from '../error/error_notifier'; +export const __supportedPlatforms = ['__universal__'] as const; + interface NotificationCenterOptions { logger?: LoggerFacade; errorNotifier?: ErrorNotifier; diff --git a/lib/notification_center/type.ts b/lib/notification_center/type.ts index 28b3dfeb0..3426c4a76 100644 --- a/lib/notification_center/type.ts +++ b/lib/notification_center/type.ts @@ -16,6 +16,9 @@ import { LogEvent } from '../event_processor/event_dispatcher/event_dispatcher'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + EventTags, Experiment, FeatureVariableValue, diff --git a/lib/odp/constant.ts b/lib/odp/constant.ts index c33f3f0c9..415d241e7 100644 --- a/lib/odp/constant.ts +++ b/lib/odp/constant.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export const __supportedPlatforms = ['__universal__'] as const; + export enum ODP_USER_KEY { VUID = 'vuid', FS_USER_ID = 'fs_user_id', diff --git a/lib/odp/event_manager/odp_event.ts b/lib/odp/event_manager/odp_event.ts index 062798d1b..585024aaf 100644 --- a/lib/odp/event_manager/odp_event.ts +++ b/lib/odp/event_manager/odp_event.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export const __supportedPlatforms = ['__universal__'] as const; + export class OdpEvent { /** * Type of event (typically "fullstack") diff --git a/lib/odp/event_manager/odp_event_api_manager.ts b/lib/odp/event_manager/odp_event_api_manager.ts index 79154b06e..3b9f39c5b 100644 --- a/lib/odp/event_manager/odp_event_api_manager.ts +++ b/lib/odp/event_manager/odp_event_api_manager.ts @@ -19,6 +19,8 @@ import { OdpEvent } from './odp_event'; import { HttpMethod, RequestHandler } from '../../utils/http_request_handler/http'; import { OdpConfig } from '../odp_config'; +export const __supportedPlatforms = ['__universal__'] as const; + export type EventDispatchResponse = { statusCode?: number; }; diff --git a/lib/odp/event_manager/odp_event_manager.ts b/lib/odp/event_manager/odp_event_manager.ts index d1a30d3ff..68e623625 100644 --- a/lib/odp/event_manager/odp_event_manager.ts +++ b/lib/odp/event_manager/odp_event_manager.ts @@ -24,6 +24,9 @@ import { runWithRetry } from '../../utils/executor/backoff_retry_runner'; import { isSuccessStatusCode } from '../../utils/http_request_handler/http_util'; import { ODP_DEFAULT_EVENT_TYPE, ODP_USER_KEY } from '../constant'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + EVENT_ACTION_INVALID, EVENT_DATA_INVALID, FAILED_TO_SEND_ODP_EVENTS, diff --git a/lib/odp/odp_config.ts b/lib/odp/odp_config.ts index 5003e1238..7462993b0 100644 --- a/lib/odp/odp_config.ts +++ b/lib/odp/odp_config.ts @@ -16,6 +16,8 @@ import { checkArrayEquality } from '../utils/fns'; +export const __supportedPlatforms = ['__universal__'] as const; + export class OdpConfig { /** * Host of ODP audience segments API. diff --git a/lib/odp/odp_manager.ts b/lib/odp/odp_manager.ts index feaca24b9..e99f2db6b 100644 --- a/lib/odp/odp_manager.ts +++ b/lib/odp/odp_manager.ts @@ -32,6 +32,8 @@ import { Maybe } from '../utils/type'; import { sprintf } from '../utils/fns'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; +export const __supportedPlatforms = ['__universal__'] as const; + export interface OdpManager extends Service { updateConfig(odpIntegrationConfig: OdpIntegrationConfig): boolean; fetchQualifiedSegments(userId: string, options?: Array): Promise; diff --git a/lib/odp/odp_manager_factory.browser.ts b/lib/odp/odp_manager_factory.browser.ts index e5d97d8e1..f7c01cea9 100644 --- a/lib/odp/odp_manager_factory.browser.ts +++ b/lib/odp/odp_manager_factory.browser.ts @@ -18,6 +18,8 @@ import { BrowserRequestHandler } from '../utils/http_request_handler/request_han import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; +export const __supportedPlatforms = ['browser'] as const; + export const BROWSER_DEFAULT_API_TIMEOUT = 10_000; export const BROWSER_DEFAULT_BATCH_SIZE = 10; export const BROWSER_DEFAULT_FLUSH_INTERVAL = 1000; diff --git a/lib/odp/odp_manager_factory.node.ts b/lib/odp/odp_manager_factory.node.ts index 7b8f737a7..83c0ad61a 100644 --- a/lib/odp/odp_manager_factory.node.ts +++ b/lib/odp/odp_manager_factory.node.ts @@ -18,6 +18,8 @@ import { NodeRequestHandler } from '../utils/http_request_handler/request_handle import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; +export const __supportedPlatforms = ['node'] as const; + export const NODE_DEFAULT_API_TIMEOUT = 10_000; export const NODE_DEFAULT_BATCH_SIZE = 10; export const NODE_DEFAULT_FLUSH_INTERVAL = 1000; diff --git a/lib/odp/odp_manager_factory.react_native.ts b/lib/odp/odp_manager_factory.react_native.ts index c76312d6d..a266c3f9f 100644 --- a/lib/odp/odp_manager_factory.react_native.ts +++ b/lib/odp/odp_manager_factory.react_native.ts @@ -19,6 +19,8 @@ import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager' import { OdpManager } from './odp_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; +export const __supportedPlatforms = ['react_native'] as const; + export const RN_DEFAULT_API_TIMEOUT = 10_000; export const RN_DEFAULT_BATCH_SIZE = 10; export const RN_DEFAULT_FLUSH_INTERVAL = 1000; diff --git a/lib/odp/odp_manager_factory.ts b/lib/odp/odp_manager_factory.ts index 45c79e591..eba342b3b 100644 --- a/lib/odp/odp_manager_factory.ts +++ b/lib/odp/odp_manager_factory.ts @@ -26,6 +26,8 @@ import { DefaultOdpSegmentApiManager } from "./segment_manager/odp_segment_api_m import { DefaultOdpSegmentManager, OdpSegmentManager } from "./segment_manager/odp_segment_manager"; import { UserAgentParser } from "./ua_parser/user_agent_parser"; +export const __supportedPlatforms = ['__universal__'] as const; + export const DEFAULT_CACHE_SIZE = 10_000; export const DEFAULT_CACHE_TIMEOUT = 600_000; diff --git a/lib/odp/odp_manager_factory.universal.ts b/lib/odp/odp_manager_factory.universal.ts index 6bf509611..f56ccb60d 100644 --- a/lib/odp/odp_manager_factory.universal.ts +++ b/lib/odp/odp_manager_factory.universal.ts @@ -19,6 +19,8 @@ import { validateRequestHandler } from '../utils/http_request_handler/request_ha import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; +export const __supportedPlatforms = ['__universal__'] as const; + export const DEFAULT_API_TIMEOUT = 10_000; export const DEFAULT_BATCH_SIZE = 1; export const DEFAULT_FLUSH_INTERVAL = 1000; diff --git a/lib/odp/odp_types.ts b/lib/odp/odp_types.ts index abe47b245..82e781cd4 100644 --- a/lib/odp/odp_types.ts +++ b/lib/odp/odp_types.ts @@ -17,6 +17,8 @@ /** * Wrapper around valid data and error responses */ +export const __supportedPlatforms = ['__universal__'] as const; + export interface Response { data: Data; errors: Error[]; diff --git a/lib/odp/segment_manager/odp_response_schema.ts b/lib/odp/segment_manager/odp_response_schema.ts index 4221178af..f69aa6dd1 100644 --- a/lib/odp/segment_manager/odp_response_schema.ts +++ b/lib/odp/segment_manager/odp_response_schema.ts @@ -19,6 +19,9 @@ import { JSONSchema4 } from 'json-schema'; /** * JSON Schema used to validate the ODP GraphQL response */ +export const __supportedPlatforms = ['__universal__'] as const; + + export const OdpResponseSchema = { $schema: 'https://json-schema.org/draft/2019-09/schema', $id: 'https://example.com/example.json', diff --git a/lib/odp/segment_manager/odp_segment_api_manager.ts b/lib/odp/segment_manager/odp_segment_api_manager.ts index 92eeaa02e..deded7a19 100644 --- a/lib/odp/segment_manager/odp_segment_api_manager.ts +++ b/lib/odp/segment_manager/odp_segment_api_manager.ts @@ -24,6 +24,9 @@ import { log } from 'console'; /** * Expected value for a qualified/valid segment */ +export const __supportedPlatforms = ['__universal__'] as const; + + const QUALIFIED = 'qualified'; /** * Return value when no valid segments found diff --git a/lib/odp/segment_manager/odp_segment_manager.ts b/lib/odp/segment_manager/odp_segment_manager.ts index 4ff125672..57027877b 100644 --- a/lib/odp/segment_manager/odp_segment_manager.ts +++ b/lib/odp/segment_manager/odp_segment_manager.ts @@ -22,6 +22,8 @@ import { ODP_USER_KEY } from '../constant'; import { LoggerFacade } from '../../logging/logger'; import { ODP_CONFIG_NOT_AVAILABLE, ODP_NOT_INTEGRATED } from 'error_message'; +export const __supportedPlatforms = ['__universal__'] as const; + export interface OdpSegmentManager { fetchQualifiedSegments( userKey: ODP_USER_KEY, diff --git a/lib/odp/segment_manager/optimizely_segment_option.ts b/lib/odp/segment_manager/optimizely_segment_option.ts index cf7c801ef..013664d3b 100644 --- a/lib/odp/segment_manager/optimizely_segment_option.ts +++ b/lib/odp/segment_manager/optimizely_segment_option.ts @@ -15,6 +15,8 @@ */ // Options for defining behavior of OdpSegmentManager's caching mechanism when calling fetchSegments() +export const __supportedPlatforms = ['__universal__'] as const; + export enum OptimizelySegmentOption { IGNORE_CACHE = 'IGNORE_CACHE', RESET_CACHE = 'RESET_CACHE', diff --git a/lib/odp/ua_parser/ua_parser.ts b/lib/odp/ua_parser/ua_parser.ts index 8622b0ade..facf6a962 100644 --- a/lib/odp/ua_parser/ua_parser.ts +++ b/lib/odp/ua_parser/ua_parser.ts @@ -17,6 +17,8 @@ import { UAParser } from 'ua-parser-js'; import { UserAgentInfo } from './user_agent_info'; import { UserAgentParser } from './user_agent_parser'; +export const __supportedPlatforms = ['__universal__'] as const; + const userAgentParser: UserAgentParser = { parseUserAgentInfo(): UserAgentInfo { const parser = new UAParser(); diff --git a/lib/odp/ua_parser/user_agent_info.ts b/lib/odp/ua_parser/user_agent_info.ts index e83b3b032..c158aa62a 100644 --- a/lib/odp/ua_parser/user_agent_info.ts +++ b/lib/odp/ua_parser/user_agent_info.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export const __supportedPlatforms = ['__universal__'] as const; + export type UserAgentInfo = { os: { name?: string, diff --git a/lib/odp/ua_parser/user_agent_parser.ts b/lib/odp/ua_parser/user_agent_parser.ts index 9ca30c141..4adaac1a7 100644 --- a/lib/odp/ua_parser/user_agent_parser.ts +++ b/lib/odp/ua_parser/user_agent_parser.ts @@ -16,6 +16,8 @@ import { UserAgentInfo } from "./user_agent_info"; +export const __supportedPlatforms = ['__universal__'] as const; + export interface UserAgentParser { parseUserAgentInfo(): UserAgentInfo, } diff --git a/lib/optimizely/index.ts b/lib/optimizely/index.ts index 2381e8a80..eda649c28 100644 --- a/lib/optimizely/index.ts +++ b/lib/optimizely/index.ts @@ -25,6 +25,9 @@ import { OptimizelySegmentOption } from '../odp/segment_manager/optimizely_segme import { BaseService, ServiceState } from '../service'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + UserAttributes, EventTags, OptimizelyConfig, diff --git a/lib/optimizely_decision/index.ts b/lib/optimizely_decision/index.ts index b4adaed14..fa293b5cf 100644 --- a/lib/optimizely_decision/index.ts +++ b/lib/optimizely_decision/index.ts @@ -15,6 +15,8 @@ ***************************************************************************/ import { OptimizelyUserContext, OptimizelyDecision } from '../shared_types'; +export const __supportedPlatforms = ['__universal__'] as const; + export function newErrorDecision(key: string, user: OptimizelyUserContext, reasons: string[]): OptimizelyDecision { return { variationKey: null, diff --git a/lib/optimizely_user_context/index.ts b/lib/optimizely_user_context/index.ts index 7b2af6488..9667acc01 100644 --- a/lib/optimizely_user_context/index.ts +++ b/lib/optimizely_user_context/index.ts @@ -15,6 +15,9 @@ */ import Optimizely from '../optimizely'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + EventTags, OptimizelyDecideOption, OptimizelyDecision, diff --git a/lib/platform_support.ts b/lib/platform_support.ts new file mode 100644 index 000000000..39a94dedb --- /dev/null +++ b/lib/platform_support.ts @@ -0,0 +1,56 @@ +/** + * Platform Support Type Definitions + * + * Every source file (except tests) must export __supportedPlatforms to declare + * which platforms it supports. This is enforced at both type-level and runtime. + */ + +/** + * Valid platform identifiers + */ +export type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; + +/** + * Platform support declaration + * + * Every file must export this to declare which platforms it supports: + * - Specific platforms: export const __supportedPlatforms = ['browser', 'node']; + * - All platforms (universal): export const __supportedPlatforms = ['__universal__']; + * + * @example + * // Browser and React Native only + * export const __supportedPlatforms = ['browser', 'react_native'] satisfies Platform[]; + * + * @example + * // Node.js only + * export const __supportedPlatforms = ['node'] satisfies Platform[]; + * + * @example + * // Universal (all platforms) + * export const __supportedPlatforms = ['__universal__'] satisfies Platform[]; + */ +export type SupportedPlatforms = readonly Platform[]; + +/** + * Helper type to enforce that a module exports __supportedPlatforms + * + * Usage in your module: + * ```typescript + * import type { RequirePlatformDeclaration, Platform } from './platform_support'; + * + * export const __supportedPlatforms = ['browser'] satisfies Platform[]; + * + * // This type check ensures __supportedPlatforms is exported + * // type _Check = RequirePlatformDeclaration; + * ``` + */ +export type RequirePlatformDeclaration = T extends { __supportedPlatforms: readonly Platform[] } + ? T + : never & { error: '__supportedPlatforms export is required' }; + +/** + * Utility to check if a file is universal (supports all platforms) + */ +export function isUniversal(platforms: readonly Platform[]): boolean { + return platforms.length === 1 && platforms[0] === '__universal__'; +} diff --git a/lib/project_config/config_manager_factory.browser.ts b/lib/project_config/config_manager_factory.browser.ts index 17741acb2..fc7b9fc13 100644 --- a/lib/project_config/config_manager_factory.browser.ts +++ b/lib/project_config/config_manager_factory.browser.ts @@ -17,6 +17,8 @@ import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; +export const __supportedPlatforms = ['browser'] as const; + export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { autoUpdate: false, diff --git a/lib/project_config/config_manager_factory.node.ts b/lib/project_config/config_manager_factory.node.ts index 8e063e347..4d69dca79 100644 --- a/lib/project_config/config_manager_factory.node.ts +++ b/lib/project_config/config_manager_factory.node.ts @@ -17,6 +17,8 @@ import { NodeRequestHandler } from "../utils/http_request_handler/request_handler.node"; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; +export const __supportedPlatforms = ['node'] as const; + export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { autoUpdate: true, diff --git a/lib/project_config/config_manager_factory.react_native.ts b/lib/project_config/config_manager_factory.react_native.ts index e30a565ca..17c5fe84c 100644 --- a/lib/project_config/config_manager_factory.react_native.ts +++ b/lib/project_config/config_manager_factory.react_native.ts @@ -18,6 +18,8 @@ import { AsyncStorageCache } from "../utils/cache/async_storage_cache.react_nati import { BrowserRequestHandler } from "../utils/http_request_handler/request_handler.browser"; import { getOpaquePollingConfigManager, PollingConfigManagerConfig, OpaqueConfigManager } from "./config_manager_factory"; +export const __supportedPlatforms = ['react_native'] as const; + export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { autoUpdate: true, diff --git a/lib/project_config/config_manager_factory.ts b/lib/project_config/config_manager_factory.ts index 3224d4f91..8f7913d5d 100644 --- a/lib/project_config/config_manager_factory.ts +++ b/lib/project_config/config_manager_factory.ts @@ -27,6 +27,8 @@ import { LogLevel } from '../logging/logger' import { Store } from "../utils/cache/store"; import { validateStore } from "../utils/cache/store_validator"; +export const __supportedPlatforms = ['__universal__'] as const; + export const INVALID_CONFIG_MANAGER = "Invalid config manager"; const configManagerSymbol: unique symbol = Symbol(); diff --git a/lib/project_config/config_manager_factory.universal.ts b/lib/project_config/config_manager_factory.universal.ts index bcc664082..c7871ae30 100644 --- a/lib/project_config/config_manager_factory.universal.ts +++ b/lib/project_config/config_manager_factory.universal.ts @@ -18,6 +18,8 @@ import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManage import { RequestHandler } from "../utils/http_request_handler/http"; import { validateRequestHandler } from "../utils/http_request_handler/request_handler_validator"; +export const __supportedPlatforms = ['__universal__'] as const; + export type UniversalPollingConfigManagerConfig = PollingConfigManagerConfig & { requestHandler: RequestHandler; } diff --git a/lib/project_config/constant.ts b/lib/project_config/constant.ts index 55e69a33e..862da30b9 100644 --- a/lib/project_config/constant.ts +++ b/lib/project_config/constant.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export const __supportedPlatforms = ['__universal__'] as const; + const DEFAULT_UPDATE_INTERVAL_MINUTES = 5; /** Standard interval (5 minutes in milliseconds) for polling datafile updates.; */ export const DEFAULT_UPDATE_INTERVAL = DEFAULT_UPDATE_INTERVAL_MINUTES * 60 * 1000; diff --git a/lib/project_config/datafile_manager.ts b/lib/project_config/datafile_manager.ts index b7c724113..3f17239ef 100644 --- a/lib/project_config/datafile_manager.ts +++ b/lib/project_config/datafile_manager.ts @@ -20,6 +20,8 @@ import { Fn, Consumer } from '../utils/type'; import { Repeater } from '../utils/repeater/repeater'; import { LoggerFacade } from '../logging/logger'; +export const __supportedPlatforms = ['__universal__'] as const; + export interface DatafileManager extends Service { get(): string | undefined; onUpdate(listener: Consumer): Fn; diff --git a/lib/project_config/optimizely_config.ts b/lib/project_config/optimizely_config.ts index b01255c43..cc4494b71 100644 --- a/lib/project_config/optimizely_config.ts +++ b/lib/project_config/optimizely_config.ts @@ -17,6 +17,9 @@ import { LoggerFacade } from '../logging/logger' import { ProjectConfig } from '../project_config/project_config'; import { DEFAULT_OPERATOR_TYPES } from '../core/condition_tree_evaluator'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + Audience, Experiment, FeatureVariable, diff --git a/lib/project_config/polling_datafile_manager.ts b/lib/project_config/polling_datafile_manager.ts index 7e928b8f8..c0a2ebbf4 100644 --- a/lib/project_config/polling_datafile_manager.ts +++ b/lib/project_config/polling_datafile_manager.ts @@ -24,6 +24,9 @@ import { Repeater } from '../utils/repeater/repeater'; import { Consumer, Fn } from '../utils/type'; import { isSuccessStatusCode } from '../utils/http_request_handler/http_util'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + DATAFILE_FETCH_REQUEST_FAILED, ERROR_FETCHING_DATAFILE, } from 'error_message'; diff --git a/lib/project_config/project_config.ts b/lib/project_config/project_config.ts index 1194b15cb..fa3984702 100644 --- a/lib/project_config/project_config.ts +++ b/lib/project_config/project_config.ts @@ -21,6 +21,9 @@ import configValidator from '../utils/config_validator'; import { LoggerFacade } from '../logging/logger'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + Audience, Experiment, FeatureFlag, diff --git a/lib/project_config/project_config_manager.ts b/lib/project_config/project_config_manager.ts index 8d7002c03..e15d83d80 100644 --- a/lib/project_config/project_config_manager.ts +++ b/lib/project_config/project_config_manager.ts @@ -23,6 +23,9 @@ import { Consumer, Fn, Transformer } from '../utils/type'; import { EventEmitter } from '../utils/event_emitter/event_emitter'; import { +export const __supportedPlatforms = ['__universal__'] as const; + + SERVICE_FAILED_TO_START, SERVICE_STOPPED_BEFORE_RUNNING, } from '../service' diff --git a/lib/project_config/project_config_schema.ts b/lib/project_config/project_config_schema.ts index f842179dc..558357e36 100644 --- a/lib/project_config/project_config_schema.ts +++ b/lib/project_config/project_config_schema.ts @@ -19,6 +19,8 @@ */ import { JSONSchema4 } from 'json-schema'; +export const __supportedPlatforms = ['__universal__'] as const; + var schemaDefinition = { $schema: 'http://json-schema.org/draft-04/schema#', title: 'Project Config JSON Schema', diff --git a/lib/service.ts b/lib/service.ts index 3022aa806..dcc86c195 100644 --- a/lib/service.ts +++ b/lib/service.ts @@ -17,6 +17,8 @@ import { LoggerFacade, LogLevel, LogLevelToLower } from './logging/logger' import { resolvablePromise, ResolvablePromise } from "./utils/promise/resolvablePromise"; +export const __supportedPlatforms = ['__universal__'] as const; + export const SERVICE_FAILED_TO_START = '%s failed to start, reason: %s'; export const SERVICE_STOPPED_BEFORE_RUNNING = '%s stopped before running'; diff --git a/lib/shared_types.ts b/lib/shared_types.ts index 1b450b1dd..4bd5495f0 100644 --- a/lib/shared_types.ts +++ b/lib/shared_types.ts @@ -47,6 +47,8 @@ import { OpaqueOdpManager } from './odp/odp_manager_factory'; import { OpaqueVuidManager } from './vuid/vuid_manager_factory'; import { CacheWithRemove } from './utils/cache/cache'; +export const __supportedPlatforms = ['__universal__'] as const; + export { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; export { EventProcessor } from './event_processor/event_processor'; export { NotificationCenter } from './notification_center'; diff --git a/lib/utils/attributes_validator/index.ts b/lib/utils/attributes_validator/index.ts index 08b50eb43..82ebc4f4b 100644 --- a/lib/utils/attributes_validator/index.ts +++ b/lib/utils/attributes_validator/index.ts @@ -26,6 +26,8 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @throws If the attributes are not valid */ +export const __supportedPlatforms = ['__universal__'] as const; + export function validate(attributes: unknown): boolean { if (typeof attributes === 'object' && !Array.isArray(attributes) && attributes !== null) { Object.keys(attributes).forEach(function(key) { diff --git a/lib/utils/cache/async_storage_cache.react_native.ts b/lib/utils/cache/async_storage_cache.react_native.ts index e5e76024e..369beb221 100644 --- a/lib/utils/cache/async_storage_cache.react_native.ts +++ b/lib/utils/cache/async_storage_cache.react_native.ts @@ -18,6 +18,8 @@ import { Maybe } from "../type"; import { AsyncStore } from "./store"; import { getDefaultAsyncStorage } from "../import.react_native/@react-native-async-storage/async-storage"; +export const __supportedPlatforms = ['react_native'] as const; + export class AsyncStorageCache implements AsyncStore { public readonly operation = 'async'; private asyncStorage = getDefaultAsyncStorage(); diff --git a/lib/utils/cache/cache.ts b/lib/utils/cache/cache.ts index 685b43a7b..6a8329598 100644 --- a/lib/utils/cache/cache.ts +++ b/lib/utils/cache/cache.ts @@ -15,6 +15,9 @@ */ import { OpType, OpValue } from '../../utils/type'; import { Transformer } from '../../utils/type'; +export const __supportedPlatforms = ['__universal__'] as const; + + export interface OpCache { operation: OP; save(key: string, value: V): OpValue; diff --git a/lib/utils/cache/in_memory_lru_cache.ts b/lib/utils/cache/in_memory_lru_cache.ts index 6ed92d1fd..6f9e68621 100644 --- a/lib/utils/cache/in_memory_lru_cache.ts +++ b/lib/utils/cache/in_memory_lru_cache.ts @@ -17,6 +17,8 @@ import { Maybe } from "../type"; import { SyncCacheWithRemove } from "./cache"; +export const __supportedPlatforms = ['__universal__'] as const; + type CacheElement = { value: V; expiresAt?: number; diff --git a/lib/utils/cache/local_storage_cache.browser.ts b/lib/utils/cache/local_storage_cache.browser.ts index b16d77571..f552f1346 100644 --- a/lib/utils/cache/local_storage_cache.browser.ts +++ b/lib/utils/cache/local_storage_cache.browser.ts @@ -17,6 +17,8 @@ import { Maybe } from "../type"; import { SyncStore } from "./store"; +export const __supportedPlatforms = ['browser'] as const; + export class LocalStorageCache implements SyncStore { public readonly operation = 'sync'; diff --git a/lib/utils/cache/store.ts b/lib/utils/cache/store.ts index c2df7bb66..84e875b01 100644 --- a/lib/utils/cache/store.ts +++ b/lib/utils/cache/store.ts @@ -18,6 +18,8 @@ import { Transformer } from '../../utils/type'; import { Maybe } from '../../utils/type'; import { OpType, OpValue } from '../../utils/type'; +export const __supportedPlatforms = ['__universal__'] as const; + export interface OpStore { operation: OP; set(key: string, value: V): OpValue; diff --git a/lib/utils/cache/store_validator.ts b/lib/utils/cache/store_validator.ts index 949bb25c3..54bd7d05b 100644 --- a/lib/utils/cache/store_validator.ts +++ b/lib/utils/cache/store_validator.ts @@ -14,6 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +export const __supportedPlatforms = ['__universal__'] as const; + export const INVALID_STORE = 'Invalid store'; export const INVALID_STORE_METHOD = 'Invalid store method %s'; diff --git a/lib/utils/config_validator/index.ts b/lib/utils/config_validator/index.ts index 49c927f49..c57da17a6 100644 --- a/lib/utils/config_validator/index.ts +++ b/lib/utils/config_validator/index.ts @@ -14,6 +14,9 @@ * limitations under the License. */ import { +export const __supportedPlatforms = ['__universal__'] as const; + + DATAFILE_VERSIONS, } from '../enums'; import { diff --git a/lib/utils/enums/index.ts b/lib/utils/enums/index.ts index 0364a34b1..2ee1b82a2 100644 --- a/lib/utils/enums/index.ts +++ b/lib/utils/enums/index.ts @@ -17,6 +17,8 @@ /** * Contains global enums used throughout the library */ +export const __supportedPlatforms = ['__universal__'] as const; + export const LOG_LEVEL = { NOTSET: 0, DEBUG: 1, diff --git a/lib/utils/event_emitter/event_emitter.ts b/lib/utils/event_emitter/event_emitter.ts index 6bfa57f8d..be108ca8a 100644 --- a/lib/utils/event_emitter/event_emitter.ts +++ b/lib/utils/event_emitter/event_emitter.ts @@ -16,6 +16,8 @@ import { Fn } from "../type"; +export const __supportedPlatforms = ['__universal__'] as const; + type Consumer = (arg: T) => void; type Listeners = { diff --git a/lib/utils/event_tag_utils/index.ts b/lib/utils/event_tag_utils/index.ts index d50292a39..e13a3944f 100644 --- a/lib/utils/event_tag_utils/index.ts +++ b/lib/utils/event_tag_utils/index.ts @@ -14,6 +14,9 @@ * limitations under the License. */ import { +export const __supportedPlatforms = ['__universal__'] as const; + + FAILED_TO_PARSE_REVENUE, FAILED_TO_PARSE_VALUE, PARSED_NUMERIC_VALUE, diff --git a/lib/utils/event_tags_validator/index.ts b/lib/utils/event_tags_validator/index.ts index 421321f69..3ff932c27 100644 --- a/lib/utils/event_tags_validator/index.ts +++ b/lib/utils/event_tags_validator/index.ts @@ -26,6 +26,9 @@ import { INVALID_EVENT_TAGS } from 'error_message'; * @return {boolean} true if event tags are valid * @throws If event tags are not valid */ +export const __supportedPlatforms = ['__universal__'] as const; + + export function validate(eventTags: unknown): boolean { if (typeof eventTags === 'object' && !Array.isArray(eventTags) && eventTags !== null) { return true; diff --git a/lib/utils/executor/backoff_retry_runner.ts b/lib/utils/executor/backoff_retry_runner.ts index f0b185a99..f13769675 100644 --- a/lib/utils/executor/backoff_retry_runner.ts +++ b/lib/utils/executor/backoff_retry_runner.ts @@ -4,6 +4,8 @@ import { resolvablePromise, ResolvablePromise } from "../promise/resolvablePromi import { BackoffController } from "../repeater/repeater"; import { AsyncProducer, Fn } from "../type"; +export const __supportedPlatforms = ['__universal__'] as const; + export type RunResult = { result: Promise; cancelRetry: Fn; diff --git a/lib/utils/executor/serial_runner.ts b/lib/utils/executor/serial_runner.ts index 243cae0b1..ed096ac90 100644 --- a/lib/utils/executor/serial_runner.ts +++ b/lib/utils/executor/serial_runner.ts @@ -16,6 +16,8 @@ import { AsyncProducer } from "../type"; +export const __supportedPlatforms = ['__universal__'] as const; + class SerialRunner { private waitPromise: Promise = Promise.resolve(); diff --git a/lib/utils/fns/index.ts b/lib/utils/fns/index.ts index 5b07b3aad..9b5fde4aa 100644 --- a/lib/utils/fns/index.ts +++ b/lib/utils/fns/index.ts @@ -15,6 +15,8 @@ */ import { v4 } from 'uuid'; +export const __supportedPlatforms = ['__universal__'] as const; + const MAX_SAFE_INTEGER_LIMIT = Math.pow(2, 53); export function currentTimestamp(): number { diff --git a/lib/utils/http_request_handler/http.ts b/lib/utils/http_request_handler/http.ts index ca7e63ae3..72ae48eb9 100644 --- a/lib/utils/http_request_handler/http.ts +++ b/lib/utils/http_request_handler/http.ts @@ -17,6 +17,8 @@ /** * List of key-value pairs to be used in an HTTP requests */ +export const __supportedPlatforms = ['__universal__'] as const; + export interface Headers { [header: string]: string | undefined; } diff --git a/lib/utils/http_request_handler/http_util.ts b/lib/utils/http_request_handler/http_util.ts index c38217a40..41807d88e 100644 --- a/lib/utils/http_request_handler/http_util.ts +++ b/lib/utils/http_request_handler/http_util.ts @@ -1,4 +1,6 @@ +export const __supportedPlatforms = ['__universal__'] as const; + export const isSuccessStatusCode = (statusCode: number): boolean => { return statusCode >= 200 && statusCode < 400; } diff --git a/lib/utils/http_request_handler/request_handler.node.ts b/lib/utils/http_request_handler/request_handler.node.ts index 520a8f3ed..cdbf39933 100644 --- a/lib/utils/http_request_handler/request_handler.node.ts +++ b/lib/utils/http_request_handler/request_handler.node.ts @@ -26,6 +26,9 @@ import { OptimizelyError } from '../../error/optimizly_error'; /** * Handles sending requests and receiving responses over HTTP via NodeJS http module */ +export const __supportedPlatforms = ['node'] as const; + + export class NodeRequestHandler implements RequestHandler { private readonly logger?: LoggerFacade; private readonly timeout: number; diff --git a/lib/utils/http_request_handler/request_handler_validator.ts b/lib/utils/http_request_handler/request_handler_validator.ts index a9df4cc7c..ee61140d0 100644 --- a/lib/utils/http_request_handler/request_handler_validator.ts +++ b/lib/utils/http_request_handler/request_handler_validator.ts @@ -15,6 +15,8 @@ */ import { RequestHandler } from './http'; +export const __supportedPlatforms = ['__universal__'] as const; + export const INVALID_REQUEST_HANDLER = 'Invalid request handler'; export const validateRequestHandler = (requestHandler: RequestHandler): void => { diff --git a/lib/utils/id_generator/index.ts b/lib/utils/id_generator/index.ts index 5f3c72387..353e854ae 100644 --- a/lib/utils/id_generator/index.ts +++ b/lib/utils/id_generator/index.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export const __supportedPlatforms = ['__universal__'] as const; + const idSuffixBase = 10_000; export class IdGenerator { diff --git a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts index 4a2fb77ed..06500a1e4 100644 --- a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts +++ b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts @@ -16,6 +16,8 @@ import type { AsyncStorageStatic } from '@react-native-async-storage/async-storage' +export const __supportedPlatforms = ['__universal__'] as const; + export const MODULE_NOT_FOUND_REACT_NATIVE_ASYNC_STORAGE = 'Module not found: @react-native-async-storage/async-storage'; export const getDefaultAsyncStorage = (): AsyncStorageStatic => { diff --git a/lib/utils/json_schema_validator/index.ts b/lib/utils/json_schema_validator/index.ts index 42fe19f11..5a02c58f3 100644 --- a/lib/utils/json_schema_validator/index.ts +++ b/lib/utils/json_schema_validator/index.ts @@ -26,6 +26,9 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @param {boolean} shouldThrowOnError Should validation throw if invalid JSON object * @return {boolean} true if the given object is valid; throws or false if invalid */ +export const __supportedPlatforms = ['__universal__'] as const; + + export function validate( jsonObject: unknown, validationSchema: JSONSchema4 = schema, diff --git a/lib/utils/microtask/index.ts b/lib/utils/microtask/index.ts index 02e2c474e..d64d5724a 100644 --- a/lib/utils/microtask/index.ts +++ b/lib/utils/microtask/index.ts @@ -16,6 +16,8 @@ type Callback = () => void; +export const __supportedPlatforms = ['__universal__'] as const; + export const scheduleMicrotask = (callback: Callback): void => { if (typeof queueMicrotask === 'function') { queueMicrotask(callback); diff --git a/lib/utils/promise/operation_value.ts b/lib/utils/promise/operation_value.ts index 7f7aa3779..baf297d9e 100644 --- a/lib/utils/promise/operation_value.ts +++ b/lib/utils/promise/operation_value.ts @@ -3,6 +3,8 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { OpType, OpValue } from '../type'; +export const __supportedPlatforms = ['__universal__'] as const; + const isPromise = (val: any): boolean => { return val && typeof val.then === 'function'; } diff --git a/lib/utils/promise/resolvablePromise.ts b/lib/utils/promise/resolvablePromise.ts index 354df2b7d..c96196006 100644 --- a/lib/utils/promise/resolvablePromise.ts +++ b/lib/utils/promise/resolvablePromise.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export const __supportedPlatforms = ['__universal__'] as const; + const noop = () => {}; export type ResolvablePromise = { diff --git a/lib/utils/repeater/repeater.ts b/lib/utils/repeater/repeater.ts index 829702729..7b68ee31d 100644 --- a/lib/utils/repeater/repeater.ts +++ b/lib/utils/repeater/repeater.ts @@ -24,6 +24,9 @@ import { scheduleMicrotask } from "../microtask"; // If the retuned promise resolves, the repeater will assume the task succeeded, // and will reset the failure count. If the promise is rejected, the repeater will // assume the task failed and will increase the current consecutive failure count. +export const __supportedPlatforms = ['__universal__'] as const; + + export interface Repeater { // If immediateExecution is true, the first exection of // the task will be immediate but asynchronous. diff --git a/lib/utils/semantic_version/index.ts b/lib/utils/semantic_version/index.ts index 56fad06a5..fe3321c83 100644 --- a/lib/utils/semantic_version/index.ts +++ b/lib/utils/semantic_version/index.ts @@ -23,6 +23,9 @@ import { VERSION_TYPE } from '../enums'; * @return {boolean} true if the string is number only * */ +export const __supportedPlatforms = ['__universal__'] as const; + + function isNumber(content: string): boolean { return /^\d+$/.test(content); } diff --git a/lib/utils/string_value_validator/index.ts b/lib/utils/string_value_validator/index.ts index fd0ceb5f0..ced52e780 100644 --- a/lib/utils/string_value_validator/index.ts +++ b/lib/utils/string_value_validator/index.ts @@ -19,6 +19,8 @@ * @param {unknown} input * @return {boolean} true for non-empty string, false otherwise */ +export const __supportedPlatforms = ['__universal__'] as const; + export function validate(input: unknown): boolean { return typeof input === 'string' && input !== ''; } diff --git a/lib/utils/type.ts b/lib/utils/type.ts index c60f85d60..5c6c4b3cc 100644 --- a/lib/utils/type.ts +++ b/lib/utils/type.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +export const __supportedPlatforms = ['__universal__'] as const; + export type Fn = () => unknown; export type AsyncFn = () => Promise; export type AsyncTransformer = (arg: A) => Promise; diff --git a/lib/utils/user_profile_service_validator/index.ts b/lib/utils/user_profile_service_validator/index.ts index 95e8cf61a..c8e3efbb8 100644 --- a/lib/utils/user_profile_service_validator/index.ts +++ b/lib/utils/user_profile_service_validator/index.ts @@ -30,6 +30,8 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @throws If the instance is not valid */ +export const __supportedPlatforms = ['__universal__'] as const; + export function validate(userProfileServiceInstance: unknown): boolean { if (typeof userProfileServiceInstance === 'object' && userProfileServiceInstance !== null) { if (typeof (userProfileServiceInstance as ObjectWithUnknownProperties)['lookup'] !== 'function') { diff --git a/lib/vuid/vuid.ts b/lib/vuid/vuid.ts index d335c329d..ac3d32bd6 100644 --- a/lib/vuid/vuid.ts +++ b/lib/vuid/vuid.ts @@ -16,6 +16,8 @@ import { v4 as uuidV4 } from 'uuid'; +export const __supportedPlatforms = ['__universal__'] as const; + export const VUID_PREFIX = `vuid_`; export const VUID_MAX_LENGTH = 32; diff --git a/lib/vuid/vuid_manager.ts b/lib/vuid/vuid_manager.ts index dd0c0322a..4a2faa2ee 100644 --- a/lib/vuid/vuid_manager.ts +++ b/lib/vuid/vuid_manager.ts @@ -18,6 +18,8 @@ import { Store } from '../utils/cache/store'; import { AsyncProducer, Maybe } from '../utils/type'; import { isVuid, makeVuid } from './vuid'; +export const __supportedPlatforms = ['__universal__'] as const; + export interface VuidManager { getVuid(): Maybe; isVuidEnabled(): boolean; diff --git a/lib/vuid/vuid_manager_factory.browser.ts b/lib/vuid/vuid_manager_factory.browser.ts index 0691fd5e7..3ebce4ac9 100644 --- a/lib/vuid/vuid_manager_factory.browser.ts +++ b/lib/vuid/vuid_manager_factory.browser.ts @@ -17,6 +17,8 @@ import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manage import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; +export const __supportedPlatforms = ['browser'] as const; + export const vuidCacheManager = new VuidCacheManager(); export const createVuidManager = (options: VuidManagerOptions = {}): OpaqueVuidManager => { diff --git a/lib/vuid/vuid_manager_factory.node.ts b/lib/vuid/vuid_manager_factory.node.ts index 439e70ec1..d9950b868 100644 --- a/lib/vuid/vuid_manager_factory.node.ts +++ b/lib/vuid/vuid_manager_factory.node.ts @@ -15,6 +15,8 @@ */ import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; +export const __supportedPlatforms = ['node'] as const; + export const createVuidManager = (options: VuidManagerOptions = {}): OpaqueVuidManager => { return wrapVuidManager(undefined); }; diff --git a/lib/vuid/vuid_manager_factory.react_native.ts b/lib/vuid/vuid_manager_factory.react_native.ts index 0aeb1c537..a75ee446c 100644 --- a/lib/vuid/vuid_manager_factory.react_native.ts +++ b/lib/vuid/vuid_manager_factory.react_native.ts @@ -17,6 +17,8 @@ import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manage import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; +export const __supportedPlatforms = ['react_native'] as const; + export const vuidCacheManager = new VuidCacheManager(); export const createVuidManager = (options: VuidManagerOptions = {}): OpaqueVuidManager => { diff --git a/lib/vuid/vuid_manager_factory.ts b/lib/vuid/vuid_manager_factory.ts index f7f1b760f..802a109d8 100644 --- a/lib/vuid/vuid_manager_factory.ts +++ b/lib/vuid/vuid_manager_factory.ts @@ -18,6 +18,8 @@ import { Store } from '../utils/cache/store'; import { Maybe } from '../utils/type'; import { VuidManager } from './vuid_manager'; +export const __supportedPlatforms = ['__universal__'] as const; + export type VuidManagerOptions = { vuidCache?: Store; enableVuid?: boolean; diff --git a/package.json b/package.json index d1e50ca13..dfcde6255 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "lint": "tsc --noEmit && eslint 'lib/**/*.js' 'lib/**/*.ts'", "validate-platform-isolation": "node scripts/validate-platform-isolation.js", "test-platform-isolation": "node scripts/test-validator.js", + "add-platform-exports": "node scripts/add-platform-exports.js", "test-vitest": "vitest run", "test-mocha": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register -r tsconfig-paths/register -r lib/tests/exit_on_unhandled_rejection.js 'lib/**/*.tests.ts' 'lib/**/*.tests.js'", "test": "npm run test-mocha && npm run test-vitest", diff --git a/scripts/add-platform-exports.js b/scripts/add-platform-exports.js new file mode 100644 index 000000000..840cf2e87 --- /dev/null +++ b/scripts/add-platform-exports.js @@ -0,0 +1,162 @@ +#!/usr/bin/env node + +/** + * Auto-add __supportedPlatforms to files + * + * This script automatically adds __supportedPlatforms export to files that don't have it. + * + * Strategy: + * 1. Files with platform-specific naming (.browser.ts, .node.ts, .react_native.ts) get their specific platform(s) + * 2. All other files are assumed to be universal and get ['__universal__'] + */ + +const fs = require('fs'); +const path = require('path'); + +const PLATFORMS = ['browser', 'node', 'react_native']; +const LIB_DIR = path.join(__dirname, '..', 'lib'); + +function getPlatformFromFilename(filename) { + const platforms = []; + for (const platform of PLATFORMS) { + if (filename.includes(`.${platform}.`)) { + platforms.push(platform); + } + } + return platforms.length > 0 ? platforms : null; +} + +function hasSupportedPlatformsExport(content) { + return /export\s+(?:const|let|var)\s+__supportedPlatforms/.test(content); +} + +function findSourceFiles(dir, files = []) { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + + if (entry.isDirectory()) { + if (!entry.name.startsWith('.') && + entry.name !== 'node_modules' && + entry.name !== 'dist' && + entry.name !== 'coverage' && + entry.name !== 'tests') { + findSourceFiles(fullPath, files); + } + } else if (entry.isFile()) { + if ((entry.name.endsWith('.ts') || entry.name.endsWith('.js')) && + !entry.name.endsWith('.spec.ts') && + !entry.name.endsWith('.test.ts') && + !entry.name.endsWith('.tests.ts') && + !entry.name.endsWith('.tests.js') && + !entry.name.endsWith('.test-d.ts') && + !entry.name.endsWith('.d.ts')) { + files.push(fullPath); + } + } + } + + return files; +} + +function addSupportedPlatforms(filePath) { + const content = fs.readFileSync(filePath, 'utf-8'); + + // Skip if already has __supportedPlatforms + if (hasSupportedPlatformsExport(content)) { + return { skipped: true, reason: 'already has export' }; + } + + // Determine platforms + const platformsFromFilename = getPlatformFromFilename(filePath); + const platforms = platformsFromFilename || ['__universal__']; + + // Format the export statement + const platformsStr = platforms.map(p => `'${p}'`).join(', '); + const exportStatement = `export const __supportedPlatforms = [${platformsStr}] as const;\n`; + + // Find where to insert (after imports, before first export or code) + const lines = content.split('\n'); + let insertIndex = 0; + let inComment = false; + let foundImports = false; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + + // Track multi-line comments + if (line.startsWith('/*')) inComment = true; + if (line.endsWith('*/')) inComment = false; + + // Skip empty lines and comments at the start + if (inComment || line.startsWith('//') || line.startsWith('*') || line === '') { + insertIndex = i + 1; + continue; + } + + // Track imports + if (line.startsWith('import ') || line.includes(' import ')) { + foundImports = true; + insertIndex = i + 1; + continue; + } + + // If we've seen imports and now see something else, insert before it + if (foundImports && !line.startsWith('import')) { + // Add a blank line after imports if not already there + if (lines[i - 1].trim() !== '') { + lines.splice(i, 0, ''); + i++; + } + break; + } + + // If no imports found, insert after copyright/header + if (!foundImports && (line.startsWith('export ') || line.startsWith('const ') || + line.startsWith('function ') || line.startsWith('class '))) { + break; + } + } + + // Insert the export statement + lines.splice(insertIndex, 0, exportStatement); + + // Write back to file + fs.writeFileSync(filePath, lines.join('\n'), 'utf-8'); + + return { skipped: false, platforms }; +} + +function main() { + console.log('🔧 Adding __supportedPlatforms to files...\n'); + + const files = findSourceFiles(LIB_DIR); + let added = 0; + let skipped = 0; + + for (const file of files) { + const result = addSupportedPlatforms(file); + const relativePath = path.relative(process.cwd(), file); + + if (result.skipped) { + skipped++; + } else { + added++; + console.log(`✅ ${relativePath} → [${result.platforms.join(', ')}]`); + } + } + + console.log(`\n📊 Summary:`); + console.log(` Added: ${added} files`); + console.log(` Skipped: ${skipped} files (already had export)`); + console.log(` Total: ${files.length} files\n`); + + console.log('✅ Done! Run npm run validate-platform-isolation to verify.\n'); +} + +if (require.main === module) { + main(); +} + +module.exports = { addSupportedPlatforms }; diff --git a/scripts/test-validator.js b/scripts/test-validator.js index 32cef60b2..d7900ef9f 100644 --- a/scripts/test-validator.js +++ b/scripts/test-validator.js @@ -30,31 +30,31 @@ console.log('=' .repeat(70)); console.log('\n1. UNIVERSAL IMPORTS (always compatible)'); console.log('-'.repeat(70)); test('Browser file can import universal', - validator.isPlatformCompatible('browser', null), true); + validator.isPlatformCompatible(['browser'], ['__universal__']), true); test('Node file can import universal', - validator.isPlatformCompatible('node', null), true); + validator.isPlatformCompatible(['node'], ['__universal__']), true); test('Multi-platform file can import universal', - validator.isPlatformCompatible(['browser', 'react_native'], null), true); + validator.isPlatformCompatible(['browser', 'react_native'], ['__universal__']), true); console.log('\n2. SINGLE PLATFORM FILES'); console.log('-'.repeat(70)); test('Browser file can import from browser file', - validator.isPlatformCompatible('browser', 'browser'), true); + validator.isPlatformCompatible(['browser'], ['browser']), true); test('Browser file CANNOT import from node file', - validator.isPlatformCompatible('browser', 'node'), false); + validator.isPlatformCompatible(['browser'], ['node']), false); test('Node file can import from node file', - validator.isPlatformCompatible('node', 'node'), true); + validator.isPlatformCompatible(['node'], ['node']), true); test('React Native file can import from react_native file', - validator.isPlatformCompatible('react_native', 'react_native'), true); + validator.isPlatformCompatible(['react_native'], ['react_native']), true); console.log('\n3. SINGLE PLATFORM IMPORTING FROM MULTI-PLATFORM'); console.log('-'.repeat(70)); test('Browser file CAN import from [browser, react_native] file', - validator.isPlatformCompatible('browser', ['browser', 'react_native']), true); + validator.isPlatformCompatible(['browser'], ['browser', 'react_native']), true); test('React Native file CAN import from [browser, react_native] file', - validator.isPlatformCompatible('react_native', ['browser', 'react_native']), true); + validator.isPlatformCompatible(['react_native'], ['browser', 'react_native']), true); test('Node file CANNOT import from [browser, react_native] file', - validator.isPlatformCompatible('node', ['browser', 'react_native']), false); + validator.isPlatformCompatible(['node'], ['browser', 'react_native']), false); console.log('\n4. MULTI-PLATFORM FILES (strictest rules)'); console.log('-'.repeat(70)); @@ -79,6 +79,11 @@ const platforms2 = validator.extractSupportedPlatforms(testExport2); test('Extract __supportedPlatforms with type annotation', JSON.stringify(platforms2), JSON.stringify(['browser', 'node'])); +const testExport3 = `export const __supportedPlatforms = ['__universal__'];`; +const platforms3 = validator.extractSupportedPlatforms(testExport3); +test('Extract __universal__ marker', + JSON.stringify(platforms3), JSON.stringify(['__universal__'])); + console.log('\n' + '='.repeat(70)); console.log(`\nResults: ${passed} passed, ${failed} failed`); diff --git a/scripts/validate-platform-isolation.js b/scripts/validate-platform-isolation.js index 4fe6b0aa4..f9deb8b7a 100644 --- a/scripts/validate-platform-isolation.js +++ b/scripts/validate-platform-isolation.js @@ -7,13 +7,15 @@ * from universal or compatible platform files. * * Platform Detection: - * 1. Files with naming convention: .browser.ts, .node.ts, .react_native.ts - * 2. Files exporting __supportedPlatforms array (for multi-platform support) + * - ALL source files (except tests) MUST export __supportedPlatforms array + * - Universal files use: export const __supportedPlatforms = ['__universal__']; + * - Platform-specific files use: export const __supportedPlatforms = ['browser', 'node']; + * - Legacy naming convention (.browser.ts, etc.) is deprecated * * Rules: * - Platform-specific files can only import from: - * - Universal files (no platform restrictions) - * - Files supporting the same platform + * - Universal files (marked with '__universal__') + * - Files supporting the same platforms * - External packages (node_modules) * * Usage: node scripts/validate-platform-isolation.js @@ -28,6 +30,9 @@ const LIB_DIR = path.join(__dirname, '..', 'lib'); // Cache for __supportedPlatforms exports const platformCache = new Map(); +// Track files missing __supportedPlatforms export +const filesWithoutExport = []; + /** * Extracts the platform from a filename using naming convention */ @@ -45,8 +50,10 @@ function getPlatformFromFilename(filename) { */ function extractSupportedPlatforms(content) { // Match: export const __supportedPlatforms = ['browser', 'react_native']; - // or: export const __supportedPlatforms: string[] = ['browser', 'react_native']; - const regex = /export\s+(?:const|let|var)\s+__supportedPlatforms\s*(?::\s*[^=]+)?\s*=\s*\[([^\]]+)\]/; + // or: export const __supportedPlatforms: Platform[] = ['browser', 'react_native']; + // or with satisfies: export const __supportedPlatforms = ['browser'] satisfies Platform[]; + // or universal: export const __supportedPlatforms = ['__universal__']; + const regex = /export\s+(?:const|let|var)\s+__supportedPlatforms\s*(?::\s*[^=]+)?\s*=\s*\[([^\]]+)\](?:\s+satisfies\s+[^;]+)?/; const match = content.match(regex); if (!match) { @@ -55,6 +62,12 @@ function extractSupportedPlatforms(content) { // Extract platform names from the array const platformsStr = match[1]; + + // Check for __universal__ marker + if (platformsStr.includes(`'__universal__'`) || platformsStr.includes(`"__universal__"`)) { + return ['__universal__']; + } + const platforms = []; for (const platform of PLATFORMS) { @@ -66,12 +79,20 @@ function extractSupportedPlatforms(content) { return platforms.length > 0 ? platforms : null; } +/** + * Check if file content has __supportedPlatforms export + */ +function hasSupportedPlatformsExport(content) { + return /export\s+(?:const|let|var)\s+__supportedPlatforms/.test(content); +} + /** * Gets the supported platforms for a file (with caching) * Returns: - * - string (single platform from filename) - * - string[] (multiple platforms from __supportedPlatforms) - * - null (universal, no restrictions) + * - string[] (platforms from __supportedPlatforms) + * - 'MISSING' (file is missing __supportedPlatforms export) + * + * Note: ALL files must have __supportedPlatforms export */ function getSupportedPlatforms(filePath) { // Check cache first @@ -81,9 +102,10 @@ function getSupportedPlatforms(filePath) { let result; - // Check for __supportedPlatforms export first (takes priority) try { const content = fs.readFileSync(filePath, 'utf-8'); + + // Check for __supportedPlatforms export const supportedPlatforms = extractSupportedPlatforms(content); if (supportedPlatforms) { @@ -91,22 +113,19 @@ function getSupportedPlatforms(filePath) { platformCache.set(filePath, result); return result; } + + // File exists but missing __supportedPlatforms export + result = 'MISSING'; + platformCache.set(filePath, result); + filesWithoutExport.push(filePath); + return result; + } catch (error) { - // If file doesn't exist or can't be read, try filename convention - } - - // Check filename convention - const platformFromFilename = getPlatformFromFilename(filePath); - if (platformFromFilename) { - result = platformFromFilename; + // If file doesn't exist or can't be read, return MISSING + result = 'MISSING'; platformCache.set(filePath, result); return result; } - - // Universal file - result = null; - platformCache.set(filePath, result); - return result; } /** @@ -125,22 +144,38 @@ function getPlatformName(platform) { * Formats platform info for display */ function formatPlatforms(platforms) { - if (!platforms) return 'Universal'; + if (platforms === 'MISSING') return 'MISSING __supportedPlatforms'; + if (!platforms || platforms.length === 0) return 'Unknown'; + if (Array.isArray(platforms) && platforms.length === 1 && platforms[0] === '__universal__') return 'Universal (all platforms)'; if (typeof platforms === 'string') return getPlatformName(platforms); return platforms.map(p => getPlatformName(p)).join(' + '); } +/** + * Checks if platforms represent universal (all platforms) + */ +function isUniversal(platforms) { + return Array.isArray(platforms) && + platforms.length === 1 && + platforms[0] === '__universal__'; +} + /** * Checks if a platform is compatible with target platforms * * Rules: - * - Universal imports (no platform restrictions) are always compatible - * - If the file has multiple platforms, the import must support ALL of them - * - If the file has a single platform, the import must support at least that one + * - If either file is MISSING __supportedPlatforms, not compatible + * - Universal files (all 3 platforms) are compatible with any file + * - The import must support ALL platforms that the file supports */ function isPlatformCompatible(filePlatforms, importPlatforms) { + // If either is missing __supportedPlatforms, not compatible + if (filePlatforms === 'MISSING' || importPlatforms === 'MISSING') { + return false; + } + // Universal imports are always compatible - if (!importPlatforms) { + if (isUniversal(importPlatforms)) { return true; } @@ -155,27 +190,40 @@ function isPlatformCompatible(filePlatforms, importPlatforms) { /** * Extract import statements from a TypeScript/JavaScript file + * Skips commented imports */ function extractImports(content) { const imports = []; + const lines = content.split('\n'); - // Match: import ... from '...' - const importRegex = /import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?['"]([^'"]+)['"]/g; - let match; - while ((match = importRegex.exec(content)) !== null) { - imports.push({ type: 'import', path: match[1], line: content.substring(0, match.index).split('\n').length }); - } - - // Match: require('...') - const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g; - while ((match = requireRegex.exec(content)) !== null) { - imports.push({ type: 'require', path: match[1], line: content.substring(0, match.index).split('\n').length }); - } - - // Match: import('...') - dynamic imports - const dynamicImportRegex = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g; - while ((match = dynamicImportRegex.exec(content)) !== null) { - imports.push({ type: 'dynamic-import', path: match[1], line: content.substring(0, match.index).split('\n').length }); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const trimmed = line.trim(); + + // Skip lines that are comments + if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*')) { + continue; + } + + // Match: import ... from '...' + const importMatch = /import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?['"]([^'"]+)['"]/.exec(line); + if (importMatch) { + imports.push({ type: 'import', path: importMatch[1], line: i + 1 }); + continue; + } + + // Match: require('...') + const requireMatch = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/.exec(line); + if (requireMatch) { + imports.push({ type: 'require', path: requireMatch[1], line: i + 1 }); + continue; + } + + // Match: import('...') - dynamic imports + const dynamicImportMatch = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/.exec(line); + if (dynamicImportMatch) { + imports.push({ type: 'dynamic-import', path: dynamicImportMatch[1], line: i + 1 }); + } } return imports; @@ -193,7 +241,20 @@ function resolveImportPath(importPath, currentFilePath) { const currentDir = path.dirname(currentFilePath); let resolved = path.resolve(currentDir, importPath); - // Check if file exists as-is + // Check if it's a directory - if so, look for index file + if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) { + const extensions = ['.ts', '.js', '.tsx', '.jsx']; + for (const ext of extensions) { + const indexFile = path.join(resolved, `index${ext}`); + if (fs.existsSync(indexFile)) { + return { isExternal: false, resolved: indexFile }; + } + } + // Directory exists but no index file found + return { isExternal: false, resolved }; + } + + // Check if file exists as-is (with extension already) if (fs.existsSync(resolved)) { return { isExternal: false, resolved }; } @@ -203,17 +264,15 @@ function resolveImportPath(importPath, currentFilePath) { for (const ext of extensions) { const withExt = resolved + ext; if (fs.existsSync(withExt)) { - resolved = withExt; - return { isExternal: false, resolved }; + return { isExternal: false, resolved: withExt }; } } - // Try index files + // Try index files (for cases where the directory doesn't exist yet) for (const ext of extensions) { const indexFile = path.join(resolved, `index${ext}`); if (fs.existsSync(indexFile)) { - resolved = indexFile; - return { isExternal: false, resolved }; + return { isExternal: false, resolved: indexFile }; } } @@ -228,9 +287,9 @@ function resolveImportPath(importPath, currentFilePath) { function validateFile(filePath) { const filePlatforms = getSupportedPlatforms(filePath); - // Skip if universal file - if (!filePlatforms) { - return { valid: true, errors: [] }; + // If file is missing __supportedPlatforms, that's a validation error handled separately + if (filePlatforms === 'MISSING') { + return { valid: true, errors: [] }; // Reported separately } const content = fs.readFileSync(filePath, 'utf-8'); @@ -249,12 +308,16 @@ function validateFile(filePath) { // Check compatibility if (!isPlatformCompatible(filePlatforms, importPlatforms)) { + const message = importPlatforms === 'MISSING' + ? `Import is missing __supportedPlatforms export: "${importInfo.path}"` + : `${formatPlatforms(filePlatforms)} file cannot import from ${formatPlatforms(importPlatforms)}-only file: "${importInfo.path}"`; + errors.push({ line: importInfo.line, importPath: importInfo.path, filePlatforms, importPlatforms, - message: `${formatPlatforms(filePlatforms)} file cannot import from ${formatPlatforms(importPlatforms)}-only file: "${importInfo.path}"` + message }); } } @@ -287,6 +350,7 @@ function findSourceFiles(dir, files = []) { !entry.name.endsWith('.test.ts') && !entry.name.endsWith('.tests.ts') && !entry.name.endsWith('.tests.js') && + !entry.name.endsWith('.umdtests.js') && !entry.name.endsWith('.test-d.ts') && !entry.name.endsWith('.d.ts')) { files.push(fullPath); @@ -304,14 +368,46 @@ function main() { console.log('🔍 Validating platform isolation...\n'); const files = findSourceFiles(LIB_DIR); - const platformFiles = files.filter(f => getSupportedPlatforms(f) !== null); - console.log(`Found ${files.length} source files (${platformFiles.length} platform-specific)\n`); + // First pass: check for __supportedPlatforms export + console.log(`Found ${files.length} source files\n`); + console.log('Checking for __supportedPlatforms exports...\n'); + + files.forEach(f => getSupportedPlatforms(f)); // Populate cache and filesWithoutExport + + // Report files missing __supportedPlatforms + if (filesWithoutExport.length > 0) { + console.error(`❌ Found ${filesWithoutExport.length} file(s) missing __supportedPlatforms export:\n`); + + for (const file of filesWithoutExport) { + const relativePath = path.relative(process.cwd(), file); + console.error(` 📄 ${relativePath}`); + } + + console.error('\n'); + console.error('REQUIRED: Every source file must export __supportedPlatforms array'); + console.error(''); + console.error('Examples:'); + console.error(' // Platform-specific file'); + console.error(' export const __supportedPlatforms = [\'browser\', \'react_native\'];'); + console.error(''); + console.error(' // Universal file (all platforms)'); + console.error(' export const __supportedPlatforms = [\'browser\', \'node\', \'react_native\'];'); + console.error(''); + console.error('See lib/platform_support.ts for type definitions.\n'); + + process.exit(1); + } + + console.log('✅ All files have __supportedPlatforms export\n'); + + // Second pass: validate platform isolation + console.log('Validating platform compatibility...\n'); let totalErrors = 0; const filesWithErrors = []; - for (const file of platformFiles) { + for (const file of files) { const result = validateFile(file); if (!result.valid) { @@ -321,7 +417,7 @@ function main() { } if (totalErrors === 0) { - console.log('✅ All platform-specific files are properly isolated!\n'); + console.log('✅ All files are properly isolated!\n'); process.exit(0); } else { console.error(`❌ Found ${totalErrors} platform isolation violation(s) in ${filesWithErrors.length} file(s):\n`); @@ -338,11 +434,9 @@ function main() { console.error('\n'); console.error('Platform isolation rules:'); - console.error(' - Platform-specific files can only import from universal or compatible platform files'); - console.error(' - Specify platforms using:'); - console.error(' 1. Naming convention: .browser.ts, .node.ts, .react_native.ts'); - console.error(' 2. Export __supportedPlatforms array: export const __supportedPlatforms = [\'browser\', \'react_native\'];'); - console.error(' - Universal files (no platform restrictions) can be imported by any platform\n'); + console.error(' - Files can only import from files supporting ALL their platforms'); + console.error(' - Universal files ([browser, node, react_native]) can be imported by any file'); + console.error(' - All files must have __supportedPlatforms export\n'); process.exit(1); } @@ -364,5 +458,7 @@ module.exports = { getSupportedPlatforms, extractImports, extractSupportedPlatforms, - isPlatformCompatible + isPlatformCompatible, + isUniversal, + hasSupportedPlatformsExport }; From 965b073324092bb8a6a5365a7a94cac5cc20e193 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Mon, 24 Nov 2025 19:20:58 +0600 Subject: [PATCH 04/40] use ts parser --- lib/core/bucketer/index.ts | 5 +- .../index.ts | 3 - package.json | 1 + scripts/validate-platform-isolation-ts.js | 528 ++++++++++++++++++ 4 files changed, 531 insertions(+), 6 deletions(-) create mode 100644 scripts/validate-platform-isolation-ts.js diff --git a/lib/core/bucketer/index.ts b/lib/core/bucketer/index.ts index 610d868a2..27cd169ed 100644 --- a/lib/core/bucketer/index.ts +++ b/lib/core/bucketer/index.ts @@ -19,9 +19,6 @@ */ import { LoggerFacade } from '../../logging/logger'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - DecisionResponse, BucketerParams, TrafficAllocation, @@ -32,6 +29,8 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { generateBucketValue } from './bucket_value_generator'; import { DecisionReason } from '../decision_service'; +export const __supportedPlatforms = ['__universal__'] as const; + export const USER_NOT_IN_ANY_EXPERIMENT = 'User %s is not in any experiment of group %s.'; export const USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP = 'User %s is not in experiment %s of group %s.'; export const USER_BUCKETED_INTO_EXPERIMENT_IN_GROUP = 'User %s is in experiment %s of group %s.'; diff --git a/lib/core/custom_attribute_condition_evaluator/index.ts b/lib/core/custom_attribute_condition_evaluator/index.ts index cbb3606de..797a7d4e0 100644 --- a/lib/core/custom_attribute_condition_evaluator/index.ts +++ b/lib/core/custom_attribute_condition_evaluator/index.ts @@ -18,9 +18,6 @@ import { Condition, OptimizelyUserContext } from '../../shared_types'; import fns from '../../utils/fns'; import { compareVersion } from '../../utils/semantic_version'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - MISSING_ATTRIBUTE_VALUE, UNEXPECTED_TYPE_NULL, } from 'log_message'; diff --git a/package.json b/package.json index dfcde6255..bb2cccd16 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "clean:win": "(if exist dist rd /s/q dist)", "lint": "tsc --noEmit && eslint 'lib/**/*.js' 'lib/**/*.ts'", "validate-platform-isolation": "node scripts/validate-platform-isolation.js", + "validate-platform-isolation-ts": "node scripts/validate-platform-isolation-ts.js", "test-platform-isolation": "node scripts/test-validator.js", "add-platform-exports": "node scripts/add-platform-exports.js", "test-vitest": "vitest run", diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js new file mode 100644 index 000000000..2fb8e8033 --- /dev/null +++ b/scripts/validate-platform-isolation-ts.js @@ -0,0 +1,528 @@ +#!/usr/bin/env node + +/** + * Platform Isolation Validator (using TypeScript parser) + * + * This script ensures that platform-specific entry points only import + * from universal or compatible platform files. + * + * Uses TypeScript compiler API for robust parsing instead of regex. + * + * Platform Detection: + * - ALL source files (except tests) MUST export __supportedPlatforms array + * - Universal files use: export const __supportedPlatforms = ['__universal__']; + * - Platform-specific files use: export const __supportedPlatforms = ['browser', 'node']; + * + * Rules: + * - Platform-specific files can only import from: + * - Universal files (marked with '__universal__') + * - Files supporting the same platforms + * - External packages (node_modules) + * + * Usage: node scripts/validate-platform-isolation-ts.js + */ + +const fs = require('fs'); +const path = require('path'); +const ts = require('typescript'); + +const PLATFORMS = ['browser', 'node', 'react_native']; +const LIB_DIR = path.join(__dirname, '..', 'lib'); + +// Cache for __supportedPlatforms exports +const platformCache = new Map(); + +// Track files missing __supportedPlatforms export +const filesWithoutExport = []; + +/** + * Extracts the platform from a filename using naming convention + */ +function getPlatformFromFilename(filename) { + for (const platform of PLATFORMS) { + if (filename.includes(`.${platform}.`)) { + return platform; + } + } + return null; +} + +/** + * Extracts __supportedPlatforms array from AST + */ +function extractSupportedPlatformsFromAST(sourceFile) { + let platforms = null; + + function visit(node) { + // Look for: export const __supportedPlatforms = [...] + if (ts.isVariableStatement(node)) { + // Check if it has export modifier + const hasExport = node.modifiers?.some( + mod => mod.kind === ts.SyntaxKind.ExportKeyword + ); + + if (hasExport) { + for (const declaration of node.declarationList.declarations) { + if (ts.isVariableDeclaration(declaration) && + ts.isIdentifier(declaration.name) && + declaration.name.text === '__supportedPlatforms') { + + let initializer = declaration.initializer; + + // Handle "as const" assertion: [...] as const + if (initializer && ts.isAsExpression(initializer)) { + initializer = initializer.expression; + } + + // Handle type assertion: [...] + if (initializer && ts.isTypeAssertionExpression(initializer)) { + initializer = initializer.expression; + } + + // Extract array elements + if (initializer && ts.isArrayLiteralExpression(initializer)) { + platforms = []; + for (const element of initializer.elements) { + if (ts.isStringLiteral(element)) { + platforms.push(element.text); + } + } + return; // Found it, stop visiting + } + } + } + } + } + + ts.forEachChild(node, visit); + } + + visit(sourceFile); + return platforms; +} + +/** + * Gets the supported platforms for a file (with caching) + * Returns: + * - string[] (platforms from __supportedPlatforms) + * - 'MISSING' (file is missing __supportedPlatforms export) + * + * Note: ALL files must have __supportedPlatforms export + */ +function getSupportedPlatforms(filePath) { + // Check cache first + if (platformCache.has(filePath)) { + return platformCache.get(filePath); + } + + let result; + + try { + const content = fs.readFileSync(filePath, 'utf-8'); + + // Parse with TypeScript + const sourceFile = ts.createSourceFile( + filePath, + content, + ts.ScriptTarget.Latest, + true + ); + + // Extract platforms from AST + const supportedPlatforms = extractSupportedPlatformsFromAST(sourceFile); + + if (supportedPlatforms && supportedPlatforms.length > 0) { + result = supportedPlatforms; + platformCache.set(filePath, result); + return result; + } + + // File exists but missing __supportedPlatforms export + result = 'MISSING'; + platformCache.set(filePath, result); + filesWithoutExport.push(filePath); + return result; + + } catch (error) { + // If file doesn't exist or can't be read, return MISSING + result = 'MISSING'; + platformCache.set(filePath, result); + return result; + } +} + +/** + * Gets a human-readable platform name + */ +function getPlatformName(platform) { + const names = { + 'browser': 'Browser', + 'node': 'Node.js', + 'react_native': 'React Native' + }; + return names[platform] || platform; +} + +/** + * Formats platform info for display + */ +function formatPlatforms(platforms) { + if (platforms === 'MISSING') return 'MISSING __supportedPlatforms'; + if (!platforms || platforms.length === 0) return 'Unknown'; + if (Array.isArray(platforms) && platforms.length === 1 && platforms[0] === '__universal__') return 'Universal (all platforms)'; + if (typeof platforms === 'string') return getPlatformName(platforms); + return platforms.map(p => getPlatformName(p)).join(' + '); +} + +/** + * Checks if platforms represent universal (all platforms) + */ +function isUniversal(platforms) { + return Array.isArray(platforms) && + platforms.length === 1 && + platforms[0] === '__universal__'; +} + +/** + * Checks if a platform is compatible with target platforms + * + * Rules: + * - If either file is MISSING __supportedPlatforms, not compatible + * - Universal files are compatible with any file + * - The import must support ALL platforms that the file supports + */ +function isPlatformCompatible(filePlatforms, importPlatforms) { + // If either is missing platforms, not compatible + if (filePlatforms === 'MISSING' || importPlatforms === 'MISSING') { + return false; + } + + // If import is universal, always compatible + if (isUniversal(importPlatforms)) { + return true; + } + + // If file is universal, import must be universal too + if (isUniversal(filePlatforms)) { + return isUniversal(importPlatforms); + } + + // Otherwise, import must support ALL platforms that the file supports + // filePlatforms is an array of platforms the file needs + // importPlatforms is an array of platforms the import provides + + for (const platform of filePlatforms) { + if (!importPlatforms.includes(platform)) { + return false; + } + } + + return true; +} + +/** + * Extract imports using TypeScript AST + */ +function extractImports(filePath) { + const content = fs.readFileSync(filePath, 'utf-8'); + const imports = []; + + const sourceFile = ts.createSourceFile( + filePath, + content, + ts.ScriptTarget.Latest, + true + ); + + function visit(node) { + // Import declarations: import ... from '...' + if (ts.isImportDeclaration(node)) { + const moduleSpecifier = node.moduleSpecifier; + if (ts.isStringLiteral(moduleSpecifier)) { + imports.push({ + type: 'import', + path: moduleSpecifier.text, + line: sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1 + }); + } + } + + // Export declarations: export ... from '...' + if (ts.isExportDeclaration(node) && node.moduleSpecifier) { + if (ts.isStringLiteral(node.moduleSpecifier)) { + imports.push({ + type: 'export', + path: node.moduleSpecifier.text, + line: sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1 + }); + } + } + + // Call expressions: require('...') or import('...') + if (ts.isCallExpression(node)) { + const expression = node.expression; + + // require('...') + if (ts.isIdentifier(expression) && expression.text === 'require') { + const arg = node.arguments[0]; + if (arg && ts.isStringLiteral(arg)) { + imports.push({ + type: 'require', + path: arg.text, + line: sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1 + }); + } + } + + // import('...') + if (expression.kind === ts.SyntaxKind.ImportKeyword) { + const arg = node.arguments[0]; + if (arg && ts.isStringLiteral(arg)) { + imports.push({ + type: 'dynamic-import', + path: arg.text, + line: sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1 + }); + } + } + } + + ts.forEachChild(node, visit); + } + + visit(sourceFile); + return imports; +} + +/** + * Resolve import path relative to current file + */ +function resolveImportPath(importPath, currentFilePath) { + // External imports (node_modules) - return as-is + if (!importPath.startsWith('.') && !importPath.startsWith('/')) { + return { isExternal: true, resolved: importPath }; + } + + const currentDir = path.dirname(currentFilePath); + let resolved = path.resolve(currentDir, importPath); + + // Check if it's a directory - if so, look for index file + if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) { + const extensions = ['.ts', '.js', '.tsx', '.jsx']; + for (const ext of extensions) { + const indexFile = path.join(resolved, `index${ext}`); + if (fs.existsSync(indexFile)) { + return { isExternal: false, resolved: indexFile }; + } + } + // Directory exists but no index file found + return { isExternal: false, resolved }; + } + + // Check if file exists as-is (with extension already) + if (fs.existsSync(resolved)) { + return { isExternal: false, resolved }; + } + + // Try different extensions + const extensions = ['.ts', '.js', '.tsx', '.jsx']; + for (const ext of extensions) { + const withExt = resolved + ext; + if (fs.existsSync(withExt)) { + return { isExternal: false, resolved: withExt }; + } + } + + // Try index files (for cases where the directory doesn't exist yet) + for (const ext of extensions) { + const indexFile = path.join(resolved, `index${ext}`); + if (fs.existsSync(indexFile)) { + return { isExternal: false, resolved: indexFile }; + } + } + + // Return the resolved path even if it doesn't exist + // (getSupportedPlatforms will handle it) + return { isExternal: false, resolved }; +} + +/** + * Validate a single file + */ +function validateFile(filePath) { + const filePlatforms = getSupportedPlatforms(filePath); + + // If file is missing __supportedPlatforms, that's a validation error handled separately + if (filePlatforms === 'MISSING') { + return { valid: true, errors: [] }; // Reported separately + } + + const imports = extractImports(filePath); + const errors = []; + + for (const importInfo of imports) { + const { isExternal, resolved } = resolveImportPath(importInfo.path, filePath); + + // External imports are always allowed + if (isExternal) { + continue; + } + + const importPlatforms = getSupportedPlatforms(resolved); + + // Check compatibility + if (!isPlatformCompatible(filePlatforms, importPlatforms)) { + const message = importPlatforms === 'MISSING' + ? `Import is missing __supportedPlatforms export: "${importInfo.path}"` + : `${formatPlatforms(filePlatforms)} file cannot import from ${formatPlatforms(importPlatforms)}-only file: "${importInfo.path}"`; + + errors.push({ + line: importInfo.line, + importPath: importInfo.path, + filePlatforms, + importPlatforms, + message + }); + } + } + + return { valid: errors.length === 0, errors }; +} + +/** + * Recursively find all TypeScript/JavaScript files in a directory + */ +function findSourceFiles(dir, files = []) { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + + if (entry.isDirectory()) { + // Skip test directories and node_modules + if (!entry.name.startsWith('.') && + entry.name !== 'node_modules' && + entry.name !== 'dist' && + entry.name !== 'coverage' && + entry.name !== 'tests') { + findSourceFiles(fullPath, files); + } + } else if (entry.isFile()) { + // Only include TypeScript and JavaScript files, skip test files + if ((entry.name.endsWith('.ts') || entry.name.endsWith('.js')) && + !entry.name.endsWith('.spec.ts') && + !entry.name.endsWith('.test.ts') && + !entry.name.endsWith('.tests.ts') && + !entry.name.endsWith('.tests.js') && + !entry.name.endsWith('.umdtests.js') && + !entry.name.endsWith('.test-d.ts') && + !entry.name.endsWith('.d.ts')) { + files.push(fullPath); + } + } + } + + return files; +} + +/** + * Main validation function + */ +function main() { + console.log('🔍 Validating platform isolation (using TypeScript parser)...\n'); + + const files = findSourceFiles(LIB_DIR); + + // First pass: check for __supportedPlatforms export + console.log(`Found ${files.length} source files\n`); + console.log('Checking for __supportedPlatforms exports...\n'); + + files.forEach(f => getSupportedPlatforms(f)); // Populate cache and filesWithoutExport + + // Report files missing __supportedPlatforms + if (filesWithoutExport.length > 0) { + console.error(`❌ Found ${filesWithoutExport.length} file(s) missing __supportedPlatforms export:\n`); + + for (const file of filesWithoutExport) { + const relativePath = path.relative(process.cwd(), file); + console.error(` 📄 ${relativePath}`); + } + + console.error('\n'); + console.error('REQUIRED: Every source file must export __supportedPlatforms array'); + console.error(''); + console.error('Examples:'); + console.error(' // Platform-specific file'); + console.error(' export const __supportedPlatforms = [\'browser\', \'react_native\'];'); + console.error(''); + console.error(' // Universal file (all platforms)'); + console.error(' export const __supportedPlatforms = [\'__universal__\'];'); + console.error(''); + console.error('See lib/platform_support.ts for type definitions.\n'); + + process.exit(1); + } + + console.log('✅ All files have __supportedPlatforms export\n'); + + // Second pass: validate platform isolation + console.log('Validating platform compatibility...\n'); + + let totalErrors = 0; + const filesWithErrors = []; + + for (const file of files) { + const result = validateFile(file); + + if (!result.valid) { + totalErrors += result.errors.length; + filesWithErrors.push({ file, errors: result.errors }); + } + } + + if (totalErrors === 0) { + console.log('✅ All files are properly isolated!\n'); + process.exit(0); + } else { + console.error(`❌ Found ${totalErrors} platform isolation violation(s) in ${filesWithErrors.length} file(s):\n`); + + for (const { file, errors } of filesWithErrors) { + const relativePath = path.relative(process.cwd(), file); + const filePlatforms = getSupportedPlatforms(file); + console.error(`\n📄 ${relativePath} [${formatPlatforms(filePlatforms)}]`); + + for (const error of errors) { + console.error(` Line ${error.line}: ${error.message}`); + } + } + + console.error('\n'); + console.error('Platform isolation rules:'); + console.error(' - Files can only import from files supporting ALL their platforms'); + console.error(' - Universal files ([\'__universal__\']) can be imported by any file'); + console.error(' - All files must have __supportedPlatforms export\n'); + + process.exit(1); + } +} + +// Export functions for testing +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + isPlatformCompatible, + extractSupportedPlatformsFromAST, + getSupportedPlatforms, + extractImports, + }; +} + +// Run the validator +if (require.main === module) { + try { + main(); + } catch (error) { + console.error('❌ Validation failed with error:', error.message); + console.error(error.stack); + process.exit(1); + } +} From 87fcd5d676ad1ef3e752af017615e7d7693e05e3 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 00:01:21 +0600 Subject: [PATCH 05/40] add eslint rule for platform export --- .eslintrc.js | 9 + ESLINT_TROUBLESHOOTING.md | 84 +++++++++ eslint-local-rules/README.md | 77 ++++++++ eslint-local-rules/index.js | 10 + .../require-platform-declaration.js | 113 ++++++++++++ lib/platform_support.ts | 2 + package-lock.json | 172 ++++++++++++++++++ package.json | 2 + 8 files changed, 469 insertions(+) create mode 100644 ESLINT_TROUBLESHOOTING.md create mode 100644 eslint-local-rules/README.md create mode 100644 eslint-local-rules/index.js create mode 100644 eslint-local-rules/require-platform-declaration.js diff --git a/.eslintrc.js b/.eslintrc.js index 20feb2cb4..e8764b0d9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -8,6 +8,8 @@ module.exports = { 'eslint:recommended', 'plugin:@typescript-eslint/recommended', ], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint', 'local-rules'], globals: { Atomics: 'readonly', SharedArrayBuffer: 'readonly', @@ -25,6 +27,13 @@ module.exports = { 'rules': { '@typescript-eslint/explicit-module-boundary-types': ['error'] } + }, + { + 'files': ['*.ts', '!*.spec.ts', '!*.test.ts', '!*.tests.ts', '!*.test-d.ts'], + 'excludedFiles': ['**/__mocks__/**', '**/tests/**'], + 'rules': { + 'local-rules/require-platform-declaration': 'error', + } } ], rules: { diff --git a/ESLINT_TROUBLESHOOTING.md b/ESLINT_TROUBLESHOOTING.md new file mode 100644 index 000000000..8f5bb949b --- /dev/null +++ b/ESLINT_TROUBLESHOOTING.md @@ -0,0 +1,84 @@ +# ESLint Rule Troubleshooting + +## The Rule is Working! + +The `require-platform-declaration` rule **is** working correctly from the command line: + +```bash +$ npx eslint lib/core/custom_attribute_condition_evaluator/index.ts + +lib/core/custom_attribute_condition_evaluator/index.ts + 16:1 error File must export __supportedPlatforms to declare which platforms + it supports. Example: export const __supportedPlatforms = ['__universal__'] as const; +``` + +## VSCode Not Showing Errors? + +If VSCode isn't showing the ESLint errors, try these steps: + +### 1. Restart ESLint Server +- Open Command Palette: `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Windows/Linux) +- Type: `ESLint: Restart ESLint Server` +- Press Enter + +### 2. Check ESLint Extension is Installed +- Open Extensions panel: `Cmd+Shift+X` (Mac) or `Ctrl+Shift+X` (Windows/Linux) +- Search for "ESLint" by Microsoft +- Make sure it's installed and enabled + +### 3. Check ESLint Output +- Open Output panel: `Cmd+Shift+U` (Mac) or `Ctrl+Shift+U` (Windows/Linux) +- Select "ESLint" from the dropdown +- Look for any error messages + +### 4. Reload VSCode Window +- Open Command Palette: `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Windows/Linux) +- Type: `Developer: Reload Window` +- Press Enter + +### 5. Check File is Being Linted +The rule only applies to: +- ✅ Files in `lib/` or `src/` directory +- ✅ TypeScript files (`.ts`) +- ❌ Test files (`.spec.ts`, `.test.ts`, etc.) +- ❌ Declaration files (`.d.ts`) + +### 6. Verify ESLint Configuration +Check that `.eslintrc.js` has the parser set: +```javascript +parser: '@typescript-eslint/parser', +``` + +And that the rule is in the overrides: +```javascript +overrides: [{ + files: ['*.ts', '!*.spec.ts', '!*.test.ts', '!*.tests.ts', '!*.test-d.ts'], + rules: { + 'local-rules/require-platform-declaration': 'error', + } +}] +``` + +## Manual Verification + +You can always verify the rule works by running: + +```bash +# Check a specific file +npx eslint lib/service.ts + +# Check all lib files (shows only errors) +npx eslint lib/**/*.ts --quiet +``` + +## Adding __supportedPlatforms + +To fix the error, add this export to your file (after imports): + +```typescript +// Universal file (all platforms) +export const __supportedPlatforms = ['__universal__'] as const; + +// OR platform-specific file +export const __supportedPlatforms = ['browser', 'node'] as const; +``` diff --git a/eslint-local-rules/README.md b/eslint-local-rules/README.md new file mode 100644 index 000000000..05672838f --- /dev/null +++ b/eslint-local-rules/README.md @@ -0,0 +1,77 @@ +# Local ESLint Rules + +This directory contains custom ESLint rules specific to this project. + +## Rules + +### `require-platform-declaration` + +**Purpose:** Ensures all source files (except tests) export `__supportedPlatforms` to declare which platforms they support. + +**Why:** This enforces platform isolation at the linting level, catching missing declarations before build time. + +**Enforcement:** +- ✅ Enabled for all `.ts` files in `lib/` directory +- ❌ Disabled for test files (`.spec.ts`, `.test.ts`, etc.) +- ❌ Disabled for `__mocks__` and `tests` directories + +**Valid Examples:** + +```typescript +// Universal file (all platforms) +export const __supportedPlatforms = ['__universal__'] as const; + +// Platform-specific file +export const __supportedPlatforms = ['browser', 'node'] as const; + +// With type annotation +export const __supportedPlatforms: Platform[] = ['react_native'] as const; +``` + +**Invalid:** + +```typescript +// Missing __supportedPlatforms export +// ESLint Error: File must export __supportedPlatforms to declare which platforms it supports +``` + +## Configuration + +The rules are loaded via `eslint-plugin-local-rules` and configured in `.eslintrc.js`: + +```javascript +{ + plugins: ['local-rules'], + overrides: [{ + files: ['*.ts', '!*.spec.ts', '!*.test.ts'], + rules: { + 'local-rules/require-platform-declaration': 'error' + } + }] +} +``` + +## Adding New Rules + +1. Create a new rule file in this directory (e.g., `my-rule.js`) +2. Export the rule following ESLint's rule format +3. Add it to `index.js`: + ```javascript + module.exports = { + 'require-platform-declaration': require('./require-platform-declaration'), + 'my-rule': require('./my-rule'), // Add here + }; + ``` +4. Enable it in `.eslintrc.js` + +## Testing Rules + +Run ESLint on specific files to test: + +```bash +# Test on a specific file +npx eslint lib/service.ts + +# Test on all lib files +npx eslint lib/**/*.ts --quiet +``` diff --git a/eslint-local-rules/index.js b/eslint-local-rules/index.js new file mode 100644 index 000000000..587ccd041 --- /dev/null +++ b/eslint-local-rules/index.js @@ -0,0 +1,10 @@ +/** + * Local ESLint Rules + * + * Custom ESLint rules for the project. + * Loaded by eslint-plugin-local-rules. + */ + +module.exports = { + 'require-platform-declaration': require('./require-platform-declaration'), +}; diff --git a/eslint-local-rules/require-platform-declaration.js b/eslint-local-rules/require-platform-declaration.js new file mode 100644 index 000000000..170549dba --- /dev/null +++ b/eslint-local-rules/require-platform-declaration.js @@ -0,0 +1,113 @@ +/** + * ESLint Rule: require-platform-declaration + * + * Ensures that all non-test source files export __supportedPlatforms + * + * Valid: + * export const __supportedPlatforms = ['browser'] as const; + * export const __supportedPlatforms = ['__universal__'] as const; + * export const __supportedPlatforms: Platform[] = ['browser', 'node']; + * + * Invalid: + * // Missing __supportedPlatforms export + */ + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'Require __supportedPlatforms export in all source files', + category: 'Best Practices', + recommended: true, + }, + messages: { + missingPlatformDeclaration: 'File must export __supportedPlatforms to declare which platforms it supports. Example: export const __supportedPlatforms = [\'__universal__\'] as const;', + invalidPlatformDeclaration: '__supportedPlatforms must be exported as a const array. Example: export const __supportedPlatforms = [\'browser\', \'node\'] as const;', + }, + schema: [], + }, + + create(context) { + const filename = context.getFilename(); + + // Skip test files + if (filename.endsWith('.spec.ts') || + filename.endsWith('.test.ts') || + filename.endsWith('.tests.ts') || + filename.endsWith('.test.js') || + filename.endsWith('.spec.js') || + filename.endsWith('.tests.js') || + filename.endsWith('.test-d.ts') || + filename.endsWith('.d.ts') || + filename.includes('/__mocks__/') || + filename.includes('/tests/')) { + return {}; + } + + // Skip non-source files + if (!filename.includes('/lib/') && !filename.includes('/src/')) { + return {}; + } + + let hasPlatformExport = false; + let isValidExport = false; + + return { + ExportNamedDeclaration(node) { + // Check for: export const __supportedPlatforms = [...] + if (node.declaration && + node.declaration.type === 'VariableDeclaration') { + + for (const declarator of node.declaration.declarations) { + if (declarator.id.type === 'Identifier' && + declarator.id.name === '__supportedPlatforms') { + + hasPlatformExport = true; + + // Validate it's a const + if (node.declaration.kind !== 'const') { + context.report({ + node: declarator, + messageId: 'invalidPlatformDeclaration', + }); + return; + } + + // Validate it's an array + let init = declarator.init; + + // Handle TSAsExpression: [...] as const + if (init && init.type === 'TSAsExpression') { + init = init.expression; + } + + // Handle TSTypeAssertion: [...] + if (init && init.type === 'TSTypeAssertion') { + init = init.expression; + } + + if (init && init.type === 'ArrayExpression') { + isValidExport = true; + } else { + context.report({ + node: declarator, + messageId: 'invalidPlatformDeclaration', + }); + } + } + } + } + }, + + 'Program:exit'(node) { + // At the end of the file, check if __supportedPlatforms was exported + if (!hasPlatformExport) { + context.report({ + node, + messageId: 'missingPlatformDeclaration', + }); + } + }, + }; + }, +}; diff --git a/lib/platform_support.ts b/lib/platform_support.ts index 39a94dedb..aa3982f70 100644 --- a/lib/platform_support.ts +++ b/lib/platform_support.ts @@ -10,6 +10,8 @@ */ export type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; +export const __supportedPlatforms = ['__universal__'] as const; + /** * Platform support declaration * diff --git a/package-lock.json b/package-lock.json index deb59a64d..14162e7ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "uuid": "^10.0.0" }, "devDependencies": { + "@biomejs/biome": "^2.3.7", "@react-native-async-storage/async-storage": "^2", "@react-native-community/netinfo": "^11.3.2", "@rollup/plugin-commonjs": "^11.0.2", @@ -32,6 +33,7 @@ "coveralls-next": "^4.2.0", "eslint": "^8.21.0", "eslint-config-prettier": "^6.10.0", + "eslint-plugin-local-rules": "^3.0.2", "eslint-plugin-prettier": "^3.1.2", "happy-dom": "^16.6.0", "jiti": "^2.4.1", @@ -2484,6 +2486,169 @@ "node": ">=6.9.0" } }, + "node_modules/@biomejs/biome": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.7.tgz", + "integrity": "sha512-CTbAS/jNAiUc6rcq94BrTB8z83O9+BsgWj2sBCQg9rD6Wkh2gjfR87usjx0Ncx0zGXP1NKgT7JNglay5Zfs9jw==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.3.7", + "@biomejs/cli-darwin-x64": "2.3.7", + "@biomejs/cli-linux-arm64": "2.3.7", + "@biomejs/cli-linux-arm64-musl": "2.3.7", + "@biomejs/cli-linux-x64": "2.3.7", + "@biomejs/cli-linux-x64-musl": "2.3.7", + "@biomejs/cli-win32-arm64": "2.3.7", + "@biomejs/cli-win32-x64": "2.3.7" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.7.tgz", + "integrity": "sha512-LirkamEwzIUULhXcf2D5b+NatXKeqhOwilM+5eRkbrnr6daKz9rsBL0kNZ16Hcy4b8RFq22SG4tcLwM+yx/wFA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.7.tgz", + "integrity": "sha512-Q4TO633kvrMQkKIV7wmf8HXwF0dhdTD9S458LGE24TYgBjSRbuhvio4D5eOQzirEYg6eqxfs53ga/rbdd8nBKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.7.tgz", + "integrity": "sha512-inHOTdlstUBzgjDcx0ge71U4SVTbwAljmkfi3MC5WzsYCRhancqfeL+sa4Ke6v2ND53WIwCFD5hGsYExoI3EZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.7.tgz", + "integrity": "sha512-/afy8lto4CB8scWfMdt+NoCZtatBUF62Tk3ilWH2w8ENd5spLhM77zKlFZEvsKJv9AFNHknMl03zO67CiklL2Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.7.tgz", + "integrity": "sha512-fJMc3ZEuo/NaMYo5rvoWjdSS5/uVSW+HPRQujucpZqm2ZCq71b8MKJ9U4th9yrv2L5+5NjPF0nqqILCl8HY/fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.7.tgz", + "integrity": "sha512-CQUtgH1tIN6e5wiYSJqzSwJumHYolNtaj1dwZGCnZXm2PZU1jOJof9TsyiP3bXNDb+VOR7oo7ZvY01If0W3iFQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.7.tgz", + "integrity": "sha512-aJAE8eCNyRpcfx2JJAtsPtISnELJ0H4xVVSwnxm13bzI8RwbXMyVtxy2r5DV1xT3WiSP+7LxORcApWw0LM8HiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.7.tgz", + "integrity": "sha512-pulzUshqv9Ed//MiE8MOUeeEkbkSHVDVY5Cz5wVAnH1DUqliCQG3j6s1POaITTFqFfo7AVIx2sWdKpx/GS+Nqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -7307,6 +7472,13 @@ "eslint": ">=3.14.1" } }, + "node_modules/eslint-plugin-local-rules": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-local-rules/-/eslint-plugin-local-rules-3.0.2.tgz", + "integrity": "sha512-IWME7GIYHXogTkFsToLdBCQVJ0U4kbSuVyDT+nKoR4UgtnVrrVeNWuAZkdEu1nxkvi9nsPccGehEEF6dgA28IQ==", + "dev": true, + "license": "MIT" + }, "node_modules/eslint-plugin-prettier": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz", diff --git a/package.json b/package.json index bb2cccd16..b411352f5 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "uuid": "^10.0.0" }, "devDependencies": { + "@biomejs/biome": "^2.3.7", "@react-native-async-storage/async-storage": "^2", "@react-native-community/netinfo": "^11.3.2", "@rollup/plugin-commonjs": "^11.0.2", @@ -118,6 +119,7 @@ "coveralls-next": "^4.2.0", "eslint": "^8.21.0", "eslint-config-prettier": "^6.10.0", + "eslint-plugin-local-rules": "^3.0.2", "eslint-plugin-prettier": "^3.1.2", "happy-dom": "^16.6.0", "jiti": "^2.4.1", From 7729865290c30cf18601c0e82bccc16b9b04f381 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 01:15:35 +0600 Subject: [PATCH 06/40] eslint rule update --- .../require-platform-declaration.js | 123 ++++++++++++++++-- lib/client_factory.ts | 2 +- lib/common_exports.ts | 2 +- lib/core/audience_evaluator/index.ts | 2 +- .../odp_segment_condition_evaluator/index.ts | 2 +- lib/core/bucketer/bucket_value_generator.ts | 2 +- lib/core/bucketer/index.ts | 2 +- lib/core/condition_tree_evaluator/index.ts | 2 +- lib/core/decision/index.ts | 2 +- lib/core/decision_service/cmab/cmab_client.ts | 2 +- .../decision_service/cmab/cmab_service.ts | 6 +- lib/core/decision_service/index.ts | 7 +- lib/entrypoint.test-d.ts | 4 + lib/entrypoint.universal.test-d.ts | 4 + lib/error/error_handler.ts | 2 +- lib/error/error_notifier.ts | 2 +- lib/error/error_notifier_factory.ts | 2 +- lib/error/error_reporter.ts | 2 +- lib/error/optimizly_error.ts | 2 +- .../batch_event_processor.react_native.ts | 2 +- lib/event_processor/batch_event_processor.ts | 2 +- .../event_builder/log_event.ts | 2 +- .../event_builder/user_event.ts | 6 +- .../default_dispatcher.node.ts | 2 +- .../event_dispatcher/default_dispatcher.ts | 2 +- .../event_dispatcher/event_dispatcher.ts | 2 +- .../event_dispatcher_factory.ts | 2 +- .../send_beacon_dispatcher.browser.ts | 2 +- lib/event_processor/event_processor.ts | 2 +- .../event_processor_factory.browser.ts | 6 +- .../event_processor_factory.node.ts | 6 +- .../event_processor_factory.react_native.ts | 6 +- .../event_processor_factory.ts | 2 +- .../event_processor_factory.universal.ts | 6 +- lib/event_processor/event_store.ts | 6 +- .../forwarding_event_processor.ts | 2 +- lib/export_types.ts | 2 +- lib/feature_toggle.ts | 2 +- lib/index.browser.ts | 3 +- lib/index.node.ts | 3 +- lib/index.react_native.ts | 3 +- lib/index.universal.ts | 3 +- lib/logging/logger.ts | 2 +- lib/logging/logger_factory.ts | 2 +- lib/message/error_message.ts | 2 +- lib/message/log_message.ts | 2 +- lib/message/message_resolver.ts | 2 +- lib/notification_center/index.ts | 2 +- lib/notification_center/type.ts | 6 +- lib/odp/constant.ts | 2 +- lib/odp/event_manager/odp_event.ts | 2 +- .../event_manager/odp_event_api_manager.ts | 2 +- lib/odp/event_manager/odp_event_manager.ts | 6 +- lib/odp/odp_config.ts | 2 +- lib/odp/odp_manager.ts | 2 +- lib/odp/odp_manager_factory.browser.ts | 2 +- lib/odp/odp_manager_factory.node.ts | 2 +- lib/odp/odp_manager_factory.react_native.ts | 2 +- lib/odp/odp_manager_factory.ts | 2 +- lib/odp/odp_manager_factory.universal.ts | 2 +- lib/odp/odp_types.ts | 2 +- .../segment_manager/odp_response_schema.ts | 2 +- .../odp_segment_api_manager.ts | 2 +- .../segment_manager/odp_segment_manager.ts | 2 +- .../optimizely_segment_option.ts | 2 +- lib/odp/ua_parser/ua_parser.ts | 2 +- lib/odp/ua_parser/user_agent_info.ts | 2 +- lib/odp/ua_parser/user_agent_parser.ts | 2 +- lib/optimizely/index.ts | 6 +- lib/optimizely_decision/index.ts | 2 +- lib/optimizely_user_context/index.ts | 6 +- lib/platform_support.ts | 2 +- .../config_manager_factory.browser.ts | 2 +- .../config_manager_factory.node.ts | 2 +- .../config_manager_factory.react_native.ts | 2 +- lib/project_config/config_manager_factory.ts | 2 +- .../config_manager_factory.universal.ts | 2 +- lib/project_config/constant.ts | 2 +- lib/project_config/datafile_manager.ts | 2 +- lib/project_config/optimizely_config.ts | 8 +- .../polling_datafile_manager.ts | 6 +- lib/project_config/project_config.ts | 6 +- lib/project_config/project_config_manager.ts | 6 +- lib/project_config/project_config_schema.ts | 2 +- lib/service.ts | 2 +- lib/shared_types.ts | 2 +- lib/utils/attributes_validator/index.ts | 2 +- .../cache/async_storage_cache.react_native.ts | 2 +- lib/utils/cache/cache.ts | 4 +- lib/utils/cache/in_memory_lru_cache.ts | 2 +- .../cache/local_storage_cache.browser.ts | 2 +- lib/utils/cache/store.ts | 2 +- lib/utils/cache/store_validator.ts | 2 +- lib/utils/config_validator/index.ts | 6 +- lib/utils/enums/index.ts | 2 +- lib/utils/event_emitter/event_emitter.ts | 2 +- lib/utils/event_tag_utils/index.ts | 3 - lib/utils/event_tags_validator/index.ts | 2 +- lib/utils/executor/backoff_retry_runner.ts | 2 +- lib/utils/executor/serial_runner.ts | 2 +- lib/utils/fns/index.ts | 2 +- lib/utils/http_request_handler/http.ts | 2 +- lib/utils/http_request_handler/http_util.ts | 2 +- .../request_handler.node.ts | 2 +- .../request_handler_validator.ts | 2 +- lib/utils/id_generator/index.ts | 2 +- .../async-storage.ts | 2 +- lib/utils/json_schema_validator/index.ts | 2 +- lib/utils/microtask/index.ts | 2 +- lib/utils/promise/operation_value.ts | 2 +- lib/utils/promise/resolvablePromise.ts | 2 +- lib/utils/repeater/repeater.ts | 2 +- lib/utils/semantic_version/index.ts | 2 +- lib/utils/string_value_validator/index.ts | 2 +- lib/utils/type.ts | 2 +- .../user_profile_service_validator/index.ts | 2 +- lib/vuid/vuid.ts | 2 +- lib/vuid/vuid_manager.ts | 2 +- lib/vuid/vuid_manager_factory.browser.ts | 2 +- lib/vuid/vuid_manager_factory.node.ts | 2 +- lib/vuid/vuid_manager_factory.react_native.ts | 2 +- lib/vuid/vuid_manager_factory.ts | 2 +- 122 files changed, 280 insertions(+), 167 deletions(-) diff --git a/eslint-local-rules/require-platform-declaration.js b/eslint-local-rules/require-platform-declaration.js index 170549dba..cee9ac4fe 100644 --- a/eslint-local-rules/require-platform-declaration.js +++ b/eslint-local-rules/require-platform-declaration.js @@ -1,28 +1,97 @@ /** * ESLint Rule: require-platform-declaration * - * Ensures that all non-test source files export __supportedPlatforms + * Ensures that all non-test source files export __supportedPlatforms with valid platform values * * Valid: - * export const __supportedPlatforms = ['browser'] as const; - * export const __supportedPlatforms = ['__universal__'] as const; - * export const __supportedPlatforms: Platform[] = ['browser', 'node']; + * export const __supportedPlatforms = ['browser']; + * export const __supportedPlatforms = ['__universal__']; + * export const __supportedPlatforms = ['browser', 'node']; * * Invalid: * // Missing __supportedPlatforms export + * // Invalid platform values (must match Platform type definition in platform_support.ts) + * // Not exported as const array */ +const fs = require('fs'); +const path = require('path'); +const ts = require('typescript'); + +// Cache for valid platforms +let validPlatformsCache = null; + +function getValidPlatforms(context) { + if (validPlatformsCache) { + return validPlatformsCache; + } + + try { + const filename = context.getFilename(); + const workspaceRoot = filename.split('/lib/')[0]; + const platformSupportPath = path.join(workspaceRoot, 'lib', 'platform_support.ts'); + + if (fs.existsSync(platformSupportPath)) { + const content = fs.readFileSync(platformSupportPath, 'utf8'); + const sourceFile = ts.createSourceFile( + platformSupportPath, + content, + ts.ScriptTarget.Latest, + true + ); + + const platforms = []; + + // Visit all nodes in the AST + function visit(node) { + // Look for: export type Platform = 'browser' | 'node' | ... + if (ts.isTypeAliasDeclaration(node) && + node.name.text === 'Platform' && + node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) { + + // Parse the union type + if (ts.isUnionTypeNode(node.type)) { + for (const type of node.type.types) { + if (ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal)) { + platforms.push(type.literal.text); + } + } + } + } + + ts.forEachChild(node, visit); + } + + visit(sourceFile); + + if (platforms.length > 0) { + validPlatformsCache = platforms; + return validPlatformsCache; + } + } + } catch (error) { + // Fallback to hardcoded values if parsing fails + console.warn('Could not parse platform_support.ts, using fallback values:', error.message); + } + + // Fallback to default platforms + validPlatformsCache = ['browser', 'node', 'react_native', '__universal__']; + return validPlatformsCache; +} + module.exports = { meta: { type: 'problem', docs: { - description: 'Require __supportedPlatforms export in all source files', + description: 'Require __supportedPlatforms export with valid platform values in all source files', category: 'Best Practices', recommended: true, }, messages: { - missingPlatformDeclaration: 'File must export __supportedPlatforms to declare which platforms it supports. Example: export const __supportedPlatforms = [\'__universal__\'] as const;', - invalidPlatformDeclaration: '__supportedPlatforms must be exported as a const array. Example: export const __supportedPlatforms = [\'browser\', \'node\'] as const;', + missingPlatformDeclaration: 'File must export __supportedPlatforms to declare which platforms it supports. Example: export const __supportedPlatforms = [\'__universal__\'];', + invalidPlatformDeclaration: '__supportedPlatforms must be exported as a const array. Example: export const __supportedPlatforms = [\'browser\', \'node\'];', + invalidPlatformValue: '__supportedPlatforms contains invalid platform value "{{value}}". Valid platforms are: {{validPlatforms}}', + emptyPlatformArray: '__supportedPlatforms array cannot be empty. Specify at least one platform or use [\'__universal__\']', }, schema: [], }, @@ -49,8 +118,8 @@ module.exports = { return {}; } + const VALID_PLATFORMS = getValidPlatforms(context); let hasPlatformExport = false; - let isValidExport = false; return { ExportNamedDeclaration(node) { @@ -73,7 +142,7 @@ module.exports = { return; } - // Validate it's an array + // Validate it's an array expression let init = declarator.init; // Handle TSAsExpression: [...] as const @@ -86,13 +155,43 @@ module.exports = { init = init.expression; } - if (init && init.type === 'ArrayExpression') { - isValidExport = true; - } else { + if (!init || init.type !== 'ArrayExpression') { context.report({ node: declarator, messageId: 'invalidPlatformDeclaration', }); + return; + } + + // Check if array is empty + if (init.elements.length === 0) { + context.report({ + node: init, + messageId: 'emptyPlatformArray', + }); + return; + } + + // Validate each array element is a valid platform string + for (const element of init.elements) { + if (element && element.type === 'Literal' && typeof element.value === 'string') { + if (!VALID_PLATFORMS.includes(element.value)) { + context.report({ + node: element, + messageId: 'invalidPlatformValue', + data: { + value: element.value, + validPlatforms: VALID_PLATFORMS.map(p => `'${p}'`).join(', ') + } + }); + } + } else { + // Not a string literal + context.report({ + node: element || init, + messageId: 'invalidPlatformDeclaration', + }); + } } } } diff --git a/lib/client_factory.ts b/lib/client_factory.ts index 1ae8d9112..981145bfd 100644 --- a/lib/client_factory.ts +++ b/lib/client_factory.ts @@ -29,7 +29,7 @@ import { InMemoryLruCache } from "./utils/cache/in_memory_lru_cache"; import { transformCache, CacheWithRemove } from "./utils/cache/cache"; import { ConstantBackoff } from "./utils/repeater/repeater"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export type OptimizelyFactoryConfig = Config & { requestHandler: RequestHandler; diff --git a/lib/common_exports.ts b/lib/common_exports.ts index 8ce7af0e7..ad73c332e 100644 --- a/lib/common_exports.ts +++ b/lib/common_exports.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export { createStaticProjectConfigManager } from './project_config/config_manager_factory'; diff --git a/lib/core/audience_evaluator/index.ts b/lib/core/audience_evaluator/index.ts index 71d54b9c3..41539ec7e 100644 --- a/lib/core/audience_evaluator/index.ts +++ b/lib/core/audience_evaluator/index.ts @@ -21,7 +21,7 @@ import { CONDITION_EVALUATOR_ERROR, UNKNOWN_CONDITION_TYPE } from 'error_message import { AUDIENCE_EVALUATION_RESULT, EVALUATING_AUDIENCE} from 'log_message'; import { LoggerFacade } from '../../logging/logger'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export class AudienceEvaluator { private logger?: LoggerFacade; diff --git a/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts b/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts index 1bd08ee17..f28c8112c 100644 --- a/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts +++ b/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts @@ -17,7 +17,7 @@ import { UNKNOWN_MATCH_TYPE } from 'error_message'; import { LoggerFacade } from '../../../logging/logger'; import { Condition, OptimizelyUserContext } from '../../../shared_types'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; const QUALIFIED_MATCH_TYPE = 'qualified'; diff --git a/lib/core/bucketer/bucket_value_generator.ts b/lib/core/bucketer/bucket_value_generator.ts index 05f2f19b2..07567bf07 100644 --- a/lib/core/bucketer/bucket_value_generator.ts +++ b/lib/core/bucketer/bucket_value_generator.ts @@ -17,7 +17,7 @@ import murmurhash from 'murmurhash'; import { INVALID_BUCKETING_ID } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; const HASH_SEED = 1; const MAX_HASH_VALUE = Math.pow(2, 32); diff --git a/lib/core/bucketer/index.ts b/lib/core/bucketer/index.ts index 27cd169ed..0ec7ed5ee 100644 --- a/lib/core/bucketer/index.ts +++ b/lib/core/bucketer/index.ts @@ -29,7 +29,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { generateBucketValue } from './bucket_value_generator'; import { DecisionReason } from '../decision_service'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const USER_NOT_IN_ANY_EXPERIMENT = 'User %s is not in any experiment of group %s.'; export const USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP = 'User %s is not in experiment %s of group %s.'; diff --git a/lib/core/condition_tree_evaluator/index.ts b/lib/core/condition_tree_evaluator/index.ts index 2f73231e5..3f1bc3a35 100644 --- a/lib/core/condition_tree_evaluator/index.ts +++ b/lib/core/condition_tree_evaluator/index.ts @@ -14,7 +14,7 @@ * limitations under the License. * ***************************************************************************/ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; const AND_CONDITION = 'and'; const OR_CONDITION = 'or'; diff --git a/lib/core/decision/index.ts b/lib/core/decision/index.ts index 5db330468..e66b33dcd 100644 --- a/lib/core/decision/index.ts +++ b/lib/core/decision/index.ts @@ -21,7 +21,7 @@ import { DecisionObj } from '../decision_service'; * @param {DecisionObj} decisionObj Object representing decision * @returns {string} Experiment key or empty string if experiment is null */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export function getExperimentKey(decisionObj: DecisionObj): string { diff --git a/lib/core/decision_service/cmab/cmab_client.ts b/lib/core/decision_service/cmab/cmab_client.ts index a9a8c576f..f6c3bb266 100644 --- a/lib/core/decision_service/cmab/cmab_client.ts +++ b/lib/core/decision_service/cmab/cmab_client.ts @@ -24,7 +24,7 @@ import { isSuccessStatusCode } from "../../../utils/http_request_handler/http_ut import { BackoffController } from "../../../utils/repeater/repeater"; import { Producer } from "../../../utils/type"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface CmabClient { fetchDecision( diff --git a/lib/core/decision_service/cmab/cmab_service.ts b/lib/core/decision_service/cmab/cmab_service.ts index 66f0ec0bf..95b7ac8c0 100644 --- a/lib/core/decision_service/cmab/cmab_service.ts +++ b/lib/core/decision_service/cmab/cmab_service.ts @@ -25,9 +25,6 @@ import murmurhash from "murmurhash"; import { DecideOptionsMap } from ".."; import { SerialRunner } from "../../../utils/executor/serial_runner"; import { -export const __supportedPlatforms = ['__universal__'] as const; - - CMAB_CACHE_ATTRIBUTES_MISMATCH, CMAB_CACHE_HIT, CMAB_CACHE_MISS, @@ -35,6 +32,9 @@ export const __supportedPlatforms = ['__universal__'] as const; INVALIDATE_CMAB_CACHE, RESET_CMAB_CACHE, } from 'log_message'; +import { Platform } from '../../../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; export type CmabDecision = { variationId: string, diff --git a/lib/core/decision_service/index.ts b/lib/core/decision_service/index.ts index a807b3c1f..147636665 100644 --- a/lib/core/decision_service/index.ts +++ b/lib/core/decision_service/index.ts @@ -16,9 +16,6 @@ import { LoggerFacade } from '../../logging/logger' import { bucket } from '../bucketer'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - AUDIENCE_EVALUATION_TYPES, CONTROL_ATTRIBUTES, DECISION_SOURCES, @@ -79,6 +76,10 @@ import { CmabService } from './cmab/cmab_service'; import { Maybe, OpType, OpValue } from '../../utils/type'; import { Value } from '../../utils/promise/operation_value'; +import { Platform } from '../../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; + export const EXPERIMENT_NOT_RUNNING = 'Experiment %s is not running.'; export const RETURNING_STORED_VARIATION = 'Returning previously activated variation "%s" of experiment "%s" for user "%s" from user profile.'; diff --git a/lib/entrypoint.test-d.ts b/lib/entrypoint.test-d.ts index 366889ea8..4b70c5885 100644 --- a/lib/entrypoint.test-d.ts +++ b/lib/entrypoint.test-d.ts @@ -56,8 +56,12 @@ import { LogLevel } from './logging/logger'; import { OptimizelyDecideOption } from './shared_types'; import { Maybe } from './utils/type'; +import { Platform } from './platform_support'; export type Entrypoint = { + // platform declaration + __supportedPlatforms: Platform[]; + // client factory createInstance: (config: Config) => Client; diff --git a/lib/entrypoint.universal.test-d.ts b/lib/entrypoint.universal.test-d.ts index 184583a35..9e5d6ac08 100644 --- a/lib/entrypoint.universal.test-d.ts +++ b/lib/entrypoint.universal.test-d.ts @@ -50,10 +50,14 @@ import { LogLevel } from './logging/logger'; import { OptimizelyDecideOption } from './shared_types'; import { UniversalConfig } from './index.universal'; import { OpaqueOdpManager } from './odp/odp_manager_factory'; +import { Platform } from './platform_support'; import { UniversalOdpManagerOptions } from './odp/odp_manager_factory.universal'; export type UniversalEntrypoint = { + // platform declaration + __supportedPlatforms: Platform[]; + // client factory createInstance: (config: UniversalConfig) => Client; diff --git a/lib/error/error_handler.ts b/lib/error/error_handler.ts index 7ad1402e8..2886f35bb 100644 --- a/lib/error/error_handler.ts +++ b/lib/error/error_handler.ts @@ -17,7 +17,7 @@ * @export * @interface ErrorHandler */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface ErrorHandler { /** diff --git a/lib/error/error_notifier.ts b/lib/error/error_notifier.ts index 30ca8ec0e..9c3770be1 100644 --- a/lib/error/error_notifier.ts +++ b/lib/error/error_notifier.ts @@ -17,7 +17,7 @@ import { MessageResolver } from "../message/message_resolver"; import { ErrorHandler } from "./error_handler"; import { OptimizelyError } from "./optimizly_error"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface ErrorNotifier { notify(error: Error): void; diff --git a/lib/error/error_notifier_factory.ts b/lib/error/error_notifier_factory.ts index fac284880..8bed24ca9 100644 --- a/lib/error/error_notifier_factory.ts +++ b/lib/error/error_notifier_factory.ts @@ -18,7 +18,7 @@ import { Maybe } from "../utils/type"; import { ErrorHandler } from "./error_handler"; import { DefaultErrorNotifier } from "./error_notifier"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const INVALID_ERROR_HANDLER = 'Invalid error handler'; diff --git a/lib/error/error_reporter.ts b/lib/error/error_reporter.ts index 9be3873c9..34f354932 100644 --- a/lib/error/error_reporter.ts +++ b/lib/error/error_reporter.ts @@ -17,7 +17,7 @@ import { LoggerFacade } from "../logging/logger"; import { ErrorNotifier } from "./error_notifier"; import { OptimizelyError } from "./optimizly_error"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export class ErrorReporter { private logger?: LoggerFacade; diff --git a/lib/error/optimizly_error.ts b/lib/error/optimizly_error.ts index 841b9567e..eec66fbf4 100644 --- a/lib/error/optimizly_error.ts +++ b/lib/error/optimizly_error.ts @@ -16,7 +16,7 @@ import { MessageResolver } from "../message/message_resolver"; import { sprintf } from "../utils/fns"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export class OptimizelyError extends Error { baseMessage: string; diff --git a/lib/event_processor/batch_event_processor.react_native.ts b/lib/event_processor/batch_event_processor.react_native.ts index 5fedae039..7cdd5533b 100644 --- a/lib/event_processor/batch_event_processor.react_native.ts +++ b/lib/event_processor/batch_event_processor.react_native.ts @@ -19,7 +19,7 @@ import { NetInfoState, addEventListener } from '@react-native-community/netinfo' import { BatchEventProcessor, BatchEventProcessorConfig } from './batch_event_processor'; import { Fn } from '../utils/type'; -export const __supportedPlatforms = ['react_native'] as const; +export const __supportedPlatforms = ['react_native']; export class ReactNativeNetInfoEventProcessor extends BatchEventProcessor { private isInternetReachable = true; diff --git a/lib/event_processor/batch_event_processor.ts b/lib/event_processor/batch_event_processor.ts index 4e17fbac9..79cd41744 100644 --- a/lib/event_processor/batch_event_processor.ts +++ b/lib/event_processor/batch_event_processor.ts @@ -32,7 +32,7 @@ import { OptimizelyError } from "../error/optimizly_error"; import { sprintf } from "../utils/fns"; import { SERVICE_STOPPED_BEFORE_RUNNING } from "../service"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const DEFAULT_MIN_BACKOFF = 1000; export const DEFAULT_MAX_BACKOFF = 32000; diff --git a/lib/event_processor/event_builder/log_event.ts b/lib/event_processor/event_builder/log_event.ts index 48051dd19..6b87b6aa9 100644 --- a/lib/event_processor/event_builder/log_event.ts +++ b/lib/event_processor/event_builder/log_event.ts @@ -21,7 +21,7 @@ import { LogEvent } from '../event_dispatcher/event_dispatcher'; import { EventTags } from '../../shared_types'; import { Region } from '../../project_config/project_config'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; const ACTIVATE_EVENT_KEY = 'campaign_activated' const CUSTOM_ATTRIBUTE_FEATURE_TYPE = 'custom' diff --git a/lib/event_processor/event_builder/user_event.ts b/lib/event_processor/event_builder/user_event.ts index 9b10311b0..a3a0dea61 100644 --- a/lib/event_processor/event_builder/user_event.ts +++ b/lib/event_processor/event_builder/user_event.ts @@ -19,9 +19,6 @@ import { isAttributeValid } from '../../utils/attributes_validator'; import * as eventTagUtils from '../../utils/event_tag_utils'; import fns from '../../utils/fns'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - getAttributeId, getEventId, getLayerId, @@ -32,6 +29,9 @@ export const __supportedPlatforms = ['__universal__'] as const; import { EventTags, UserAttributes } from '../../shared_types'; import { LoggerFacade } from '../../logging/logger'; import { DECISION_SOURCES } from '../../common_exports'; +import { Platform } from '../../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; export type VisitorAttribute = { entityId: string diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.node.ts b/lib/event_processor/event_dispatcher/default_dispatcher.node.ts index a66f08d78..dabcbc554 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.node.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.node.ts @@ -17,7 +17,7 @@ import { EventDispatcher } from './event_dispatcher'; import { NodeRequestHandler } from '../../utils/http_request_handler/request_handler.node'; import { DefaultEventDispatcher } from './default_dispatcher'; -export const __supportedPlatforms = ['node'] as const; +export const __supportedPlatforms = ['node']; const eventDispatcher: EventDispatcher = new DefaultEventDispatcher(new NodeRequestHandler()); diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.ts b/lib/event_processor/event_dispatcher/default_dispatcher.ts index 63a1bf2bb..d06f5f1f5 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.ts @@ -18,7 +18,7 @@ import { ONLY_POST_REQUESTS_ARE_SUPPORTED } from 'error_message'; import { RequestHandler } from '../../utils/http_request_handler/http'; import { EventDispatcher, EventDispatcherResponse, LogEvent } from './event_dispatcher'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export class DefaultEventDispatcher implements EventDispatcher { private requestHandler: RequestHandler; diff --git a/lib/event_processor/event_dispatcher/event_dispatcher.ts b/lib/event_processor/event_dispatcher/event_dispatcher.ts index ea1a3e3e8..1e1a5a5aa 100644 --- a/lib/event_processor/event_dispatcher/event_dispatcher.ts +++ b/lib/event_processor/event_dispatcher/event_dispatcher.ts @@ -15,7 +15,7 @@ */ import { EventBatch } from "../event_builder/log_event"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export type EventDispatcherResponse = { statusCode?: number diff --git a/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts b/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts index a1cb12c94..caafeb77b 100644 --- a/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts +++ b/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts @@ -20,7 +20,7 @@ import { EventDispatcher } from './event_dispatcher'; import { validateRequestHandler } from '../../utils/http_request_handler/request_handler_validator'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const createEventDispatcher = (requestHander: RequestHandler): EventDispatcher => { validateRequestHandler(requestHander); diff --git a/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts index bfd748c89..0558e3e0f 100644 --- a/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts @@ -18,7 +18,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { SEND_BEACON_FAILED } from 'error_message'; import { EventDispatcher, EventDispatcherResponse } from './event_dispatcher'; -export const __supportedPlatforms = ['browser'] as const; +export const __supportedPlatforms = ['browser']; export type Event = { url: string; diff --git a/lib/event_processor/event_processor.ts b/lib/event_processor/event_processor.ts index 079cbdd42..9fc8563d1 100644 --- a/lib/event_processor/event_processor.ts +++ b/lib/event_processor/event_processor.ts @@ -19,7 +19,7 @@ import { Service } from '../service' import { Consumer, Fn } from '../utils/type'; import { LoggerFacade } from '../logging/logger'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const DEFAULT_FLUSH_INTERVAL = 30000 // Unit is ms - default flush interval is 30s export const DEFAULT_BATCH_SIZE = 10 diff --git a/lib/event_processor/event_processor_factory.browser.ts b/lib/event_processor/event_processor_factory.browser.ts index 21b035288..5ea05dcce 100644 --- a/lib/event_processor/event_processor_factory.browser.ts +++ b/lib/event_processor/event_processor_factory.browser.ts @@ -17,9 +17,6 @@ import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import { EventProcessor } from './event_processor'; import { EventWithId } from './batch_event_processor'; import { -export const __supportedPlatforms = ['browser'] as const; - - getOpaqueBatchEventProcessor, BatchEventProcessorOptions, OpaqueEventProcessor, @@ -32,6 +29,9 @@ import sendBeaconEventDispatcher from './event_dispatcher/send_beacon_dispatcher import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; import { FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory'; import { DEFAULT_MAX_EVENTS_IN_STORE, EventStore } from './event_store'; +import { Platform } from '../platform_support'; + +export const __supportedPlatforms: Platform[] = ['browser']; export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 1_000; diff --git a/lib/event_processor/event_processor_factory.node.ts b/lib/event_processor/event_processor_factory.node.ts index a9d4c935e..da6f08554 100644 --- a/lib/event_processor/event_processor_factory.node.ts +++ b/lib/event_processor/event_processor_factory.node.ts @@ -16,9 +16,6 @@ import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import defaultEventDispatcher from './event_dispatcher/default_dispatcher.node'; import { -export const __supportedPlatforms = ['node'] as const; - - BatchEventProcessorOptions, FAILED_EVENT_RETRY_INTERVAL, getOpaqueBatchEventProcessor, @@ -27,6 +24,9 @@ export const __supportedPlatforms = ['node'] as const; wrapEventProcessor, getForwardingEventProcessor, } from './event_processor_factory'; +import { Platform } from '../platform_support'; + +export const __supportedPlatforms: Platform[] = ['node']; export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 30_000; diff --git a/lib/event_processor/event_processor_factory.react_native.ts b/lib/event_processor/event_processor_factory.react_native.ts index b56a807ad..892187de1 100644 --- a/lib/event_processor/event_processor_factory.react_native.ts +++ b/lib/event_processor/event_processor_factory.react_native.ts @@ -16,9 +16,6 @@ import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import defaultEventDispatcher from './event_dispatcher/default_dispatcher.browser'; import { -export const __supportedPlatforms = ['react_native'] as const; - - BatchEventProcessorOptions, getOpaqueBatchEventProcessor, getPrefixEventStore, @@ -31,6 +28,9 @@ import { EventWithId } from './batch_event_processor'; import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; import { ReactNativeNetInfoEventProcessor } from './batch_event_processor.react_native'; import { DEFAULT_MAX_EVENTS_IN_STORE, EventStore } from './event_store'; +import { Platform } from '../platform_support'; + +export const __supportedPlatforms: Platform[] = ['react_native']; export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 1_000; diff --git a/lib/event_processor/event_processor_factory.ts b/lib/event_processor/event_processor_factory.ts index e7b419be5..4e133a506 100644 --- a/lib/event_processor/event_processor_factory.ts +++ b/lib/event_processor/event_processor_factory.ts @@ -26,7 +26,7 @@ import { EventProcessor } from "./event_processor"; import { EVENT_STORE_PREFIX } from "./event_store"; import { ForwardingEventProcessor } from "./forwarding_event_processor"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const INVALID_EVENT_DISPATCHER = 'Invalid event dispatcher'; diff --git a/lib/event_processor/event_processor_factory.universal.ts b/lib/event_processor/event_processor_factory.universal.ts index 9f423d131..deb7ef6a8 100644 --- a/lib/event_processor/event_processor_factory.universal.ts +++ b/lib/event_processor/event_processor_factory.universal.ts @@ -18,15 +18,15 @@ import { getForwardingEventProcessor } from './event_processor_factory'; import { EventDispatcher } from './event_dispatcher/event_dispatcher'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - getOpaqueBatchEventProcessor, BatchEventProcessorOptions, OpaqueEventProcessor, wrapEventProcessor, getPrefixEventStore, } from './event_processor_factory'; +import { Platform } from '../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 1_000; diff --git a/lib/event_processor/event_store.ts b/lib/event_processor/event_store.ts index d807afb27..75de1f2f7 100644 --- a/lib/event_processor/event_store.ts +++ b/lib/event_processor/event_store.ts @@ -2,9 +2,6 @@ import { OptimizelyError } from "../error/optimizly_error"; import { LoggerFacade } from "../logging/logger"; import { EVENT_STORE_FULL } from "error_message"; import { -export const __supportedPlatforms = ['__universal__'] as const; - - AsyncPrefixStore, AsyncStore, AsyncStoreWithBatchedGet, @@ -15,6 +12,9 @@ export const __supportedPlatforms = ['__universal__'] as const; import { SerialRunner } from "../utils/executor/serial_runner"; import { Maybe } from "../utils/type"; import { EventWithId } from "./batch_event_processor"; +import { Platform } from '../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; export type StoredEvent = EventWithId & { _time?: { diff --git a/lib/event_processor/forwarding_event_processor.ts b/lib/event_processor/forwarding_event_processor.ts index c33b86b7b..e0bb73cfb 100644 --- a/lib/event_processor/forwarding_event_processor.ts +++ b/lib/event_processor/forwarding_event_processor.ts @@ -26,7 +26,7 @@ import { Consumer, Fn } from '../utils/type'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; import { sprintf } from '../utils/fns'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export class ForwardingEventProcessor extends BaseService implements EventProcessor { private dispatcher: EventDispatcher; diff --git a/lib/export_types.ts b/lib/export_types.ts index 1236bca7c..2d48555f4 100644 --- a/lib/export_types.ts +++ b/lib/export_types.ts @@ -15,7 +15,7 @@ */ // config manager related types -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export type { StaticConfigManagerConfig, diff --git a/lib/feature_toggle.ts b/lib/feature_toggle.ts index df42b3460..6dd29ce5c 100644 --- a/lib/feature_toggle.ts +++ b/lib/feature_toggle.ts @@ -34,6 +34,6 @@ // example feature flag definition // export const wipFeat = () => false as const; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export type IfActive boolean, Y, N = unknown> = ReturnType extends true ? Y : N; diff --git a/lib/index.browser.ts b/lib/index.browser.ts index 60993e640..2cf8aa4eb 100644 --- a/lib/index.browser.ts +++ b/lib/index.browser.ts @@ -19,6 +19,7 @@ import { getOptimizelyInstance } from './client_factory'; import { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; import { JAVASCRIPT_CLIENT_ENGINE } from './utils/enums'; import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; +import { Platform } from './platform_support'; /** * Creates an instance of the Optimizely class @@ -26,7 +27,7 @@ import { BrowserRequestHandler } from './utils/http_request_handler/request_hand * @return {Client|null} the Optimizely client object * null on error */ -export const __supportedPlatforms = ['browser'] as const; +export const __supportedPlatforms: Platform[] = ['browser']; export const createInstance = function(config: Config): Client { diff --git a/lib/index.node.ts b/lib/index.node.ts index dad0d692c..74e84e0c1 100644 --- a/lib/index.node.ts +++ b/lib/index.node.ts @@ -18,6 +18,7 @@ import { Client, Config } from './shared_types'; import { getOptimizelyInstance, OptimizelyFactoryConfig } from './client_factory'; import { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; import { NodeRequestHandler } from './utils/http_request_handler/request_handler.node'; +import { Platform } from './platform_support'; /** * Creates an instance of the Optimizely class @@ -25,7 +26,7 @@ import { NodeRequestHandler } from './utils/http_request_handler/request_handler * @return {Client|null} the Optimizely client object * null on error */ -export const __supportedPlatforms = ['node'] as const; +export const __supportedPlatforms: Platform[] = ['node']; export const createInstance = function(config: Config): Client { diff --git a/lib/index.react_native.ts b/lib/index.react_native.ts index 85bd079e6..76d589d93 100644 --- a/lib/index.react_native.ts +++ b/lib/index.react_native.ts @@ -21,6 +21,7 @@ import { getOptimizelyInstance, OptimizelyFactoryConfig } from './client_factory import { REACT_NATIVE_JS_CLIENT_ENGINE } from './utils/enums'; import { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; +import { Platform } from './platform_support'; /** * Creates an instance of the Optimizely class @@ -28,7 +29,7 @@ import { BrowserRequestHandler } from './utils/http_request_handler/request_hand * @return {Client|null} the Optimizely client object * null on error */ -export const __supportedPlatforms = ['react_native'] as const; +export const __supportedPlatforms: Platform[] = ['react_native']; export const createInstance = function(config: Config): Client { diff --git a/lib/index.universal.ts b/lib/index.universal.ts index fffc46a16..5ca9921a3 100644 --- a/lib/index.universal.ts +++ b/lib/index.universal.ts @@ -18,8 +18,9 @@ import { getOptimizelyInstance } from './client_factory'; import { JAVASCRIPT_CLIENT_ENGINE } from './utils/enums'; import { RequestHandler } from './utils/http_request_handler/http'; +import { Platform } from './platform_support'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms: Platform[] = ['__universal__']; export type UniversalConfig = Config & { requestHandler: RequestHandler; diff --git a/lib/logging/logger.ts b/lib/logging/logger.ts index 14b51b299..33eec65d2 100644 --- a/lib/logging/logger.ts +++ b/lib/logging/logger.ts @@ -17,7 +17,7 @@ import { OptimizelyError } from '../error/optimizly_error'; import { MessageResolver } from '../message/message_resolver'; import { sprintf } from '../utils/fns' -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export enum LogLevel { Debug, diff --git a/lib/logging/logger_factory.ts b/lib/logging/logger_factory.ts index 7307c6ee9..f2a12cf4d 100644 --- a/lib/logging/logger_factory.ts +++ b/lib/logging/logger_factory.ts @@ -17,7 +17,7 @@ import { ConsoleLogHandler, LogHandler, LogLevel, OptimizelyLogger } from './log import { errorResolver, infoResolver, MessageResolver } from '../message/message_resolver'; import { Maybe } from '../utils/type'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const INVALID_LOG_HANDLER = 'Invalid log handler'; export const INVALID_LEVEL_PRESET = 'Invalid level preset'; diff --git a/lib/message/error_message.ts b/lib/message/error_message.ts index 72639676f..5c8429741 100644 --- a/lib/message/error_message.ts +++ b/lib/message/error_message.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const NOTIFICATION_LISTENER_EXCEPTION = 'Notification listener for (%s) threw exception: %s'; export const CONDITION_EVALUATOR_ERROR = 'Error evaluating audience condition of type %s: %s'; diff --git a/lib/message/log_message.ts b/lib/message/log_message.ts index 4e32036d6..669d19f62 100644 --- a/lib/message/log_message.ts +++ b/lib/message/log_message.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const FEATURE_ENABLED_FOR_USER = 'Feature %s is enabled for user %s.'; export const FEATURE_NOT_ENABLED_FOR_USER = 'Feature %s is not enabled for user %s.'; diff --git a/lib/message/message_resolver.ts b/lib/message/message_resolver.ts index 8e38f0a40..6504681c0 100644 --- a/lib/message/message_resolver.ts +++ b/lib/message/message_resolver.ts @@ -1,7 +1,7 @@ import { messages as infoMessages } from 'log_message'; import { messages as errorMessages } from 'error_message'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface MessageResolver { resolve(baseMessage: string): string; diff --git a/lib/notification_center/index.ts b/lib/notification_center/index.ts index 8f0f43634..ee6893d76 100644 --- a/lib/notification_center/index.ts +++ b/lib/notification_center/index.ts @@ -24,7 +24,7 @@ import { NOTIFICATION_LISTENER_EXCEPTION } from 'error_message'; import { ErrorReporter } from '../error/error_reporter'; import { ErrorNotifier } from '../error/error_notifier'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; interface NotificationCenterOptions { logger?: LoggerFacade; diff --git a/lib/notification_center/type.ts b/lib/notification_center/type.ts index 3426c4a76..e569f9cde 100644 --- a/lib/notification_center/type.ts +++ b/lib/notification_center/type.ts @@ -16,9 +16,6 @@ import { LogEvent } from '../event_processor/event_dispatcher/event_dispatcher'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - EventTags, Experiment, FeatureVariableValue, @@ -29,6 +26,9 @@ export const __supportedPlatforms = ['__universal__'] as const; } from '../shared_types'; import { DecisionSource } from '../utils/enums'; import { Nullable } from '../utils/type'; +import { Platform } from '../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; export type UserEventListenerPayload = { userId: string; diff --git a/lib/odp/constant.ts b/lib/odp/constant.ts index 415d241e7..01665cb82 100644 --- a/lib/odp/constant.ts +++ b/lib/odp/constant.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export enum ODP_USER_KEY { VUID = 'vuid', diff --git a/lib/odp/event_manager/odp_event.ts b/lib/odp/event_manager/odp_event.ts index 585024aaf..c8f0d013e 100644 --- a/lib/odp/event_manager/odp_event.ts +++ b/lib/odp/event_manager/odp_event.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export class OdpEvent { /** diff --git a/lib/odp/event_manager/odp_event_api_manager.ts b/lib/odp/event_manager/odp_event_api_manager.ts index 3b9f39c5b..10e879bd0 100644 --- a/lib/odp/event_manager/odp_event_api_manager.ts +++ b/lib/odp/event_manager/odp_event_api_manager.ts @@ -19,7 +19,7 @@ import { OdpEvent } from './odp_event'; import { HttpMethod, RequestHandler } from '../../utils/http_request_handler/http'; import { OdpConfig } from '../odp_config'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export type EventDispatchResponse = { statusCode?: number; diff --git a/lib/odp/event_manager/odp_event_manager.ts b/lib/odp/event_manager/odp_event_manager.ts index 68e623625..68afb9e8e 100644 --- a/lib/odp/event_manager/odp_event_manager.ts +++ b/lib/odp/event_manager/odp_event_manager.ts @@ -24,9 +24,6 @@ import { runWithRetry } from '../../utils/executor/backoff_retry_runner'; import { isSuccessStatusCode } from '../../utils/http_request_handler/http_util'; import { ODP_DEFAULT_EVENT_TYPE, ODP_USER_KEY } from '../constant'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - EVENT_ACTION_INVALID, EVENT_DATA_INVALID, FAILED_TO_SEND_ODP_EVENTS, @@ -40,6 +37,9 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { LoggerFacade } from '../../logging/logger'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../../service'; import { sprintf } from '../../utils/fns'; +import { Platform } from '../../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; export interface OdpEventManager extends Service { updateConfig(odpIntegrationConfig: OdpIntegrationConfig): void; diff --git a/lib/odp/odp_config.ts b/lib/odp/odp_config.ts index 7462993b0..789a8d16b 100644 --- a/lib/odp/odp_config.ts +++ b/lib/odp/odp_config.ts @@ -16,7 +16,7 @@ import { checkArrayEquality } from '../utils/fns'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export class OdpConfig { /** diff --git a/lib/odp/odp_manager.ts b/lib/odp/odp_manager.ts index e99f2db6b..10f6eb340 100644 --- a/lib/odp/odp_manager.ts +++ b/lib/odp/odp_manager.ts @@ -32,7 +32,7 @@ import { Maybe } from '../utils/type'; import { sprintf } from '../utils/fns'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface OdpManager extends Service { updateConfig(odpIntegrationConfig: OdpIntegrationConfig): boolean; diff --git a/lib/odp/odp_manager_factory.browser.ts b/lib/odp/odp_manager_factory.browser.ts index f7c01cea9..1ff50bc0e 100644 --- a/lib/odp/odp_manager_factory.browser.ts +++ b/lib/odp/odp_manager_factory.browser.ts @@ -18,7 +18,7 @@ import { BrowserRequestHandler } from '../utils/http_request_handler/request_han import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __supportedPlatforms = ['browser'] as const; +export const __supportedPlatforms = ['browser']; export const BROWSER_DEFAULT_API_TIMEOUT = 10_000; export const BROWSER_DEFAULT_BATCH_SIZE = 10; diff --git a/lib/odp/odp_manager_factory.node.ts b/lib/odp/odp_manager_factory.node.ts index 83c0ad61a..1dd70ed4b 100644 --- a/lib/odp/odp_manager_factory.node.ts +++ b/lib/odp/odp_manager_factory.node.ts @@ -18,7 +18,7 @@ import { NodeRequestHandler } from '../utils/http_request_handler/request_handle import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __supportedPlatforms = ['node'] as const; +export const __supportedPlatforms = ['node']; export const NODE_DEFAULT_API_TIMEOUT = 10_000; export const NODE_DEFAULT_BATCH_SIZE = 10; diff --git a/lib/odp/odp_manager_factory.react_native.ts b/lib/odp/odp_manager_factory.react_native.ts index a266c3f9f..7aa855fc5 100644 --- a/lib/odp/odp_manager_factory.react_native.ts +++ b/lib/odp/odp_manager_factory.react_native.ts @@ -19,7 +19,7 @@ import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager' import { OdpManager } from './odp_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __supportedPlatforms = ['react_native'] as const; +export const __supportedPlatforms = ['react_native']; export const RN_DEFAULT_API_TIMEOUT = 10_000; export const RN_DEFAULT_BATCH_SIZE = 10; diff --git a/lib/odp/odp_manager_factory.ts b/lib/odp/odp_manager_factory.ts index eba342b3b..bcdddc5ad 100644 --- a/lib/odp/odp_manager_factory.ts +++ b/lib/odp/odp_manager_factory.ts @@ -26,7 +26,7 @@ import { DefaultOdpSegmentApiManager } from "./segment_manager/odp_segment_api_m import { DefaultOdpSegmentManager, OdpSegmentManager } from "./segment_manager/odp_segment_manager"; import { UserAgentParser } from "./ua_parser/user_agent_parser"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const DEFAULT_CACHE_SIZE = 10_000; export const DEFAULT_CACHE_TIMEOUT = 600_000; diff --git a/lib/odp/odp_manager_factory.universal.ts b/lib/odp/odp_manager_factory.universal.ts index f56ccb60d..af1777213 100644 --- a/lib/odp/odp_manager_factory.universal.ts +++ b/lib/odp/odp_manager_factory.universal.ts @@ -19,7 +19,7 @@ import { validateRequestHandler } from '../utils/http_request_handler/request_ha import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const DEFAULT_API_TIMEOUT = 10_000; export const DEFAULT_BATCH_SIZE = 1; diff --git a/lib/odp/odp_types.ts b/lib/odp/odp_types.ts index 82e781cd4..91da1850d 100644 --- a/lib/odp/odp_types.ts +++ b/lib/odp/odp_types.ts @@ -17,7 +17,7 @@ /** * Wrapper around valid data and error responses */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface Response { data: Data; diff --git a/lib/odp/segment_manager/odp_response_schema.ts b/lib/odp/segment_manager/odp_response_schema.ts index f69aa6dd1..ca42c39be 100644 --- a/lib/odp/segment_manager/odp_response_schema.ts +++ b/lib/odp/segment_manager/odp_response_schema.ts @@ -19,7 +19,7 @@ import { JSONSchema4 } from 'json-schema'; /** * JSON Schema used to validate the ODP GraphQL response */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const OdpResponseSchema = { diff --git a/lib/odp/segment_manager/odp_segment_api_manager.ts b/lib/odp/segment_manager/odp_segment_api_manager.ts index deded7a19..19d5dd3f1 100644 --- a/lib/odp/segment_manager/odp_segment_api_manager.ts +++ b/lib/odp/segment_manager/odp_segment_api_manager.ts @@ -24,7 +24,7 @@ import { log } from 'console'; /** * Expected value for a qualified/valid segment */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; const QUALIFIED = 'qualified'; diff --git a/lib/odp/segment_manager/odp_segment_manager.ts b/lib/odp/segment_manager/odp_segment_manager.ts index 57027877b..1f8f0302b 100644 --- a/lib/odp/segment_manager/odp_segment_manager.ts +++ b/lib/odp/segment_manager/odp_segment_manager.ts @@ -22,7 +22,7 @@ import { ODP_USER_KEY } from '../constant'; import { LoggerFacade } from '../../logging/logger'; import { ODP_CONFIG_NOT_AVAILABLE, ODP_NOT_INTEGRATED } from 'error_message'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface OdpSegmentManager { fetchQualifiedSegments( diff --git a/lib/odp/segment_manager/optimizely_segment_option.ts b/lib/odp/segment_manager/optimizely_segment_option.ts index 013664d3b..b81142fcd 100644 --- a/lib/odp/segment_manager/optimizely_segment_option.ts +++ b/lib/odp/segment_manager/optimizely_segment_option.ts @@ -15,7 +15,7 @@ */ // Options for defining behavior of OdpSegmentManager's caching mechanism when calling fetchSegments() -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export enum OptimizelySegmentOption { IGNORE_CACHE = 'IGNORE_CACHE', diff --git a/lib/odp/ua_parser/ua_parser.ts b/lib/odp/ua_parser/ua_parser.ts index facf6a962..5f3085115 100644 --- a/lib/odp/ua_parser/ua_parser.ts +++ b/lib/odp/ua_parser/ua_parser.ts @@ -17,7 +17,7 @@ import { UAParser } from 'ua-parser-js'; import { UserAgentInfo } from './user_agent_info'; import { UserAgentParser } from './user_agent_parser'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; const userAgentParser: UserAgentParser = { parseUserAgentInfo(): UserAgentInfo { diff --git a/lib/odp/ua_parser/user_agent_info.ts b/lib/odp/ua_parser/user_agent_info.ts index c158aa62a..67b84d61b 100644 --- a/lib/odp/ua_parser/user_agent_info.ts +++ b/lib/odp/ua_parser/user_agent_info.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export type UserAgentInfo = { os: { diff --git a/lib/odp/ua_parser/user_agent_parser.ts b/lib/odp/ua_parser/user_agent_parser.ts index 4adaac1a7..a8ce56316 100644 --- a/lib/odp/ua_parser/user_agent_parser.ts +++ b/lib/odp/ua_parser/user_agent_parser.ts @@ -16,7 +16,7 @@ import { UserAgentInfo } from "./user_agent_info"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface UserAgentParser { parseUserAgentInfo(): UserAgentInfo, diff --git a/lib/optimizely/index.ts b/lib/optimizely/index.ts index eda649c28..ef9b8ac4d 100644 --- a/lib/optimizely/index.ts +++ b/lib/optimizely/index.ts @@ -25,9 +25,6 @@ import { OptimizelySegmentOption } from '../odp/segment_manager/optimizely_segme import { BaseService, ServiceState } from '../service'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - UserAttributes, EventTags, OptimizelyConfig, @@ -49,6 +46,9 @@ import { createDecisionService, DecisionService, DecisionObj } from '../core/dec import { buildLogEvent } from '../event_processor/event_builder/log_event'; import { buildImpressionEvent, buildConversionEvent } from '../event_processor/event_builder/user_event'; import { isSafeInteger } from '../utils/fns'; +import { Platform } from '../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; import { validate } from '../utils/attributes_validator'; import * as eventTagsValidator from '../utils/event_tags_validator'; import * as projectConfig from '../project_config/project_config'; diff --git a/lib/optimizely_decision/index.ts b/lib/optimizely_decision/index.ts index fa293b5cf..f15421d5d 100644 --- a/lib/optimizely_decision/index.ts +++ b/lib/optimizely_decision/index.ts @@ -15,7 +15,7 @@ ***************************************************************************/ import { OptimizelyUserContext, OptimizelyDecision } from '../shared_types'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export function newErrorDecision(key: string, user: OptimizelyUserContext, reasons: string[]): OptimizelyDecision { return { diff --git a/lib/optimizely_user_context/index.ts b/lib/optimizely_user_context/index.ts index 9667acc01..dd9a6effb 100644 --- a/lib/optimizely_user_context/index.ts +++ b/lib/optimizely_user_context/index.ts @@ -15,9 +15,6 @@ */ import Optimizely from '../optimizely'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - EventTags, OptimizelyDecideOption, OptimizelyDecision, @@ -27,6 +24,9 @@ export const __supportedPlatforms = ['__universal__'] as const; UserAttributes, } from '../shared_types'; import { OptimizelySegmentOption } from '../odp/segment_manager/optimizely_segment_option'; +import { Platform } from '../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; export const FORCED_DECISION_NULL_RULE_KEY = '$opt_null_rule_key'; diff --git a/lib/platform_support.ts b/lib/platform_support.ts index aa3982f70..f09d26584 100644 --- a/lib/platform_support.ts +++ b/lib/platform_support.ts @@ -10,7 +10,7 @@ */ export type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; /** * Platform support declaration diff --git a/lib/project_config/config_manager_factory.browser.ts b/lib/project_config/config_manager_factory.browser.ts index fc7b9fc13..7905df770 100644 --- a/lib/project_config/config_manager_factory.browser.ts +++ b/lib/project_config/config_manager_factory.browser.ts @@ -17,7 +17,7 @@ import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; -export const __supportedPlatforms = ['browser'] as const; +export const __supportedPlatforms = ['browser']; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { diff --git a/lib/project_config/config_manager_factory.node.ts b/lib/project_config/config_manager_factory.node.ts index 4d69dca79..14eb18812 100644 --- a/lib/project_config/config_manager_factory.node.ts +++ b/lib/project_config/config_manager_factory.node.ts @@ -17,7 +17,7 @@ import { NodeRequestHandler } from "../utils/http_request_handler/request_handler.node"; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; -export const __supportedPlatforms = ['node'] as const; +export const __supportedPlatforms = ['node']; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { diff --git a/lib/project_config/config_manager_factory.react_native.ts b/lib/project_config/config_manager_factory.react_native.ts index 17c5fe84c..b2355c995 100644 --- a/lib/project_config/config_manager_factory.react_native.ts +++ b/lib/project_config/config_manager_factory.react_native.ts @@ -18,7 +18,7 @@ import { AsyncStorageCache } from "../utils/cache/async_storage_cache.react_nati import { BrowserRequestHandler } from "../utils/http_request_handler/request_handler.browser"; import { getOpaquePollingConfigManager, PollingConfigManagerConfig, OpaqueConfigManager } from "./config_manager_factory"; -export const __supportedPlatforms = ['react_native'] as const; +export const __supportedPlatforms = ['react_native']; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { diff --git a/lib/project_config/config_manager_factory.ts b/lib/project_config/config_manager_factory.ts index 8f7913d5d..0e64f7221 100644 --- a/lib/project_config/config_manager_factory.ts +++ b/lib/project_config/config_manager_factory.ts @@ -27,7 +27,7 @@ import { LogLevel } from '../logging/logger' import { Store } from "../utils/cache/store"; import { validateStore } from "../utils/cache/store_validator"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const INVALID_CONFIG_MANAGER = "Invalid config manager"; diff --git a/lib/project_config/config_manager_factory.universal.ts b/lib/project_config/config_manager_factory.universal.ts index c7871ae30..2c0352cd1 100644 --- a/lib/project_config/config_manager_factory.universal.ts +++ b/lib/project_config/config_manager_factory.universal.ts @@ -18,7 +18,7 @@ import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManage import { RequestHandler } from "../utils/http_request_handler/http"; import { validateRequestHandler } from "../utils/http_request_handler/request_handler_validator"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export type UniversalPollingConfigManagerConfig = PollingConfigManagerConfig & { requestHandler: RequestHandler; diff --git a/lib/project_config/constant.ts b/lib/project_config/constant.ts index 862da30b9..788e8dfeb 100644 --- a/lib/project_config/constant.ts +++ b/lib/project_config/constant.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; const DEFAULT_UPDATE_INTERVAL_MINUTES = 5; /** Standard interval (5 minutes in milliseconds) for polling datafile updates.; */ diff --git a/lib/project_config/datafile_manager.ts b/lib/project_config/datafile_manager.ts index 3f17239ef..4cf05ac0c 100644 --- a/lib/project_config/datafile_manager.ts +++ b/lib/project_config/datafile_manager.ts @@ -20,7 +20,7 @@ import { Fn, Consumer } from '../utils/type'; import { Repeater } from '../utils/repeater/repeater'; import { LoggerFacade } from '../logging/logger'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface DatafileManager extends Service { get(): string | undefined; diff --git a/lib/project_config/optimizely_config.ts b/lib/project_config/optimizely_config.ts index cc4494b71..ce302286a 100644 --- a/lib/project_config/optimizely_config.ts +++ b/lib/project_config/optimizely_config.ts @@ -17,9 +17,6 @@ import { LoggerFacade } from '../logging/logger' import { ProjectConfig } from '../project_config/project_config'; import { DEFAULT_OPERATOR_TYPES } from '../core/condition_tree_evaluator'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - Audience, Experiment, FeatureVariable, @@ -37,6 +34,11 @@ export const __supportedPlatforms = ['__universal__'] as const; VariationVariable, } from '../shared_types'; + +import { DATAFILE_VERSIONS } from '../utils/enums'; +import { Platform } from '../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; interface FeatureVariablesMap { [key: string]: FeatureVariable[]; } diff --git a/lib/project_config/polling_datafile_manager.ts b/lib/project_config/polling_datafile_manager.ts index c0a2ebbf4..fad6abe80 100644 --- a/lib/project_config/polling_datafile_manager.ts +++ b/lib/project_config/polling_datafile_manager.ts @@ -24,9 +24,6 @@ import { Repeater } from '../utils/repeater/repeater'; import { Consumer, Fn } from '../utils/type'; import { isSuccessStatusCode } from '../utils/http_request_handler/http_util'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - DATAFILE_FETCH_REQUEST_FAILED, ERROR_FETCHING_DATAFILE, } from 'error_message'; @@ -43,6 +40,9 @@ export const LOGGER_NAME = 'PollingDatafileManager'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; +import { Platform } from '../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; export const FAILED_TO_FETCH_DATAFILE = 'Failed to fetch datafile'; export class PollingDatafileManager extends BaseService implements DatafileManager { diff --git a/lib/project_config/project_config.ts b/lib/project_config/project_config.ts index fa3984702..fa2106a86 100644 --- a/lib/project_config/project_config.ts +++ b/lib/project_config/project_config.ts @@ -21,9 +21,6 @@ import configValidator from '../utils/config_validator'; import { LoggerFacade } from '../logging/logger'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - Audience, Experiment, FeatureFlag, @@ -56,6 +53,9 @@ import { import { SKIPPING_JSON_VALIDATION, VALID_DATAFILE } from 'log_message'; import { OptimizelyError } from '../error/optimizly_error'; +import { Platform } from '../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; interface TryCreatingProjectConfigConfig { // TODO[OASIS-6649]: Don't use object type // eslint-disable-next-line @typescript-eslint/ban-types diff --git a/lib/project_config/project_config_manager.ts b/lib/project_config/project_config_manager.ts index e15d83d80..2d7f4fc8b 100644 --- a/lib/project_config/project_config_manager.ts +++ b/lib/project_config/project_config_manager.ts @@ -23,9 +23,6 @@ import { Consumer, Fn, Transformer } from '../utils/type'; import { EventEmitter } from '../utils/event_emitter/event_emitter'; import { -export const __supportedPlatforms = ['__universal__'] as const; - - SERVICE_FAILED_TO_START, SERVICE_STOPPED_BEFORE_RUNNING, } from '../service' @@ -34,6 +31,9 @@ export const NO_SDKKEY_OR_DATAFILE = 'sdkKey or datafile must be provided'; export const GOT_INVALID_DATAFILE = 'got invalid datafile'; import { sprintf } from '../utils/fns'; +import { Platform } from '../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; interface ProjectConfigManagerConfig { datafile?: string | Record; jsonSchemaValidator?: Transformer, diff --git a/lib/project_config/project_config_schema.ts b/lib/project_config/project_config_schema.ts index 558357e36..956f0af30 100644 --- a/lib/project_config/project_config_schema.ts +++ b/lib/project_config/project_config_schema.ts @@ -19,7 +19,7 @@ */ import { JSONSchema4 } from 'json-schema'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; var schemaDefinition = { $schema: 'http://json-schema.org/draft-04/schema#', diff --git a/lib/service.ts b/lib/service.ts index dcc86c195..9c251f5e1 100644 --- a/lib/service.ts +++ b/lib/service.ts @@ -17,7 +17,7 @@ import { LoggerFacade, LogLevel, LogLevelToLower } from './logging/logger' import { resolvablePromise, ResolvablePromise } from "./utils/promise/resolvablePromise"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const SERVICE_FAILED_TO_START = '%s failed to start, reason: %s'; export const SERVICE_STOPPED_BEFORE_RUNNING = '%s stopped before running'; diff --git a/lib/shared_types.ts b/lib/shared_types.ts index 4bd5495f0..ed643c3c2 100644 --- a/lib/shared_types.ts +++ b/lib/shared_types.ts @@ -47,7 +47,7 @@ import { OpaqueOdpManager } from './odp/odp_manager_factory'; import { OpaqueVuidManager } from './vuid/vuid_manager_factory'; import { CacheWithRemove } from './utils/cache/cache'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; export { EventProcessor } from './event_processor/event_processor'; diff --git a/lib/utils/attributes_validator/index.ts b/lib/utils/attributes_validator/index.ts index 82ebc4f4b..e131f31c7 100644 --- a/lib/utils/attributes_validator/index.ts +++ b/lib/utils/attributes_validator/index.ts @@ -26,7 +26,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @throws If the attributes are not valid */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export function validate(attributes: unknown): boolean { if (typeof attributes === 'object' && !Array.isArray(attributes) && attributes !== null) { diff --git a/lib/utils/cache/async_storage_cache.react_native.ts b/lib/utils/cache/async_storage_cache.react_native.ts index 369beb221..5a94779fb 100644 --- a/lib/utils/cache/async_storage_cache.react_native.ts +++ b/lib/utils/cache/async_storage_cache.react_native.ts @@ -18,7 +18,7 @@ import { Maybe } from "../type"; import { AsyncStore } from "./store"; import { getDefaultAsyncStorage } from "../import.react_native/@react-native-async-storage/async-storage"; -export const __supportedPlatforms = ['react_native'] as const; +export const __supportedPlatforms = ['react_native']; export class AsyncStorageCache implements AsyncStore { public readonly operation = 'async'; diff --git a/lib/utils/cache/cache.ts b/lib/utils/cache/cache.ts index 6a8329598..84168f129 100644 --- a/lib/utils/cache/cache.ts +++ b/lib/utils/cache/cache.ts @@ -15,7 +15,9 @@ */ import { OpType, OpValue } from '../../utils/type'; import { Transformer } from '../../utils/type'; -export const __supportedPlatforms = ['__universal__'] as const; +import { Platform } from '../../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; export interface OpCache { diff --git a/lib/utils/cache/in_memory_lru_cache.ts b/lib/utils/cache/in_memory_lru_cache.ts index 6f9e68621..d1675ab0e 100644 --- a/lib/utils/cache/in_memory_lru_cache.ts +++ b/lib/utils/cache/in_memory_lru_cache.ts @@ -17,7 +17,7 @@ import { Maybe } from "../type"; import { SyncCacheWithRemove } from "./cache"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; type CacheElement = { value: V; diff --git a/lib/utils/cache/local_storage_cache.browser.ts b/lib/utils/cache/local_storage_cache.browser.ts index f552f1346..6d097206d 100644 --- a/lib/utils/cache/local_storage_cache.browser.ts +++ b/lib/utils/cache/local_storage_cache.browser.ts @@ -17,7 +17,7 @@ import { Maybe } from "../type"; import { SyncStore } from "./store"; -export const __supportedPlatforms = ['browser'] as const; +export const __supportedPlatforms = ['browser']; export class LocalStorageCache implements SyncStore { public readonly operation = 'sync'; diff --git a/lib/utils/cache/store.ts b/lib/utils/cache/store.ts index 84e875b01..58ac4ef59 100644 --- a/lib/utils/cache/store.ts +++ b/lib/utils/cache/store.ts @@ -18,7 +18,7 @@ import { Transformer } from '../../utils/type'; import { Maybe } from '../../utils/type'; import { OpType, OpValue } from '../../utils/type'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface OpStore { operation: OP; diff --git a/lib/utils/cache/store_validator.ts b/lib/utils/cache/store_validator.ts index 54bd7d05b..eec699f22 100644 --- a/lib/utils/cache/store_validator.ts +++ b/lib/utils/cache/store_validator.ts @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const INVALID_STORE = 'Invalid store'; export const INVALID_STORE_METHOD = 'Invalid store method %s'; diff --git a/lib/utils/config_validator/index.ts b/lib/utils/config_validator/index.ts index c57da17a6..e2628dfdd 100644 --- a/lib/utils/config_validator/index.ts +++ b/lib/utils/config_validator/index.ts @@ -14,9 +14,6 @@ * limitations under the License. */ import { -export const __supportedPlatforms = ['__universal__'] as const; - - DATAFILE_VERSIONS, } from '../enums'; import { @@ -25,6 +22,9 @@ import { NO_DATAFILE_SPECIFIED, } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; +import { Platform } from '../../platform_support'; + +export const __supportedPlatforms: Platform[] = ['__universal__']; const SUPPORTED_VERSIONS = [DATAFILE_VERSIONS.V2, DATAFILE_VERSIONS.V3, DATAFILE_VERSIONS.V4]; diff --git a/lib/utils/enums/index.ts b/lib/utils/enums/index.ts index 2ee1b82a2..a8dec7dd2 100644 --- a/lib/utils/enums/index.ts +++ b/lib/utils/enums/index.ts @@ -17,7 +17,7 @@ /** * Contains global enums used throughout the library */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const LOG_LEVEL = { NOTSET: 0, diff --git a/lib/utils/event_emitter/event_emitter.ts b/lib/utils/event_emitter/event_emitter.ts index be108ca8a..b04f459d0 100644 --- a/lib/utils/event_emitter/event_emitter.ts +++ b/lib/utils/event_emitter/event_emitter.ts @@ -16,7 +16,7 @@ import { Fn } from "../type"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; type Consumer = (arg: T) => void; diff --git a/lib/utils/event_tag_utils/index.ts b/lib/utils/event_tag_utils/index.ts index e13a3944f..d50292a39 100644 --- a/lib/utils/event_tag_utils/index.ts +++ b/lib/utils/event_tag_utils/index.ts @@ -14,9 +14,6 @@ * limitations under the License. */ import { -export const __supportedPlatforms = ['__universal__'] as const; - - FAILED_TO_PARSE_REVENUE, FAILED_TO_PARSE_VALUE, PARSED_NUMERIC_VALUE, diff --git a/lib/utils/event_tags_validator/index.ts b/lib/utils/event_tags_validator/index.ts index 3ff932c27..8d163d38e 100644 --- a/lib/utils/event_tags_validator/index.ts +++ b/lib/utils/event_tags_validator/index.ts @@ -26,7 +26,7 @@ import { INVALID_EVENT_TAGS } from 'error_message'; * @return {boolean} true if event tags are valid * @throws If event tags are not valid */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export function validate(eventTags: unknown): boolean { diff --git a/lib/utils/executor/backoff_retry_runner.ts b/lib/utils/executor/backoff_retry_runner.ts index f13769675..b3252da80 100644 --- a/lib/utils/executor/backoff_retry_runner.ts +++ b/lib/utils/executor/backoff_retry_runner.ts @@ -4,7 +4,7 @@ import { resolvablePromise, ResolvablePromise } from "../promise/resolvablePromi import { BackoffController } from "../repeater/repeater"; import { AsyncProducer, Fn } from "../type"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export type RunResult = { result: Promise; diff --git a/lib/utils/executor/serial_runner.ts b/lib/utils/executor/serial_runner.ts index ed096ac90..acfc12fd0 100644 --- a/lib/utils/executor/serial_runner.ts +++ b/lib/utils/executor/serial_runner.ts @@ -16,7 +16,7 @@ import { AsyncProducer } from "../type"; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; class SerialRunner { private waitPromise: Promise = Promise.resolve(); diff --git a/lib/utils/fns/index.ts b/lib/utils/fns/index.ts index 9b5fde4aa..265bcc685 100644 --- a/lib/utils/fns/index.ts +++ b/lib/utils/fns/index.ts @@ -15,7 +15,7 @@ */ import { v4 } from 'uuid'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; const MAX_SAFE_INTEGER_LIMIT = Math.pow(2, 53); diff --git a/lib/utils/http_request_handler/http.ts b/lib/utils/http_request_handler/http.ts index 72ae48eb9..4271c6971 100644 --- a/lib/utils/http_request_handler/http.ts +++ b/lib/utils/http_request_handler/http.ts @@ -17,7 +17,7 @@ /** * List of key-value pairs to be used in an HTTP requests */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface Headers { [header: string]: string | undefined; diff --git a/lib/utils/http_request_handler/http_util.ts b/lib/utils/http_request_handler/http_util.ts index 41807d88e..1d4f48ab3 100644 --- a/lib/utils/http_request_handler/http_util.ts +++ b/lib/utils/http_request_handler/http_util.ts @@ -1,5 +1,5 @@ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const isSuccessStatusCode = (statusCode: number): boolean => { return statusCode >= 200 && statusCode < 400; diff --git a/lib/utils/http_request_handler/request_handler.node.ts b/lib/utils/http_request_handler/request_handler.node.ts index cdbf39933..61a2d27a8 100644 --- a/lib/utils/http_request_handler/request_handler.node.ts +++ b/lib/utils/http_request_handler/request_handler.node.ts @@ -26,7 +26,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; /** * Handles sending requests and receiving responses over HTTP via NodeJS http module */ -export const __supportedPlatforms = ['node'] as const; +export const __supportedPlatforms = ['node']; export class NodeRequestHandler implements RequestHandler { diff --git a/lib/utils/http_request_handler/request_handler_validator.ts b/lib/utils/http_request_handler/request_handler_validator.ts index ee61140d0..5afc8a96e 100644 --- a/lib/utils/http_request_handler/request_handler_validator.ts +++ b/lib/utils/http_request_handler/request_handler_validator.ts @@ -15,7 +15,7 @@ */ import { RequestHandler } from './http'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const INVALID_REQUEST_HANDLER = 'Invalid request handler'; diff --git a/lib/utils/id_generator/index.ts b/lib/utils/id_generator/index.ts index 353e854ae..d98db3154 100644 --- a/lib/utils/id_generator/index.ts +++ b/lib/utils/id_generator/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; const idSuffixBase = 10_000; diff --git a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts index 06500a1e4..e30766201 100644 --- a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts +++ b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts @@ -16,7 +16,7 @@ import type { AsyncStorageStatic } from '@react-native-async-storage/async-storage' -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const MODULE_NOT_FOUND_REACT_NATIVE_ASYNC_STORAGE = 'Module not found: @react-native-async-storage/async-storage'; diff --git a/lib/utils/json_schema_validator/index.ts b/lib/utils/json_schema_validator/index.ts index 5a02c58f3..29c371e28 100644 --- a/lib/utils/json_schema_validator/index.ts +++ b/lib/utils/json_schema_validator/index.ts @@ -26,7 +26,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @param {boolean} shouldThrowOnError Should validation throw if invalid JSON object * @return {boolean} true if the given object is valid; throws or false if invalid */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export function validate( diff --git a/lib/utils/microtask/index.ts b/lib/utils/microtask/index.ts index d64d5724a..9919f981b 100644 --- a/lib/utils/microtask/index.ts +++ b/lib/utils/microtask/index.ts @@ -16,7 +16,7 @@ type Callback = () => void; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const scheduleMicrotask = (callback: Callback): void => { if (typeof queueMicrotask === 'function') { diff --git a/lib/utils/promise/operation_value.ts b/lib/utils/promise/operation_value.ts index baf297d9e..6de4a7de1 100644 --- a/lib/utils/promise/operation_value.ts +++ b/lib/utils/promise/operation_value.ts @@ -3,7 +3,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { OpType, OpValue } from '../type'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; const isPromise = (val: any): boolean => { return val && typeof val.then === 'function'; diff --git a/lib/utils/promise/resolvablePromise.ts b/lib/utils/promise/resolvablePromise.ts index c96196006..43a52cf80 100644 --- a/lib/utils/promise/resolvablePromise.ts +++ b/lib/utils/promise/resolvablePromise.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; const noop = () => {}; diff --git a/lib/utils/repeater/repeater.ts b/lib/utils/repeater/repeater.ts index 7b68ee31d..614517656 100644 --- a/lib/utils/repeater/repeater.ts +++ b/lib/utils/repeater/repeater.ts @@ -24,7 +24,7 @@ import { scheduleMicrotask } from "../microtask"; // If the retuned promise resolves, the repeater will assume the task succeeded, // and will reset the failure count. If the promise is rejected, the repeater will // assume the task failed and will increase the current consecutive failure count. -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface Repeater { diff --git a/lib/utils/semantic_version/index.ts b/lib/utils/semantic_version/index.ts index fe3321c83..2a908991f 100644 --- a/lib/utils/semantic_version/index.ts +++ b/lib/utils/semantic_version/index.ts @@ -23,7 +23,7 @@ import { VERSION_TYPE } from '../enums'; * @return {boolean} true if the string is number only * */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; function isNumber(content: string): boolean { diff --git a/lib/utils/string_value_validator/index.ts b/lib/utils/string_value_validator/index.ts index ced52e780..d326f5ebf 100644 --- a/lib/utils/string_value_validator/index.ts +++ b/lib/utils/string_value_validator/index.ts @@ -19,7 +19,7 @@ * @param {unknown} input * @return {boolean} true for non-empty string, false otherwise */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export function validate(input: unknown): boolean { return typeof input === 'string' && input !== ''; diff --git a/lib/utils/type.ts b/lib/utils/type.ts index 5c6c4b3cc..b8e038873 100644 --- a/lib/utils/type.ts +++ b/lib/utils/type.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export type Fn = () => unknown; export type AsyncFn = () => Promise; diff --git a/lib/utils/user_profile_service_validator/index.ts b/lib/utils/user_profile_service_validator/index.ts index c8e3efbb8..268d781ba 100644 --- a/lib/utils/user_profile_service_validator/index.ts +++ b/lib/utils/user_profile_service_validator/index.ts @@ -30,7 +30,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @throws If the instance is not valid */ -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export function validate(userProfileServiceInstance: unknown): boolean { if (typeof userProfileServiceInstance === 'object' && userProfileServiceInstance !== null) { diff --git a/lib/vuid/vuid.ts b/lib/vuid/vuid.ts index ac3d32bd6..1212adc37 100644 --- a/lib/vuid/vuid.ts +++ b/lib/vuid/vuid.ts @@ -16,7 +16,7 @@ import { v4 as uuidV4 } from 'uuid'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export const VUID_PREFIX = `vuid_`; export const VUID_MAX_LENGTH = 32; diff --git a/lib/vuid/vuid_manager.ts b/lib/vuid/vuid_manager.ts index 4a2faa2ee..e2cdb3b6a 100644 --- a/lib/vuid/vuid_manager.ts +++ b/lib/vuid/vuid_manager.ts @@ -18,7 +18,7 @@ import { Store } from '../utils/cache/store'; import { AsyncProducer, Maybe } from '../utils/type'; import { isVuid, makeVuid } from './vuid'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export interface VuidManager { getVuid(): Maybe; diff --git a/lib/vuid/vuid_manager_factory.browser.ts b/lib/vuid/vuid_manager_factory.browser.ts index 3ebce4ac9..d6742eb69 100644 --- a/lib/vuid/vuid_manager_factory.browser.ts +++ b/lib/vuid/vuid_manager_factory.browser.ts @@ -17,7 +17,7 @@ import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manage import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; -export const __supportedPlatforms = ['browser'] as const; +export const __supportedPlatforms = ['browser']; export const vuidCacheManager = new VuidCacheManager(); diff --git a/lib/vuid/vuid_manager_factory.node.ts b/lib/vuid/vuid_manager_factory.node.ts index d9950b868..b15dc3900 100644 --- a/lib/vuid/vuid_manager_factory.node.ts +++ b/lib/vuid/vuid_manager_factory.node.ts @@ -15,7 +15,7 @@ */ import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; -export const __supportedPlatforms = ['node'] as const; +export const __supportedPlatforms = ['node']; export const createVuidManager = (options: VuidManagerOptions = {}): OpaqueVuidManager => { return wrapVuidManager(undefined); diff --git a/lib/vuid/vuid_manager_factory.react_native.ts b/lib/vuid/vuid_manager_factory.react_native.ts index a75ee446c..d36e31e58 100644 --- a/lib/vuid/vuid_manager_factory.react_native.ts +++ b/lib/vuid/vuid_manager_factory.react_native.ts @@ -17,7 +17,7 @@ import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manage import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; -export const __supportedPlatforms = ['react_native'] as const; +export const __supportedPlatforms = ['react_native']; export const vuidCacheManager = new VuidCacheManager(); diff --git a/lib/vuid/vuid_manager_factory.ts b/lib/vuid/vuid_manager_factory.ts index 802a109d8..2cf0ce900 100644 --- a/lib/vuid/vuid_manager_factory.ts +++ b/lib/vuid/vuid_manager_factory.ts @@ -18,7 +18,7 @@ import { Store } from '../utils/cache/store'; import { Maybe } from '../utils/type'; import { VuidManager } from './vuid_manager'; -export const __supportedPlatforms = ['__universal__'] as const; +export const __supportedPlatforms = ['__universal__']; export type VuidManagerOptions = { vuidCache?: Store; From 4bc72f73f45aee3b2eec9aa45d9104ff5c24b0f3 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 15:34:46 +0600 Subject: [PATCH 07/40] use __platform --- ESLINT_TROUBLESHOOTING.md | 10 +-- docs/PLATFORM_ISOLATION.md | 28 ++++---- eslint-local-rules/README.md | 12 ++-- .../require-platform-declaration.js | 26 ++++---- lib/client_factory.ts | 2 +- lib/common_exports.ts | 2 +- lib/core/audience_evaluator/index.ts | 2 +- .../odp_segment_condition_evaluator/index.ts | 2 +- lib/core/bucketer/bucket_value_generator.ts | 2 +- lib/core/bucketer/index.ts | 2 +- lib/core/condition_tree_evaluator/index.ts | 2 +- lib/core/decision/index.ts | 2 +- lib/core/decision_service/cmab/cmab_client.ts | 2 +- .../decision_service/cmab/cmab_service.ts | 2 +- lib/core/decision_service/index.ts | 2 +- lib/entrypoint.test-d.ts | 2 +- lib/entrypoint.universal.test-d.ts | 2 +- lib/error/error_handler.ts | 2 +- lib/error/error_notifier.ts | 2 +- lib/error/error_notifier_factory.ts | 2 +- lib/error/error_reporter.ts | 2 +- lib/error/optimizly_error.ts | 2 +- .../batch_event_processor.react_native.ts | 2 +- lib/event_processor/batch_event_processor.ts | 2 +- .../event_builder/log_event.ts | 2 +- .../event_builder/user_event.ts | 2 +- .../default_dispatcher.browser.ts | 2 +- .../default_dispatcher.node.ts | 2 +- .../event_dispatcher/default_dispatcher.ts | 2 +- .../event_dispatcher/event_dispatcher.ts | 2 +- .../event_dispatcher_factory.ts | 2 +- .../send_beacon_dispatcher.browser.ts | 2 +- lib/event_processor/event_processor.ts | 2 +- .../event_processor_factory.browser.ts | 2 +- .../event_processor_factory.node.ts | 2 +- .../event_processor_factory.react_native.ts | 2 +- .../event_processor_factory.ts | 2 +- .../event_processor_factory.universal.ts | 2 +- lib/event_processor/event_store.ts | 2 +- .../forwarding_event_processor.ts | 2 +- lib/export_types.ts | 2 +- lib/feature_toggle.ts | 2 +- lib/index.browser.ts | 2 +- lib/index.browser.umdtests.js | 2 +- lib/index.node.ts | 2 +- lib/index.react_native.ts | 2 +- lib/index.universal.ts | 2 +- lib/logging/logger.ts | 2 +- lib/logging/logger_factory.ts | 2 +- lib/message/error_message.ts | 2 +- lib/message/log_message.ts | 2 +- lib/message/message_resolver.ts | 2 +- lib/notification_center/index.ts | 2 +- lib/notification_center/type.ts | 2 +- lib/odp/constant.ts | 2 +- lib/odp/event_manager/odp_event.ts | 2 +- .../event_manager/odp_event_api_manager.ts | 2 +- lib/odp/event_manager/odp_event_manager.ts | 2 +- lib/odp/odp_config.ts | 2 +- lib/odp/odp_manager.ts | 2 +- lib/odp/odp_manager_factory.browser.ts | 2 +- lib/odp/odp_manager_factory.node.ts | 2 +- lib/odp/odp_manager_factory.react_native.ts | 2 +- lib/odp/odp_manager_factory.ts | 2 +- lib/odp/odp_manager_factory.universal.ts | 2 +- lib/odp/odp_types.ts | 2 +- .../segment_manager/odp_response_schema.ts | 2 +- .../odp_segment_api_manager.ts | 2 +- .../segment_manager/odp_segment_manager.ts | 2 +- .../optimizely_segment_option.ts | 2 +- lib/odp/ua_parser/ua_parser.ts | 2 +- lib/odp/ua_parser/user_agent_info.ts | 2 +- lib/odp/ua_parser/user_agent_parser.ts | 2 +- lib/optimizely/index.ts | 2 +- lib/optimizely_decision/index.ts | 2 +- lib/optimizely_user_context/index.ts | 2 +- lib/platform_support.ts | 24 +++---- .../config_manager_factory.browser.ts | 2 +- .../config_manager_factory.node.ts | 2 +- .../config_manager_factory.react_native.ts | 2 +- lib/project_config/config_manager_factory.ts | 2 +- .../config_manager_factory.universal.ts | 2 +- lib/project_config/constant.ts | 2 +- lib/project_config/datafile_manager.ts | 2 +- lib/project_config/optimizely_config.ts | 2 +- .../polling_datafile_manager.ts | 2 +- lib/project_config/project_config.ts | 2 +- lib/project_config/project_config_manager.ts | 2 +- lib/project_config/project_config_schema.ts | 2 +- lib/service.ts | 2 +- lib/shared_types.ts | 2 +- lib/utils/attributes_validator/index.ts | 2 +- .../cache/async_storage_cache.react_native.ts | 2 +- lib/utils/cache/cache.ts | 2 +- lib/utils/cache/in_memory_lru_cache.ts | 2 +- .../cache/local_storage_cache.browser.ts | 2 +- lib/utils/cache/store.ts | 2 +- lib/utils/cache/store_validator.ts | 2 +- lib/utils/config_validator/index.ts | 2 +- lib/utils/enums/index.ts | 2 +- lib/utils/event_emitter/event_emitter.ts | 2 +- lib/utils/event_tags_validator/index.ts | 2 +- lib/utils/executor/backoff_retry_runner.ts | 2 +- lib/utils/executor/serial_runner.ts | 2 +- lib/utils/fns/index.ts | 2 +- lib/utils/http_request_handler/http.ts | 2 +- lib/utils/http_request_handler/http_util.ts | 2 +- .../request_handler.browser.ts | 2 +- .../request_handler.node.ts | 2 +- .../request_handler_validator.ts | 2 +- lib/utils/id_generator/index.ts | 2 +- .../async-storage.ts | 2 +- lib/utils/json_schema_validator/index.ts | 2 +- lib/utils/microtask/index.ts | 2 +- lib/utils/promise/operation_value.ts | 2 +- lib/utils/promise/resolvablePromise.ts | 2 +- lib/utils/repeater/repeater.ts | 2 +- lib/utils/semantic_version/index.ts | 2 +- lib/utils/string_value_validator/index.ts | 2 +- lib/utils/type.ts | 2 +- .../user_profile_service_validator/index.ts | 2 +- lib/vuid/vuid.ts | 2 +- lib/vuid/vuid_manager.ts | 2 +- lib/vuid/vuid_manager_factory.browser.ts | 2 +- lib/vuid/vuid_manager_factory.node.ts | 2 +- lib/vuid/vuid_manager_factory.react_native.ts | 2 +- lib/vuid/vuid_manager_factory.ts | 2 +- scripts/README.md | 4 +- scripts/add-platform-exports.js | 12 ++-- scripts/test-validator.js | 10 +-- scripts/validate-platform-isolation-ts.js | 50 +++++++-------- scripts/validate-platform-isolation.js | 64 +++++++++---------- 132 files changed, 242 insertions(+), 242 deletions(-) diff --git a/ESLINT_TROUBLESHOOTING.md b/ESLINT_TROUBLESHOOTING.md index 8f5bb949b..1731c5e71 100644 --- a/ESLINT_TROUBLESHOOTING.md +++ b/ESLINT_TROUBLESHOOTING.md @@ -8,8 +8,8 @@ The `require-platform-declaration` rule **is** working correctly from the comman $ npx eslint lib/core/custom_attribute_condition_evaluator/index.ts lib/core/custom_attribute_condition_evaluator/index.ts - 16:1 error File must export __supportedPlatforms to declare which platforms - it supports. Example: export const __supportedPlatforms = ['__universal__'] as const; + 16:1 error File must export __platforms to declare which platforms + it supports. Example: export const __platforms = ['__universal__'] as const; ``` ## VSCode Not Showing Errors? @@ -71,14 +71,14 @@ npx eslint lib/service.ts npx eslint lib/**/*.ts --quiet ``` -## Adding __supportedPlatforms +## Adding __platforms To fix the error, add this export to your file (after imports): ```typescript // Universal file (all platforms) -export const __supportedPlatforms = ['__universal__'] as const; +export const __platforms = ['__universal__'] as const; // OR platform-specific file -export const __supportedPlatforms = ['browser', 'node'] as const; +export const __platforms = ['browser', 'node'] as const; ``` diff --git a/docs/PLATFORM_ISOLATION.md b/docs/PLATFORM_ISOLATION.md index 1ecb4fcaa..18c6b4c53 100644 --- a/docs/PLATFORM_ISOLATION.md +++ b/docs/PLATFORM_ISOLATION.md @@ -18,11 +18,11 @@ For files specific to a single platform, use a suffix pattern: ### 2. Export Declaration (Multiple Platforms) -For files that support multiple platforms but not all (e.g., Browser + React Native, but not Node.js), export a `__supportedPlatforms` array: +For files that support multiple platforms but not all (e.g., Browser + React Native, but not Node.js), export a `__platforms` array: ```typescript // lib/utils/web-features.ts -export const __supportedPlatforms = ['browser', 'react_native']; +export const __platforms = ['browser', 'react_native']; // Your code that works on both browser and react_native export function getWindowSize() { @@ -34,7 +34,7 @@ Valid platform identifiers: `'browser'`, `'node'`, `'react_native'` ### Priority -If a file has both a platform suffix in its name AND a `__supportedPlatforms` export, the `__supportedPlatforms` export **takes priority**. This allows you to keep the `.browser.ts` naming convention while expanding support to additional platforms like React Native. +If a file has both a platform suffix in its name AND a `__platforms` export, the `__platforms` export **takes priority**. This allows you to keep the `.browser.ts` naming convention while expanding support to additional platforms like React Native. ## Import Rules @@ -51,11 +51,11 @@ A file is compatible if: ### Compatibility Examples -**Single Platform File (`.browser.ts` or `__supportedPlatforms = ['browser']`)** +**Single Platform File (`.browser.ts` or `__platforms = ['browser']`)** - ✅ Can import from: universal files, `.browser.ts` files, files with `['browser']` or `['browser', 'react_native']` - ❌ Cannot import from: `.node.ts` files, files with `['node']` or `['react_native']` only -**Multi-Platform File (`__supportedPlatforms = ['browser', 'react_native']`)** +**Multi-Platform File (`__platforms = ['browser', 'react_native']`)** - ✅ Can import from: universal files, files with exactly `['browser', 'react_native']` - ❌ Cannot import from: `.browser.ts` (browser only), `.react_native.ts` (react_native only), `.node.ts` - **Why?** A file supporting both platforms needs imports that work in BOTH environments @@ -81,17 +81,17 @@ import { NodeRequestHandler } from './utils/http_request_handler/request_handler // In lib/index.react_native.ts (React Native platform only) import { Config } from './shared_types'; // ✅ Universal file -// If web-features.ts has: __supportedPlatforms = ['browser', 'react_native'] +// If web-features.ts has: __platforms = ['browser', 'react_native'] import { getWindowSize } from './utils/web-features'; // ✅ Compatible (supports react_native) ``` ```typescript // In lib/utils/web-api.ts -// export const __supportedPlatforms = ['browser', 'react_native']; +// export const __platforms = ['browser', 'react_native']; import { Config } from './shared_types'; // ✅ Universal file -// If dom-helpers.ts has: __supportedPlatforms = ['browser', 'react_native'] +// If dom-helpers.ts has: __platforms = ['browser', 'react_native'] import { helpers } from './dom-helpers'; // ✅ Compatible (supports BOTH browser and react_native) ``` @@ -104,15 +104,15 @@ import { NodeRequestHandler } from './utils/http_request_handler/request_handler ```typescript // In lib/index.node.ts (Node platform only) -// If web-features.ts has: __supportedPlatforms = ['browser', 'react_native'] +// If web-features.ts has: __platforms = ['browser', 'react_native'] import { getWindowSize } from './utils/web-features'; // ❌ Not compatible with Node ``` ```typescript // In lib/utils/web-api.ts -// export const __supportedPlatforms = ['browser', 'react_native']; +// export const __platforms = ['browser', 'react_native']; -// If helper.browser.ts is browser-only (no __supportedPlatforms export) +// If helper.browser.ts is browser-only (no __platforms export) import { helper } from './helper.browser'; // ❌ Browser-only, doesn't support react_native // This file needs imports that work in BOTH browser AND react_native @@ -203,13 +203,13 @@ export const createMyFeature = () => new NodeMyFeature(); ### Multiple Platforms (But Not All) -For code that works on multiple platforms but not all, use the `__supportedPlatforms` export: +For code that works on multiple platforms but not all, use the `__platforms` export: **Example: Browser + React Native only** ```typescript // lib/utils/dom-helpers.ts -export const __supportedPlatforms = ['browser', 'react_native']; +export const __platforms = ['browser', 'react_native']; // This code works on both browser and react_native, but not node export function getElementById(id: string): Element | null { @@ -225,7 +225,7 @@ export function getElementById(id: string): Element | null { ```typescript // lib/utils/native-crypto.ts -export const __supportedPlatforms = ['node', 'react_native']; +export const __platforms = ['node', 'react_native']; import crypto from 'crypto'; // Available in both Node and React Native diff --git a/eslint-local-rules/README.md b/eslint-local-rules/README.md index 05672838f..514621456 100644 --- a/eslint-local-rules/README.md +++ b/eslint-local-rules/README.md @@ -6,7 +6,7 @@ This directory contains custom ESLint rules specific to this project. ### `require-platform-declaration` -**Purpose:** Ensures all source files (except tests) export `__supportedPlatforms` to declare which platforms they support. +**Purpose:** Ensures all source files (except tests) export `__platforms` to declare which platforms they support. **Why:** This enforces platform isolation at the linting level, catching missing declarations before build time. @@ -19,20 +19,20 @@ This directory contains custom ESLint rules specific to this project. ```typescript // Universal file (all platforms) -export const __supportedPlatforms = ['__universal__'] as const; +export const __platforms = ['__universal__'] as const; // Platform-specific file -export const __supportedPlatforms = ['browser', 'node'] as const; +export const __platforms = ['browser', 'node'] as const; // With type annotation -export const __supportedPlatforms: Platform[] = ['react_native'] as const; +export const __platforms: Platform[] = ['react_native'] as const; ``` **Invalid:** ```typescript -// Missing __supportedPlatforms export -// ESLint Error: File must export __supportedPlatforms to declare which platforms it supports +// Missing __platforms export +// ESLint Error: File must export __platforms to declare which platforms it supports ``` ## Configuration diff --git a/eslint-local-rules/require-platform-declaration.js b/eslint-local-rules/require-platform-declaration.js index cee9ac4fe..92cdf536a 100644 --- a/eslint-local-rules/require-platform-declaration.js +++ b/eslint-local-rules/require-platform-declaration.js @@ -1,15 +1,15 @@ /** * ESLint Rule: require-platform-declaration * - * Ensures that all non-test source files export __supportedPlatforms with valid platform values + * Ensures that all non-test source files export __platforms with valid platform values * * Valid: - * export const __supportedPlatforms = ['browser']; - * export const __supportedPlatforms = ['__universal__']; - * export const __supportedPlatforms = ['browser', 'node']; + * export const __platforms = ['browser']; + * export const __platforms = ['__universal__']; + * export const __platforms = ['browser', 'node']; * * Invalid: - * // Missing __supportedPlatforms export + * // Missing __platforms export * // Invalid platform values (must match Platform type definition in platform_support.ts) * // Not exported as const array */ @@ -83,15 +83,15 @@ module.exports = { meta: { type: 'problem', docs: { - description: 'Require __supportedPlatforms export with valid platform values in all source files', + description: 'Require __platforms export with valid platform values in all source files', category: 'Best Practices', recommended: true, }, messages: { - missingPlatformDeclaration: 'File must export __supportedPlatforms to declare which platforms it supports. Example: export const __supportedPlatforms = [\'__universal__\'];', - invalidPlatformDeclaration: '__supportedPlatforms must be exported as a const array. Example: export const __supportedPlatforms = [\'browser\', \'node\'];', - invalidPlatformValue: '__supportedPlatforms contains invalid platform value "{{value}}". Valid platforms are: {{validPlatforms}}', - emptyPlatformArray: '__supportedPlatforms array cannot be empty. Specify at least one platform or use [\'__universal__\']', + missingPlatformDeclaration: 'File must export __platforms to declare which platforms it supports. Example: export const __platforms = [\'__universal__\'];', + invalidPlatformDeclaration: '__platforms must be exported as a const array. Example: export const __platforms = [\'browser\', \'node\'];', + invalidPlatformValue: '__platforms contains invalid platform value "{{value}}". Valid platforms are: {{validPlatforms}}', + emptyPlatformArray: '__platforms array cannot be empty. Specify at least one platform or use [\'__universal__\']', }, schema: [], }, @@ -123,13 +123,13 @@ module.exports = { return { ExportNamedDeclaration(node) { - // Check for: export const __supportedPlatforms = [...] + // Check for: export const __platforms = [...] if (node.declaration && node.declaration.type === 'VariableDeclaration') { for (const declarator of node.declaration.declarations) { if (declarator.id.type === 'Identifier' && - declarator.id.name === '__supportedPlatforms') { + declarator.id.name === '__platforms') { hasPlatformExport = true; @@ -199,7 +199,7 @@ module.exports = { }, 'Program:exit'(node) { - // At the end of the file, check if __supportedPlatforms was exported + // At the end of the file, check if __platforms was exported if (!hasPlatformExport) { context.report({ node, diff --git a/lib/client_factory.ts b/lib/client_factory.ts index 981145bfd..d4dbf8c23 100644 --- a/lib/client_factory.ts +++ b/lib/client_factory.ts @@ -29,7 +29,7 @@ import { InMemoryLruCache } from "./utils/cache/in_memory_lru_cache"; import { transformCache, CacheWithRemove } from "./utils/cache/cache"; import { ConstantBackoff } from "./utils/repeater/repeater"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export type OptimizelyFactoryConfig = Config & { requestHandler: RequestHandler; diff --git a/lib/common_exports.ts b/lib/common_exports.ts index ad73c332e..55af919f0 100644 --- a/lib/common_exports.ts +++ b/lib/common_exports.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export { createStaticProjectConfigManager } from './project_config/config_manager_factory'; diff --git a/lib/core/audience_evaluator/index.ts b/lib/core/audience_evaluator/index.ts index 41539ec7e..9d670b229 100644 --- a/lib/core/audience_evaluator/index.ts +++ b/lib/core/audience_evaluator/index.ts @@ -21,7 +21,7 @@ import { CONDITION_EVALUATOR_ERROR, UNKNOWN_CONDITION_TYPE } from 'error_message import { AUDIENCE_EVALUATION_RESULT, EVALUATING_AUDIENCE} from 'log_message'; import { LoggerFacade } from '../../logging/logger'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export class AudienceEvaluator { private logger?: LoggerFacade; diff --git a/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts b/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts index f28c8112c..b2a8d9dd3 100644 --- a/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts +++ b/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts @@ -17,7 +17,7 @@ import { UNKNOWN_MATCH_TYPE } from 'error_message'; import { LoggerFacade } from '../../../logging/logger'; import { Condition, OptimizelyUserContext } from '../../../shared_types'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; const QUALIFIED_MATCH_TYPE = 'qualified'; diff --git a/lib/core/bucketer/bucket_value_generator.ts b/lib/core/bucketer/bucket_value_generator.ts index 07567bf07..551601c32 100644 --- a/lib/core/bucketer/bucket_value_generator.ts +++ b/lib/core/bucketer/bucket_value_generator.ts @@ -17,7 +17,7 @@ import murmurhash from 'murmurhash'; import { INVALID_BUCKETING_ID } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; const HASH_SEED = 1; const MAX_HASH_VALUE = Math.pow(2, 32); diff --git a/lib/core/bucketer/index.ts b/lib/core/bucketer/index.ts index 0ec7ed5ee..b0a83f4f0 100644 --- a/lib/core/bucketer/index.ts +++ b/lib/core/bucketer/index.ts @@ -29,7 +29,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { generateBucketValue } from './bucket_value_generator'; import { DecisionReason } from '../decision_service'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const USER_NOT_IN_ANY_EXPERIMENT = 'User %s is not in any experiment of group %s.'; export const USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP = 'User %s is not in experiment %s of group %s.'; diff --git a/lib/core/condition_tree_evaluator/index.ts b/lib/core/condition_tree_evaluator/index.ts index 3f1bc3a35..884a46eee 100644 --- a/lib/core/condition_tree_evaluator/index.ts +++ b/lib/core/condition_tree_evaluator/index.ts @@ -14,7 +14,7 @@ * limitations under the License. * ***************************************************************************/ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; const AND_CONDITION = 'and'; const OR_CONDITION = 'or'; diff --git a/lib/core/decision/index.ts b/lib/core/decision/index.ts index e66b33dcd..24738b393 100644 --- a/lib/core/decision/index.ts +++ b/lib/core/decision/index.ts @@ -21,7 +21,7 @@ import { DecisionObj } from '../decision_service'; * @param {DecisionObj} decisionObj Object representing decision * @returns {string} Experiment key or empty string if experiment is null */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export function getExperimentKey(decisionObj: DecisionObj): string { diff --git a/lib/core/decision_service/cmab/cmab_client.ts b/lib/core/decision_service/cmab/cmab_client.ts index f6c3bb266..d76c865f5 100644 --- a/lib/core/decision_service/cmab/cmab_client.ts +++ b/lib/core/decision_service/cmab/cmab_client.ts @@ -24,7 +24,7 @@ import { isSuccessStatusCode } from "../../../utils/http_request_handler/http_ut import { BackoffController } from "../../../utils/repeater/repeater"; import { Producer } from "../../../utils/type"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface CmabClient { fetchDecision( diff --git a/lib/core/decision_service/cmab/cmab_service.ts b/lib/core/decision_service/cmab/cmab_service.ts index 95b7ac8c0..5a1effb3b 100644 --- a/lib/core/decision_service/cmab/cmab_service.ts +++ b/lib/core/decision_service/cmab/cmab_service.ts @@ -34,7 +34,7 @@ import { } from 'log_message'; import { Platform } from '../../../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type CmabDecision = { variationId: string, diff --git a/lib/core/decision_service/index.ts b/lib/core/decision_service/index.ts index 147636665..42e99221b 100644 --- a/lib/core/decision_service/index.ts +++ b/lib/core/decision_service/index.ts @@ -78,7 +78,7 @@ import { Value } from '../../utils/promise/operation_value'; import { Platform } from '../../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const EXPERIMENT_NOT_RUNNING = 'Experiment %s is not running.'; export const RETURNING_STORED_VARIATION = diff --git a/lib/entrypoint.test-d.ts b/lib/entrypoint.test-d.ts index 4b70c5885..09be638c0 100644 --- a/lib/entrypoint.test-d.ts +++ b/lib/entrypoint.test-d.ts @@ -60,7 +60,7 @@ import { Platform } from './platform_support'; export type Entrypoint = { // platform declaration - __supportedPlatforms: Platform[]; + __platforms: Platform[]; // client factory createInstance: (config: Config) => Client; diff --git a/lib/entrypoint.universal.test-d.ts b/lib/entrypoint.universal.test-d.ts index 9e5d6ac08..c399fc169 100644 --- a/lib/entrypoint.universal.test-d.ts +++ b/lib/entrypoint.universal.test-d.ts @@ -56,7 +56,7 @@ import { UniversalOdpManagerOptions } from './odp/odp_manager_factory.universal' export type UniversalEntrypoint = { // platform declaration - __supportedPlatforms: Platform[]; + __platforms: Platform[]; // client factory createInstance: (config: UniversalConfig) => Client; diff --git a/lib/error/error_handler.ts b/lib/error/error_handler.ts index 2886f35bb..35439e9dc 100644 --- a/lib/error/error_handler.ts +++ b/lib/error/error_handler.ts @@ -17,7 +17,7 @@ * @export * @interface ErrorHandler */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface ErrorHandler { /** diff --git a/lib/error/error_notifier.ts b/lib/error/error_notifier.ts index 9c3770be1..79776a4f7 100644 --- a/lib/error/error_notifier.ts +++ b/lib/error/error_notifier.ts @@ -17,7 +17,7 @@ import { MessageResolver } from "../message/message_resolver"; import { ErrorHandler } from "./error_handler"; import { OptimizelyError } from "./optimizly_error"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface ErrorNotifier { notify(error: Error): void; diff --git a/lib/error/error_notifier_factory.ts b/lib/error/error_notifier_factory.ts index 8bed24ca9..59cfbb18d 100644 --- a/lib/error/error_notifier_factory.ts +++ b/lib/error/error_notifier_factory.ts @@ -18,7 +18,7 @@ import { Maybe } from "../utils/type"; import { ErrorHandler } from "./error_handler"; import { DefaultErrorNotifier } from "./error_notifier"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const INVALID_ERROR_HANDLER = 'Invalid error handler'; diff --git a/lib/error/error_reporter.ts b/lib/error/error_reporter.ts index 34f354932..35944f47f 100644 --- a/lib/error/error_reporter.ts +++ b/lib/error/error_reporter.ts @@ -17,7 +17,7 @@ import { LoggerFacade } from "../logging/logger"; import { ErrorNotifier } from "./error_notifier"; import { OptimizelyError } from "./optimizly_error"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export class ErrorReporter { private logger?: LoggerFacade; diff --git a/lib/error/optimizly_error.ts b/lib/error/optimizly_error.ts index eec66fbf4..1aa2725f5 100644 --- a/lib/error/optimizly_error.ts +++ b/lib/error/optimizly_error.ts @@ -16,7 +16,7 @@ import { MessageResolver } from "../message/message_resolver"; import { sprintf } from "../utils/fns"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export class OptimizelyError extends Error { baseMessage: string; diff --git a/lib/event_processor/batch_event_processor.react_native.ts b/lib/event_processor/batch_event_processor.react_native.ts index 7cdd5533b..7d783ab45 100644 --- a/lib/event_processor/batch_event_processor.react_native.ts +++ b/lib/event_processor/batch_event_processor.react_native.ts @@ -19,7 +19,7 @@ import { NetInfoState, addEventListener } from '@react-native-community/netinfo' import { BatchEventProcessor, BatchEventProcessorConfig } from './batch_event_processor'; import { Fn } from '../utils/type'; -export const __supportedPlatforms = ['react_native']; +export const __platforms = ['react_native']; export class ReactNativeNetInfoEventProcessor extends BatchEventProcessor { private isInternetReachable = true; diff --git a/lib/event_processor/batch_event_processor.ts b/lib/event_processor/batch_event_processor.ts index 79cd41744..9687c82b0 100644 --- a/lib/event_processor/batch_event_processor.ts +++ b/lib/event_processor/batch_event_processor.ts @@ -32,7 +32,7 @@ import { OptimizelyError } from "../error/optimizly_error"; import { sprintf } from "../utils/fns"; import { SERVICE_STOPPED_BEFORE_RUNNING } from "../service"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const DEFAULT_MIN_BACKOFF = 1000; export const DEFAULT_MAX_BACKOFF = 32000; diff --git a/lib/event_processor/event_builder/log_event.ts b/lib/event_processor/event_builder/log_event.ts index 6b87b6aa9..5daa43cab 100644 --- a/lib/event_processor/event_builder/log_event.ts +++ b/lib/event_processor/event_builder/log_event.ts @@ -21,7 +21,7 @@ import { LogEvent } from '../event_dispatcher/event_dispatcher'; import { EventTags } from '../../shared_types'; import { Region } from '../../project_config/project_config'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; const ACTIVATE_EVENT_KEY = 'campaign_activated' const CUSTOM_ATTRIBUTE_FEATURE_TYPE = 'custom' diff --git a/lib/event_processor/event_builder/user_event.ts b/lib/event_processor/event_builder/user_event.ts index a3a0dea61..c977be868 100644 --- a/lib/event_processor/event_builder/user_event.ts +++ b/lib/event_processor/event_builder/user_event.ts @@ -31,7 +31,7 @@ import { LoggerFacade } from '../../logging/logger'; import { DECISION_SOURCES } from '../../common_exports'; import { Platform } from '../../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type VisitorAttribute = { entityId: string diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts index 10b7fc1c6..28b1523af 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts @@ -15,7 +15,7 @@ */ // This implementation works in both browser and react_native environments -export const __supportedPlatforms = ['browser', 'react_native']; +export const __platforms = ['browser', 'react_native']; import { BrowserRequestHandler } from "../../utils/http_request_handler/request_handler.browser"; import { EventDispatcher } from './event_dispatcher'; diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.node.ts b/lib/event_processor/event_dispatcher/default_dispatcher.node.ts index dabcbc554..c082170fa 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.node.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.node.ts @@ -17,7 +17,7 @@ import { EventDispatcher } from './event_dispatcher'; import { NodeRequestHandler } from '../../utils/http_request_handler/request_handler.node'; import { DefaultEventDispatcher } from './default_dispatcher'; -export const __supportedPlatforms = ['node']; +export const __platforms = ['node']; const eventDispatcher: EventDispatcher = new DefaultEventDispatcher(new NodeRequestHandler()); diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.ts b/lib/event_processor/event_dispatcher/default_dispatcher.ts index d06f5f1f5..62fe6b3f7 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.ts @@ -18,7 +18,7 @@ import { ONLY_POST_REQUESTS_ARE_SUPPORTED } from 'error_message'; import { RequestHandler } from '../../utils/http_request_handler/http'; import { EventDispatcher, EventDispatcherResponse, LogEvent } from './event_dispatcher'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export class DefaultEventDispatcher implements EventDispatcher { private requestHandler: RequestHandler; diff --git a/lib/event_processor/event_dispatcher/event_dispatcher.ts b/lib/event_processor/event_dispatcher/event_dispatcher.ts index 1e1a5a5aa..b9d8a6600 100644 --- a/lib/event_processor/event_dispatcher/event_dispatcher.ts +++ b/lib/event_processor/event_dispatcher/event_dispatcher.ts @@ -15,7 +15,7 @@ */ import { EventBatch } from "../event_builder/log_event"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export type EventDispatcherResponse = { statusCode?: number diff --git a/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts b/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts index caafeb77b..6aea5c040 100644 --- a/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts +++ b/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts @@ -20,7 +20,7 @@ import { EventDispatcher } from './event_dispatcher'; import { validateRequestHandler } from '../../utils/http_request_handler/request_handler_validator'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const createEventDispatcher = (requestHander: RequestHandler): EventDispatcher => { validateRequestHandler(requestHander); diff --git a/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts index 0558e3e0f..cac5235fd 100644 --- a/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts @@ -18,7 +18,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { SEND_BEACON_FAILED } from 'error_message'; import { EventDispatcher, EventDispatcherResponse } from './event_dispatcher'; -export const __supportedPlatforms = ['browser']; +export const __platforms = ['browser']; export type Event = { url: string; diff --git a/lib/event_processor/event_processor.ts b/lib/event_processor/event_processor.ts index 9fc8563d1..10e7ff843 100644 --- a/lib/event_processor/event_processor.ts +++ b/lib/event_processor/event_processor.ts @@ -19,7 +19,7 @@ import { Service } from '../service' import { Consumer, Fn } from '../utils/type'; import { LoggerFacade } from '../logging/logger'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const DEFAULT_FLUSH_INTERVAL = 30000 // Unit is ms - default flush interval is 30s export const DEFAULT_BATCH_SIZE = 10 diff --git a/lib/event_processor/event_processor_factory.browser.ts b/lib/event_processor/event_processor_factory.browser.ts index 5ea05dcce..de86024fd 100644 --- a/lib/event_processor/event_processor_factory.browser.ts +++ b/lib/event_processor/event_processor_factory.browser.ts @@ -31,7 +31,7 @@ import { FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory'; import { DEFAULT_MAX_EVENTS_IN_STORE, EventStore } from './event_store'; import { Platform } from '../platform_support'; -export const __supportedPlatforms: Platform[] = ['browser']; +export const __platforms: Platform[] = ['browser']; export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 1_000; diff --git a/lib/event_processor/event_processor_factory.node.ts b/lib/event_processor/event_processor_factory.node.ts index da6f08554..22c655988 100644 --- a/lib/event_processor/event_processor_factory.node.ts +++ b/lib/event_processor/event_processor_factory.node.ts @@ -26,7 +26,7 @@ import { } from './event_processor_factory'; import { Platform } from '../platform_support'; -export const __supportedPlatforms: Platform[] = ['node']; +export const __platforms: Platform[] = ['node']; export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 30_000; diff --git a/lib/event_processor/event_processor_factory.react_native.ts b/lib/event_processor/event_processor_factory.react_native.ts index 892187de1..cda75d68b 100644 --- a/lib/event_processor/event_processor_factory.react_native.ts +++ b/lib/event_processor/event_processor_factory.react_native.ts @@ -30,7 +30,7 @@ import { ReactNativeNetInfoEventProcessor } from './batch_event_processor.react_ import { DEFAULT_MAX_EVENTS_IN_STORE, EventStore } from './event_store'; import { Platform } from '../platform_support'; -export const __supportedPlatforms: Platform[] = ['react_native']; +export const __platforms: Platform[] = ['react_native']; export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 1_000; diff --git a/lib/event_processor/event_processor_factory.ts b/lib/event_processor/event_processor_factory.ts index 4e133a506..79b408552 100644 --- a/lib/event_processor/event_processor_factory.ts +++ b/lib/event_processor/event_processor_factory.ts @@ -26,7 +26,7 @@ import { EventProcessor } from "./event_processor"; import { EVENT_STORE_PREFIX } from "./event_store"; import { ForwardingEventProcessor } from "./forwarding_event_processor"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const INVALID_EVENT_DISPATCHER = 'Invalid event dispatcher'; diff --git a/lib/event_processor/event_processor_factory.universal.ts b/lib/event_processor/event_processor_factory.universal.ts index deb7ef6a8..22d597587 100644 --- a/lib/event_processor/event_processor_factory.universal.ts +++ b/lib/event_processor/event_processor_factory.universal.ts @@ -26,7 +26,7 @@ import { } from './event_processor_factory'; import { Platform } from '../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 1_000; diff --git a/lib/event_processor/event_store.ts b/lib/event_processor/event_store.ts index 75de1f2f7..856b92f1c 100644 --- a/lib/event_processor/event_store.ts +++ b/lib/event_processor/event_store.ts @@ -14,7 +14,7 @@ import { Maybe } from "../utils/type"; import { EventWithId } from "./batch_event_processor"; import { Platform } from '../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type StoredEvent = EventWithId & { _time?: { diff --git a/lib/event_processor/forwarding_event_processor.ts b/lib/event_processor/forwarding_event_processor.ts index e0bb73cfb..008d674ff 100644 --- a/lib/event_processor/forwarding_event_processor.ts +++ b/lib/event_processor/forwarding_event_processor.ts @@ -26,7 +26,7 @@ import { Consumer, Fn } from '../utils/type'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; import { sprintf } from '../utils/fns'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export class ForwardingEventProcessor extends BaseService implements EventProcessor { private dispatcher: EventDispatcher; diff --git a/lib/export_types.ts b/lib/export_types.ts index 2d48555f4..ddec4a8dd 100644 --- a/lib/export_types.ts +++ b/lib/export_types.ts @@ -15,7 +15,7 @@ */ // config manager related types -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export type { StaticConfigManagerConfig, diff --git a/lib/feature_toggle.ts b/lib/feature_toggle.ts index 6dd29ce5c..271e5cdda 100644 --- a/lib/feature_toggle.ts +++ b/lib/feature_toggle.ts @@ -34,6 +34,6 @@ // example feature flag definition // export const wipFeat = () => false as const; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export type IfActive boolean, Y, N = unknown> = ReturnType extends true ? Y : N; diff --git a/lib/index.browser.ts b/lib/index.browser.ts index 2cf8aa4eb..abc025be3 100644 --- a/lib/index.browser.ts +++ b/lib/index.browser.ts @@ -27,7 +27,7 @@ import { Platform } from './platform_support'; * @return {Client|null} the Optimizely client object * null on error */ -export const __supportedPlatforms: Platform[] = ['browser']; +export const __platforms: Platform[] = ['browser']; export const createInstance = function(config: Config): Client { diff --git a/lib/index.browser.umdtests.js b/lib/index.browser.umdtests.js index a57f1193c..a9cbdeed2 100644 --- a/lib/index.browser.umdtests.js +++ b/lib/index.browser.umdtests.js @@ -25,7 +25,7 @@ import packageJSON from '../package.json'; import eventDispatcher from './plugins/event_dispatcher/index.browser'; import { INVALID_CONFIG_OR_SOMETHING } from './exception_messages'; -export const __supportedPlatforms = ['browser'] as const; +export const __platforms = ['browser'] as const; describe('javascript-sdk', function() { describe('APIs', function() { diff --git a/lib/index.node.ts b/lib/index.node.ts index 74e84e0c1..1ad400732 100644 --- a/lib/index.node.ts +++ b/lib/index.node.ts @@ -26,7 +26,7 @@ import { Platform } from './platform_support'; * @return {Client|null} the Optimizely client object * null on error */ -export const __supportedPlatforms: Platform[] = ['node']; +export const __platforms: Platform[] = ['node']; export const createInstance = function(config: Config): Client { diff --git a/lib/index.react_native.ts b/lib/index.react_native.ts index 76d589d93..9619f50f9 100644 --- a/lib/index.react_native.ts +++ b/lib/index.react_native.ts @@ -29,7 +29,7 @@ import { Platform } from './platform_support'; * @return {Client|null} the Optimizely client object * null on error */ -export const __supportedPlatforms: Platform[] = ['react_native']; +export const __platforms: Platform[] = ['react_native']; export const createInstance = function(config: Config): Client { diff --git a/lib/index.universal.ts b/lib/index.universal.ts index 5ca9921a3..823dcfa29 100644 --- a/lib/index.universal.ts +++ b/lib/index.universal.ts @@ -20,7 +20,7 @@ import { JAVASCRIPT_CLIENT_ENGINE } from './utils/enums'; import { RequestHandler } from './utils/http_request_handler/http'; import { Platform } from './platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type UniversalConfig = Config & { requestHandler: RequestHandler; diff --git a/lib/logging/logger.ts b/lib/logging/logger.ts index 33eec65d2..53a034cef 100644 --- a/lib/logging/logger.ts +++ b/lib/logging/logger.ts @@ -17,7 +17,7 @@ import { OptimizelyError } from '../error/optimizly_error'; import { MessageResolver } from '../message/message_resolver'; import { sprintf } from '../utils/fns' -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export enum LogLevel { Debug, diff --git a/lib/logging/logger_factory.ts b/lib/logging/logger_factory.ts index f2a12cf4d..ca423ddce 100644 --- a/lib/logging/logger_factory.ts +++ b/lib/logging/logger_factory.ts @@ -17,7 +17,7 @@ import { ConsoleLogHandler, LogHandler, LogLevel, OptimizelyLogger } from './log import { errorResolver, infoResolver, MessageResolver } from '../message/message_resolver'; import { Maybe } from '../utils/type'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const INVALID_LOG_HANDLER = 'Invalid log handler'; export const INVALID_LEVEL_PRESET = 'Invalid level preset'; diff --git a/lib/message/error_message.ts b/lib/message/error_message.ts index 5c8429741..3c27d185a 100644 --- a/lib/message/error_message.ts +++ b/lib/message/error_message.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const NOTIFICATION_LISTENER_EXCEPTION = 'Notification listener for (%s) threw exception: %s'; export const CONDITION_EVALUATOR_ERROR = 'Error evaluating audience condition of type %s: %s'; diff --git a/lib/message/log_message.ts b/lib/message/log_message.ts index 669d19f62..63d47680f 100644 --- a/lib/message/log_message.ts +++ b/lib/message/log_message.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const FEATURE_ENABLED_FOR_USER = 'Feature %s is enabled for user %s.'; export const FEATURE_NOT_ENABLED_FOR_USER = 'Feature %s is not enabled for user %s.'; diff --git a/lib/message/message_resolver.ts b/lib/message/message_resolver.ts index 6504681c0..106975fef 100644 --- a/lib/message/message_resolver.ts +++ b/lib/message/message_resolver.ts @@ -1,7 +1,7 @@ import { messages as infoMessages } from 'log_message'; import { messages as errorMessages } from 'error_message'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface MessageResolver { resolve(baseMessage: string): string; diff --git a/lib/notification_center/index.ts b/lib/notification_center/index.ts index ee6893d76..dc2038139 100644 --- a/lib/notification_center/index.ts +++ b/lib/notification_center/index.ts @@ -24,7 +24,7 @@ import { NOTIFICATION_LISTENER_EXCEPTION } from 'error_message'; import { ErrorReporter } from '../error/error_reporter'; import { ErrorNotifier } from '../error/error_notifier'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; interface NotificationCenterOptions { logger?: LoggerFacade; diff --git a/lib/notification_center/type.ts b/lib/notification_center/type.ts index e569f9cde..8ed9b24d4 100644 --- a/lib/notification_center/type.ts +++ b/lib/notification_center/type.ts @@ -28,7 +28,7 @@ import { DecisionSource } from '../utils/enums'; import { Nullable } from '../utils/type'; import { Platform } from '../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type UserEventListenerPayload = { userId: string; diff --git a/lib/odp/constant.ts b/lib/odp/constant.ts index 01665cb82..1c8efc4df 100644 --- a/lib/odp/constant.ts +++ b/lib/odp/constant.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export enum ODP_USER_KEY { VUID = 'vuid', diff --git a/lib/odp/event_manager/odp_event.ts b/lib/odp/event_manager/odp_event.ts index c8f0d013e..7cbe3d455 100644 --- a/lib/odp/event_manager/odp_event.ts +++ b/lib/odp/event_manager/odp_event.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export class OdpEvent { /** diff --git a/lib/odp/event_manager/odp_event_api_manager.ts b/lib/odp/event_manager/odp_event_api_manager.ts index 10e879bd0..bbdb44fa9 100644 --- a/lib/odp/event_manager/odp_event_api_manager.ts +++ b/lib/odp/event_manager/odp_event_api_manager.ts @@ -19,7 +19,7 @@ import { OdpEvent } from './odp_event'; import { HttpMethod, RequestHandler } from '../../utils/http_request_handler/http'; import { OdpConfig } from '../odp_config'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export type EventDispatchResponse = { statusCode?: number; diff --git a/lib/odp/event_manager/odp_event_manager.ts b/lib/odp/event_manager/odp_event_manager.ts index 68afb9e8e..d0ea30d2f 100644 --- a/lib/odp/event_manager/odp_event_manager.ts +++ b/lib/odp/event_manager/odp_event_manager.ts @@ -39,7 +39,7 @@ import { SERVICE_STOPPED_BEFORE_RUNNING } from '../../service'; import { sprintf } from '../../utils/fns'; import { Platform } from '../../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface OdpEventManager extends Service { updateConfig(odpIntegrationConfig: OdpIntegrationConfig): void; diff --git a/lib/odp/odp_config.ts b/lib/odp/odp_config.ts index 789a8d16b..7ae353998 100644 --- a/lib/odp/odp_config.ts +++ b/lib/odp/odp_config.ts @@ -16,7 +16,7 @@ import { checkArrayEquality } from '../utils/fns'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export class OdpConfig { /** diff --git a/lib/odp/odp_manager.ts b/lib/odp/odp_manager.ts index 10f6eb340..217eeb2ee 100644 --- a/lib/odp/odp_manager.ts +++ b/lib/odp/odp_manager.ts @@ -32,7 +32,7 @@ import { Maybe } from '../utils/type'; import { sprintf } from '../utils/fns'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface OdpManager extends Service { updateConfig(odpIntegrationConfig: OdpIntegrationConfig): boolean; diff --git a/lib/odp/odp_manager_factory.browser.ts b/lib/odp/odp_manager_factory.browser.ts index 1ff50bc0e..e0be6ef2b 100644 --- a/lib/odp/odp_manager_factory.browser.ts +++ b/lib/odp/odp_manager_factory.browser.ts @@ -18,7 +18,7 @@ import { BrowserRequestHandler } from '../utils/http_request_handler/request_han import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __supportedPlatforms = ['browser']; +export const __platforms = ['browser']; export const BROWSER_DEFAULT_API_TIMEOUT = 10_000; export const BROWSER_DEFAULT_BATCH_SIZE = 10; diff --git a/lib/odp/odp_manager_factory.node.ts b/lib/odp/odp_manager_factory.node.ts index 1dd70ed4b..0e7ebb9a3 100644 --- a/lib/odp/odp_manager_factory.node.ts +++ b/lib/odp/odp_manager_factory.node.ts @@ -18,7 +18,7 @@ import { NodeRequestHandler } from '../utils/http_request_handler/request_handle import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __supportedPlatforms = ['node']; +export const __platforms = ['node']; export const NODE_DEFAULT_API_TIMEOUT = 10_000; export const NODE_DEFAULT_BATCH_SIZE = 10; diff --git a/lib/odp/odp_manager_factory.react_native.ts b/lib/odp/odp_manager_factory.react_native.ts index 7aa855fc5..a83dfe1f7 100644 --- a/lib/odp/odp_manager_factory.react_native.ts +++ b/lib/odp/odp_manager_factory.react_native.ts @@ -19,7 +19,7 @@ import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager' import { OdpManager } from './odp_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __supportedPlatforms = ['react_native']; +export const __platforms = ['react_native']; export const RN_DEFAULT_API_TIMEOUT = 10_000; export const RN_DEFAULT_BATCH_SIZE = 10; diff --git a/lib/odp/odp_manager_factory.ts b/lib/odp/odp_manager_factory.ts index bcdddc5ad..46bf81399 100644 --- a/lib/odp/odp_manager_factory.ts +++ b/lib/odp/odp_manager_factory.ts @@ -26,7 +26,7 @@ import { DefaultOdpSegmentApiManager } from "./segment_manager/odp_segment_api_m import { DefaultOdpSegmentManager, OdpSegmentManager } from "./segment_manager/odp_segment_manager"; import { UserAgentParser } from "./ua_parser/user_agent_parser"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const DEFAULT_CACHE_SIZE = 10_000; export const DEFAULT_CACHE_TIMEOUT = 600_000; diff --git a/lib/odp/odp_manager_factory.universal.ts b/lib/odp/odp_manager_factory.universal.ts index af1777213..fc0e6484b 100644 --- a/lib/odp/odp_manager_factory.universal.ts +++ b/lib/odp/odp_manager_factory.universal.ts @@ -19,7 +19,7 @@ import { validateRequestHandler } from '../utils/http_request_handler/request_ha import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const DEFAULT_API_TIMEOUT = 10_000; export const DEFAULT_BATCH_SIZE = 1; diff --git a/lib/odp/odp_types.ts b/lib/odp/odp_types.ts index 91da1850d..967418828 100644 --- a/lib/odp/odp_types.ts +++ b/lib/odp/odp_types.ts @@ -17,7 +17,7 @@ /** * Wrapper around valid data and error responses */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface Response { data: Data; diff --git a/lib/odp/segment_manager/odp_response_schema.ts b/lib/odp/segment_manager/odp_response_schema.ts index ca42c39be..5031ff9e6 100644 --- a/lib/odp/segment_manager/odp_response_schema.ts +++ b/lib/odp/segment_manager/odp_response_schema.ts @@ -19,7 +19,7 @@ import { JSONSchema4 } from 'json-schema'; /** * JSON Schema used to validate the ODP GraphQL response */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const OdpResponseSchema = { diff --git a/lib/odp/segment_manager/odp_segment_api_manager.ts b/lib/odp/segment_manager/odp_segment_api_manager.ts index 19d5dd3f1..c19227491 100644 --- a/lib/odp/segment_manager/odp_segment_api_manager.ts +++ b/lib/odp/segment_manager/odp_segment_api_manager.ts @@ -24,7 +24,7 @@ import { log } from 'console'; /** * Expected value for a qualified/valid segment */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; const QUALIFIED = 'qualified'; diff --git a/lib/odp/segment_manager/odp_segment_manager.ts b/lib/odp/segment_manager/odp_segment_manager.ts index 1f8f0302b..c87784ba4 100644 --- a/lib/odp/segment_manager/odp_segment_manager.ts +++ b/lib/odp/segment_manager/odp_segment_manager.ts @@ -22,7 +22,7 @@ import { ODP_USER_KEY } from '../constant'; import { LoggerFacade } from '../../logging/logger'; import { ODP_CONFIG_NOT_AVAILABLE, ODP_NOT_INTEGRATED } from 'error_message'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface OdpSegmentManager { fetchQualifiedSegments( diff --git a/lib/odp/segment_manager/optimizely_segment_option.ts b/lib/odp/segment_manager/optimizely_segment_option.ts index b81142fcd..9e8049baa 100644 --- a/lib/odp/segment_manager/optimizely_segment_option.ts +++ b/lib/odp/segment_manager/optimizely_segment_option.ts @@ -15,7 +15,7 @@ */ // Options for defining behavior of OdpSegmentManager's caching mechanism when calling fetchSegments() -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export enum OptimizelySegmentOption { IGNORE_CACHE = 'IGNORE_CACHE', diff --git a/lib/odp/ua_parser/ua_parser.ts b/lib/odp/ua_parser/ua_parser.ts index 5f3085115..af1a8e92f 100644 --- a/lib/odp/ua_parser/ua_parser.ts +++ b/lib/odp/ua_parser/ua_parser.ts @@ -17,7 +17,7 @@ import { UAParser } from 'ua-parser-js'; import { UserAgentInfo } from './user_agent_info'; import { UserAgentParser } from './user_agent_parser'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; const userAgentParser: UserAgentParser = { parseUserAgentInfo(): UserAgentInfo { diff --git a/lib/odp/ua_parser/user_agent_info.ts b/lib/odp/ua_parser/user_agent_info.ts index 67b84d61b..6d5edddda 100644 --- a/lib/odp/ua_parser/user_agent_info.ts +++ b/lib/odp/ua_parser/user_agent_info.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export type UserAgentInfo = { os: { diff --git a/lib/odp/ua_parser/user_agent_parser.ts b/lib/odp/ua_parser/user_agent_parser.ts index a8ce56316..b60586850 100644 --- a/lib/odp/ua_parser/user_agent_parser.ts +++ b/lib/odp/ua_parser/user_agent_parser.ts @@ -16,7 +16,7 @@ import { UserAgentInfo } from "./user_agent_info"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface UserAgentParser { parseUserAgentInfo(): UserAgentInfo, diff --git a/lib/optimizely/index.ts b/lib/optimizely/index.ts index ef9b8ac4d..499cb6e37 100644 --- a/lib/optimizely/index.ts +++ b/lib/optimizely/index.ts @@ -48,7 +48,7 @@ import { buildImpressionEvent, buildConversionEvent } from '../event_processor/e import { isSafeInteger } from '../utils/fns'; import { Platform } from '../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; import { validate } from '../utils/attributes_validator'; import * as eventTagsValidator from '../utils/event_tags_validator'; import * as projectConfig from '../project_config/project_config'; diff --git a/lib/optimizely_decision/index.ts b/lib/optimizely_decision/index.ts index f15421d5d..e34059fcc 100644 --- a/lib/optimizely_decision/index.ts +++ b/lib/optimizely_decision/index.ts @@ -15,7 +15,7 @@ ***************************************************************************/ import { OptimizelyUserContext, OptimizelyDecision } from '../shared_types'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export function newErrorDecision(key: string, user: OptimizelyUserContext, reasons: string[]): OptimizelyDecision { return { diff --git a/lib/optimizely_user_context/index.ts b/lib/optimizely_user_context/index.ts index dd9a6effb..303661a2a 100644 --- a/lib/optimizely_user_context/index.ts +++ b/lib/optimizely_user_context/index.ts @@ -26,7 +26,7 @@ import { import { OptimizelySegmentOption } from '../odp/segment_manager/optimizely_segment_option'; import { Platform } from '../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const FORCED_DECISION_NULL_RULE_KEY = '$opt_null_rule_key'; diff --git a/lib/platform_support.ts b/lib/platform_support.ts index f09d26584..47e439eb7 100644 --- a/lib/platform_support.ts +++ b/lib/platform_support.ts @@ -1,7 +1,7 @@ /** * Platform Support Type Definitions * - * Every source file (except tests) must export __supportedPlatforms to declare + * Every source file (except tests) must export __platforms to declare * which platforms it supports. This is enforced at both type-level and runtime. */ @@ -10,45 +10,45 @@ */ export type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; /** * Platform support declaration * * Every file must export this to declare which platforms it supports: - * - Specific platforms: export const __supportedPlatforms = ['browser', 'node']; - * - All platforms (universal): export const __supportedPlatforms = ['__universal__']; + * - Specific platforms: export const __platforms = ['browser', 'node']; + * - All platforms (universal): export const __platforms = ['__universal__']; * * @example * // Browser and React Native only - * export const __supportedPlatforms = ['browser', 'react_native'] satisfies Platform[]; + * export const __platforms = ['browser', 'react_native'] satisfies Platform[]; * * @example * // Node.js only - * export const __supportedPlatforms = ['node'] satisfies Platform[]; + * export const __platforms = ['node'] satisfies Platform[]; * * @example * // Universal (all platforms) - * export const __supportedPlatforms = ['__universal__'] satisfies Platform[]; + * export const __platforms = ['__universal__'] satisfies Platform[]; */ export type SupportedPlatforms = readonly Platform[]; /** - * Helper type to enforce that a module exports __supportedPlatforms + * Helper type to enforce that a module exports __platforms * * Usage in your module: * ```typescript * import type { RequirePlatformDeclaration, Platform } from './platform_support'; * - * export const __supportedPlatforms = ['browser'] satisfies Platform[]; + * export const __platforms = ['browser'] satisfies Platform[]; * - * // This type check ensures __supportedPlatforms is exported + * // This type check ensures __platforms is exported * // type _Check = RequirePlatformDeclaration; * ``` */ -export type RequirePlatformDeclaration = T extends { __supportedPlatforms: readonly Platform[] } +export type RequirePlatformDeclaration = T extends { __platforms: readonly Platform[] } ? T - : never & { error: '__supportedPlatforms export is required' }; + : never & { error: '__platforms export is required' }; /** * Utility to check if a file is universal (supports all platforms) diff --git a/lib/project_config/config_manager_factory.browser.ts b/lib/project_config/config_manager_factory.browser.ts index 7905df770..afbb152f3 100644 --- a/lib/project_config/config_manager_factory.browser.ts +++ b/lib/project_config/config_manager_factory.browser.ts @@ -17,7 +17,7 @@ import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; -export const __supportedPlatforms = ['browser']; +export const __platforms = ['browser']; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { diff --git a/lib/project_config/config_manager_factory.node.ts b/lib/project_config/config_manager_factory.node.ts index 14eb18812..c8715bd28 100644 --- a/lib/project_config/config_manager_factory.node.ts +++ b/lib/project_config/config_manager_factory.node.ts @@ -17,7 +17,7 @@ import { NodeRequestHandler } from "../utils/http_request_handler/request_handler.node"; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; -export const __supportedPlatforms = ['node']; +export const __platforms = ['node']; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { diff --git a/lib/project_config/config_manager_factory.react_native.ts b/lib/project_config/config_manager_factory.react_native.ts index b2355c995..b1d297802 100644 --- a/lib/project_config/config_manager_factory.react_native.ts +++ b/lib/project_config/config_manager_factory.react_native.ts @@ -18,7 +18,7 @@ import { AsyncStorageCache } from "../utils/cache/async_storage_cache.react_nati import { BrowserRequestHandler } from "../utils/http_request_handler/request_handler.browser"; import { getOpaquePollingConfigManager, PollingConfigManagerConfig, OpaqueConfigManager } from "./config_manager_factory"; -export const __supportedPlatforms = ['react_native']; +export const __platforms = ['react_native']; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { diff --git a/lib/project_config/config_manager_factory.ts b/lib/project_config/config_manager_factory.ts index 0e64f7221..eeaa373ce 100644 --- a/lib/project_config/config_manager_factory.ts +++ b/lib/project_config/config_manager_factory.ts @@ -27,7 +27,7 @@ import { LogLevel } from '../logging/logger' import { Store } from "../utils/cache/store"; import { validateStore } from "../utils/cache/store_validator"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const INVALID_CONFIG_MANAGER = "Invalid config manager"; diff --git a/lib/project_config/config_manager_factory.universal.ts b/lib/project_config/config_manager_factory.universal.ts index 2c0352cd1..eb997cbcc 100644 --- a/lib/project_config/config_manager_factory.universal.ts +++ b/lib/project_config/config_manager_factory.universal.ts @@ -18,7 +18,7 @@ import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManage import { RequestHandler } from "../utils/http_request_handler/http"; import { validateRequestHandler } from "../utils/http_request_handler/request_handler_validator"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export type UniversalPollingConfigManagerConfig = PollingConfigManagerConfig & { requestHandler: RequestHandler; diff --git a/lib/project_config/constant.ts b/lib/project_config/constant.ts index 788e8dfeb..2cbfe480c 100644 --- a/lib/project_config/constant.ts +++ b/lib/project_config/constant.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; const DEFAULT_UPDATE_INTERVAL_MINUTES = 5; /** Standard interval (5 minutes in milliseconds) for polling datafile updates.; */ diff --git a/lib/project_config/datafile_manager.ts b/lib/project_config/datafile_manager.ts index 4cf05ac0c..4837bc0db 100644 --- a/lib/project_config/datafile_manager.ts +++ b/lib/project_config/datafile_manager.ts @@ -20,7 +20,7 @@ import { Fn, Consumer } from '../utils/type'; import { Repeater } from '../utils/repeater/repeater'; import { LoggerFacade } from '../logging/logger'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface DatafileManager extends Service { get(): string | undefined; diff --git a/lib/project_config/optimizely_config.ts b/lib/project_config/optimizely_config.ts index ce302286a..93066fe68 100644 --- a/lib/project_config/optimizely_config.ts +++ b/lib/project_config/optimizely_config.ts @@ -38,7 +38,7 @@ import { import { DATAFILE_VERSIONS } from '../utils/enums'; import { Platform } from '../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; interface FeatureVariablesMap { [key: string]: FeatureVariable[]; } diff --git a/lib/project_config/polling_datafile_manager.ts b/lib/project_config/polling_datafile_manager.ts index fad6abe80..7d11d8c1c 100644 --- a/lib/project_config/polling_datafile_manager.ts +++ b/lib/project_config/polling_datafile_manager.ts @@ -42,7 +42,7 @@ import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; import { Platform } from '../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const FAILED_TO_FETCH_DATAFILE = 'Failed to fetch datafile'; export class PollingDatafileManager extends BaseService implements DatafileManager { diff --git a/lib/project_config/project_config.ts b/lib/project_config/project_config.ts index fa2106a86..e4cd855a5 100644 --- a/lib/project_config/project_config.ts +++ b/lib/project_config/project_config.ts @@ -55,7 +55,7 @@ import { OptimizelyError } from '../error/optimizly_error'; import { Platform } from '../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; interface TryCreatingProjectConfigConfig { // TODO[OASIS-6649]: Don't use object type // eslint-disable-next-line @typescript-eslint/ban-types diff --git a/lib/project_config/project_config_manager.ts b/lib/project_config/project_config_manager.ts index 2d7f4fc8b..345dcabb5 100644 --- a/lib/project_config/project_config_manager.ts +++ b/lib/project_config/project_config_manager.ts @@ -33,7 +33,7 @@ export const GOT_INVALID_DATAFILE = 'got invalid datafile'; import { sprintf } from '../utils/fns'; import { Platform } from '../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; interface ProjectConfigManagerConfig { datafile?: string | Record; jsonSchemaValidator?: Transformer, diff --git a/lib/project_config/project_config_schema.ts b/lib/project_config/project_config_schema.ts index 956f0af30..88c58a4e3 100644 --- a/lib/project_config/project_config_schema.ts +++ b/lib/project_config/project_config_schema.ts @@ -19,7 +19,7 @@ */ import { JSONSchema4 } from 'json-schema'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; var schemaDefinition = { $schema: 'http://json-schema.org/draft-04/schema#', diff --git a/lib/service.ts b/lib/service.ts index 9c251f5e1..3d3b485e5 100644 --- a/lib/service.ts +++ b/lib/service.ts @@ -17,7 +17,7 @@ import { LoggerFacade, LogLevel, LogLevelToLower } from './logging/logger' import { resolvablePromise, ResolvablePromise } from "./utils/promise/resolvablePromise"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const SERVICE_FAILED_TO_START = '%s failed to start, reason: %s'; export const SERVICE_STOPPED_BEFORE_RUNNING = '%s stopped before running'; diff --git a/lib/shared_types.ts b/lib/shared_types.ts index ed643c3c2..4a5282eb2 100644 --- a/lib/shared_types.ts +++ b/lib/shared_types.ts @@ -47,7 +47,7 @@ import { OpaqueOdpManager } from './odp/odp_manager_factory'; import { OpaqueVuidManager } from './vuid/vuid_manager_factory'; import { CacheWithRemove } from './utils/cache/cache'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; export { EventProcessor } from './event_processor/event_processor'; diff --git a/lib/utils/attributes_validator/index.ts b/lib/utils/attributes_validator/index.ts index e131f31c7..0cac07df1 100644 --- a/lib/utils/attributes_validator/index.ts +++ b/lib/utils/attributes_validator/index.ts @@ -26,7 +26,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @throws If the attributes are not valid */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export function validate(attributes: unknown): boolean { if (typeof attributes === 'object' && !Array.isArray(attributes) && attributes !== null) { diff --git a/lib/utils/cache/async_storage_cache.react_native.ts b/lib/utils/cache/async_storage_cache.react_native.ts index 5a94779fb..d6852b7d0 100644 --- a/lib/utils/cache/async_storage_cache.react_native.ts +++ b/lib/utils/cache/async_storage_cache.react_native.ts @@ -18,7 +18,7 @@ import { Maybe } from "../type"; import { AsyncStore } from "./store"; import { getDefaultAsyncStorage } from "../import.react_native/@react-native-async-storage/async-storage"; -export const __supportedPlatforms = ['react_native']; +export const __platforms = ['react_native']; export class AsyncStorageCache implements AsyncStore { public readonly operation = 'async'; diff --git a/lib/utils/cache/cache.ts b/lib/utils/cache/cache.ts index 84168f129..27b8e720f 100644 --- a/lib/utils/cache/cache.ts +++ b/lib/utils/cache/cache.ts @@ -17,7 +17,7 @@ import { OpType, OpValue } from '../../utils/type'; import { Transformer } from '../../utils/type'; import { Platform } from '../../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface OpCache { diff --git a/lib/utils/cache/in_memory_lru_cache.ts b/lib/utils/cache/in_memory_lru_cache.ts index d1675ab0e..b9787495e 100644 --- a/lib/utils/cache/in_memory_lru_cache.ts +++ b/lib/utils/cache/in_memory_lru_cache.ts @@ -17,7 +17,7 @@ import { Maybe } from "../type"; import { SyncCacheWithRemove } from "./cache"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; type CacheElement = { value: V; diff --git a/lib/utils/cache/local_storage_cache.browser.ts b/lib/utils/cache/local_storage_cache.browser.ts index 6d097206d..550da3856 100644 --- a/lib/utils/cache/local_storage_cache.browser.ts +++ b/lib/utils/cache/local_storage_cache.browser.ts @@ -17,7 +17,7 @@ import { Maybe } from "../type"; import { SyncStore } from "./store"; -export const __supportedPlatforms = ['browser']; +export const __platforms = ['browser']; export class LocalStorageCache implements SyncStore { public readonly operation = 'sync'; diff --git a/lib/utils/cache/store.ts b/lib/utils/cache/store.ts index 58ac4ef59..2c526b734 100644 --- a/lib/utils/cache/store.ts +++ b/lib/utils/cache/store.ts @@ -18,7 +18,7 @@ import { Transformer } from '../../utils/type'; import { Maybe } from '../../utils/type'; import { OpType, OpValue } from '../../utils/type'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface OpStore { operation: OP; diff --git a/lib/utils/cache/store_validator.ts b/lib/utils/cache/store_validator.ts index eec699f22..b9b36fd67 100644 --- a/lib/utils/cache/store_validator.ts +++ b/lib/utils/cache/store_validator.ts @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const INVALID_STORE = 'Invalid store'; export const INVALID_STORE_METHOD = 'Invalid store method %s'; diff --git a/lib/utils/config_validator/index.ts b/lib/utils/config_validator/index.ts index e2628dfdd..47a6b2d0d 100644 --- a/lib/utils/config_validator/index.ts +++ b/lib/utils/config_validator/index.ts @@ -24,7 +24,7 @@ import { import { OptimizelyError } from '../../error/optimizly_error'; import { Platform } from '../../platform_support'; -export const __supportedPlatforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; const SUPPORTED_VERSIONS = [DATAFILE_VERSIONS.V2, DATAFILE_VERSIONS.V3, DATAFILE_VERSIONS.V4]; diff --git a/lib/utils/enums/index.ts b/lib/utils/enums/index.ts index a8dec7dd2..73ba8e2bd 100644 --- a/lib/utils/enums/index.ts +++ b/lib/utils/enums/index.ts @@ -17,7 +17,7 @@ /** * Contains global enums used throughout the library */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const LOG_LEVEL = { NOTSET: 0, diff --git a/lib/utils/event_emitter/event_emitter.ts b/lib/utils/event_emitter/event_emitter.ts index b04f459d0..e949bd039 100644 --- a/lib/utils/event_emitter/event_emitter.ts +++ b/lib/utils/event_emitter/event_emitter.ts @@ -16,7 +16,7 @@ import { Fn } from "../type"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; type Consumer = (arg: T) => void; diff --git a/lib/utils/event_tags_validator/index.ts b/lib/utils/event_tags_validator/index.ts index 8d163d38e..690758b67 100644 --- a/lib/utils/event_tags_validator/index.ts +++ b/lib/utils/event_tags_validator/index.ts @@ -26,7 +26,7 @@ import { INVALID_EVENT_TAGS } from 'error_message'; * @return {boolean} true if event tags are valid * @throws If event tags are not valid */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export function validate(eventTags: unknown): boolean { diff --git a/lib/utils/executor/backoff_retry_runner.ts b/lib/utils/executor/backoff_retry_runner.ts index b3252da80..db1dc03e6 100644 --- a/lib/utils/executor/backoff_retry_runner.ts +++ b/lib/utils/executor/backoff_retry_runner.ts @@ -4,7 +4,7 @@ import { resolvablePromise, ResolvablePromise } from "../promise/resolvablePromi import { BackoffController } from "../repeater/repeater"; import { AsyncProducer, Fn } from "../type"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export type RunResult = { result: Promise; diff --git a/lib/utils/executor/serial_runner.ts b/lib/utils/executor/serial_runner.ts index acfc12fd0..60d4bf2ce 100644 --- a/lib/utils/executor/serial_runner.ts +++ b/lib/utils/executor/serial_runner.ts @@ -16,7 +16,7 @@ import { AsyncProducer } from "../type"; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; class SerialRunner { private waitPromise: Promise = Promise.resolve(); diff --git a/lib/utils/fns/index.ts b/lib/utils/fns/index.ts index 265bcc685..e7e540c5e 100644 --- a/lib/utils/fns/index.ts +++ b/lib/utils/fns/index.ts @@ -15,7 +15,7 @@ */ import { v4 } from 'uuid'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; const MAX_SAFE_INTEGER_LIMIT = Math.pow(2, 53); diff --git a/lib/utils/http_request_handler/http.ts b/lib/utils/http_request_handler/http.ts index 4271c6971..643334984 100644 --- a/lib/utils/http_request_handler/http.ts +++ b/lib/utils/http_request_handler/http.ts @@ -17,7 +17,7 @@ /** * List of key-value pairs to be used in an HTTP requests */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface Headers { [header: string]: string | undefined; diff --git a/lib/utils/http_request_handler/http_util.ts b/lib/utils/http_request_handler/http_util.ts index 1d4f48ab3..4f619f34d 100644 --- a/lib/utils/http_request_handler/http_util.ts +++ b/lib/utils/http_request_handler/http_util.ts @@ -1,5 +1,5 @@ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const isSuccessStatusCode = (statusCode: number): boolean => { return statusCode >= 200 && statusCode < 400; diff --git a/lib/utils/http_request_handler/request_handler.browser.ts b/lib/utils/http_request_handler/request_handler.browser.ts index 784245b3e..738e62418 100644 --- a/lib/utils/http_request_handler/request_handler.browser.ts +++ b/lib/utils/http_request_handler/request_handler.browser.ts @@ -15,7 +15,7 @@ */ // This implementation works in both browser and react_native environments -export const __supportedPlatforms = ['browser', 'react_native']; +export const __platforms = ['browser', 'react_native']; import { AbortableRequest, Headers, RequestHandler, Response } from './http'; import { LoggerFacade, LogLevel } from '../../logging/logger'; diff --git a/lib/utils/http_request_handler/request_handler.node.ts b/lib/utils/http_request_handler/request_handler.node.ts index 61a2d27a8..1d6538638 100644 --- a/lib/utils/http_request_handler/request_handler.node.ts +++ b/lib/utils/http_request_handler/request_handler.node.ts @@ -26,7 +26,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; /** * Handles sending requests and receiving responses over HTTP via NodeJS http module */ -export const __supportedPlatforms = ['node']; +export const __platforms = ['node']; export class NodeRequestHandler implements RequestHandler { diff --git a/lib/utils/http_request_handler/request_handler_validator.ts b/lib/utils/http_request_handler/request_handler_validator.ts index 5afc8a96e..3421428bc 100644 --- a/lib/utils/http_request_handler/request_handler_validator.ts +++ b/lib/utils/http_request_handler/request_handler_validator.ts @@ -15,7 +15,7 @@ */ import { RequestHandler } from './http'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const INVALID_REQUEST_HANDLER = 'Invalid request handler'; diff --git a/lib/utils/id_generator/index.ts b/lib/utils/id_generator/index.ts index d98db3154..0b71b3d24 100644 --- a/lib/utils/id_generator/index.ts +++ b/lib/utils/id_generator/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; const idSuffixBase = 10_000; diff --git a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts index e30766201..9dd9764ef 100644 --- a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts +++ b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts @@ -16,7 +16,7 @@ import type { AsyncStorageStatic } from '@react-native-async-storage/async-storage' -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const MODULE_NOT_FOUND_REACT_NATIVE_ASYNC_STORAGE = 'Module not found: @react-native-async-storage/async-storage'; diff --git a/lib/utils/json_schema_validator/index.ts b/lib/utils/json_schema_validator/index.ts index 29c371e28..bbf7547e8 100644 --- a/lib/utils/json_schema_validator/index.ts +++ b/lib/utils/json_schema_validator/index.ts @@ -26,7 +26,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @param {boolean} shouldThrowOnError Should validation throw if invalid JSON object * @return {boolean} true if the given object is valid; throws or false if invalid */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export function validate( diff --git a/lib/utils/microtask/index.ts b/lib/utils/microtask/index.ts index 9919f981b..9a5f05bbb 100644 --- a/lib/utils/microtask/index.ts +++ b/lib/utils/microtask/index.ts @@ -16,7 +16,7 @@ type Callback = () => void; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const scheduleMicrotask = (callback: Callback): void => { if (typeof queueMicrotask === 'function') { diff --git a/lib/utils/promise/operation_value.ts b/lib/utils/promise/operation_value.ts index 6de4a7de1..d2f173936 100644 --- a/lib/utils/promise/operation_value.ts +++ b/lib/utils/promise/operation_value.ts @@ -3,7 +3,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { OpType, OpValue } from '../type'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; const isPromise = (val: any): boolean => { return val && typeof val.then === 'function'; diff --git a/lib/utils/promise/resolvablePromise.ts b/lib/utils/promise/resolvablePromise.ts index 43a52cf80..a9ccf1ef4 100644 --- a/lib/utils/promise/resolvablePromise.ts +++ b/lib/utils/promise/resolvablePromise.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; const noop = () => {}; diff --git a/lib/utils/repeater/repeater.ts b/lib/utils/repeater/repeater.ts index 614517656..9c82b32db 100644 --- a/lib/utils/repeater/repeater.ts +++ b/lib/utils/repeater/repeater.ts @@ -24,7 +24,7 @@ import { scheduleMicrotask } from "../microtask"; // If the retuned promise resolves, the repeater will assume the task succeeded, // and will reset the failure count. If the promise is rejected, the repeater will // assume the task failed and will increase the current consecutive failure count. -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface Repeater { diff --git a/lib/utils/semantic_version/index.ts b/lib/utils/semantic_version/index.ts index 2a908991f..627bf09cc 100644 --- a/lib/utils/semantic_version/index.ts +++ b/lib/utils/semantic_version/index.ts @@ -23,7 +23,7 @@ import { VERSION_TYPE } from '../enums'; * @return {boolean} true if the string is number only * */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; function isNumber(content: string): boolean { diff --git a/lib/utils/string_value_validator/index.ts b/lib/utils/string_value_validator/index.ts index d326f5ebf..200aadc9c 100644 --- a/lib/utils/string_value_validator/index.ts +++ b/lib/utils/string_value_validator/index.ts @@ -19,7 +19,7 @@ * @param {unknown} input * @return {boolean} true for non-empty string, false otherwise */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export function validate(input: unknown): boolean { return typeof input === 'string' && input !== ''; diff --git a/lib/utils/type.ts b/lib/utils/type.ts index b8e038873..a8869b011 100644 --- a/lib/utils/type.ts +++ b/lib/utils/type.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export type Fn = () => unknown; export type AsyncFn = () => Promise; diff --git a/lib/utils/user_profile_service_validator/index.ts b/lib/utils/user_profile_service_validator/index.ts index 268d781ba..d791c16c1 100644 --- a/lib/utils/user_profile_service_validator/index.ts +++ b/lib/utils/user_profile_service_validator/index.ts @@ -30,7 +30,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @throws If the instance is not valid */ -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export function validate(userProfileServiceInstance: unknown): boolean { if (typeof userProfileServiceInstance === 'object' && userProfileServiceInstance !== null) { diff --git a/lib/vuid/vuid.ts b/lib/vuid/vuid.ts index 1212adc37..0dfb1d32d 100644 --- a/lib/vuid/vuid.ts +++ b/lib/vuid/vuid.ts @@ -16,7 +16,7 @@ import { v4 as uuidV4 } from 'uuid'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export const VUID_PREFIX = `vuid_`; export const VUID_MAX_LENGTH = 32; diff --git a/lib/vuid/vuid_manager.ts b/lib/vuid/vuid_manager.ts index e2cdb3b6a..88608fa3b 100644 --- a/lib/vuid/vuid_manager.ts +++ b/lib/vuid/vuid_manager.ts @@ -18,7 +18,7 @@ import { Store } from '../utils/cache/store'; import { AsyncProducer, Maybe } from '../utils/type'; import { isVuid, makeVuid } from './vuid'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export interface VuidManager { getVuid(): Maybe; diff --git a/lib/vuid/vuid_manager_factory.browser.ts b/lib/vuid/vuid_manager_factory.browser.ts index d6742eb69..50e4317f7 100644 --- a/lib/vuid/vuid_manager_factory.browser.ts +++ b/lib/vuid/vuid_manager_factory.browser.ts @@ -17,7 +17,7 @@ import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manage import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; -export const __supportedPlatforms = ['browser']; +export const __platforms = ['browser']; export const vuidCacheManager = new VuidCacheManager(); diff --git a/lib/vuid/vuid_manager_factory.node.ts b/lib/vuid/vuid_manager_factory.node.ts index b15dc3900..73e82e7da 100644 --- a/lib/vuid/vuid_manager_factory.node.ts +++ b/lib/vuid/vuid_manager_factory.node.ts @@ -15,7 +15,7 @@ */ import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; -export const __supportedPlatforms = ['node']; +export const __platforms = ['node']; export const createVuidManager = (options: VuidManagerOptions = {}): OpaqueVuidManager => { return wrapVuidManager(undefined); diff --git a/lib/vuid/vuid_manager_factory.react_native.ts b/lib/vuid/vuid_manager_factory.react_native.ts index d36e31e58..fd2171830 100644 --- a/lib/vuid/vuid_manager_factory.react_native.ts +++ b/lib/vuid/vuid_manager_factory.react_native.ts @@ -17,7 +17,7 @@ import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manage import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; -export const __supportedPlatforms = ['react_native']; +export const __platforms = ['react_native']; export const vuidCacheManager = new VuidCacheManager(); diff --git a/lib/vuid/vuid_manager_factory.ts b/lib/vuid/vuid_manager_factory.ts index 2cf0ce900..ccf0c7e95 100644 --- a/lib/vuid/vuid_manager_factory.ts +++ b/lib/vuid/vuid_manager_factory.ts @@ -18,7 +18,7 @@ import { Store } from '../utils/cache/store'; import { Maybe } from '../utils/type'; import { VuidManager } from './vuid_manager'; -export const __supportedPlatforms = ['__universal__']; +export const __platforms = ['__universal__']; export type VuidManagerOptions = { vuidCache?: Store; diff --git a/scripts/README.md b/scripts/README.md index b8132e953..620a0e55c 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -25,7 +25,7 @@ The script: 1. Scans all TypeScript/JavaScript files in the `lib/` directory 2. Identifies platform-specific files by: - Naming convention (`.browser.ts`, `.node.ts`, `.react_native.ts`) - - `__supportedPlatforms` export for multi-platform files + - `__platforms` export for multi-platform files 3. Parses import statements (ES6 imports, require(), dynamic imports) 4. Validates that each import is compatible with the file's platform 5. Fails with exit code 1 if any violations are found @@ -54,7 +54,7 @@ Tests cover: - Single platform file imports - Single platform importing from multi-platform files - Multi-platform file imports (strictest rules) -- `__supportedPlatforms` extraction +- `__platforms` extraction --- diff --git a/scripts/add-platform-exports.js b/scripts/add-platform-exports.js index 840cf2e87..ecb43ec9b 100644 --- a/scripts/add-platform-exports.js +++ b/scripts/add-platform-exports.js @@ -1,9 +1,9 @@ #!/usr/bin/env node /** - * Auto-add __supportedPlatforms to files + * Auto-add __platforms to files * - * This script automatically adds __supportedPlatforms export to files that don't have it. + * This script automatically adds __platforms export to files that don't have it. * * Strategy: * 1. Files with platform-specific naming (.browser.ts, .node.ts, .react_native.ts) get their specific platform(s) @@ -27,7 +27,7 @@ function getPlatformFromFilename(filename) { } function hasSupportedPlatformsExport(content) { - return /export\s+(?:const|let|var)\s+__supportedPlatforms/.test(content); + return /export\s+(?:const|let|var)\s+__platforms/.test(content); } function findSourceFiles(dir, files = []) { @@ -63,7 +63,7 @@ function findSourceFiles(dir, files = []) { function addSupportedPlatforms(filePath) { const content = fs.readFileSync(filePath, 'utf-8'); - // Skip if already has __supportedPlatforms + // Skip if already has __platforms if (hasSupportedPlatformsExport(content)) { return { skipped: true, reason: 'already has export' }; } @@ -74,7 +74,7 @@ function addSupportedPlatforms(filePath) { // Format the export statement const platformsStr = platforms.map(p => `'${p}'`).join(', '); - const exportStatement = `export const __supportedPlatforms = [${platformsStr}] as const;\n`; + const exportStatement = `export const __platforms = [${platformsStr}] as const;\n`; // Find where to insert (after imports, before first export or code) const lines = content.split('\n'); @@ -129,7 +129,7 @@ function addSupportedPlatforms(filePath) { } function main() { - console.log('🔧 Adding __supportedPlatforms to files...\n'); + console.log('🔧 Adding __platforms to files...\n'); const files = findSourceFiles(LIB_DIR); let added = 0; diff --git a/scripts/test-validator.js b/scripts/test-validator.js index d7900ef9f..e0e0ccfe1 100644 --- a/scripts/test-validator.js +++ b/scripts/test-validator.js @@ -69,17 +69,17 @@ test('[browser, react_native] file CANNOT import from node file', console.log('\n5. SUPPORTED PLATFORMS EXTRACTION'); console.log('-'.repeat(70)); -const testExport1 = `export const __supportedPlatforms = ['browser', 'react_native'];`; +const testExport1 = `export const __platforms = ['browser', 'react_native'];`; const platforms1 = validator.extractSupportedPlatforms(testExport1); -test('Extract __supportedPlatforms array', +test('Extract __platforms array', JSON.stringify(platforms1), JSON.stringify(['browser', 'react_native'])); -const testExport2 = `export const __supportedPlatforms: string[] = ["browser", "node"];`; +const testExport2 = `export const __platforms: string[] = ["browser", "node"];`; const platforms2 = validator.extractSupportedPlatforms(testExport2); -test('Extract __supportedPlatforms with type annotation', +test('Extract __platforms with type annotation', JSON.stringify(platforms2), JSON.stringify(['browser', 'node'])); -const testExport3 = `export const __supportedPlatforms = ['__universal__'];`; +const testExport3 = `export const __platforms = ['__universal__'];`; const platforms3 = validator.extractSupportedPlatforms(testExport3); test('Extract __universal__ marker', JSON.stringify(platforms3), JSON.stringify(['__universal__'])); diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index 2fb8e8033..7589e7af0 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -9,9 +9,9 @@ * Uses TypeScript compiler API for robust parsing instead of regex. * * Platform Detection: - * - ALL source files (except tests) MUST export __supportedPlatforms array - * - Universal files use: export const __supportedPlatforms = ['__universal__']; - * - Platform-specific files use: export const __supportedPlatforms = ['browser', 'node']; + * - ALL source files (except tests) MUST export __platforms array + * - Universal files use: export const __platforms = ['__universal__']; + * - Platform-specific files use: export const __platforms = ['browser', 'node']; * * Rules: * - Platform-specific files can only import from: @@ -29,10 +29,10 @@ const ts = require('typescript'); const PLATFORMS = ['browser', 'node', 'react_native']; const LIB_DIR = path.join(__dirname, '..', 'lib'); -// Cache for __supportedPlatforms exports +// Cache for __platforms exports const platformCache = new Map(); -// Track files missing __supportedPlatforms export +// Track files missing __platforms export const filesWithoutExport = []; /** @@ -48,13 +48,13 @@ function getPlatformFromFilename(filename) { } /** - * Extracts __supportedPlatforms array from AST + * Extracts __platforms array from AST */ function extractSupportedPlatformsFromAST(sourceFile) { let platforms = null; function visit(node) { - // Look for: export const __supportedPlatforms = [...] + // Look for: export const __platforms = [...] if (ts.isVariableStatement(node)) { // Check if it has export modifier const hasExport = node.modifiers?.some( @@ -65,7 +65,7 @@ function extractSupportedPlatformsFromAST(sourceFile) { for (const declaration of node.declarationList.declarations) { if (ts.isVariableDeclaration(declaration) && ts.isIdentifier(declaration.name) && - declaration.name.text === '__supportedPlatforms') { + declaration.name.text === '__platforms') { let initializer = declaration.initializer; @@ -104,10 +104,10 @@ function extractSupportedPlatformsFromAST(sourceFile) { /** * Gets the supported platforms for a file (with caching) * Returns: - * - string[] (platforms from __supportedPlatforms) - * - 'MISSING' (file is missing __supportedPlatforms export) + * - string[] (platforms from __platforms) + * - 'MISSING' (file is missing __platforms export) * - * Note: ALL files must have __supportedPlatforms export + * Note: ALL files must have __platforms export */ function getSupportedPlatforms(filePath) { // Check cache first @@ -137,7 +137,7 @@ function getSupportedPlatforms(filePath) { return result; } - // File exists but missing __supportedPlatforms export + // File exists but missing __platforms export result = 'MISSING'; platformCache.set(filePath, result); filesWithoutExport.push(filePath); @@ -167,7 +167,7 @@ function getPlatformName(platform) { * Formats platform info for display */ function formatPlatforms(platforms) { - if (platforms === 'MISSING') return 'MISSING __supportedPlatforms'; + if (platforms === 'MISSING') return 'MISSING __platforms'; if (!platforms || platforms.length === 0) return 'Unknown'; if (Array.isArray(platforms) && platforms.length === 1 && platforms[0] === '__universal__') return 'Universal (all platforms)'; if (typeof platforms === 'string') return getPlatformName(platforms); @@ -187,7 +187,7 @@ function isUniversal(platforms) { * Checks if a platform is compatible with target platforms * * Rules: - * - If either file is MISSING __supportedPlatforms, not compatible + * - If either file is MISSING __platforms, not compatible * - Universal files are compatible with any file * - The import must support ALL platforms that the file supports */ @@ -352,7 +352,7 @@ function resolveImportPath(importPath, currentFilePath) { function validateFile(filePath) { const filePlatforms = getSupportedPlatforms(filePath); - // If file is missing __supportedPlatforms, that's a validation error handled separately + // If file is missing __platforms, that's a validation error handled separately if (filePlatforms === 'MISSING') { return { valid: true, errors: [] }; // Reported separately } @@ -373,7 +373,7 @@ function validateFile(filePath) { // Check compatibility if (!isPlatformCompatible(filePlatforms, importPlatforms)) { const message = importPlatforms === 'MISSING' - ? `Import is missing __supportedPlatforms export: "${importInfo.path}"` + ? `Import is missing __platforms export: "${importInfo.path}"` : `${formatPlatforms(filePlatforms)} file cannot import from ${formatPlatforms(importPlatforms)}-only file: "${importInfo.path}"`; errors.push({ @@ -433,15 +433,15 @@ function main() { const files = findSourceFiles(LIB_DIR); - // First pass: check for __supportedPlatforms export + // First pass: check for __platforms export console.log(`Found ${files.length} source files\n`); - console.log('Checking for __supportedPlatforms exports...\n'); + console.log('Checking for __platforms exports...\n'); files.forEach(f => getSupportedPlatforms(f)); // Populate cache and filesWithoutExport - // Report files missing __supportedPlatforms + // Report files missing __platforms if (filesWithoutExport.length > 0) { - console.error(`❌ Found ${filesWithoutExport.length} file(s) missing __supportedPlatforms export:\n`); + console.error(`❌ Found ${filesWithoutExport.length} file(s) missing __platforms export:\n`); for (const file of filesWithoutExport) { const relativePath = path.relative(process.cwd(), file); @@ -449,21 +449,21 @@ function main() { } console.error('\n'); - console.error('REQUIRED: Every source file must export __supportedPlatforms array'); + console.error('REQUIRED: Every source file must export __platforms array'); console.error(''); console.error('Examples:'); console.error(' // Platform-specific file'); - console.error(' export const __supportedPlatforms = [\'browser\', \'react_native\'];'); + console.error(' export const __platforms = [\'browser\', \'react_native\'];'); console.error(''); console.error(' // Universal file (all platforms)'); - console.error(' export const __supportedPlatforms = [\'__universal__\'];'); + console.error(' export const __platforms = [\'__universal__\'];'); console.error(''); console.error('See lib/platform_support.ts for type definitions.\n'); process.exit(1); } - console.log('✅ All files have __supportedPlatforms export\n'); + console.log('✅ All files have __platforms export\n'); // Second pass: validate platform isolation console.log('Validating platform compatibility...\n'); @@ -500,7 +500,7 @@ function main() { console.error('Platform isolation rules:'); console.error(' - Files can only import from files supporting ALL their platforms'); console.error(' - Universal files ([\'__universal__\']) can be imported by any file'); - console.error(' - All files must have __supportedPlatforms export\n'); + console.error(' - All files must have __platforms export\n'); process.exit(1); } diff --git a/scripts/validate-platform-isolation.js b/scripts/validate-platform-isolation.js index f9deb8b7a..551ced47a 100644 --- a/scripts/validate-platform-isolation.js +++ b/scripts/validate-platform-isolation.js @@ -7,9 +7,9 @@ * from universal or compatible platform files. * * Platform Detection: - * - ALL source files (except tests) MUST export __supportedPlatforms array - * - Universal files use: export const __supportedPlatforms = ['__universal__']; - * - Platform-specific files use: export const __supportedPlatforms = ['browser', 'node']; + * - ALL source files (except tests) MUST export __platforms array + * - Universal files use: export const __platforms = ['__universal__']; + * - Platform-specific files use: export const __platforms = ['browser', 'node']; * - Legacy naming convention (.browser.ts, etc.) is deprecated * * Rules: @@ -27,10 +27,10 @@ const path = require('path'); const PLATFORMS = ['browser', 'node', 'react_native']; const LIB_DIR = path.join(__dirname, '..', 'lib'); -// Cache for __supportedPlatforms exports +// Cache for __platforms exports const platformCache = new Map(); -// Track files missing __supportedPlatforms export +// Track files missing __platforms export const filesWithoutExport = []; /** @@ -46,14 +46,14 @@ function getPlatformFromFilename(filename) { } /** - * Extracts __supportedPlatforms array from file content + * Extracts __platforms array from file content */ function extractSupportedPlatforms(content) { - // Match: export const __supportedPlatforms = ['browser', 'react_native']; - // or: export const __supportedPlatforms: Platform[] = ['browser', 'react_native']; - // or with satisfies: export const __supportedPlatforms = ['browser'] satisfies Platform[]; - // or universal: export const __supportedPlatforms = ['__universal__']; - const regex = /export\s+(?:const|let|var)\s+__supportedPlatforms\s*(?::\s*[^=]+)?\s*=\s*\[([^\]]+)\](?:\s+satisfies\s+[^;]+)?/; + // Match: export const __platforms = ['browser', 'react_native']; + // or: export const __platforms: Platform[] = ['browser', 'react_native']; + // or with satisfies: export const __platforms = ['browser'] satisfies Platform[]; + // or universal: export const __platforms = ['__universal__']; + const regex = /export\s+(?:const|let|var)\s+__platforms\s*(?::\s*[^=]+)?\s*=\s*\[([^\]]+)\](?:\s+satisfies\s+[^;]+)?/; const match = content.match(regex); if (!match) { @@ -80,19 +80,19 @@ function extractSupportedPlatforms(content) { } /** - * Check if file content has __supportedPlatforms export + * Check if file content has __platforms export */ function hasSupportedPlatformsExport(content) { - return /export\s+(?:const|let|var)\s+__supportedPlatforms/.test(content); + return /export\s+(?:const|let|var)\s+__platforms/.test(content); } /** * Gets the supported platforms for a file (with caching) * Returns: - * - string[] (platforms from __supportedPlatforms) - * - 'MISSING' (file is missing __supportedPlatforms export) + * - string[] (platforms from __platforms) + * - 'MISSING' (file is missing __platforms export) * - * Note: ALL files must have __supportedPlatforms export + * Note: ALL files must have __platforms export */ function getSupportedPlatforms(filePath) { // Check cache first @@ -105,7 +105,7 @@ function getSupportedPlatforms(filePath) { try { const content = fs.readFileSync(filePath, 'utf-8'); - // Check for __supportedPlatforms export + // Check for __platforms export const supportedPlatforms = extractSupportedPlatforms(content); if (supportedPlatforms) { @@ -114,7 +114,7 @@ function getSupportedPlatforms(filePath) { return result; } - // File exists but missing __supportedPlatforms export + // File exists but missing __platforms export result = 'MISSING'; platformCache.set(filePath, result); filesWithoutExport.push(filePath); @@ -144,7 +144,7 @@ function getPlatformName(platform) { * Formats platform info for display */ function formatPlatforms(platforms) { - if (platforms === 'MISSING') return 'MISSING __supportedPlatforms'; + if (platforms === 'MISSING') return 'MISSING __platforms'; if (!platforms || platforms.length === 0) return 'Unknown'; if (Array.isArray(platforms) && platforms.length === 1 && platforms[0] === '__universal__') return 'Universal (all platforms)'; if (typeof platforms === 'string') return getPlatformName(platforms); @@ -164,12 +164,12 @@ function isUniversal(platforms) { * Checks if a platform is compatible with target platforms * * Rules: - * - If either file is MISSING __supportedPlatforms, not compatible + * - If either file is MISSING __platforms, not compatible * - Universal files (all 3 platforms) are compatible with any file * - The import must support ALL platforms that the file supports */ function isPlatformCompatible(filePlatforms, importPlatforms) { - // If either is missing __supportedPlatforms, not compatible + // If either is missing __platforms, not compatible if (filePlatforms === 'MISSING' || importPlatforms === 'MISSING') { return false; } @@ -287,7 +287,7 @@ function resolveImportPath(importPath, currentFilePath) { function validateFile(filePath) { const filePlatforms = getSupportedPlatforms(filePath); - // If file is missing __supportedPlatforms, that's a validation error handled separately + // If file is missing __platforms, that's a validation error handled separately if (filePlatforms === 'MISSING') { return { valid: true, errors: [] }; // Reported separately } @@ -309,7 +309,7 @@ function validateFile(filePath) { // Check compatibility if (!isPlatformCompatible(filePlatforms, importPlatforms)) { const message = importPlatforms === 'MISSING' - ? `Import is missing __supportedPlatforms export: "${importInfo.path}"` + ? `Import is missing __platforms export: "${importInfo.path}"` : `${formatPlatforms(filePlatforms)} file cannot import from ${formatPlatforms(importPlatforms)}-only file: "${importInfo.path}"`; errors.push({ @@ -369,15 +369,15 @@ function main() { const files = findSourceFiles(LIB_DIR); - // First pass: check for __supportedPlatforms export + // First pass: check for __platforms export console.log(`Found ${files.length} source files\n`); - console.log('Checking for __supportedPlatforms exports...\n'); + console.log('Checking for __platforms exports...\n'); files.forEach(f => getSupportedPlatforms(f)); // Populate cache and filesWithoutExport - // Report files missing __supportedPlatforms + // Report files missing __platforms if (filesWithoutExport.length > 0) { - console.error(`❌ Found ${filesWithoutExport.length} file(s) missing __supportedPlatforms export:\n`); + console.error(`❌ Found ${filesWithoutExport.length} file(s) missing __platforms export:\n`); for (const file of filesWithoutExport) { const relativePath = path.relative(process.cwd(), file); @@ -385,21 +385,21 @@ function main() { } console.error('\n'); - console.error('REQUIRED: Every source file must export __supportedPlatforms array'); + console.error('REQUIRED: Every source file must export __platforms array'); console.error(''); console.error('Examples:'); console.error(' // Platform-specific file'); - console.error(' export const __supportedPlatforms = [\'browser\', \'react_native\'];'); + console.error(' export const __platforms = [\'browser\', \'react_native\'];'); console.error(''); console.error(' // Universal file (all platforms)'); - console.error(' export const __supportedPlatforms = [\'browser\', \'node\', \'react_native\'];'); + console.error(' export const __platforms = [\'browser\', \'node\', \'react_native\'];'); console.error(''); console.error('See lib/platform_support.ts for type definitions.\n'); process.exit(1); } - console.log('✅ All files have __supportedPlatforms export\n'); + console.log('✅ All files have __platforms export\n'); // Second pass: validate platform isolation console.log('Validating platform compatibility...\n'); @@ -436,7 +436,7 @@ function main() { console.error('Platform isolation rules:'); console.error(' - Files can only import from files supporting ALL their platforms'); console.error(' - Universal files ([browser, node, react_native]) can be imported by any file'); - console.error(' - All files must have __supportedPlatforms export\n'); + console.error(' - All files must have __platforms export\n'); process.exit(1); } From a12858dacf990771db14aec21efd8404f9acb354 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 16:25:46 +0600 Subject: [PATCH 08/40] doc updates --- docs/PLATFORM_ISOLATION.md | 69 +++++++++++++++++++++++------------- eslint-local-rules/README.md | 6 ++-- scripts/README.md | 12 +++---- 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/docs/PLATFORM_ISOLATION.md b/docs/PLATFORM_ISOLATION.md index 18c6b4c53..2cc08e702 100644 --- a/docs/PLATFORM_ISOLATION.md +++ b/docs/PLATFORM_ISOLATION.md @@ -4,21 +4,27 @@ This project supports multiple runtime platforms (Browser, Node.js, React Native, and Universal), with separate entry points for each. To ensure the build artifacts work correctly, platform-specific code must not be mixed. -## Naming Convention +## Platform Declaration -Platform-specific files can be identified in two ways: +**Every non-test source file MUST export a `__platforms` array** to declare which platforms it supports. This is enforced by ESLint and validated at build time. -### 1. File Naming Convention (Single Platform) +### Export Declaration (Required) -For files specific to a single platform, use a suffix pattern: -- `.browser.ts` - Browser-specific implementation -- `.node.ts` - Node.js-specific implementation -- `.react_native.ts` - React Native-specific implementation -- `.ts` (no suffix) - Universal code (works across all platforms) +All files must include a `__platforms` export: -### 2. Export Declaration (Multiple Platforms) +**For universal files (all platforms):** +```typescript +export const __platforms = ['__universal__']; +``` + +**For platform-specific files:** +```typescript +export const __platforms = ['browser']; // Browser only +export const __platforms = ['node']; // Node.js only +export const __platforms = ['react_native']; // React Native only +``` -For files that support multiple platforms but not all (e.g., Browser + React Native, but not Node.js), export a `__platforms` array: +**For multi-platform files (but not all):** ```typescript // lib/utils/web-features.ts @@ -30,11 +36,17 @@ export function getWindowSize() { } ``` -Valid platform identifiers: `'browser'`, `'node'`, `'react_native'` +Valid platform identifiers: `'browser'`, `'node'`, `'react_native'`, `'__universal__'` -### Priority +### File Naming Convention (Optional) -If a file has both a platform suffix in its name AND a `__platforms` export, the `__platforms` export **takes priority**. This allows you to keep the `.browser.ts` naming convention while expanding support to additional platforms like React Native. +While not enforced, you may optionally use file name suffixes for clarity: +- `.browser.ts` - Typically browser-specific +- `.node.ts` - Typically Node.js-specific +- `.react_native.ts` - Typically React Native-specific +- `.ts` (no suffix) - Typically universal + +**Note:** The validator currently enforces only the `__platforms` export declaration. File naming is informational and not validated. The `__platforms` export is the source of truth. ## Import Rules @@ -51,9 +63,9 @@ A file is compatible if: ### Compatibility Examples -**Single Platform File (`.browser.ts` or `__platforms = ['browser']`)** -- ✅ Can import from: universal files, `.browser.ts` files, files with `['browser']` or `['browser', 'react_native']` -- ❌ Cannot import from: `.node.ts` files, files with `['node']` or `['react_native']` only +**Single Platform File (`__platforms = ['browser']`)** +- ✅ Can import from: universal files, files with `['browser']` or `['browser', 'react_native']` +- ❌ Cannot import from: files with `['node']` or `['react_native']` only **Multi-Platform File (`__platforms = ['browser', 'react_native']`)** - ✅ Can import from: universal files, files with exactly `['browser', 'react_native']` @@ -134,13 +146,15 @@ npm run build ### How It Works -The validation script (`scripts/validate-platform-isolation.js`): +The validation script (`scripts/validate-platform-isolation-ts.js`): + +1. Scans all source files in the `lib/` directory (excluding tests) +2. **Verifies every file has a `__platforms` export** - fails immediately if any file is missing this +3. Parses import statements using TypeScript AST (ES6 imports, require, dynamic imports) +4. Checks that each import follows the platform isolation rules based on declared platforms +5. Fails the build if violations are found or if any file lacks `__platforms` export -1. Scans all source files in the `lib/` directory -2. Identifies platform-specific files by their suffix -3. Parses import statements (ES6 imports, require, dynamic imports) -4. Checks that each import follows the platform isolation rules -5. Fails the build if violations are found +**ESLint Integration:** The `require-platform-declaration` ESLint rule also enforces the `__platforms` export requirement during development. ### Build Integration @@ -166,9 +180,10 @@ When creating new platform-specific implementations: ### Single Platform -1. Name the file with the appropriate platform suffix (e.g., `my-feature.browser.ts`) -2. Only import from universal or same-platform files -3. Create a universal factory or interface if multiple platforms need different implementations +1. **Add `__platforms` export** declaring the platform (e.g., `export const __platforms = ['browser'];`) +2. Optionally name the file with a platform suffix for clarity (e.g., `my-feature.browser.ts`) +3. Only import from universal or compatible platform files +4. Create a universal factory or interface if multiple platforms need different implementations **Example:** @@ -179,6 +194,8 @@ export interface MyFeature { } // lib/features/my-feature.browser.ts +export const __platforms = ['browser']; + export class BrowserMyFeature implements MyFeature { doSomething(): void { // Browser-specific implementation @@ -186,6 +203,8 @@ export class BrowserMyFeature implements MyFeature { } // lib/features/my-feature.node.ts +export const __platforms = ['node']; + export class NodeMyFeature implements MyFeature { doSomething(): void { // Node.js-specific implementation diff --git a/eslint-local-rules/README.md b/eslint-local-rules/README.md index 514621456..7df08767e 100644 --- a/eslint-local-rules/README.md +++ b/eslint-local-rules/README.md @@ -6,9 +6,11 @@ This directory contains custom ESLint rules specific to this project. ### `require-platform-declaration` -**Purpose:** Ensures all source files (except tests) export `__platforms` to declare which platforms they support. +**Purpose:** **Enforces that every non-test source file exports `__platforms`** to declare which platforms it supports. -**Why:** This enforces platform isolation at the linting level, catching missing declarations before build time. +**Why:** This is a mandatory requirement for platform isolation. The rule catches missing declarations at lint time, before build or runtime. + +**Requirement:** Every `.ts`/`.js` file in `lib/` (except tests) MUST export `__platforms` array with valid platform values. **Enforcement:** - ✅ Enabled for all `.ts` files in `lib/` directory diff --git a/scripts/README.md b/scripts/README.md index 620a0e55c..99cf98774 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -23,12 +23,12 @@ npm run build The script: 1. Scans all TypeScript/JavaScript files in the `lib/` directory -2. Identifies platform-specific files by: - - Naming convention (`.browser.ts`, `.node.ts`, `.react_native.ts`) - - `__platforms` export for multi-platform files -3. Parses import statements (ES6 imports, require(), dynamic imports) -4. Validates that each import is compatible with the file's platform -5. Fails with exit code 1 if any violations are found +2. **Requires every non-test file to export `__platforms` array** declaring supported platforms +3. Parses import statements (ES6 imports, require(), dynamic imports) using TypeScript AST +4. Validates that each import is compatible with the file's declared platforms +5. Fails with exit code 1 if any violations are found or if `__platforms` export is missing + +**Note:** The validator can theoretically support file naming conventions (`.browser.ts`, etc.) in addition to `__platforms` exports, but currently enforces only the `__platforms` export. File naming is not validated and is considered redundant. ### Exit Codes From cc266fe74468aae57a21d748e3e51b5e10a4d537 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 17:15:35 +0600 Subject: [PATCH 09/40] update validation --- docs/PLATFORM_ISOLATION.md | 43 ++++- .../index.ts | 2 + lib/utils/event_tag_utils/index.ts | 2 + scripts/README.md | 11 +- scripts/validate-platform-isolation-ts.js | 176 ++++++++++++++++-- 5 files changed, 204 insertions(+), 30 deletions(-) diff --git a/docs/PLATFORM_ISOLATION.md b/docs/PLATFORM_ISOLATION.md index 2cc08e702..a3b718d55 100644 --- a/docs/PLATFORM_ISOLATION.md +++ b/docs/PLATFORM_ISOLATION.md @@ -38,6 +38,8 @@ export function getWindowSize() { Valid platform identifiers: `'browser'`, `'node'`, `'react_native'`, `'__universal__'` +**Important**: Only files that explicitly include `'__universal__'` in their `__platforms` array are considered universal. Files that list all concrete platforms (e.g., `['browser', 'node', 'react_native']`) are treated as multi-platform files, NOT universal files. They must still ensure imports support all their declared platforms. + ### File Naming Convention (Optional) While not enforced, you may optionally use file name suffixes for clarity: @@ -63,14 +65,29 @@ A file is compatible if: ### Compatibility Examples +**Core Principle**: When file A imports file B, file B must support ALL platforms that file A runs on. + +**Universal File (`__platforms = ['__universal__']`)** +- ✅ Can import from: universal files (with `__universal__`) +- ❌ Cannot import from: any platform-specific files, even `['browser', 'node', 'react_native']` +- **Why**: Universal files run everywhere, so all imports must explicitly be universal +- **Note**: Listing all platforms like `['browser', 'node', 'react_native']` is NOT considered universal + **Single Platform File (`__platforms = ['browser']`)** -- ✅ Can import from: universal files, files with `['browser']` or `['browser', 'react_native']` -- ❌ Cannot import from: files with `['node']` or `['react_native']` only +- ✅ Can import from: universal files, files with `['browser']`, multi-platform files that include browser like `['browser', 'react_native']` +- ❌ Cannot import from: files without browser support like `['node']` or `['react_native']` only +- **Why**: The import must support the browser platform **Multi-Platform File (`__platforms = ['browser', 'react_native']`)** -- ✅ Can import from: universal files, files with exactly `['browser', 'react_native']` -- ❌ Cannot import from: `.browser.ts` (browser only), `.react_native.ts` (react_native only), `.node.ts` -- **Why?** A file supporting both platforms needs imports that work in BOTH environments +- ✅ Can import from: universal files, files with `['browser', 'react_native']`, supersets like `['browser', 'node', 'react_native']` +- ❌ Cannot import from: files missing any platform like `['browser']` only or `['node']` +- **Why**: The import must support BOTH browser AND react_native + +**All-Platforms File (`__platforms = ['browser', 'node', 'react_native']`)** +- ✅ Can import from: universal files, files with exactly `['browser', 'node', 'react_native']` +- ❌ Cannot import from: any subset like `['browser']`, `['browser', 'react_native']`, etc. +- **Why**: This is NOT considered universal - imports must support all three platforms +- **Note**: If your code truly works everywhere, use `['__universal__']` instead ### Examples @@ -120,11 +137,18 @@ import { NodeRequestHandler } from './utils/http_request_handler/request_handler import { getWindowSize } from './utils/web-features'; // ❌ Not compatible with Node ``` +```typescript +// In lib/shared_types.ts (Universal file) +// export const __platforms = ['__universal__']; + +import { helper } from './helper.browser'; // ❌ Browser-only, universal file needs imports that work everywhere +``` + ```typescript // In lib/utils/web-api.ts // export const __platforms = ['browser', 'react_native']; -// If helper.browser.ts is browser-only (no __platforms export) +// If helper.browser.ts is browser-only import { helper } from './helper.browser'; // ❌ Browser-only, doesn't support react_native // This file needs imports that work in BOTH browser AND react_native @@ -150,9 +174,10 @@ The validation script (`scripts/validate-platform-isolation-ts.js`): 1. Scans all source files in the `lib/` directory (excluding tests) 2. **Verifies every file has a `__platforms` export** - fails immediately if any file is missing this -3. Parses import statements using TypeScript AST (ES6 imports, require, dynamic imports) -4. Checks that each import follows the platform isolation rules based on declared platforms -5. Fails the build if violations are found or if any file lacks `__platforms` export +3. **Validates all platform values** - ensures values in `__platforms` arrays are valid (read from Platform type) +4. Parses import statements using TypeScript AST (ES6 imports, require, dynamic imports) +5. **Checks import compatibility**: For each import, verifies that the imported file supports ALL platforms that the importing file runs on +6. Fails the build if violations are found or if any file lacks `__platforms` export **ESLint Integration:** The `require-platform-declaration` ESLint rule also enforces the `__platforms` export requirement during development. diff --git a/lib/core/custom_attribute_condition_evaluator/index.ts b/lib/core/custom_attribute_condition_evaluator/index.ts index 797a7d4e0..147da75e3 100644 --- a/lib/core/custom_attribute_condition_evaluator/index.ts +++ b/lib/core/custom_attribute_condition_evaluator/index.ts @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * * limitations under the License. * ***************************************************************************/ +export const __platforms = ['__universal__']; + import { Condition, OptimizelyUserContext } from '../../shared_types'; import fns from '../../utils/fns'; diff --git a/lib/utils/event_tag_utils/index.ts b/lib/utils/event_tag_utils/index.ts index d50292a39..fb1b0098f 100644 --- a/lib/utils/event_tag_utils/index.ts +++ b/lib/utils/event_tag_utils/index.ts @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +export const __platforms = ['__universal__']; + import { FAILED_TO_PARSE_REVENUE, FAILED_TO_PARSE_VALUE, diff --git a/scripts/README.md b/scripts/README.md index 99cf98774..31963250c 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -24,9 +24,14 @@ npm run build The script: 1. Scans all TypeScript/JavaScript files in the `lib/` directory 2. **Requires every non-test file to export `__platforms` array** declaring supported platforms -3. Parses import statements (ES6 imports, require(), dynamic imports) using TypeScript AST -4. Validates that each import is compatible with the file's declared platforms -5. Fails with exit code 1 if any violations are found or if `__platforms` export is missing +3. **Validates platform values** by reading the Platform type definition from `platform_support.ts` +4. Parses import statements (ES6 imports, require(), dynamic imports) using TypeScript AST +5. **Validates import compatibility**: For each import, ensures the imported file supports ALL platforms that the importing file runs on +6. Fails with exit code 1 if any violations are found, if `__platforms` export is missing, or if invalid platform values are used + +**Import Rule**: When file A imports file B, file B must support ALL platforms that file A runs on. +- Example: A universal file can only import from universal files (or files supporting all platforms) +- Example: A browser file can import from universal files or any file that supports browser **Note:** The validator can theoretically support file naming conventions (`.browser.ts`, etc.) in addition to `__platforms` exports, but currently enforces only the `__platforms` export. File naming is not validated and is considered redundant. diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index 7589e7af0..e71b7220a 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -12,10 +12,11 @@ * - ALL source files (except tests) MUST export __platforms array * - Universal files use: export const __platforms = ['__universal__']; * - Platform-specific files use: export const __platforms = ['browser', 'node']; + * - Valid platform values are dynamically read from Platform type in platform_support.ts * * Rules: * - Platform-specific files can only import from: - * - Universal files (marked with '__universal__') + * - Universal files (containing '__universal__' or all concrete platform values) * - Files supporting the same platforms * - External packages (node_modules) * @@ -26,20 +27,79 @@ const fs = require('fs'); const path = require('path'); const ts = require('typescript'); -const PLATFORMS = ['browser', 'node', 'react_native']; const LIB_DIR = path.join(__dirname, '..', 'lib'); +const PLATFORM_SUPPORT_FILE = path.join(LIB_DIR, 'platform_support.ts'); // Cache for __platforms exports const platformCache = new Map(); -// Track files missing __platforms export -const filesWithoutExport = []; +// Valid platforms (loaded dynamically) +let VALID_PLATFORMS = null; +let ALL_CONCRETE_PLATFORMS = null; + +/** + * Extract valid platform values from Platform type definition + * Parses: type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; + */ +function getValidPlatformsFromSource() { + if (VALID_PLATFORMS !== null) { + return VALID_PLATFORMS; + } + + try { + const content = fs.readFileSync(PLATFORM_SUPPORT_FILE, 'utf-8'); + const sourceFile = ts.createSourceFile( + PLATFORM_SUPPORT_FILE, + content, + ts.ScriptTarget.Latest, + true + ); + + function visit(node) { + // Look for: export type Platform = ... + if (ts.isTypeAliasDeclaration(node) && + ts.isIdentifier(node.name) && + node.name.text === 'Platform') { + + const type = node.type; + if (ts.isUnionTypeNode(type)) { + const platforms = []; + for (const member of type.types) { + if (ts.isLiteralTypeNode(member) && + ts.isStringLiteral(member.literal)) { + platforms.push(member.literal.text); + } + } + VALID_PLATFORMS = platforms; + ALL_CONCRETE_PLATFORMS = platforms.filter(p => p !== '__universal__'); + return; + } + } + + ts.forEachChild(node, visit); + } + + visit(sourceFile); + + if (!VALID_PLATFORMS) { + throw new Error('Could not find Platform type definition in platform_support.ts'); + } + + return VALID_PLATFORMS; + } catch (error) { + console.error('❌ Failed to read Platform type from platform_support.ts:', error.message); + process.exit(1); + } +} /** - * Extracts the platform from a filename using naming convention + * Gets a human-readable platform name */ function getPlatformFromFilename(filename) { - for (const platform of PLATFORMS) { + const validPlatforms = getValidPlatformsFromSource(); + const concretePlatforms = validPlatforms.filter(p => p !== '__universal__'); + + for (const platform of concretePlatforms) { if (filename.includes(`.${platform}.`)) { return platform; } @@ -47,6 +107,12 @@ function getPlatformFromFilename(filename) { return null; } +// Track files missing __platforms export +const filesWithoutExport = []; + +// Track files with invalid platform values +const filesWithInvalidPlatforms = []; + /** * Extracts __platforms array from AST */ @@ -101,6 +167,35 @@ function extractSupportedPlatformsFromAST(sourceFile) { return platforms; } +/** + * Validates that platform values are valid according to Platform type + */ +function validatePlatformValues(platforms, filePath) { + if (!platforms || platforms.length === 0) { + return { valid: false, invalidValues: [] }; + } + + const validPlatforms = getValidPlatformsFromSource(); + const invalidValues = []; + + for (const platform of platforms) { + if (!validPlatforms.includes(platform)) { + invalidValues.push(platform); + } + } + + if (invalidValues.length > 0) { + filesWithInvalidPlatforms.push({ + filePath, + platforms, + invalidValues + }); + return { valid: false, invalidValues }; + } + + return { valid: true, invalidValues: [] }; +} + /** * Gets the supported platforms for a file (with caching) * Returns: @@ -132,6 +227,15 @@ function getSupportedPlatforms(filePath) { const supportedPlatforms = extractSupportedPlatformsFromAST(sourceFile); if (supportedPlatforms && supportedPlatforms.length > 0) { + // Validate platform values + const validation = validatePlatformValues(supportedPlatforms, filePath); + if (!validation.valid) { + // Still cache it but it will be reported as error + result = supportedPlatforms; + platformCache.set(filePath, result); + return result; + } + result = supportedPlatforms; platformCache.set(filePath, result); return result; @@ -176,11 +280,23 @@ function formatPlatforms(platforms) { /** * Checks if platforms represent universal (all platforms) + * + * A file is universal if and only if: + * 1. It contains '__universal__' in its platforms array + * + * Note: If array contains '__universal__' plus other values (e.g., ['__universal__', 'browser']), + * it's still considered universal because __universal__ makes it available everywhere. + * + * Files that list all concrete platforms (e.g., ['browser', 'node', 'react_native']) are NOT + * considered universal - they must explicitly declare '__universal__' to be universal. */ function isUniversal(platforms) { - return Array.isArray(platforms) && - platforms.length === 1 && - platforms[0] === '__universal__'; + if (!Array.isArray(platforms) || platforms.length === 0) { + return false; + } + + // ONLY if it explicitly declares __universal__, it's universal + return platforms.includes('__universal__'); } /** @@ -188,8 +304,9 @@ function isUniversal(platforms) { * * Rules: * - If either file is MISSING __platforms, not compatible - * - Universal files are compatible with any file - * - The import must support ALL platforms that the file supports + * - Import must support ALL platforms that the importing file runs on + * - Universal imports can be used by any file (they support all platforms) + * - Platform-specific files can only import from universal or files supporting all their platforms */ function isPlatformCompatible(filePlatforms, importPlatforms) { // If either is missing platforms, not compatible @@ -197,20 +314,19 @@ function isPlatformCompatible(filePlatforms, importPlatforms) { return false; } - // If import is universal, always compatible + // If import is universal, always compatible (universal supports all platforms) if (isUniversal(importPlatforms)) { return true; } - // If file is universal, import must be universal too + // If file is universal but import is not, NOT compatible + // (universal file runs everywhere, so imports must also run everywhere) if (isUniversal(filePlatforms)) { - return isUniversal(importPlatforms); + return false; } - // Otherwise, import must support ALL platforms that the file supports - // filePlatforms is an array of platforms the file needs - // importPlatforms is an array of platforms the import provides - + // Otherwise, import must support ALL platforms that the file runs on + // For each platform the file runs on, check if the import also supports it for (const platform of filePlatforms) { if (!importPlatforms.includes(platform)) { return false; @@ -433,6 +549,10 @@ function main() { const files = findSourceFiles(LIB_DIR); + // Load valid platforms first + const validPlatforms = getValidPlatformsFromSource(); + console.log(`Valid platforms: ${validPlatforms.join(', ')}\n`); + // First pass: check for __platforms export console.log(`Found ${files.length} source files\n`); console.log('Checking for __platforms exports...\n'); @@ -465,6 +585,26 @@ function main() { console.log('✅ All files have __platforms export\n'); + // Report files with invalid platform values + if (filesWithInvalidPlatforms.length > 0) { + console.error(`❌ Found ${filesWithInvalidPlatforms.length} file(s) with invalid platform values:\n`); + + for (const { filePath, platforms, invalidValues } of filesWithInvalidPlatforms) { + const relativePath = path.relative(process.cwd(), filePath); + console.error(` 📄 ${relativePath}`); + console.error(` Declared: [${platforms.map(p => `'${p}'`).join(', ')}]`); + console.error(` Invalid values: [${invalidValues.map(p => `'${p}'`).join(', ')}]`); + } + + console.error('\n'); + console.error(`Valid platform values: ${validPlatforms.map(p => `'${p}'`).join(', ')}`); + console.error('See lib/platform_support.ts for Platform type definition.\n'); + + process.exit(1); + } + + console.log('✅ All __platforms arrays have valid values\n'); + // Second pass: validate platform isolation console.log('Validating platform compatibility...\n'); From ff034eb758ef2c2eb0a5cd2515a35cd47476e687 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 17:29:02 +0600 Subject: [PATCH 10/40] declraration --- lib/client_factory.ts | 3 ++- lib/common_exports.ts | 4 +++- lib/core/audience_evaluator/index.ts | 3 ++- .../odp_segment_condition_evaluator/index.ts | 3 ++- lib/core/bucketer/bucket_value_generator.ts | 3 ++- lib/core/bucketer/index.ts | 3 ++- lib/core/condition_tree_evaluator/index.ts | 4 +++- lib/core/custom_attribute_condition_evaluator/index.ts | 3 ++- lib/core/decision/index.ts | 3 ++- lib/core/decision_service/cmab/cmab_client.ts | 3 ++- lib/error/error_handler.ts | 4 +++- lib/error/error_notifier.ts | 3 ++- lib/error/error_notifier_factory.ts | 3 ++- lib/error/error_reporter.ts | 3 ++- lib/error/optimizly_error.ts | 3 ++- lib/event_processor/batch_event_processor.react_native.ts | 3 ++- lib/event_processor/batch_event_processor.ts | 3 ++- lib/event_processor/event_builder/log_event.ts | 3 ++- .../event_dispatcher/default_dispatcher.browser.ts | 3 ++- .../event_dispatcher/default_dispatcher.node.ts | 3 ++- lib/event_processor/event_dispatcher/default_dispatcher.ts | 3 ++- lib/event_processor/event_dispatcher/event_dispatcher.ts | 3 ++- .../event_dispatcher/event_dispatcher_factory.ts | 3 ++- .../event_dispatcher/send_beacon_dispatcher.browser.ts | 3 ++- lib/event_processor/event_processor.ts | 3 ++- lib/event_processor/event_processor_factory.ts | 3 ++- lib/event_processor/forwarding_event_processor.ts | 3 ++- lib/export_types.ts | 4 +++- lib/feature_toggle.ts | 4 +++- lib/logging/logger.ts | 3 ++- lib/logging/logger_factory.ts | 3 ++- lib/message/error_message.ts | 4 +++- lib/message/log_message.ts | 4 +++- lib/message/message_resolver.ts | 3 ++- lib/notification_center/index.ts | 3 ++- lib/odp/constant.ts | 4 +++- lib/odp/event_manager/odp_event.ts | 4 +++- lib/odp/event_manager/odp_event_api_manager.ts | 3 ++- lib/odp/odp_config.ts | 3 ++- lib/odp/odp_manager.ts | 3 ++- lib/odp/odp_manager_factory.browser.ts | 3 ++- lib/odp/odp_manager_factory.node.ts | 3 ++- lib/odp/odp_manager_factory.react_native.ts | 3 ++- lib/odp/odp_manager_factory.ts | 3 ++- lib/odp/odp_manager_factory.universal.ts | 3 ++- lib/odp/odp_types.ts | 4 +++- lib/odp/segment_manager/odp_response_schema.ts | 3 ++- lib/odp/segment_manager/odp_segment_api_manager.ts | 3 ++- lib/odp/segment_manager/odp_segment_manager.ts | 3 ++- lib/odp/segment_manager/optimizely_segment_option.ts | 4 +++- lib/odp/ua_parser/ua_parser.ts | 3 ++- lib/odp/ua_parser/user_agent_info.ts | 4 +++- lib/odp/ua_parser/user_agent_parser.ts | 3 ++- lib/optimizely_decision/index.ts | 3 ++- lib/platform_support.ts | 2 +- lib/project_config/config_manager_factory.browser.ts | 3 ++- lib/project_config/config_manager_factory.node.ts | 3 ++- lib/project_config/config_manager_factory.react_native.ts | 3 ++- lib/project_config/config_manager_factory.ts | 3 ++- lib/project_config/config_manager_factory.universal.ts | 3 ++- lib/project_config/constant.ts | 4 +++- lib/project_config/datafile_manager.ts | 3 ++- lib/project_config/project_config_schema.ts | 3 ++- lib/service.ts | 3 ++- lib/shared_types.ts | 3 ++- lib/utils/attributes_validator/index.ts | 3 ++- lib/utils/cache/async_storage_cache.react_native.ts | 3 ++- lib/utils/cache/in_memory_lru_cache.ts | 3 ++- lib/utils/cache/local_storage_cache.browser.ts | 3 ++- lib/utils/cache/store.ts | 3 ++- lib/utils/cache/store_validator.ts | 4 +++- lib/utils/enums/index.ts | 4 +++- lib/utils/event_emitter/event_emitter.ts | 3 ++- lib/utils/event_tag_utils/index.ts | 3 ++- lib/utils/event_tags_validator/index.ts | 3 ++- lib/utils/executor/backoff_retry_runner.ts | 3 ++- lib/utils/executor/serial_runner.ts | 3 ++- lib/utils/fns/index.ts | 3 ++- lib/utils/http_request_handler/http.ts | 4 +++- lib/utils/http_request_handler/http_util.ts | 4 +++- lib/utils/http_request_handler/request_handler.browser.ts | 3 ++- lib/utils/http_request_handler/request_handler.node.ts | 3 ++- lib/utils/http_request_handler/request_handler_validator.ts | 3 ++- lib/utils/id_generator/index.ts | 4 +++- .../@react-native-async-storage/async-storage.ts | 3 ++- lib/utils/json_schema_validator/index.ts | 3 ++- lib/utils/microtask/index.ts | 4 +++- lib/utils/promise/operation_value.ts | 3 ++- lib/utils/promise/resolvablePromise.ts | 4 +++- lib/utils/repeater/repeater.ts | 3 ++- lib/utils/semantic_version/index.ts | 3 ++- lib/utils/string_value_validator/index.ts | 4 +++- lib/utils/type.ts | 4 +++- lib/utils/user_profile_service_validator/index.ts | 3 ++- lib/vuid/vuid.ts | 3 ++- lib/vuid/vuid_manager.ts | 3 ++- lib/vuid/vuid_manager_factory.browser.ts | 3 ++- lib/vuid/vuid_manager_factory.node.ts | 3 ++- lib/vuid/vuid_manager_factory.react_native.ts | 3 ++- lib/vuid/vuid_manager_factory.ts | 3 ++- 100 files changed, 221 insertions(+), 100 deletions(-) diff --git a/lib/client_factory.ts b/lib/client_factory.ts index d4dbf8c23..aea9e05d6 100644 --- a/lib/client_factory.ts +++ b/lib/client_factory.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './platform_support'; import { Config } from "./shared_types"; import { extractLogger } from "./logging/logger_factory"; import { extractErrorNotifier } from "./error/error_notifier_factory"; @@ -29,7 +30,7 @@ import { InMemoryLruCache } from "./utils/cache/in_memory_lru_cache"; import { transformCache, CacheWithRemove } from "./utils/cache/cache"; import { ConstantBackoff } from "./utils/repeater/repeater"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type OptimizelyFactoryConfig = Config & { requestHandler: RequestHandler; diff --git a/lib/common_exports.ts b/lib/common_exports.ts index 55af919f0..21225677d 100644 --- a/lib/common_exports.ts +++ b/lib/common_exports.ts @@ -1,3 +1,5 @@ +import { Platform } from './platform_support'; + /** * Copyright 2023-2025 Optimizely * @@ -14,7 +16,7 @@ * limitations under the License. */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export { createStaticProjectConfigManager } from './project_config/config_manager_factory'; diff --git a/lib/core/audience_evaluator/index.ts b/lib/core/audience_evaluator/index.ts index 9d670b229..6ecf162a4 100644 --- a/lib/core/audience_evaluator/index.ts +++ b/lib/core/audience_evaluator/index.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import * as conditionTreeEvaluator from '../condition_tree_evaluator'; import * as customAttributeConditionEvaluator from '../custom_attribute_condition_evaluator'; import * as odpSegmentsConditionEvaluator from './odp_segment_condition_evaluator'; @@ -21,7 +22,7 @@ import { CONDITION_EVALUATOR_ERROR, UNKNOWN_CONDITION_TYPE } from 'error_message import { AUDIENCE_EVALUATION_RESULT, EVALUATING_AUDIENCE} from 'log_message'; import { LoggerFacade } from '../../logging/logger'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export class AudienceEvaluator { private logger?: LoggerFacade; diff --git a/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts b/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts index b2a8d9dd3..ffa371ca9 100644 --- a/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts +++ b/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * * limitations under the License. * ***************************************************************************/ +import { Platform } from './../../../platform_support'; import { UNKNOWN_MATCH_TYPE } from 'error_message'; import { LoggerFacade } from '../../../logging/logger'; import { Condition, OptimizelyUserContext } from '../../../shared_types'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; const QUALIFIED_MATCH_TYPE = 'qualified'; diff --git a/lib/core/bucketer/bucket_value_generator.ts b/lib/core/bucketer/bucket_value_generator.ts index 551601c32..bb9a1b85a 100644 --- a/lib/core/bucketer/bucket_value_generator.ts +++ b/lib/core/bucketer/bucket_value_generator.ts @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import murmurhash from 'murmurhash'; import { INVALID_BUCKETING_ID } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; const HASH_SEED = 1; const MAX_HASH_VALUE = Math.pow(2, 32); diff --git a/lib/core/bucketer/index.ts b/lib/core/bucketer/index.ts index b0a83f4f0..d6512e6ac 100644 --- a/lib/core/bucketer/index.ts +++ b/lib/core/bucketer/index.ts @@ -17,6 +17,7 @@ /** * Bucketer API for determining the variation id from the specified parameters */ +import { Platform } from './../../platform_support'; import { LoggerFacade } from '../../logging/logger'; import { DecisionResponse, @@ -29,7 +30,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { generateBucketValue } from './bucket_value_generator'; import { DecisionReason } from '../decision_service'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const USER_NOT_IN_ANY_EXPERIMENT = 'User %s is not in any experiment of group %s.'; export const USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP = 'User %s is not in experiment %s of group %s.'; diff --git a/lib/core/condition_tree_evaluator/index.ts b/lib/core/condition_tree_evaluator/index.ts index 884a46eee..030da724a 100644 --- a/lib/core/condition_tree_evaluator/index.ts +++ b/lib/core/condition_tree_evaluator/index.ts @@ -1,3 +1,5 @@ +import { Platform } from './../../platform_support'; + /**************************************************************************** * Copyright 2018, 2021, Optimizely, Inc. and contributors * * * @@ -14,7 +16,7 @@ * limitations under the License. * ***************************************************************************/ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; const AND_CONDITION = 'and'; const OR_CONDITION = 'or'; diff --git a/lib/core/custom_attribute_condition_evaluator/index.ts b/lib/core/custom_attribute_condition_evaluator/index.ts index 147da75e3..d734656e3 100644 --- a/lib/core/custom_attribute_condition_evaluator/index.ts +++ b/lib/core/custom_attribute_condition_evaluator/index.ts @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * * limitations under the License. * ***************************************************************************/ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; +import { Platform } from './../../platform_support'; import { Condition, OptimizelyUserContext } from '../../shared_types'; import fns from '../../utils/fns'; diff --git a/lib/core/decision/index.ts b/lib/core/decision/index.ts index 24738b393..55b1ea482 100644 --- a/lib/core/decision/index.ts +++ b/lib/core/decision/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { DecisionObj } from '../decision_service'; /** @@ -21,7 +22,7 @@ import { DecisionObj } from '../decision_service'; * @param {DecisionObj} decisionObj Object representing decision * @returns {string} Experiment key or empty string if experiment is null */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export function getExperimentKey(decisionObj: DecisionObj): string { diff --git a/lib/core/decision_service/cmab/cmab_client.ts b/lib/core/decision_service/cmab/cmab_client.ts index d76c865f5..1679f9658 100644 --- a/lib/core/decision_service/cmab/cmab_client.ts +++ b/lib/core/decision_service/cmab/cmab_client.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Platform } from './../../../platform_support'; import { OptimizelyError } from "../../../error/optimizly_error"; import { CMAB_FETCH_FAILED, INVALID_CMAB_FETCH_RESPONSE } from "../../../message/error_message"; import { UserAttributes } from "../../../shared_types"; @@ -24,7 +25,7 @@ import { isSuccessStatusCode } from "../../../utils/http_request_handler/http_ut import { BackoffController } from "../../../utils/repeater/repeater"; import { Producer } from "../../../utils/type"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface CmabClient { fetchDecision( diff --git a/lib/error/error_handler.ts b/lib/error/error_handler.ts index 35439e9dc..cc4143307 100644 --- a/lib/error/error_handler.ts +++ b/lib/error/error_handler.ts @@ -1,3 +1,5 @@ +import { Platform } from './../platform_support'; + /** * Copyright 2019, 2025, Optimizely * @@ -17,7 +19,7 @@ * @export * @interface ErrorHandler */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface ErrorHandler { /** diff --git a/lib/error/error_notifier.ts b/lib/error/error_notifier.ts index 79776a4f7..e39fd943b 100644 --- a/lib/error/error_notifier.ts +++ b/lib/error/error_notifier.ts @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { MessageResolver } from "../message/message_resolver"; import { ErrorHandler } from "./error_handler"; import { OptimizelyError } from "./optimizly_error"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface ErrorNotifier { notify(error: Error): void; diff --git a/lib/error/error_notifier_factory.ts b/lib/error/error_notifier_factory.ts index 59cfbb18d..cf6ea97cc 100644 --- a/lib/error/error_notifier_factory.ts +++ b/lib/error/error_notifier_factory.ts @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { errorResolver } from "../message/message_resolver"; import { Maybe } from "../utils/type"; import { ErrorHandler } from "./error_handler"; import { DefaultErrorNotifier } from "./error_notifier"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const INVALID_ERROR_HANDLER = 'Invalid error handler'; diff --git a/lib/error/error_reporter.ts b/lib/error/error_reporter.ts index 35944f47f..151ec4a90 100644 --- a/lib/error/error_reporter.ts +++ b/lib/error/error_reporter.ts @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { LoggerFacade } from "../logging/logger"; import { ErrorNotifier } from "./error_notifier"; import { OptimizelyError } from "./optimizly_error"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export class ErrorReporter { private logger?: LoggerFacade; diff --git a/lib/error/optimizly_error.ts b/lib/error/optimizly_error.ts index 1aa2725f5..65cad15df 100644 --- a/lib/error/optimizly_error.ts +++ b/lib/error/optimizly_error.ts @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { MessageResolver } from "../message/message_resolver"; import { sprintf } from "../utils/fns"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export class OptimizelyError extends Error { baseMessage: string; diff --git a/lib/event_processor/batch_event_processor.react_native.ts b/lib/event_processor/batch_event_processor.react_native.ts index 7d783ab45..f4900b5ac 100644 --- a/lib/event_processor/batch_event_processor.react_native.ts +++ b/lib/event_processor/batch_event_processor.react_native.ts @@ -14,12 +14,13 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { NetInfoState, addEventListener } from '@react-native-community/netinfo'; import { BatchEventProcessor, BatchEventProcessorConfig } from './batch_event_processor'; import { Fn } from '../utils/type'; -export const __platforms = ['react_native']; +export const __platforms: Platform[] = ['react_native']; export class ReactNativeNetInfoEventProcessor extends BatchEventProcessor { private isInternetReachable = true; diff --git a/lib/event_processor/batch_event_processor.ts b/lib/event_processor/batch_event_processor.ts index 9687c82b0..41992be4c 100644 --- a/lib/event_processor/batch_event_processor.ts +++ b/lib/event_processor/batch_event_processor.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { EventProcessor, ProcessableEvent } from "./event_processor"; import { getBatchedAsync, getBatchedSync, Store } from "../utils/cache/store"; import { EventDispatcher, EventDispatcherResponse, LogEvent } from "./event_dispatcher/event_dispatcher"; @@ -32,7 +33,7 @@ import { OptimizelyError } from "../error/optimizly_error"; import { sprintf } from "../utils/fns"; import { SERVICE_STOPPED_BEFORE_RUNNING } from "../service"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const DEFAULT_MIN_BACKOFF = 1000; export const DEFAULT_MAX_BACKOFF = 32000; diff --git a/lib/event_processor/event_builder/log_event.ts b/lib/event_processor/event_builder/log_event.ts index 5daa43cab..2d17a7b7f 100644 --- a/lib/event_processor/event_builder/log_event.ts +++ b/lib/event_processor/event_builder/log_event.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import { ConversionEvent, ImpressionEvent, UserEvent } from './user_event'; import { CONTROL_ATTRIBUTES } from '../../utils/enums'; @@ -21,7 +22,7 @@ import { LogEvent } from '../event_dispatcher/event_dispatcher'; import { EventTags } from '../../shared_types'; import { Region } from '../../project_config/project_config'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; const ACTIVATE_EVENT_KEY = 'campaign_activated' const CUSTOM_ATTRIBUTE_FEATURE_TYPE = 'custom' diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts index 28b1523af..490646f47 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts @@ -15,8 +15,9 @@ */ // This implementation works in both browser and react_native environments -export const __platforms = ['browser', 'react_native']; +export const __platforms: Platform[] = ['browser', 'react_native']; +import { Platform } from './../../platform_support'; import { BrowserRequestHandler } from "../../utils/http_request_handler/request_handler.browser"; import { EventDispatcher } from './event_dispatcher'; import { DefaultEventDispatcher } from './default_dispatcher'; diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.node.ts b/lib/event_processor/event_dispatcher/default_dispatcher.node.ts index c082170fa..10b9df4d1 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.node.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.node.ts @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import { EventDispatcher } from './event_dispatcher'; import { NodeRequestHandler } from '../../utils/http_request_handler/request_handler.node'; import { DefaultEventDispatcher } from './default_dispatcher'; -export const __platforms = ['node']; +export const __platforms: Platform[] = ['node']; const eventDispatcher: EventDispatcher = new DefaultEventDispatcher(new NodeRequestHandler()); diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.ts b/lib/event_processor/event_dispatcher/default_dispatcher.ts index 62fe6b3f7..6cd09942e 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.ts @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import { OptimizelyError } from '../../error/optimizly_error'; import { ONLY_POST_REQUESTS_ARE_SUPPORTED } from 'error_message'; import { RequestHandler } from '../../utils/http_request_handler/http'; import { EventDispatcher, EventDispatcherResponse, LogEvent } from './event_dispatcher'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export class DefaultEventDispatcher implements EventDispatcher { private requestHandler: RequestHandler; diff --git a/lib/event_processor/event_dispatcher/event_dispatcher.ts b/lib/event_processor/event_dispatcher/event_dispatcher.ts index b9d8a6600..ac3f2146b 100644 --- a/lib/event_processor/event_dispatcher/event_dispatcher.ts +++ b/lib/event_processor/event_dispatcher/event_dispatcher.ts @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import { EventBatch } from "../event_builder/log_event"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type EventDispatcherResponse = { statusCode?: number diff --git a/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts b/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts index 6aea5c040..aaae8cbf0 100644 --- a/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts +++ b/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts @@ -14,13 +14,14 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { RequestHandler } from '../../utils/http_request_handler/http'; import { DefaultEventDispatcher } from './default_dispatcher'; import { EventDispatcher } from './event_dispatcher'; import { validateRequestHandler } from '../../utils/http_request_handler/request_handler_validator'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const createEventDispatcher = (requestHander: RequestHandler): EventDispatcher => { validateRequestHandler(requestHander); diff --git a/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts index cac5235fd..76c6f8401 100644 --- a/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts @@ -14,11 +14,12 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { OptimizelyError } from '../../error/optimizly_error'; import { SEND_BEACON_FAILED } from 'error_message'; import { EventDispatcher, EventDispatcherResponse } from './event_dispatcher'; -export const __platforms = ['browser']; +export const __platforms: Platform[] = ['browser']; export type Event = { url: string; diff --git a/lib/event_processor/event_processor.ts b/lib/event_processor/event_processor.ts index 10e7ff843..3a9f4139c 100644 --- a/lib/event_processor/event_processor.ts +++ b/lib/event_processor/event_processor.ts @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { ConversionEvent, ImpressionEvent } from './event_builder/user_event' import { LogEvent } from './event_dispatcher/event_dispatcher' import { Service } from '../service' import { Consumer, Fn } from '../utils/type'; import { LoggerFacade } from '../logging/logger'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const DEFAULT_FLUSH_INTERVAL = 30000 // Unit is ms - default flush interval is 30s export const DEFAULT_BATCH_SIZE = 10 diff --git a/lib/event_processor/event_processor_factory.ts b/lib/event_processor/event_processor_factory.ts index 79b408552..3fcc8d5fb 100644 --- a/lib/event_processor/event_processor_factory.ts +++ b/lib/event_processor/event_processor_factory.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { LogLevel } from "../logging/logger"; import { StartupLog } from "../service"; import { AsyncPrefixStore, Store, SyncPrefixStore } from "../utils/cache/store"; @@ -26,7 +27,7 @@ import { EventProcessor } from "./event_processor"; import { EVENT_STORE_PREFIX } from "./event_store"; import { ForwardingEventProcessor } from "./forwarding_event_processor"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const INVALID_EVENT_DISPATCHER = 'Invalid event dispatcher'; diff --git a/lib/event_processor/forwarding_event_processor.ts b/lib/event_processor/forwarding_event_processor.ts index 008d674ff..ce9fce015 100644 --- a/lib/event_processor/forwarding_event_processor.ts +++ b/lib/event_processor/forwarding_event_processor.ts @@ -15,6 +15,7 @@ */ +import { Platform } from './../platform_support'; import { LogEvent } from './event_dispatcher/event_dispatcher'; import { EventProcessor, ProcessableEvent } from './event_processor'; @@ -26,7 +27,7 @@ import { Consumer, Fn } from '../utils/type'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; import { sprintf } from '../utils/fns'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export class ForwardingEventProcessor extends BaseService implements EventProcessor { private dispatcher: EventDispatcher; diff --git a/lib/export_types.ts b/lib/export_types.ts index ddec4a8dd..876a0ca82 100644 --- a/lib/export_types.ts +++ b/lib/export_types.ts @@ -1,3 +1,5 @@ +import { Platform } from './platform_support'; + /** * Copyright 2022-2024, Optimizely * @@ -15,7 +17,7 @@ */ // config manager related types -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type { StaticConfigManagerConfig, diff --git a/lib/feature_toggle.ts b/lib/feature_toggle.ts index 271e5cdda..d1e62d118 100644 --- a/lib/feature_toggle.ts +++ b/lib/feature_toggle.ts @@ -1,3 +1,5 @@ +import { Platform } from './platform_support'; + /** * Copyright 2025, Optimizely * @@ -34,6 +36,6 @@ // example feature flag definition // export const wipFeat = () => false as const; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type IfActive boolean, Y, N = unknown> = ReturnType extends true ? Y : N; diff --git a/lib/logging/logger.ts b/lib/logging/logger.ts index 53a034cef..94fe8e583 100644 --- a/lib/logging/logger.ts +++ b/lib/logging/logger.ts @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { OptimizelyError } from '../error/optimizly_error'; import { MessageResolver } from '../message/message_resolver'; import { sprintf } from '../utils/fns' -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export enum LogLevel { Debug, diff --git a/lib/logging/logger_factory.ts b/lib/logging/logger_factory.ts index ca423ddce..00cd1f7d5 100644 --- a/lib/logging/logger_factory.ts +++ b/lib/logging/logger_factory.ts @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { ConsoleLogHandler, LogHandler, LogLevel, OptimizelyLogger } from './logger'; import { errorResolver, infoResolver, MessageResolver } from '../message/message_resolver'; import { Maybe } from '../utils/type'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const INVALID_LOG_HANDLER = 'Invalid log handler'; export const INVALID_LEVEL_PRESET = 'Invalid level preset'; diff --git a/lib/message/error_message.ts b/lib/message/error_message.ts index 3c27d185a..2d5b5eb6e 100644 --- a/lib/message/error_message.ts +++ b/lib/message/error_message.ts @@ -1,3 +1,5 @@ +import { Platform } from './../platform_support'; + /** * Copyright 2024-2025, Optimizely * @@ -13,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const NOTIFICATION_LISTENER_EXCEPTION = 'Notification listener for (%s) threw exception: %s'; export const CONDITION_EVALUATOR_ERROR = 'Error evaluating audience condition of type %s: %s'; diff --git a/lib/message/log_message.ts b/lib/message/log_message.ts index 63d47680f..d6e8cea3f 100644 --- a/lib/message/log_message.ts +++ b/lib/message/log_message.ts @@ -1,3 +1,5 @@ +import { Platform } from './../platform_support'; + /** * Copyright 2024, Optimizely * @@ -14,7 +16,7 @@ * limitations under the License. */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const FEATURE_ENABLED_FOR_USER = 'Feature %s is enabled for user %s.'; export const FEATURE_NOT_ENABLED_FOR_USER = 'Feature %s is not enabled for user %s.'; diff --git a/lib/message/message_resolver.ts b/lib/message/message_resolver.ts index 106975fef..f4817298e 100644 --- a/lib/message/message_resolver.ts +++ b/lib/message/message_resolver.ts @@ -1,7 +1,8 @@ +import { Platform } from './../platform_support'; import { messages as infoMessages } from 'log_message'; import { messages as errorMessages } from 'error_message'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface MessageResolver { resolve(baseMessage: string): string; diff --git a/lib/notification_center/index.ts b/lib/notification_center/index.ts index dc2038139..e31f5d9f8 100644 --- a/lib/notification_center/index.ts +++ b/lib/notification_center/index.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { LoggerFacade } from '../logging/logger'; import { objectValues } from '../utils/fns'; @@ -24,7 +25,7 @@ import { NOTIFICATION_LISTENER_EXCEPTION } from 'error_message'; import { ErrorReporter } from '../error/error_reporter'; import { ErrorNotifier } from '../error/error_notifier'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; interface NotificationCenterOptions { logger?: LoggerFacade; diff --git a/lib/odp/constant.ts b/lib/odp/constant.ts index 1c8efc4df..88f53f7dd 100644 --- a/lib/odp/constant.ts +++ b/lib/odp/constant.ts @@ -1,3 +1,5 @@ +import { Platform } from './../platform_support'; + /** * Copyright 2024, Optimizely * @@ -14,7 +16,7 @@ * limitations under the License. */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export enum ODP_USER_KEY { VUID = 'vuid', diff --git a/lib/odp/event_manager/odp_event.ts b/lib/odp/event_manager/odp_event.ts index 7cbe3d455..dc2f3e0df 100644 --- a/lib/odp/event_manager/odp_event.ts +++ b/lib/odp/event_manager/odp_event.ts @@ -1,3 +1,5 @@ +import { Platform } from './../../platform_support'; + /** * Copyright 2022-2024, Optimizely * @@ -14,7 +16,7 @@ * limitations under the License. */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export class OdpEvent { /** diff --git a/lib/odp/event_manager/odp_event_api_manager.ts b/lib/odp/event_manager/odp_event_api_manager.ts index bbdb44fa9..b6ad55cda 100644 --- a/lib/odp/event_manager/odp_event_api_manager.ts +++ b/lib/odp/event_manager/odp_event_api_manager.ts @@ -14,12 +14,13 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { LoggerFacade } from '../../logging/logger'; import { OdpEvent } from './odp_event'; import { HttpMethod, RequestHandler } from '../../utils/http_request_handler/http'; import { OdpConfig } from '../odp_config'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type EventDispatchResponse = { statusCode?: number; diff --git a/lib/odp/odp_config.ts b/lib/odp/odp_config.ts index 7ae353998..16254694d 100644 --- a/lib/odp/odp_config.ts +++ b/lib/odp/odp_config.ts @@ -14,9 +14,10 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { checkArrayEquality } from '../utils/fns'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export class OdpConfig { /** diff --git a/lib/odp/odp_manager.ts b/lib/odp/odp_manager.ts index 217eeb2ee..2d6eabb19 100644 --- a/lib/odp/odp_manager.ts +++ b/lib/odp/odp_manager.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { v4 as uuidV4} from 'uuid'; import { LoggerFacade } from '../logging/logger'; @@ -32,7 +33,7 @@ import { Maybe } from '../utils/type'; import { sprintf } from '../utils/fns'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface OdpManager extends Service { updateConfig(odpIntegrationConfig: OdpIntegrationConfig): boolean; diff --git a/lib/odp/odp_manager_factory.browser.ts b/lib/odp/odp_manager_factory.browser.ts index e0be6ef2b..7909f1295 100644 --- a/lib/odp/odp_manager_factory.browser.ts +++ b/lib/odp/odp_manager_factory.browser.ts @@ -14,11 +14,12 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __platforms = ['browser']; +export const __platforms: Platform[] = ['browser']; export const BROWSER_DEFAULT_API_TIMEOUT = 10_000; export const BROWSER_DEFAULT_BATCH_SIZE = 10; diff --git a/lib/odp/odp_manager_factory.node.ts b/lib/odp/odp_manager_factory.node.ts index 0e7ebb9a3..548a4d450 100644 --- a/lib/odp/odp_manager_factory.node.ts +++ b/lib/odp/odp_manager_factory.node.ts @@ -14,11 +14,12 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { NodeRequestHandler } from '../utils/http_request_handler/request_handler.node'; import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __platforms = ['node']; +export const __platforms: Platform[] = ['node']; export const NODE_DEFAULT_API_TIMEOUT = 10_000; export const NODE_DEFAULT_BATCH_SIZE = 10; diff --git a/lib/odp/odp_manager_factory.react_native.ts b/lib/odp/odp_manager_factory.react_native.ts index a83dfe1f7..a746b1cc3 100644 --- a/lib/odp/odp_manager_factory.react_native.ts +++ b/lib/odp/odp_manager_factory.react_native.ts @@ -14,12 +14,13 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { OdpManager } from './odp_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __platforms = ['react_native']; +export const __platforms: Platform[] = ['react_native']; export const RN_DEFAULT_API_TIMEOUT = 10_000; export const RN_DEFAULT_BATCH_SIZE = 10; diff --git a/lib/odp/odp_manager_factory.ts b/lib/odp/odp_manager_factory.ts index 46bf81399..7efd88a00 100644 --- a/lib/odp/odp_manager_factory.ts +++ b/lib/odp/odp_manager_factory.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { RequestHandler } from "../shared_types"; import { Cache } from "../utils/cache/cache"; import { InMemoryLruCache } from "../utils/cache/in_memory_lru_cache"; @@ -26,7 +27,7 @@ import { DefaultOdpSegmentApiManager } from "./segment_manager/odp_segment_api_m import { DefaultOdpSegmentManager, OdpSegmentManager } from "./segment_manager/odp_segment_manager"; import { UserAgentParser } from "./ua_parser/user_agent_parser"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const DEFAULT_CACHE_SIZE = 10_000; export const DEFAULT_CACHE_TIMEOUT = 600_000; diff --git a/lib/odp/odp_manager_factory.universal.ts b/lib/odp/odp_manager_factory.universal.ts index fc0e6484b..4ab5c8de9 100644 --- a/lib/odp/odp_manager_factory.universal.ts +++ b/lib/odp/odp_manager_factory.universal.ts @@ -14,12 +14,13 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { RequestHandler } from '../utils/http_request_handler/http'; import { validateRequestHandler } from '../utils/http_request_handler/request_handler_validator'; import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const DEFAULT_API_TIMEOUT = 10_000; export const DEFAULT_BATCH_SIZE = 1; diff --git a/lib/odp/odp_types.ts b/lib/odp/odp_types.ts index 967418828..ffee41832 100644 --- a/lib/odp/odp_types.ts +++ b/lib/odp/odp_types.ts @@ -1,3 +1,5 @@ +import { Platform } from './../platform_support'; + /** * Copyright 2022-2024, Optimizely * @@ -17,7 +19,7 @@ /** * Wrapper around valid data and error responses */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface Response { data: Data; diff --git a/lib/odp/segment_manager/odp_response_schema.ts b/lib/odp/segment_manager/odp_response_schema.ts index 5031ff9e6..e4c375b79 100644 --- a/lib/odp/segment_manager/odp_response_schema.ts +++ b/lib/odp/segment_manager/odp_response_schema.ts @@ -14,12 +14,13 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { JSONSchema4 } from 'json-schema'; /** * JSON Schema used to validate the ODP GraphQL response */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const OdpResponseSchema = { diff --git a/lib/odp/segment_manager/odp_segment_api_manager.ts b/lib/odp/segment_manager/odp_segment_api_manager.ts index c19227491..3af496b8b 100644 --- a/lib/odp/segment_manager/odp_segment_api_manager.ts +++ b/lib/odp/segment_manager/odp_segment_api_manager.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { LoggerFacade } from '../../logging/logger'; import { validate } from '../../utils/json_schema_validator'; import { OdpResponseSchema } from './odp_response_schema'; @@ -24,7 +25,7 @@ import { log } from 'console'; /** * Expected value for a qualified/valid segment */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; const QUALIFIED = 'qualified'; diff --git a/lib/odp/segment_manager/odp_segment_manager.ts b/lib/odp/segment_manager/odp_segment_manager.ts index c87784ba4..65bb5d8a3 100644 --- a/lib/odp/segment_manager/odp_segment_manager.ts +++ b/lib/odp/segment_manager/odp_segment_manager.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { Cache } from '../../utils/cache/cache'; import { OdpSegmentApiManager } from './odp_segment_api_manager'; import { OdpIntegrationConfig } from '../odp_config'; @@ -22,7 +23,7 @@ import { ODP_USER_KEY } from '../constant'; import { LoggerFacade } from '../../logging/logger'; import { ODP_CONFIG_NOT_AVAILABLE, ODP_NOT_INTEGRATED } from 'error_message'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface OdpSegmentManager { fetchQualifiedSegments( diff --git a/lib/odp/segment_manager/optimizely_segment_option.ts b/lib/odp/segment_manager/optimizely_segment_option.ts index 9e8049baa..51ec6c371 100644 --- a/lib/odp/segment_manager/optimizely_segment_option.ts +++ b/lib/odp/segment_manager/optimizely_segment_option.ts @@ -1,3 +1,5 @@ +import { Platform } from './../../platform_support'; + /** * Copyright 2022, 2024, Optimizely * @@ -15,7 +17,7 @@ */ // Options for defining behavior of OdpSegmentManager's caching mechanism when calling fetchSegments() -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export enum OptimizelySegmentOption { IGNORE_CACHE = 'IGNORE_CACHE', diff --git a/lib/odp/ua_parser/ua_parser.ts b/lib/odp/ua_parser/ua_parser.ts index af1a8e92f..7494ed118 100644 --- a/lib/odp/ua_parser/ua_parser.ts +++ b/lib/odp/ua_parser/ua_parser.ts @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import { UAParser } from 'ua-parser-js'; import { UserAgentInfo } from './user_agent_info'; import { UserAgentParser } from './user_agent_parser'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; const userAgentParser: UserAgentParser = { parseUserAgentInfo(): UserAgentInfo { diff --git a/lib/odp/ua_parser/user_agent_info.ts b/lib/odp/ua_parser/user_agent_info.ts index 6d5edddda..76efe298d 100644 --- a/lib/odp/ua_parser/user_agent_info.ts +++ b/lib/odp/ua_parser/user_agent_info.ts @@ -1,3 +1,5 @@ +import { Platform } from './../../platform_support'; + /** * Copyright 2023, Optimizely * @@ -14,7 +16,7 @@ * limitations under the License. */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type UserAgentInfo = { os: { diff --git a/lib/odp/ua_parser/user_agent_parser.ts b/lib/odp/ua_parser/user_agent_parser.ts index b60586850..aef124627 100644 --- a/lib/odp/ua_parser/user_agent_parser.ts +++ b/lib/odp/ua_parser/user_agent_parser.ts @@ -14,9 +14,10 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { UserAgentInfo } from "./user_agent_info"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface UserAgentParser { parseUserAgentInfo(): UserAgentInfo, diff --git a/lib/optimizely_decision/index.ts b/lib/optimizely_decision/index.ts index e34059fcc..53c41641a 100644 --- a/lib/optimizely_decision/index.ts +++ b/lib/optimizely_decision/index.ts @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * * limitations under the License. * ***************************************************************************/ +import { Platform } from './../platform_support'; import { OptimizelyUserContext, OptimizelyDecision } from '../shared_types'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export function newErrorDecision(key: string, user: OptimizelyUserContext, reasons: string[]): OptimizelyDecision { return { diff --git a/lib/platform_support.ts b/lib/platform_support.ts index 47e439eb7..ad5470945 100644 --- a/lib/platform_support.ts +++ b/lib/platform_support.ts @@ -10,7 +10,7 @@ */ export type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; /** * Platform support declaration diff --git a/lib/project_config/config_manager_factory.browser.ts b/lib/project_config/config_manager_factory.browser.ts index afbb152f3..30093ab43 100644 --- a/lib/project_config/config_manager_factory.browser.ts +++ b/lib/project_config/config_manager_factory.browser.ts @@ -14,10 +14,11 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; -export const __platforms = ['browser']; +export const __platforms: Platform[] = ['browser']; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { diff --git a/lib/project_config/config_manager_factory.node.ts b/lib/project_config/config_manager_factory.node.ts index c8715bd28..05f09ab40 100644 --- a/lib/project_config/config_manager_factory.node.ts +++ b/lib/project_config/config_manager_factory.node.ts @@ -14,10 +14,11 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { NodeRequestHandler } from "../utils/http_request_handler/request_handler.node"; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; -export const __platforms = ['node']; +export const __platforms: Platform[] = ['node']; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { diff --git a/lib/project_config/config_manager_factory.react_native.ts b/lib/project_config/config_manager_factory.react_native.ts index b1d297802..f03bec63a 100644 --- a/lib/project_config/config_manager_factory.react_native.ts +++ b/lib/project_config/config_manager_factory.react_native.ts @@ -14,11 +14,12 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { AsyncStorageCache } from "../utils/cache/async_storage_cache.react_native"; import { BrowserRequestHandler } from "../utils/http_request_handler/request_handler.browser"; import { getOpaquePollingConfigManager, PollingConfigManagerConfig, OpaqueConfigManager } from "./config_manager_factory"; -export const __platforms = ['react_native']; +export const __platforms: Platform[] = ['react_native']; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { diff --git a/lib/project_config/config_manager_factory.ts b/lib/project_config/config_manager_factory.ts index eeaa373ce..9cf44152c 100644 --- a/lib/project_config/config_manager_factory.ts +++ b/lib/project_config/config_manager_factory.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { RequestHandler } from "../utils/http_request_handler/http"; import { Maybe, Transformer } from "../utils/type"; import { DatafileManagerConfig } from "./datafile_manager"; @@ -27,7 +28,7 @@ import { LogLevel } from '../logging/logger' import { Store } from "../utils/cache/store"; import { validateStore } from "../utils/cache/store_validator"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const INVALID_CONFIG_MANAGER = "Invalid config manager"; diff --git a/lib/project_config/config_manager_factory.universal.ts b/lib/project_config/config_manager_factory.universal.ts index eb997cbcc..3eb18dcf5 100644 --- a/lib/project_config/config_manager_factory.universal.ts +++ b/lib/project_config/config_manager_factory.universal.ts @@ -14,11 +14,12 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; import { RequestHandler } from "../utils/http_request_handler/http"; import { validateRequestHandler } from "../utils/http_request_handler/request_handler_validator"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type UniversalPollingConfigManagerConfig = PollingConfigManagerConfig & { requestHandler: RequestHandler; diff --git a/lib/project_config/constant.ts b/lib/project_config/constant.ts index 2cbfe480c..038bd0c8a 100644 --- a/lib/project_config/constant.ts +++ b/lib/project_config/constant.ts @@ -1,3 +1,5 @@ +import { Platform } from './../platform_support'; + /** * Copyright 2022-2023, Optimizely * @@ -14,7 +16,7 @@ * limitations under the License. */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; const DEFAULT_UPDATE_INTERVAL_MINUTES = 5; /** Standard interval (5 minutes in milliseconds) for polling datafile updates.; */ diff --git a/lib/project_config/datafile_manager.ts b/lib/project_config/datafile_manager.ts index 4837bc0db..04154220f 100644 --- a/lib/project_config/datafile_manager.ts +++ b/lib/project_config/datafile_manager.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { Service, StartupLog } from '../service'; import { Store } from '../utils/cache/store'; import { RequestHandler } from '../utils/http_request_handler/http'; @@ -20,7 +21,7 @@ import { Fn, Consumer } from '../utils/type'; import { Repeater } from '../utils/repeater/repeater'; import { LoggerFacade } from '../logging/logger'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface DatafileManager extends Service { get(): string | undefined; diff --git a/lib/project_config/project_config_schema.ts b/lib/project_config/project_config_schema.ts index 88c58a4e3..0ac978a37 100644 --- a/lib/project_config/project_config_schema.ts +++ b/lib/project_config/project_config_schema.ts @@ -17,9 +17,10 @@ /** * Project Config JSON Schema file used to validate the project json datafile */ +import { Platform } from './../platform_support'; import { JSONSchema4 } from 'json-schema'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; var schemaDefinition = { $schema: 'http://json-schema.org/draft-04/schema#', diff --git a/lib/service.ts b/lib/service.ts index 3d3b485e5..114258c6a 100644 --- a/lib/service.ts +++ b/lib/service.ts @@ -14,10 +14,11 @@ * limitations under the License. */ +import { Platform } from './platform_support'; import { LoggerFacade, LogLevel, LogLevelToLower } from './logging/logger' import { resolvablePromise, ResolvablePromise } from "./utils/promise/resolvablePromise"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const SERVICE_FAILED_TO_START = '%s failed to start, reason: %s'; export const SERVICE_STOPPED_BEFORE_RUNNING = '%s stopped before running'; diff --git a/lib/shared_types.ts b/lib/shared_types.ts index 4a5282eb2..ab16743e9 100644 --- a/lib/shared_types.ts +++ b/lib/shared_types.ts @@ -20,6 +20,7 @@ */ // import { ErrorHandler, LogHandler, LogLevel, LoggerFacade } from './modules/logging'; +import { Platform } from './platform_support'; import { LoggerFacade, LogLevel } from './logging/logger'; import { ErrorHandler } from './error/error_handler'; @@ -47,7 +48,7 @@ import { OpaqueOdpManager } from './odp/odp_manager_factory'; import { OpaqueVuidManager } from './vuid/vuid_manager_factory'; import { CacheWithRemove } from './utils/cache/cache'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; export { EventProcessor } from './event_processor/event_processor'; diff --git a/lib/utils/attributes_validator/index.ts b/lib/utils/attributes_validator/index.ts index 0cac07df1..c4495289e 100644 --- a/lib/utils/attributes_validator/index.ts +++ b/lib/utils/attributes_validator/index.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import { ObjectWithUnknownProperties } from '../../shared_types'; import fns from '../../utils/fns'; @@ -26,7 +27,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @throws If the attributes are not valid */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export function validate(attributes: unknown): boolean { if (typeof attributes === 'object' && !Array.isArray(attributes) && attributes !== null) { diff --git a/lib/utils/cache/async_storage_cache.react_native.ts b/lib/utils/cache/async_storage_cache.react_native.ts index d6852b7d0..a75e7e881 100644 --- a/lib/utils/cache/async_storage_cache.react_native.ts +++ b/lib/utils/cache/async_storage_cache.react_native.ts @@ -14,11 +14,12 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { Maybe } from "../type"; import { AsyncStore } from "./store"; import { getDefaultAsyncStorage } from "../import.react_native/@react-native-async-storage/async-storage"; -export const __platforms = ['react_native']; +export const __platforms: Platform[] = ['react_native']; export class AsyncStorageCache implements AsyncStore { public readonly operation = 'async'; diff --git a/lib/utils/cache/in_memory_lru_cache.ts b/lib/utils/cache/in_memory_lru_cache.ts index b9787495e..506dc3319 100644 --- a/lib/utils/cache/in_memory_lru_cache.ts +++ b/lib/utils/cache/in_memory_lru_cache.ts @@ -14,10 +14,11 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { Maybe } from "../type"; import { SyncCacheWithRemove } from "./cache"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; type CacheElement = { value: V; diff --git a/lib/utils/cache/local_storage_cache.browser.ts b/lib/utils/cache/local_storage_cache.browser.ts index 550da3856..f91dd4d0c 100644 --- a/lib/utils/cache/local_storage_cache.browser.ts +++ b/lib/utils/cache/local_storage_cache.browser.ts @@ -14,10 +14,11 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { Maybe } from "../type"; import { SyncStore } from "./store"; -export const __platforms = ['browser']; +export const __platforms: Platform[] = ['browser']; export class LocalStorageCache implements SyncStore { public readonly operation = 'sync'; diff --git a/lib/utils/cache/store.ts b/lib/utils/cache/store.ts index 2c526b734..e58eb3b5a 100644 --- a/lib/utils/cache/store.ts +++ b/lib/utils/cache/store.ts @@ -14,11 +14,12 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { Transformer } from '../../utils/type'; import { Maybe } from '../../utils/type'; import { OpType, OpValue } from '../../utils/type'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface OpStore { operation: OP; diff --git a/lib/utils/cache/store_validator.ts b/lib/utils/cache/store_validator.ts index b9b36fd67..9875b8cfe 100644 --- a/lib/utils/cache/store_validator.ts +++ b/lib/utils/cache/store_validator.ts @@ -1,3 +1,5 @@ +import { Platform } from './../../platform_support'; + /** * Copyright 2025, Optimizely @@ -14,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const INVALID_STORE = 'Invalid store'; export const INVALID_STORE_METHOD = 'Invalid store method %s'; diff --git a/lib/utils/enums/index.ts b/lib/utils/enums/index.ts index 73ba8e2bd..9a9e50f02 100644 --- a/lib/utils/enums/index.ts +++ b/lib/utils/enums/index.ts @@ -1,3 +1,5 @@ +import { Platform } from './../../platform_support'; + /** * Copyright 2016-2025, Optimizely * @@ -17,7 +19,7 @@ /** * Contains global enums used throughout the library */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const LOG_LEVEL = { NOTSET: 0, diff --git a/lib/utils/event_emitter/event_emitter.ts b/lib/utils/event_emitter/event_emitter.ts index e949bd039..398b2fd72 100644 --- a/lib/utils/event_emitter/event_emitter.ts +++ b/lib/utils/event_emitter/event_emitter.ts @@ -14,9 +14,10 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { Fn } from "../type"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; type Consumer = (arg: T) => void; diff --git a/lib/utils/event_tag_utils/index.ts b/lib/utils/event_tag_utils/index.ts index fb1b0098f..01d1ea26c 100644 --- a/lib/utils/event_tag_utils/index.ts +++ b/lib/utils/event_tag_utils/index.ts @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; +import { Platform } from './../../platform_support'; import { FAILED_TO_PARSE_REVENUE, FAILED_TO_PARSE_VALUE, diff --git a/lib/utils/event_tags_validator/index.ts b/lib/utils/event_tags_validator/index.ts index 690758b67..d79efcfe7 100644 --- a/lib/utils/event_tags_validator/index.ts +++ b/lib/utils/event_tags_validator/index.ts @@ -17,6 +17,7 @@ /** * Provides utility method for validating that event tags user has provided are valid */ +import { Platform } from './../../platform_support'; import { OptimizelyError } from '../../error/optimizly_error'; import { INVALID_EVENT_TAGS } from 'error_message'; @@ -26,7 +27,7 @@ import { INVALID_EVENT_TAGS } from 'error_message'; * @return {boolean} true if event tags are valid * @throws If event tags are not valid */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export function validate(eventTags: unknown): boolean { diff --git a/lib/utils/executor/backoff_retry_runner.ts b/lib/utils/executor/backoff_retry_runner.ts index db1dc03e6..ec6307e5c 100644 --- a/lib/utils/executor/backoff_retry_runner.ts +++ b/lib/utils/executor/backoff_retry_runner.ts @@ -1,10 +1,11 @@ +import { Platform } from './../../platform_support'; import { OptimizelyError } from "../../error/optimizly_error"; import { RETRY_CANCELLED } from "error_message"; import { resolvablePromise, ResolvablePromise } from "../promise/resolvablePromise"; import { BackoffController } from "../repeater/repeater"; import { AsyncProducer, Fn } from "../type"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type RunResult = { result: Promise; diff --git a/lib/utils/executor/serial_runner.ts b/lib/utils/executor/serial_runner.ts index 60d4bf2ce..2d85a666f 100644 --- a/lib/utils/executor/serial_runner.ts +++ b/lib/utils/executor/serial_runner.ts @@ -14,9 +14,10 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { AsyncProducer } from "../type"; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; class SerialRunner { private waitPromise: Promise = Promise.resolve(); diff --git a/lib/utils/fns/index.ts b/lib/utils/fns/index.ts index e7e540c5e..55ebd9a68 100644 --- a/lib/utils/fns/index.ts +++ b/lib/utils/fns/index.ts @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import { v4 } from 'uuid'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; const MAX_SAFE_INTEGER_LIMIT = Math.pow(2, 53); diff --git a/lib/utils/http_request_handler/http.ts b/lib/utils/http_request_handler/http.ts index 643334984..80f737c49 100644 --- a/lib/utils/http_request_handler/http.ts +++ b/lib/utils/http_request_handler/http.ts @@ -1,3 +1,5 @@ +import { Platform } from './../../platform_support'; + /** * Copyright 2019-2020, 2022, 2024 Optimizely * @@ -17,7 +19,7 @@ /** * List of key-value pairs to be used in an HTTP requests */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface Headers { [header: string]: string | undefined; diff --git a/lib/utils/http_request_handler/http_util.ts b/lib/utils/http_request_handler/http_util.ts index 4f619f34d..8e03e2fe2 100644 --- a/lib/utils/http_request_handler/http_util.ts +++ b/lib/utils/http_request_handler/http_util.ts @@ -1,5 +1,7 @@ +import { Platform } from './../../platform_support'; -export const __platforms = ['__universal__']; + +export const __platforms: Platform[] = ['__universal__']; export const isSuccessStatusCode = (statusCode: number): boolean => { return statusCode >= 200 && statusCode < 400; diff --git a/lib/utils/http_request_handler/request_handler.browser.ts b/lib/utils/http_request_handler/request_handler.browser.ts index 738e62418..d99027096 100644 --- a/lib/utils/http_request_handler/request_handler.browser.ts +++ b/lib/utils/http_request_handler/request_handler.browser.ts @@ -15,8 +15,9 @@ */ // This implementation works in both browser and react_native environments -export const __platforms = ['browser', 'react_native']; +export const __platforms: Platform[] = ['browser', 'react_native']; +import { Platform } from './../../platform_support'; import { AbortableRequest, Headers, RequestHandler, Response } from './http'; import { LoggerFacade, LogLevel } from '../../logging/logger'; import { REQUEST_TIMEOUT_MS } from '../enums'; diff --git a/lib/utils/http_request_handler/request_handler.node.ts b/lib/utils/http_request_handler/request_handler.node.ts index 1d6538638..a4c12d0bf 100644 --- a/lib/utils/http_request_handler/request_handler.node.ts +++ b/lib/utils/http_request_handler/request_handler.node.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import http from 'http'; import https from 'https'; import url from 'url'; @@ -26,7 +27,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; /** * Handles sending requests and receiving responses over HTTP via NodeJS http module */ -export const __platforms = ['node']; +export const __platforms: Platform[] = ['node']; export class NodeRequestHandler implements RequestHandler { diff --git a/lib/utils/http_request_handler/request_handler_validator.ts b/lib/utils/http_request_handler/request_handler_validator.ts index 3421428bc..8ebd4b909 100644 --- a/lib/utils/http_request_handler/request_handler_validator.ts +++ b/lib/utils/http_request_handler/request_handler_validator.ts @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import { RequestHandler } from './http'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const INVALID_REQUEST_HANDLER = 'Invalid request handler'; diff --git a/lib/utils/id_generator/index.ts b/lib/utils/id_generator/index.ts index 0b71b3d24..3e70404f5 100644 --- a/lib/utils/id_generator/index.ts +++ b/lib/utils/id_generator/index.ts @@ -1,3 +1,5 @@ +import { Platform } from './../../platform_support'; + /** * Copyright 2022-2024, Optimizely * @@ -14,7 +16,7 @@ * limitations under the License. */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; const idSuffixBase = 10_000; diff --git a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts index 9dd9764ef..64d9cd0af 100644 --- a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts +++ b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts @@ -14,9 +14,10 @@ * limitations under the License. */ +import { Platform } from './../../../platform_support'; import type { AsyncStorageStatic } from '@react-native-async-storage/async-storage' -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const MODULE_NOT_FOUND_REACT_NATIVE_ASYNC_STORAGE = 'Module not found: @react-native-async-storage/async-storage'; diff --git a/lib/utils/json_schema_validator/index.ts b/lib/utils/json_schema_validator/index.ts index bbf7547e8..844580312 100644 --- a/lib/utils/json_schema_validator/index.ts +++ b/lib/utils/json_schema_validator/index.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import { JSONSchema4, validate as jsonSchemaValidator } from 'json-schema'; import schema from '../../project_config/project_config_schema'; @@ -26,7 +27,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @param {boolean} shouldThrowOnError Should validation throw if invalid JSON object * @return {boolean} true if the given object is valid; throws or false if invalid */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export function validate( diff --git a/lib/utils/microtask/index.ts b/lib/utils/microtask/index.ts index 9a5f05bbb..cb9ff7c6b 100644 --- a/lib/utils/microtask/index.ts +++ b/lib/utils/microtask/index.ts @@ -1,3 +1,5 @@ +import { Platform } from './../../platform_support'; + /** * Copyright 2024, Optimizely * @@ -16,7 +18,7 @@ type Callback = () => void; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const scheduleMicrotask = (callback: Callback): void => { if (typeof queueMicrotask === 'function') { diff --git a/lib/utils/promise/operation_value.ts b/lib/utils/promise/operation_value.ts index d2f173936..895b0a6ef 100644 --- a/lib/utils/promise/operation_value.ts +++ b/lib/utils/promise/operation_value.ts @@ -1,9 +1,10 @@ +import { Platform } from './../../platform_support'; import { PROMISE_NOT_ALLOWED } from '../../message/error_message'; import { OptimizelyError } from '../../error/optimizly_error'; import { OpType, OpValue } from '../type'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; const isPromise = (val: any): boolean => { return val && typeof val.then === 'function'; diff --git a/lib/utils/promise/resolvablePromise.ts b/lib/utils/promise/resolvablePromise.ts index a9ccf1ef4..a3422b479 100644 --- a/lib/utils/promise/resolvablePromise.ts +++ b/lib/utils/promise/resolvablePromise.ts @@ -1,3 +1,5 @@ +import { Platform } from './../../platform_support'; + /** * Copyright 2024, Optimizely * @@ -14,7 +16,7 @@ * limitations under the License. */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; const noop = () => {}; diff --git a/lib/utils/repeater/repeater.ts b/lib/utils/repeater/repeater.ts index 9c82b32db..aed251b33 100644 --- a/lib/utils/repeater/repeater.ts +++ b/lib/utils/repeater/repeater.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Platform } from './../../platform_support'; import { AsyncTransformer } from "../type"; import { scheduleMicrotask } from "../microtask"; @@ -24,7 +25,7 @@ import { scheduleMicrotask } from "../microtask"; // If the retuned promise resolves, the repeater will assume the task succeeded, // and will reset the failure count. If the promise is rejected, the repeater will // assume the task failed and will increase the current consecutive failure count. -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface Repeater { diff --git a/lib/utils/semantic_version/index.ts b/lib/utils/semantic_version/index.ts index 627bf09cc..9469ee880 100644 --- a/lib/utils/semantic_version/index.ts +++ b/lib/utils/semantic_version/index.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../../platform_support'; import { UNKNOWN_MATCH_TYPE } from 'error_message'; import { LoggerFacade } from '../../logging/logger'; import { VERSION_TYPE } from '../enums'; @@ -23,7 +24,7 @@ import { VERSION_TYPE } from '../enums'; * @return {boolean} true if the string is number only * */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; function isNumber(content: string): boolean { diff --git a/lib/utils/string_value_validator/index.ts b/lib/utils/string_value_validator/index.ts index 200aadc9c..46bf7f846 100644 --- a/lib/utils/string_value_validator/index.ts +++ b/lib/utils/string_value_validator/index.ts @@ -1,3 +1,5 @@ +import { Platform } from './../../platform_support'; + /** * Copyright 2018, 2020, Optimizely * @@ -19,7 +21,7 @@ * @param {unknown} input * @return {boolean} true for non-empty string, false otherwise */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export function validate(input: unknown): boolean { return typeof input === 'string' && input !== ''; diff --git a/lib/utils/type.ts b/lib/utils/type.ts index a8869b011..fc0a1f51b 100644 --- a/lib/utils/type.ts +++ b/lib/utils/type.ts @@ -1,3 +1,5 @@ +import { Platform } from './../platform_support'; + /** * Copyright 2024-2025, Optimizely * @@ -14,7 +16,7 @@ * limitations under the License. */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type Fn = () => unknown; export type AsyncFn = () => Promise; diff --git a/lib/utils/user_profile_service_validator/index.ts b/lib/utils/user_profile_service_validator/index.ts index d791c16c1..6cdb51887 100644 --- a/lib/utils/user_profile_service_validator/index.ts +++ b/lib/utils/user_profile_service_validator/index.ts @@ -18,6 +18,7 @@ * Provides utility method for validating that the given user profile service implementation is valid. */ +import { Platform } from './../../platform_support'; import { ObjectWithUnknownProperties } from '../../shared_types'; import { INVALID_USER_PROFILE_SERVICE } from 'error_message'; @@ -30,7 +31,7 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @throws If the instance is not valid */ -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export function validate(userProfileServiceInstance: unknown): boolean { if (typeof userProfileServiceInstance === 'object' && userProfileServiceInstance !== null) { diff --git a/lib/vuid/vuid.ts b/lib/vuid/vuid.ts index 0dfb1d32d..872f546eb 100644 --- a/lib/vuid/vuid.ts +++ b/lib/vuid/vuid.ts @@ -14,9 +14,10 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { v4 as uuidV4 } from 'uuid'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export const VUID_PREFIX = `vuid_`; export const VUID_MAX_LENGTH = 32; diff --git a/lib/vuid/vuid_manager.ts b/lib/vuid/vuid_manager.ts index 88608fa3b..a7f5a8200 100644 --- a/lib/vuid/vuid_manager.ts +++ b/lib/vuid/vuid_manager.ts @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { LoggerFacade } from '../logging/logger'; import { Store } from '../utils/cache/store'; import { AsyncProducer, Maybe } from '../utils/type'; import { isVuid, makeVuid } from './vuid'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export interface VuidManager { getVuid(): Maybe; diff --git a/lib/vuid/vuid_manager_factory.browser.ts b/lib/vuid/vuid_manager_factory.browser.ts index 50e4317f7..60260b2bf 100644 --- a/lib/vuid/vuid_manager_factory.browser.ts +++ b/lib/vuid/vuid_manager_factory.browser.ts @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manager'; import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; -export const __platforms = ['browser']; +export const __platforms: Platform[] = ['browser']; export const vuidCacheManager = new VuidCacheManager(); diff --git a/lib/vuid/vuid_manager_factory.node.ts b/lib/vuid/vuid_manager_factory.node.ts index 73e82e7da..1348c705a 100644 --- a/lib/vuid/vuid_manager_factory.node.ts +++ b/lib/vuid/vuid_manager_factory.node.ts @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; -export const __platforms = ['node']; +export const __platforms: Platform[] = ['node']; export const createVuidManager = (options: VuidManagerOptions = {}): OpaqueVuidManager => { return wrapVuidManager(undefined); diff --git a/lib/vuid/vuid_manager_factory.react_native.ts b/lib/vuid/vuid_manager_factory.react_native.ts index fd2171830..262845b29 100644 --- a/lib/vuid/vuid_manager_factory.react_native.ts +++ b/lib/vuid/vuid_manager_factory.react_native.ts @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Platform } from './../platform_support'; import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manager'; import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; -export const __platforms = ['react_native']; +export const __platforms: Platform[] = ['react_native']; export const vuidCacheManager = new VuidCacheManager(); diff --git a/lib/vuid/vuid_manager_factory.ts b/lib/vuid/vuid_manager_factory.ts index ccf0c7e95..c451fcd97 100644 --- a/lib/vuid/vuid_manager_factory.ts +++ b/lib/vuid/vuid_manager_factory.ts @@ -14,11 +14,12 @@ * limitations under the License. */ +import { Platform } from './../platform_support'; import { Store } from '../utils/cache/store'; import { Maybe } from '../utils/type'; import { VuidManager } from './vuid_manager'; -export const __platforms = ['__universal__']; +export const __platforms: Platform[] = ['__universal__']; export type VuidManagerOptions = { vuidCache?: Store; From dc0927fadcdd7e9adc34edf3f7343f81f98d6f75 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 18:05:05 +0600 Subject: [PATCH 11/40] const check --- .../require-platform-declaration.js | 1 + scripts/validate-platform-isolation-ts.js | 86 ++++++++++++++++++- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/eslint-local-rules/require-platform-declaration.js b/eslint-local-rules/require-platform-declaration.js index 92cdf536a..0b92890fc 100644 --- a/eslint-local-rules/require-platform-declaration.js +++ b/eslint-local-rules/require-platform-declaration.js @@ -107,6 +107,7 @@ module.exports = { filename.endsWith('.spec.js') || filename.endsWith('.tests.js') || filename.endsWith('.test-d.ts') || + filename.endsWith('.gen.ts') || filename.endsWith('.d.ts') || filename.includes('/__mocks__/') || filename.includes('/tests/')) { diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index e71b7220a..595e00086 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -113,11 +113,19 @@ const filesWithoutExport = []; // Track files with invalid platform values const filesWithInvalidPlatforms = []; +// Track files with non-const declaration +const filesWithNonConst = []; + +// Track files with non-literal values +const filesWithNonLiterals = []; + /** * Extracts __platforms array from AST */ function extractSupportedPlatformsFromAST(sourceFile) { let platforms = null; + let hasNonStringLiteral = false; + let isNotConst = false; function visit(node) { // Look for: export const __platforms = [...] @@ -128,11 +136,18 @@ function extractSupportedPlatformsFromAST(sourceFile) { ); if (hasExport) { + // Check if declaration is const + const isConst = (node.declarationList.flags & ts.NodeFlags.Const) !== 0; + for (const declaration of node.declarationList.declarations) { if (ts.isVariableDeclaration(declaration) && ts.isIdentifier(declaration.name) && declaration.name.text === '__platforms') { + if (!isConst) { + isNotConst = true; + } + let initializer = declaration.initializer; // Handle "as const" assertion: [...] as const @@ -151,6 +166,9 @@ function extractSupportedPlatformsFromAST(sourceFile) { for (const element of initializer.elements) { if (ts.isStringLiteral(element)) { platforms.push(element.text); + } else { + // Non-string literal found (variable, computed value, etc.) + hasNonStringLiteral = true; } } return; // Found it, stop visiting @@ -164,6 +182,16 @@ function extractSupportedPlatformsFromAST(sourceFile) { } visit(sourceFile); + + if (platforms !== null) { + if (isNotConst) { + return 'NOT_CONST'; + } + if (hasNonStringLiteral) { + return 'NOT_LITERALS'; + } + } + return platforms; } @@ -226,6 +254,20 @@ function getSupportedPlatforms(filePath) { // Extract platforms from AST const supportedPlatforms = extractSupportedPlatformsFromAST(sourceFile); + if (supportedPlatforms === 'NOT_CONST') { + filesWithNonConst.push(filePath); + result = 'NOT_CONST'; + platformCache.set(filePath, result); + return result; + } + + if (supportedPlatforms === 'NOT_LITERALS') { + filesWithNonLiterals.push(filePath); + result = 'NOT_LITERALS'; + platformCache.set(filePath, result); + return result; + } + if (supportedPlatforms && supportedPlatforms.length > 0) { // Validate platform values const validation = validatePlatformValues(supportedPlatforms, filePath); @@ -309,8 +351,10 @@ function isUniversal(platforms) { * - Platform-specific files can only import from universal or files supporting all their platforms */ function isPlatformCompatible(filePlatforms, importPlatforms) { - // If either is missing platforms, not compatible - if (filePlatforms === 'MISSING' || importPlatforms === 'MISSING') { + // If either has any error state, not compatible + if (filePlatforms === 'MISSING' || importPlatforms === 'MISSING' || + filePlatforms === 'NOT_CONST' || importPlatforms === 'NOT_CONST' || + filePlatforms === 'NOT_LITERALS' || importPlatforms === 'NOT_LITERALS') { return false; } @@ -524,7 +568,7 @@ function findSourceFiles(dir, files = []) { findSourceFiles(fullPath, files); } } else if (entry.isFile()) { - // Only include TypeScript and JavaScript files, skip test files + // Only include TypeScript and JavaScript files, skip test files and generated files if ((entry.name.endsWith('.ts') || entry.name.endsWith('.js')) && !entry.name.endsWith('.spec.ts') && !entry.name.endsWith('.test.ts') && @@ -532,6 +576,7 @@ function findSourceFiles(dir, files = []) { !entry.name.endsWith('.tests.js') && !entry.name.endsWith('.umdtests.js') && !entry.name.endsWith('.test-d.ts') && + !entry.name.endsWith('.gen.ts') && !entry.name.endsWith('.d.ts')) { files.push(fullPath); } @@ -585,6 +630,41 @@ function main() { console.log('✅ All files have __platforms export\n'); + // Report files with non-const declaration + if (filesWithNonConst.length > 0) { + console.error(`❌ Found ${filesWithNonConst.length} file(s) with __platforms not declared as const:\n`); + + for (const file of filesWithNonConst) { + const relativePath = path.relative(process.cwd(), file); + console.error(` 📄 ${relativePath}`); + } + + console.error('\n'); + console.error('REQUIRED: __platforms must be declared as const'); + console.error('Use: export const __platforms: Platform[] = [...]\n'); + + process.exit(1); + } + + // Report files with non-literal values + if (filesWithNonLiterals.length > 0) { + console.error(`❌ Found ${filesWithNonLiterals.length} file(s) with __platforms containing non-literal values:\n`); + + for (const file of filesWithNonLiterals) { + const relativePath = path.relative(process.cwd(), file); + console.error(` 📄 ${relativePath}`); + } + + console.error('\n'); + console.error('REQUIRED: __platforms array must contain only string literals'); + console.error('Use: export const __platforms: Platform[] = [\'browser\', \'node\']'); + console.error('Do NOT use variables, computed values, or spread operators\n'); + + process.exit(1); + } + + console.log('✅ All __platforms declarations are const with string literals\n'); + // Report files with invalid platform values if (filesWithInvalidPlatforms.length > 0) { console.error(`❌ Found ${filesWithInvalidPlatforms.length} file(s) with invalid platform values:\n`); From fed9c58333c174d9cf1ea4766e3f40233717b146 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 18:09:20 +0600 Subject: [PATCH 12/40] readme --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 08ab9f5ad..ebed7c3b8 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,18 @@ If you're updating your SDK version, please check the appropriate migration guid ## SDK Development +### Platform Isolation + +The SDK supports multiple JavaScript platforms (Browser, Node.js, React Native) with a unified codebase. To prevent runtime errors from platform-specific code being bundled incorrectly, we enforce **platform isolation** constraints: + +- Every source file must declare which platforms it supports using `export const __platforms: Platform[] = [...]` +- Files can only import from other files that support all their declared platforms +- Universal files (`__platforms = ['__universal__']`) work everywhere but can only import from other universal files + +This system is enforced at build time through ESLint rules and validation scripts, ensuring platform-specific code (like browser DOM APIs or Node.js `fs` module) never leaks into incompatible builds. + +**For detailed documentation**, see [docs/PLATFORM_ISOLATION.md](docs/PLATFORM_ISOLATION.md). + ### Unit Tests There is a mix of testing paradigms used within the JavaScript SDK which include Mocha, Chai, Karma, and Vitest, indicated by their respective `*.tests.js` and `*.spec.ts` filenames. From ea9e33b6a1e47b7b36cc357d056f7224b00fb3b3 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 18:42:22 +0600 Subject: [PATCH 13/40] dry up --- .../require-platform-declaration.js | 72 ++----- scripts/platform-utils.js | 189 ++++++++++++++++++ scripts/validate-platform-isolation-ts.js | 138 +------------ 3 files changed, 212 insertions(+), 187 deletions(-) create mode 100644 scripts/platform-utils.js diff --git a/eslint-local-rules/require-platform-declaration.js b/eslint-local-rules/require-platform-declaration.js index 0b92890fc..c89c71591 100644 --- a/eslint-local-rules/require-platform-declaration.js +++ b/eslint-local-rules/require-platform-declaration.js @@ -14,69 +14,23 @@ * // Not exported as const array */ -const fs = require('fs'); const path = require('path'); -const ts = require('typescript'); +const { getValidPlatforms } = require('../scripts/platform-utils'); -// Cache for valid platforms -let validPlatformsCache = null; +// Cache for valid platforms per workspace +const validPlatformsCache = new Map(); -function getValidPlatforms(context) { - if (validPlatformsCache) { - return validPlatformsCache; +function getValidPlatformsForContext(context) { + const filename = context.getFilename(); + const workspaceRoot = filename.split('/lib/')[0]; + + if (validPlatformsCache.has(workspaceRoot)) { + return validPlatformsCache.get(workspaceRoot); } - try { - const filename = context.getFilename(); - const workspaceRoot = filename.split('/lib/')[0]; - const platformSupportPath = path.join(workspaceRoot, 'lib', 'platform_support.ts'); - - if (fs.existsSync(platformSupportPath)) { - const content = fs.readFileSync(platformSupportPath, 'utf8'); - const sourceFile = ts.createSourceFile( - platformSupportPath, - content, - ts.ScriptTarget.Latest, - true - ); - - const platforms = []; - - // Visit all nodes in the AST - function visit(node) { - // Look for: export type Platform = 'browser' | 'node' | ... - if (ts.isTypeAliasDeclaration(node) && - node.name.text === 'Platform' && - node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) { - - // Parse the union type - if (ts.isUnionTypeNode(node.type)) { - for (const type of node.type.types) { - if (ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal)) { - platforms.push(type.literal.text); - } - } - } - } - - ts.forEachChild(node, visit); - } - - visit(sourceFile); - - if (platforms.length > 0) { - validPlatformsCache = platforms; - return validPlatformsCache; - } - } - } catch (error) { - // Fallback to hardcoded values if parsing fails - console.warn('Could not parse platform_support.ts, using fallback values:', error.message); - } - - // Fallback to default platforms - validPlatformsCache = ['browser', 'node', 'react_native', '__universal__']; - return validPlatformsCache; + const platforms = getValidPlatforms(workspaceRoot); + validPlatformsCache.set(workspaceRoot, platforms); + return platforms; } module.exports = { @@ -119,7 +73,7 @@ module.exports = { return {}; } - const VALID_PLATFORMS = getValidPlatforms(context); + const VALID_PLATFORMS = getValidPlatformsForContext(context); let hasPlatformExport = false; return { diff --git a/scripts/platform-utils.js b/scripts/platform-utils.js new file mode 100644 index 000000000..7ce90b39a --- /dev/null +++ b/scripts/platform-utils.js @@ -0,0 +1,189 @@ +/** + * Platform Utilities + * + * Shared utilities for platform isolation validation used by both + * the validation script and ESLint rule. + */ + +const fs = require('fs'); +const path = require('path'); +const ts = require('typescript'); + +// Cache for valid platforms +let validPlatformsCache = null; + +/** + * Extract valid platform values from Platform type definition in platform_support.ts + * Parses: type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; + * + * @param {string} workspaceRoot - The root directory of the workspace + * @returns {string[]} Array of valid platform identifiers + */ +function getValidPlatforms(workspaceRoot) { + if (validPlatformsCache) { + return validPlatformsCache; + } + + try { + const platformSupportPath = path.join(workspaceRoot, 'lib', 'platform_support.ts'); + + if (!fs.existsSync(platformSupportPath)) { + throw new Error(`platform_support.ts not found at ${platformSupportPath}`); + } + + const content = fs.readFileSync(platformSupportPath, 'utf8'); + const sourceFile = ts.createSourceFile( + platformSupportPath, + content, + ts.ScriptTarget.Latest, + true + ); + + const platforms = []; + + // Visit all nodes in the AST + function visit(node) { + // Look for: export type Platform = 'browser' | 'node' | ... + if (ts.isTypeAliasDeclaration(node) && + node.name.text === 'Platform' && + node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) { + + // Parse the union type + if (ts.isUnionTypeNode(node.type)) { + for (const type of node.type.types) { + if (ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal)) { + platforms.push(type.literal.text); + } + } + } + } + + ts.forEachChild(node, visit); + } + + visit(sourceFile); + + if (platforms.length > 0) { + validPlatformsCache = platforms; + return validPlatformsCache; + } + } catch (error) { + console.warn('Could not parse platform_support.ts, using fallback values:', error.message); + } + + // Fallback to default platforms + validPlatformsCache = ['browser', 'node', 'react_native', '__universal__']; + return validPlatformsCache; +} + +/** + * Extracts __platforms array from TypeScript AST + * + * Returns: + * - string[] if valid platforms array found + * - 'NOT_CONST' if __platforms is not declared as const + * - 'NOT_LITERALS' if array contains non-literal values + * - null if __platforms export not found + * + * @param {ts.SourceFile} sourceFile - TypeScript source file AST + * @returns {string[] | 'NOT_CONST' | 'NOT_LITERALS' | null} + */ +function extractPlatformsFromAST(sourceFile) { + let platforms = null; + let hasNonStringLiteral = false; + let isNotConst = false; + + function visit(node) { + // Look for: export const __platforms = [...] + if (ts.isVariableStatement(node)) { + // Check if it has export modifier + const hasExport = node.modifiers?.some( + mod => mod.kind === ts.SyntaxKind.ExportKeyword + ); + + if (hasExport) { + // Check if declaration is const + const isConst = (node.declarationList.flags & ts.NodeFlags.Const) !== 0; + + for (const declaration of node.declarationList.declarations) { + if (ts.isVariableDeclaration(declaration) && + ts.isIdentifier(declaration.name) && + declaration.name.text === '__platforms') { + + if (!isConst) { + isNotConst = true; + } + + let initializer = declaration.initializer; + + // Handle "as const" assertion: [...] as const + if (initializer && ts.isAsExpression(initializer)) { + initializer = initializer.expression; + } + + // Handle type assertion: [...] + if (initializer && ts.isTypeAssertionExpression(initializer)) { + initializer = initializer.expression; + } + + // Extract array elements + if (initializer && ts.isArrayLiteralExpression(initializer)) { + platforms = []; + for (const element of initializer.elements) { + if (ts.isStringLiteral(element)) { + platforms.push(element.text); + } else { + // Non-string literal found (variable, computed value, etc.) + hasNonStringLiteral = true; + } + } + return; // Found it, stop visiting + } + } + } + } + } + + ts.forEachChild(node, visit); + } + + visit(sourceFile); + + if (platforms !== null) { + if (isNotConst) { + return 'NOT_CONST'; + } + if (hasNonStringLiteral) { + return 'NOT_LITERALS'; + } + } + + return platforms; +} + +/** + * Extract platforms from a file path + * + * @param {string} filePath - Absolute path to the file + * @returns {string[] | 'NOT_CONST' | 'NOT_LITERALS' | null} + */ +function extractPlatformsFromFile(filePath) { + try { + const content = fs.readFileSync(filePath, 'utf-8'); + const sourceFile = ts.createSourceFile( + filePath, + content, + ts.ScriptTarget.Latest, + true + ); + return extractPlatformsFromAST(sourceFile); + } catch (error) { + return null; + } +} + +module.exports = { + getValidPlatforms, + extractPlatformsFromAST, + extractPlatformsFromFile, +}; diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index 595e00086..a51b491e1 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -1,13 +1,12 @@ #!/usr/bin/env node +/* eslint-disable @typescript-eslint/no-var-requires */ /** - * Platform Isolation Validator (using TypeScript parser) + * Platform Isolation Validator * * This script ensures that platform-specific entry points only import * from universal or compatible platform files. * - * Uses TypeScript compiler API for robust parsing instead of regex. - * * Platform Detection: * - ALL source files (except tests) MUST export __platforms array * - Universal files use: export const __platforms = ['__universal__']; @@ -26,9 +25,10 @@ const fs = require('fs'); const path = require('path'); const ts = require('typescript'); +const { getValidPlatforms, extractPlatformsFromAST } = require('./platform-utils'); const LIB_DIR = path.join(__dirname, '..', 'lib'); -const PLATFORM_SUPPORT_FILE = path.join(LIB_DIR, 'platform_support.ts'); +const WORKSPACE_ROOT = path.join(__dirname, '..'); // Cache for __platforms exports const platformCache = new Map(); @@ -38,58 +38,16 @@ let VALID_PLATFORMS = null; let ALL_CONCRETE_PLATFORMS = null; /** - * Extract valid platform values from Platform type definition - * Parses: type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; + * Get valid platforms from source */ function getValidPlatformsFromSource() { if (VALID_PLATFORMS !== null) { return VALID_PLATFORMS; } - try { - const content = fs.readFileSync(PLATFORM_SUPPORT_FILE, 'utf-8'); - const sourceFile = ts.createSourceFile( - PLATFORM_SUPPORT_FILE, - content, - ts.ScriptTarget.Latest, - true - ); - - function visit(node) { - // Look for: export type Platform = ... - if (ts.isTypeAliasDeclaration(node) && - ts.isIdentifier(node.name) && - node.name.text === 'Platform') { - - const type = node.type; - if (ts.isUnionTypeNode(type)) { - const platforms = []; - for (const member of type.types) { - if (ts.isLiteralTypeNode(member) && - ts.isStringLiteral(member.literal)) { - platforms.push(member.literal.text); - } - } - VALID_PLATFORMS = platforms; - ALL_CONCRETE_PLATFORMS = platforms.filter(p => p !== '__universal__'); - return; - } - } - - ts.forEachChild(node, visit); - } - - visit(sourceFile); - - if (!VALID_PLATFORMS) { - throw new Error('Could not find Platform type definition in platform_support.ts'); - } - - return VALID_PLATFORMS; - } catch (error) { - console.error('❌ Failed to read Platform type from platform_support.ts:', error.message); - process.exit(1); - } + VALID_PLATFORMS = getValidPlatforms(WORKSPACE_ROOT); + ALL_CONCRETE_PLATFORMS = VALID_PLATFORMS.filter(p => p !== '__universal__'); + return VALID_PLATFORMS; } /** @@ -119,82 +77,6 @@ const filesWithNonConst = []; // Track files with non-literal values const filesWithNonLiterals = []; -/** - * Extracts __platforms array from AST - */ -function extractSupportedPlatformsFromAST(sourceFile) { - let platforms = null; - let hasNonStringLiteral = false; - let isNotConst = false; - - function visit(node) { - // Look for: export const __platforms = [...] - if (ts.isVariableStatement(node)) { - // Check if it has export modifier - const hasExport = node.modifiers?.some( - mod => mod.kind === ts.SyntaxKind.ExportKeyword - ); - - if (hasExport) { - // Check if declaration is const - const isConst = (node.declarationList.flags & ts.NodeFlags.Const) !== 0; - - for (const declaration of node.declarationList.declarations) { - if (ts.isVariableDeclaration(declaration) && - ts.isIdentifier(declaration.name) && - declaration.name.text === '__platforms') { - - if (!isConst) { - isNotConst = true; - } - - let initializer = declaration.initializer; - - // Handle "as const" assertion: [...] as const - if (initializer && ts.isAsExpression(initializer)) { - initializer = initializer.expression; - } - - // Handle type assertion: [...] - if (initializer && ts.isTypeAssertionExpression(initializer)) { - initializer = initializer.expression; - } - - // Extract array elements - if (initializer && ts.isArrayLiteralExpression(initializer)) { - platforms = []; - for (const element of initializer.elements) { - if (ts.isStringLiteral(element)) { - platforms.push(element.text); - } else { - // Non-string literal found (variable, computed value, etc.) - hasNonStringLiteral = true; - } - } - return; // Found it, stop visiting - } - } - } - } - } - - ts.forEachChild(node, visit); - } - - visit(sourceFile); - - if (platforms !== null) { - if (isNotConst) { - return 'NOT_CONST'; - } - if (hasNonStringLiteral) { - return 'NOT_LITERALS'; - } - } - - return platforms; -} - /** * Validates that platform values are valid according to Platform type */ @@ -252,7 +134,7 @@ function getSupportedPlatforms(filePath) { ); // Extract platforms from AST - const supportedPlatforms = extractSupportedPlatformsFromAST(sourceFile); + const supportedPlatforms = extractPlatformsFromAST(sourceFile); if (supportedPlatforms === 'NOT_CONST') { filesWithNonConst.push(filePath); @@ -730,7 +612,7 @@ function main() { if (typeof module !== 'undefined' && module.exports) { module.exports = { isPlatformCompatible, - extractSupportedPlatformsFromAST, + extractPlatformsFromAST, getSupportedPlatforms, extractImports, }; From 954980d491af4881e42ec72ad2c76b798944a5e5 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 19:27:04 +0600 Subject: [PATCH 14/40] eslint config --- .eslintrc.js | 13 +++++++-- .../require-platform-declaration.js | 27 +++---------------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index e8764b0d9..950c67337 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -29,8 +29,17 @@ module.exports = { } }, { - 'files': ['*.ts', '!*.spec.ts', '!*.test.ts', '!*.tests.ts', '!*.test-d.ts'], - 'excludedFiles': ['**/__mocks__/**', '**/tests/**'], + 'files': ['lib/**/*.ts', 'src/**/*.ts'], + 'excludedFiles': [ + '**/*.spec.ts', + '**/*.test.ts', + '**/*.tests.ts', + '**/*.test-d.ts', + '**/*.gen.ts', + '**/*.d.ts', + '**/__mocks__/**', + '**/tests/**' + ], 'rules': { 'local-rules/require-platform-declaration': 'error', } diff --git a/eslint-local-rules/require-platform-declaration.js b/eslint-local-rules/require-platform-declaration.js index c89c71591..549822239 100644 --- a/eslint-local-rules/require-platform-declaration.js +++ b/eslint-local-rules/require-platform-declaration.js @@ -1,7 +1,10 @@ /** * ESLint Rule: require-platform-declaration * - * Ensures that all non-test source files export __platforms with valid platform values + * Ensures that all source files export __platforms with valid platform values. + * + * File exclusions (test files, generated files, etc.) should be configured + * in .eslintrc.js using the 'excludedFiles' option. * * Valid: * export const __platforms = ['browser']; @@ -51,28 +54,6 @@ module.exports = { }, create(context) { - const filename = context.getFilename(); - - // Skip test files - if (filename.endsWith('.spec.ts') || - filename.endsWith('.test.ts') || - filename.endsWith('.tests.ts') || - filename.endsWith('.test.js') || - filename.endsWith('.spec.js') || - filename.endsWith('.tests.js') || - filename.endsWith('.test-d.ts') || - filename.endsWith('.gen.ts') || - filename.endsWith('.d.ts') || - filename.includes('/__mocks__/') || - filename.includes('/tests/')) { - return {}; - } - - // Skip non-source files - if (!filename.includes('/lib/') && !filename.includes('/src/')) { - return {}; - } - const VALID_PLATFORMS = getValidPlatformsForContext(context); let hasPlatformExport = false; From 7dd14064c05ee02cdced64763e68e950c0aaa16b Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 20:24:12 +0600 Subject: [PATCH 15/40] update add export script --- .platform-isolation.config.js | 36 ++ lib/client_factory.ts | 3 +- lib/common_exports.ts | 3 +- lib/core/audience_evaluator/index.ts | 3 +- .../odp_segment_condition_evaluator/index.ts | 3 +- lib/core/bucketer/bucket_value_generator.ts | 3 +- lib/core/bucketer/index.ts | 3 +- lib/core/condition_tree_evaluator/index.ts | 3 +- .../index.ts | 3 +- lib/core/decision/index.ts | 3 +- lib/core/decision_service/cmab/cmab_client.ts | 3 +- .../decision_service/cmab/cmab_service.ts | 3 +- lib/core/decision_service/index.ts | 3 +- lib/error/error_handler.ts | 3 +- lib/error/error_notifier.ts | 3 +- lib/error/error_notifier_factory.ts | 3 +- lib/error/error_reporter.ts | 3 +- lib/error/optimizly_error.ts | 3 +- .../batch_event_processor.react_native.ts | 3 +- lib/event_processor/batch_event_processor.ts | 3 +- .../event_builder/log_event.ts | 3 +- .../event_builder/user_event.ts | 3 +- .../default_dispatcher.browser.ts | 3 +- .../default_dispatcher.node.ts | 3 +- .../event_dispatcher/default_dispatcher.ts | 3 +- .../event_dispatcher/event_dispatcher.ts | 3 +- .../event_dispatcher_factory.ts | 3 +- .../send_beacon_dispatcher.browser.ts | 3 +- lib/event_processor/event_processor.ts | 3 +- .../event_processor_factory.browser.ts | 3 +- .../event_processor_factory.node.ts | 3 +- .../event_processor_factory.react_native.ts | 3 +- .../event_processor_factory.ts | 3 +- .../event_processor_factory.universal.ts | 3 +- lib/event_processor/event_store.ts | 3 +- .../forwarding_event_processor.ts | 3 +- lib/export_types.ts | 3 +- lib/feature_toggle.ts | 3 +- lib/index.browser.ts | 3 +- lib/index.node.ts | 3 +- lib/index.react_native.ts | 3 +- lib/index.universal.ts | 3 +- lib/logging/logger.ts | 3 +- lib/logging/logger_factory.ts | 3 +- lib/message/error_message.ts | 3 +- lib/message/log_message.ts | 3 +- lib/message/message_resolver.ts | 3 +- lib/notification_center/index.ts | 3 +- lib/notification_center/type.ts | 3 +- lib/odp/constant.ts | 3 +- lib/odp/event_manager/odp_event.ts | 3 +- .../event_manager/odp_event_api_manager.ts | 3 +- lib/odp/event_manager/odp_event_manager.ts | 3 +- lib/odp/odp_config.ts | 3 +- lib/odp/odp_manager.ts | 3 +- lib/odp/odp_manager_factory.browser.ts | 3 +- lib/odp/odp_manager_factory.node.ts | 3 +- lib/odp/odp_manager_factory.react_native.ts | 3 +- lib/odp/odp_manager_factory.ts | 3 +- lib/odp/odp_manager_factory.universal.ts | 3 +- lib/odp/odp_types.ts | 3 +- .../segment_manager/odp_response_schema.ts | 3 +- .../odp_segment_api_manager.ts | 3 +- .../segment_manager/odp_segment_manager.ts | 3 +- .../optimizely_segment_option.ts | 3 +- lib/odp/ua_parser/ua_parser.ts | 3 +- lib/odp/ua_parser/user_agent_info.ts | 3 +- lib/odp/ua_parser/user_agent_parser.ts | 3 +- lib/optimizely/index.ts | 3 +- lib/optimizely_decision/index.ts | 3 +- lib/optimizely_user_context/index.ts | 3 +- lib/platform_support.ts | 5 +- .../config_manager_factory.browser.ts | 3 +- .../config_manager_factory.node.ts | 3 +- .../config_manager_factory.react_native.ts | 3 +- lib/project_config/config_manager_factory.ts | 3 +- .../config_manager_factory.universal.ts | 3 +- lib/project_config/constant.ts | 3 +- lib/project_config/datafile_manager.ts | 3 +- lib/project_config/optimizely_config.ts | 3 +- .../polling_datafile_manager.ts | 3 +- lib/project_config/project_config.ts | 3 +- lib/project_config/project_config_manager.ts | 3 +- lib/project_config/project_config_schema.ts | 3 +- lib/service.ts | 3 +- lib/shared_types.ts | 3 +- lib/utils/attributes_validator/index.ts | 3 +- .../cache/async_storage_cache.react_native.ts | 3 +- lib/utils/cache/cache.ts | 3 +- lib/utils/cache/in_memory_lru_cache.ts | 3 +- .../cache/local_storage_cache.browser.ts | 3 +- lib/utils/cache/store.ts | 3 +- lib/utils/cache/store_validator.ts | 3 +- lib/utils/config_validator/index.ts | 3 +- lib/utils/enums/index.ts | 3 +- lib/utils/event_emitter/event_emitter.ts | 3 +- lib/utils/event_tag_utils/index.ts | 3 +- lib/utils/event_tags_validator/index.ts | 3 +- lib/utils/executor/backoff_retry_runner.ts | 3 +- lib/utils/executor/serial_runner.ts | 3 +- lib/utils/fns/index.ts | 3 +- lib/utils/http_request_handler/http.ts | 3 +- lib/utils/http_request_handler/http_util.ts | 3 +- .../request_handler.browser.ts | 3 +- .../request_handler.node.ts | 3 +- .../request_handler_validator.ts | 3 +- lib/utils/id_generator/index.ts | 3 +- .../async-storage.ts | 3 +- lib/utils/json_schema_validator/index.ts | 3 +- lib/utils/microtask/index.ts | 3 +- lib/utils/promise/operation_value.ts | 3 +- lib/utils/promise/resolvablePromise.ts | 3 +- lib/utils/repeater/repeater.ts | 3 +- lib/utils/semantic_version/index.ts | 3 +- lib/utils/string_value_validator/index.ts | 3 +- lib/utils/type.ts | 3 +- .../user_profile_service_validator/index.ts | 3 +- lib/vuid/vuid.ts | 3 +- lib/vuid/vuid_manager.ts | 3 +- lib/vuid/vuid_manager_factory.browser.ts | 3 +- lib/vuid/vuid_manager_factory.node.ts | 3 +- lib/vuid/vuid_manager_factory.react_native.ts | 3 +- lib/vuid/vuid_manager_factory.ts | 3 +- package-lock.json | 165 +++++-- package.json | 4 +- scripts/README.md | 8 +- scripts/add-platform-exports.js | 377 ++++++++++---- scripts/validate-platform-isolation-ts.js | 52 +- scripts/validate-platform-isolation.js | 464 ------------------ 129 files changed, 730 insertions(+), 744 deletions(-) create mode 100644 .platform-isolation.config.js delete mode 100644 scripts/validate-platform-isolation.js diff --git a/.platform-isolation.config.js b/.platform-isolation.config.js new file mode 100644 index 000000000..413130cd3 --- /dev/null +++ b/.platform-isolation.config.js @@ -0,0 +1,36 @@ +/** + * Platform Isolation Configuration + * + * Configures which files should be validated by the platform isolation validator. + */ + +module.exports = { + // Base directories to scan for source files + include: [ + 'lib/**/*.ts', + 'lib/**/*.js' + ], + + // Files and patterns to exclude from validation + exclude: [ + // Test files + '**/*.spec.ts', + '**/*.test.ts', + '**/*.tests.ts', + '**/*.test.js', + '**/*.spec.js', + '**/*.tests.js', + '**/*.umdtests.js', + '**/*.test-d.ts', + + // Generated files + '**/*.gen.ts', + + // Type declaration files + '**/*.d.ts', + + // Test directories and mocks + '**/__mocks__/**', + '**/tests/**' + ] +}; diff --git a/lib/client_factory.ts b/lib/client_factory.ts index aea9e05d6..5f4bac98a 100644 --- a/lib/client_factory.ts +++ b/lib/client_factory.ts @@ -30,7 +30,6 @@ import { InMemoryLruCache } from "./utils/cache/in_memory_lru_cache"; import { transformCache, CacheWithRemove } from "./utils/cache/cache"; import { ConstantBackoff } from "./utils/repeater/repeater"; -export const __platforms: Platform[] = ['__universal__']; export type OptimizelyFactoryConfig = Config & { requestHandler: RequestHandler; @@ -97,3 +96,5 @@ export const getOptimizelyInstance = (config: OptimizelyFactoryConfig): Optimize return new Optimizely(optimizelyOptions); } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/common_exports.ts b/lib/common_exports.ts index 21225677d..b3cbd940c 100644 --- a/lib/common_exports.ts +++ b/lib/common_exports.ts @@ -16,7 +16,6 @@ import { Platform } from './platform_support'; * limitations under the License. */ -export const __platforms: Platform[] = ['__universal__']; export { createStaticProjectConfigManager } from './project_config/config_manager_factory'; @@ -39,3 +38,5 @@ export { export { NOTIFICATION_TYPES, DECISION_NOTIFICATION_TYPES } from './notification_center/type'; export { OptimizelyDecideOption } from './shared_types'; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/core/audience_evaluator/index.ts b/lib/core/audience_evaluator/index.ts index 6ecf162a4..335e61fd0 100644 --- a/lib/core/audience_evaluator/index.ts +++ b/lib/core/audience_evaluator/index.ts @@ -22,7 +22,6 @@ import { CONDITION_EVALUATOR_ERROR, UNKNOWN_CONDITION_TYPE } from 'error_message import { AUDIENCE_EVALUATION_RESULT, EVALUATING_AUDIENCE} from 'log_message'; import { LoggerFacade } from '../../logging/logger'; -export const __platforms: Platform[] = ['__universal__']; export class AudienceEvaluator { private logger?: LoggerFacade; @@ -122,3 +121,5 @@ export default AudienceEvaluator; export const createAudienceEvaluator = function(UNSTABLE_conditionEvaluators: unknown, logger?: LoggerFacade): AudienceEvaluator { return new AudienceEvaluator(UNSTABLE_conditionEvaluators, logger); }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts b/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts index ffa371ca9..ab9a8d004 100644 --- a/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts +++ b/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts @@ -18,7 +18,6 @@ import { UNKNOWN_MATCH_TYPE } from 'error_message'; import { LoggerFacade } from '../../../logging/logger'; import { Condition, OptimizelyUserContext } from '../../../shared_types'; -export const __platforms: Platform[] = ['__universal__']; const QUALIFIED_MATCH_TYPE = 'qualified'; @@ -69,3 +68,5 @@ function evaluate(condition: Condition, user: OptimizelyUserContext, logger?: Lo function qualifiedEvaluator(condition: Condition, user: OptimizelyUserContext): boolean { return user.isQualifiedFor(condition.value as string); } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/core/bucketer/bucket_value_generator.ts b/lib/core/bucketer/bucket_value_generator.ts index bb9a1b85a..17a08c14d 100644 --- a/lib/core/bucketer/bucket_value_generator.ts +++ b/lib/core/bucketer/bucket_value_generator.ts @@ -18,7 +18,6 @@ import murmurhash from 'murmurhash'; import { INVALID_BUCKETING_ID } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; -export const __platforms: Platform[] = ['__universal__']; const HASH_SEED = 1; const MAX_HASH_VALUE = Math.pow(2, 32); @@ -41,3 +40,5 @@ export const generateBucketValue = function(bucketingKey: string): number { throw new OptimizelyError(INVALID_BUCKETING_ID, bucketingKey, ex.message); } }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/core/bucketer/index.ts b/lib/core/bucketer/index.ts index d6512e6ac..58d10ee92 100644 --- a/lib/core/bucketer/index.ts +++ b/lib/core/bucketer/index.ts @@ -30,7 +30,6 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { generateBucketValue } from './bucket_value_generator'; import { DecisionReason } from '../decision_service'; -export const __platforms: Platform[] = ['__universal__']; export const USER_NOT_IN_ANY_EXPERIMENT = 'User %s is not in any experiment of group %s.'; export const USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP = 'User %s is not in experiment %s of group %s.'; @@ -211,3 +210,5 @@ export default { bucket: bucket, bucketUserIntoExperiment: bucketUserIntoExperiment, }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/core/condition_tree_evaluator/index.ts b/lib/core/condition_tree_evaluator/index.ts index 030da724a..434fd5d0a 100644 --- a/lib/core/condition_tree_evaluator/index.ts +++ b/lib/core/condition_tree_evaluator/index.ts @@ -16,7 +16,6 @@ import { Platform } from './../../platform_support'; * limitations under the License. * ***************************************************************************/ -export const __platforms: Platform[] = ['__universal__']; const AND_CONDITION = 'and'; const OR_CONDITION = 'or'; @@ -133,3 +132,5 @@ function orEvaluator(conditions: ConditionTree, leafEvaluator: LeafE } return null; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/core/custom_attribute_condition_evaluator/index.ts b/lib/core/custom_attribute_condition_evaluator/index.ts index d734656e3..936350159 100644 --- a/lib/core/custom_attribute_condition_evaluator/index.ts +++ b/lib/core/custom_attribute_condition_evaluator/index.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * * limitations under the License. * ***************************************************************************/ -export const __platforms: Platform[] = ['__universal__']; import { Platform } from './../../platform_support'; import { Condition, OptimizelyUserContext } from '../../shared_types'; @@ -481,3 +480,5 @@ function semverLessThanOrEqualEvaluator(condition: Condition, user: OptimizelyUs } return result <= 0; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/core/decision/index.ts b/lib/core/decision/index.ts index 55b1ea482..9e2b67eda 100644 --- a/lib/core/decision/index.ts +++ b/lib/core/decision/index.ts @@ -22,7 +22,6 @@ import { DecisionObj } from '../decision_service'; * @param {DecisionObj} decisionObj Object representing decision * @returns {string} Experiment key or empty string if experiment is null */ -export const __platforms: Platform[] = ['__universal__']; export function getExperimentKey(decisionObj: DecisionObj): string { @@ -64,3 +63,5 @@ export function getExperimentId(decisionObj: DecisionObj): string | null { export function getVariationId(decisionObj: DecisionObj): string | null { return decisionObj.variation?.id ?? null; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/core/decision_service/cmab/cmab_client.ts b/lib/core/decision_service/cmab/cmab_client.ts index 1679f9658..7528936e1 100644 --- a/lib/core/decision_service/cmab/cmab_client.ts +++ b/lib/core/decision_service/cmab/cmab_client.ts @@ -25,7 +25,6 @@ import { isSuccessStatusCode } from "../../../utils/http_request_handler/http_ut import { BackoffController } from "../../../utils/repeater/repeater"; import { Producer } from "../../../utils/type"; -export const __platforms: Platform[] = ['__universal__']; export interface CmabClient { fetchDecision( @@ -122,3 +121,5 @@ export class DefaultCmabClient implements CmabClient { return body.predictions && body.predictions.length > 0 && body.predictions[0].variation_id; } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/core/decision_service/cmab/cmab_service.ts b/lib/core/decision_service/cmab/cmab_service.ts index 5a1effb3b..6e739e79b 100644 --- a/lib/core/decision_service/cmab/cmab_service.ts +++ b/lib/core/decision_service/cmab/cmab_service.ts @@ -34,7 +34,6 @@ import { } from 'log_message'; import { Platform } from '../../../platform_support'; -export const __platforms: Platform[] = ['__universal__']; export type CmabDecision = { variationId: string, @@ -199,3 +198,5 @@ export class DefaultCmabService implements CmabService { return `${len}-${userId}-${ruleId}`; } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/core/decision_service/index.ts b/lib/core/decision_service/index.ts index 42e99221b..1041de7c4 100644 --- a/lib/core/decision_service/index.ts +++ b/lib/core/decision_service/index.ts @@ -78,7 +78,6 @@ import { Value } from '../../utils/promise/operation_value'; import { Platform } from '../../platform_support'; -export const __platforms: Platform[] = ['__universal__']; export const EXPERIMENT_NOT_RUNNING = 'Experiment %s is not running.'; export const RETURNING_STORED_VARIATION = @@ -1698,3 +1697,5 @@ export class DecisionService { export function createDecisionService(options: DecisionServiceOptions): DecisionService { return new DecisionService(options); } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/error/error_handler.ts b/lib/error/error_handler.ts index cc4143307..4bad9ecfa 100644 --- a/lib/error/error_handler.ts +++ b/lib/error/error_handler.ts @@ -19,7 +19,6 @@ import { Platform } from './../platform_support'; * @export * @interface ErrorHandler */ -export const __platforms: Platform[] = ['__universal__']; export interface ErrorHandler { /** @@ -28,3 +27,5 @@ export interface ErrorHandler { */ handleError(exception: Error): void } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/error/error_notifier.ts b/lib/error/error_notifier.ts index e39fd943b..807052591 100644 --- a/lib/error/error_notifier.ts +++ b/lib/error/error_notifier.ts @@ -18,7 +18,6 @@ import { MessageResolver } from "../message/message_resolver"; import { ErrorHandler } from "./error_handler"; import { OptimizelyError } from "./optimizly_error"; -export const __platforms: Platform[] = ['__universal__']; export interface ErrorNotifier { notify(error: Error): void; @@ -47,3 +46,5 @@ export class DefaultErrorNotifier implements ErrorNotifier { return new DefaultErrorNotifier(this.errorHandler, this.messageResolver, name); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/error/error_notifier_factory.ts b/lib/error/error_notifier_factory.ts index cf6ea97cc..6122ea36e 100644 --- a/lib/error/error_notifier_factory.ts +++ b/lib/error/error_notifier_factory.ts @@ -19,7 +19,6 @@ import { Maybe } from "../utils/type"; import { ErrorHandler } from "./error_handler"; import { DefaultErrorNotifier } from "./error_notifier"; -export const __platforms: Platform[] = ['__universal__']; export const INVALID_ERROR_HANDLER = 'Invalid error handler'; @@ -49,3 +48,5 @@ export const extractErrorNotifier = (errorNotifier: Maybe): return errorNotifier[errorNotifierSymbol] as Maybe; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/error/error_reporter.ts b/lib/error/error_reporter.ts index 151ec4a90..b1d909a0a 100644 --- a/lib/error/error_reporter.ts +++ b/lib/error/error_reporter.ts @@ -18,7 +18,6 @@ import { LoggerFacade } from "../logging/logger"; import { ErrorNotifier } from "./error_notifier"; import { OptimizelyError } from "./optimizly_error"; -export const __platforms: Platform[] = ['__universal__']; export class ErrorReporter { private logger?: LoggerFacade; @@ -56,3 +55,5 @@ export class ErrorReporter { this.errorNotifier = errorNotifier; } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/error/optimizly_error.ts b/lib/error/optimizly_error.ts index 65cad15df..7081daa01 100644 --- a/lib/error/optimizly_error.ts +++ b/lib/error/optimizly_error.ts @@ -17,7 +17,6 @@ import { Platform } from './../platform_support'; import { MessageResolver } from "../message/message_resolver"; import { sprintf } from "../utils/fns"; -export const __platforms: Platform[] = ['__universal__']; export class OptimizelyError extends Error { baseMessage: string; @@ -41,3 +40,5 @@ export class OptimizelyError extends Error { } } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/event_processor/batch_event_processor.react_native.ts b/lib/event_processor/batch_event_processor.react_native.ts index f4900b5ac..f1a770f78 100644 --- a/lib/event_processor/batch_event_processor.react_native.ts +++ b/lib/event_processor/batch_event_processor.react_native.ts @@ -20,7 +20,6 @@ import { NetInfoState, addEventListener } from '@react-native-community/netinfo' import { BatchEventProcessor, BatchEventProcessorConfig } from './batch_event_processor'; import { Fn } from '../utils/type'; -export const __platforms: Platform[] = ['react_native']; export class ReactNativeNetInfoEventProcessor extends BatchEventProcessor { private isInternetReachable = true; @@ -52,3 +51,5 @@ export class ReactNativeNetInfoEventProcessor extends BatchEventProcessor { super.stop(); } } + +export const __platforms: Platform[] = ['react_native']; diff --git a/lib/event_processor/batch_event_processor.ts b/lib/event_processor/batch_event_processor.ts index 41992be4c..22f56e99c 100644 --- a/lib/event_processor/batch_event_processor.ts +++ b/lib/event_processor/batch_event_processor.ts @@ -33,7 +33,6 @@ import { OptimizelyError } from "../error/optimizly_error"; import { sprintf } from "../utils/fns"; import { SERVICE_STOPPED_BEFORE_RUNNING } from "../service"; -export const __platforms: Platform[] = ['__universal__']; export const DEFAULT_MIN_BACKOFF = 1000; export const DEFAULT_MAX_BACKOFF = 32000; @@ -320,3 +319,5 @@ export class BatchEventProcessor extends BaseService implements EventProcessor { }); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/event_processor/event_builder/log_event.ts b/lib/event_processor/event_builder/log_event.ts index 2d17a7b7f..ef137fc1f 100644 --- a/lib/event_processor/event_builder/log_event.ts +++ b/lib/event_processor/event_builder/log_event.ts @@ -22,7 +22,6 @@ import { LogEvent } from '../event_dispatcher/event_dispatcher'; import { EventTags } from '../../shared_types'; import { Region } from '../../project_config/project_config'; -export const __platforms: Platform[] = ['__universal__']; const ACTIVATE_EVENT_KEY = 'campaign_activated' const CUSTOM_ATTRIBUTE_FEATURE_TYPE = 'custom' @@ -233,3 +232,5 @@ export function buildLogEvent(events: UserEvent[]): LogEvent { params: makeEventBatch(events), } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/event_processor/event_builder/user_event.ts b/lib/event_processor/event_builder/user_event.ts index c977be868..04d0e256f 100644 --- a/lib/event_processor/event_builder/user_event.ts +++ b/lib/event_processor/event_builder/user_event.ts @@ -31,7 +31,6 @@ import { LoggerFacade } from '../../logging/logger'; import { DECISION_SOURCES } from '../../common_exports'; import { Platform } from '../../platform_support'; -export const __platforms: Platform[] = ['__universal__']; export type VisitorAttribute = { entityId: string @@ -307,3 +306,5 @@ const buildVisitorAttributes = ( return builtAttributes; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts index 490646f47..d55b4a7a4 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts @@ -15,7 +15,6 @@ */ // This implementation works in both browser and react_native environments -export const __platforms: Platform[] = ['browser', 'react_native']; import { Platform } from './../../platform_support'; import { BrowserRequestHandler } from "../../utils/http_request_handler/request_handler.browser"; @@ -25,3 +24,5 @@ import { DefaultEventDispatcher } from './default_dispatcher'; const eventDispatcher: EventDispatcher = new DefaultEventDispatcher(new BrowserRequestHandler()); export default eventDispatcher; + +export const __platforms: Platform[] = ['browser']; diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.node.ts b/lib/event_processor/event_dispatcher/default_dispatcher.node.ts index 10b9df4d1..a7193dc96 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.node.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.node.ts @@ -18,8 +18,9 @@ import { EventDispatcher } from './event_dispatcher'; import { NodeRequestHandler } from '../../utils/http_request_handler/request_handler.node'; import { DefaultEventDispatcher } from './default_dispatcher'; -export const __platforms: Platform[] = ['node']; const eventDispatcher: EventDispatcher = new DefaultEventDispatcher(new NodeRequestHandler()); export default eventDispatcher; + +export const __platforms: Platform[] = ['node']; diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.ts b/lib/event_processor/event_dispatcher/default_dispatcher.ts index 6cd09942e..d82659078 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.ts @@ -19,7 +19,6 @@ import { ONLY_POST_REQUESTS_ARE_SUPPORTED } from 'error_message'; import { RequestHandler } from '../../utils/http_request_handler/http'; import { EventDispatcher, EventDispatcherResponse, LogEvent } from './event_dispatcher'; -export const __platforms: Platform[] = ['__universal__']; export class DefaultEventDispatcher implements EventDispatcher { private requestHandler: RequestHandler; @@ -46,3 +45,5 @@ export class DefaultEventDispatcher implements EventDispatcher { return abortableRequest.responsePromise; } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/event_processor/event_dispatcher/event_dispatcher.ts b/lib/event_processor/event_dispatcher/event_dispatcher.ts index ac3f2146b..d8a1e3973 100644 --- a/lib/event_processor/event_dispatcher/event_dispatcher.ts +++ b/lib/event_processor/event_dispatcher/event_dispatcher.ts @@ -16,7 +16,6 @@ import { Platform } from './../../platform_support'; import { EventBatch } from "../event_builder/log_event"; -export const __platforms: Platform[] = ['__universal__']; export type EventDispatcherResponse = { statusCode?: number @@ -31,3 +30,5 @@ export interface LogEvent { httpVerb: 'POST' | 'PUT' | 'GET' | 'PATCH' params: EventBatch, } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts b/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts index aaae8cbf0..08a1e32a0 100644 --- a/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts +++ b/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts @@ -21,9 +21,10 @@ import { EventDispatcher } from './event_dispatcher'; import { validateRequestHandler } from '../../utils/http_request_handler/request_handler_validator'; -export const __platforms: Platform[] = ['__universal__']; export const createEventDispatcher = (requestHander: RequestHandler): EventDispatcher => { validateRequestHandler(requestHander); return new DefaultEventDispatcher(requestHander); } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts index 76c6f8401..e5b66f704 100644 --- a/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts @@ -19,7 +19,6 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { SEND_BEACON_FAILED } from 'error_message'; import { EventDispatcher, EventDispatcherResponse } from './event_dispatcher'; -export const __platforms: Platform[] = ['browser']; export type Event = { url: string; @@ -54,3 +53,5 @@ const eventDispatcher : EventDispatcher = { } export default eventDispatcher; + +export const __platforms: Platform[] = ['browser']; diff --git a/lib/event_processor/event_processor.ts b/lib/event_processor/event_processor.ts index 3a9f4139c..153accdf4 100644 --- a/lib/event_processor/event_processor.ts +++ b/lib/event_processor/event_processor.ts @@ -20,7 +20,6 @@ import { Service } from '../service' import { Consumer, Fn } from '../utils/type'; import { LoggerFacade } from '../logging/logger'; -export const __platforms: Platform[] = ['__universal__']; export const DEFAULT_FLUSH_INTERVAL = 30000 // Unit is ms - default flush interval is 30s export const DEFAULT_BATCH_SIZE = 10 @@ -33,3 +32,5 @@ export interface EventProcessor extends Service { setLogger(logger: LoggerFacade): void; flushImmediately(): Promise; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/event_processor/event_processor_factory.browser.ts b/lib/event_processor/event_processor_factory.browser.ts index de86024fd..22db5492f 100644 --- a/lib/event_processor/event_processor_factory.browser.ts +++ b/lib/event_processor/event_processor_factory.browser.ts @@ -31,7 +31,6 @@ import { FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory'; import { DEFAULT_MAX_EVENTS_IN_STORE, EventStore } from './event_store'; import { Platform } from '../platform_support'; -export const __platforms: Platform[] = ['browser']; export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 1_000; @@ -69,3 +68,5 @@ export const createBatchEventProcessor = ( storeTtl: options.storeTtl, }); }; + +export const __platforms: Platform[] = ['browser']; diff --git a/lib/event_processor/event_processor_factory.node.ts b/lib/event_processor/event_processor_factory.node.ts index 22c655988..57f4c18f7 100644 --- a/lib/event_processor/event_processor_factory.node.ts +++ b/lib/event_processor/event_processor_factory.node.ts @@ -26,7 +26,6 @@ import { } from './event_processor_factory'; import { Platform } from '../platform_support'; -export const __platforms: Platform[] = ['node']; export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 30_000; @@ -58,3 +57,5 @@ export const createBatchEventProcessor = ( eventStore, }); }; + +export const __platforms: Platform[] = ['node']; diff --git a/lib/event_processor/event_processor_factory.react_native.ts b/lib/event_processor/event_processor_factory.react_native.ts index cda75d68b..018160457 100644 --- a/lib/event_processor/event_processor_factory.react_native.ts +++ b/lib/event_processor/event_processor_factory.react_native.ts @@ -30,7 +30,6 @@ import { ReactNativeNetInfoEventProcessor } from './batch_event_processor.react_ import { DEFAULT_MAX_EVENTS_IN_STORE, EventStore } from './event_store'; import { Platform } from '../platform_support'; -export const __platforms: Platform[] = ['react_native']; export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 1_000; @@ -69,3 +68,5 @@ export const createBatchEventProcessor = ( ReactNativeNetInfoEventProcessor ); }; + +export const __platforms: Platform[] = ['react_native']; diff --git a/lib/event_processor/event_processor_factory.ts b/lib/event_processor/event_processor_factory.ts index 3fcc8d5fb..139dd965b 100644 --- a/lib/event_processor/event_processor_factory.ts +++ b/lib/event_processor/event_processor_factory.ts @@ -27,7 +27,6 @@ import { EventProcessor } from "./event_processor"; import { EVENT_STORE_PREFIX } from "./event_store"; import { ForwardingEventProcessor } from "./forwarding_event_processor"; -export const __platforms: Platform[] = ['__universal__']; export const INVALID_EVENT_DISPATCHER = 'Invalid event dispatcher'; @@ -178,3 +177,5 @@ export function getForwardingEventProcessor(dispatcher: EventDispatcher): EventP validateEventDispatcher(dispatcher); return new ForwardingEventProcessor(dispatcher); } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/event_processor/event_processor_factory.universal.ts b/lib/event_processor/event_processor_factory.universal.ts index 22d597587..8a522f5f8 100644 --- a/lib/event_processor/event_processor_factory.universal.ts +++ b/lib/event_processor/event_processor_factory.universal.ts @@ -26,7 +26,6 @@ import { } from './event_processor_factory'; import { Platform } from '../platform_support'; -export const __platforms: Platform[] = ['__universal__']; export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 1_000; @@ -62,3 +61,5 @@ export const createBatchEventProcessor = ( eventStore: eventStore, }); }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/event_processor/event_store.ts b/lib/event_processor/event_store.ts index 856b92f1c..d4b051a28 100644 --- a/lib/event_processor/event_store.ts +++ b/lib/event_processor/event_store.ts @@ -14,7 +14,6 @@ import { Maybe } from "../utils/type"; import { EventWithId } from "./batch_event_processor"; import { Platform } from '../platform_support'; -export const __platforms: Platform[] = ['__universal__']; export type StoredEvent = EventWithId & { _time?: { @@ -154,3 +153,5 @@ export class EventStore extends AsyncStoreWithBatchedGet implements return values.map((value, index) => this.processStoredEvent(keys[index], value)); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/event_processor/forwarding_event_processor.ts b/lib/event_processor/forwarding_event_processor.ts index ce9fce015..73532da30 100644 --- a/lib/event_processor/forwarding_event_processor.ts +++ b/lib/event_processor/forwarding_event_processor.ts @@ -27,7 +27,6 @@ import { Consumer, Fn } from '../utils/type'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; import { sprintf } from '../utils/fns'; -export const __platforms: Platform[] = ['__universal__']; export class ForwardingEventProcessor extends BaseService implements EventProcessor { private dispatcher: EventDispatcher; @@ -77,3 +76,5 @@ export class ForwardingEventProcessor extends BaseService implements EventProces return Promise.resolve(); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/export_types.ts b/lib/export_types.ts index 876a0ca82..86aba4aca 100644 --- a/lib/export_types.ts +++ b/lib/export_types.ts @@ -17,7 +17,6 @@ import { Platform } from './platform_support'; */ // config manager related types -export const __platforms: Platform[] = ['__universal__']; export type { StaticConfigManagerConfig, @@ -107,3 +106,5 @@ export type { NotificationCenter, OptimizelySegmentOption, } from './shared_types'; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/feature_toggle.ts b/lib/feature_toggle.ts index d1e62d118..0e2a6fad9 100644 --- a/lib/feature_toggle.ts +++ b/lib/feature_toggle.ts @@ -36,6 +36,7 @@ import { Platform } from './platform_support'; // example feature flag definition // export const wipFeat = () => false as const; -export const __platforms: Platform[] = ['__universal__']; export type IfActive boolean, Y, N = unknown> = ReturnType extends true ? Y : N; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/index.browser.ts b/lib/index.browser.ts index abc025be3..b7498f007 100644 --- a/lib/index.browser.ts +++ b/lib/index.browser.ts @@ -27,7 +27,6 @@ import { Platform } from './platform_support'; * @return {Client|null} the Optimizely client object * null on error */ -export const __platforms: Platform[] = ['browser']; export const createInstance = function(config: Config): Client { @@ -66,3 +65,5 @@ export * from './common_exports'; export * from './export_types'; export const clientEngine: string = JAVASCRIPT_CLIENT_ENGINE; + +export const __platforms: Platform[] = ['browser']; diff --git a/lib/index.node.ts b/lib/index.node.ts index 1ad400732..48b99c159 100644 --- a/lib/index.node.ts +++ b/lib/index.node.ts @@ -26,7 +26,6 @@ import { Platform } from './platform_support'; * @return {Client|null} the Optimizely client object * null on error */ -export const __platforms: Platform[] = ['node']; export const createInstance = function(config: Config): Client { @@ -56,3 +55,5 @@ export * from './common_exports'; export * from './export_types'; export const clientEngine: string = NODE_CLIENT_ENGINE; + +export const __platforms: Platform[] = ['node']; diff --git a/lib/index.react_native.ts b/lib/index.react_native.ts index 9619f50f9..ee3afdd8a 100644 --- a/lib/index.react_native.ts +++ b/lib/index.react_native.ts @@ -29,7 +29,6 @@ import { Platform } from './platform_support'; * @return {Client|null} the Optimizely client object * null on error */ -export const __platforms: Platform[] = ['react_native']; export const createInstance = function(config: Config): Client { @@ -59,3 +58,5 @@ export * from './common_exports'; export * from './export_types'; export const clientEngine: string = REACT_NATIVE_JS_CLIENT_ENGINE; + +export const __platforms: Platform[] = ['react_native']; diff --git a/lib/index.universal.ts b/lib/index.universal.ts index 823dcfa29..b3f5f90de 100644 --- a/lib/index.universal.ts +++ b/lib/index.universal.ts @@ -20,7 +20,6 @@ import { JAVASCRIPT_CLIENT_ENGINE } from './utils/enums'; import { RequestHandler } from './utils/http_request_handler/http'; import { Platform } from './platform_support'; -export const __platforms: Platform[] = ['__universal__']; export type UniversalConfig = Config & { requestHandler: RequestHandler; @@ -138,3 +137,5 @@ export type { NotificationCenter, OptimizelySegmentOption, } from './shared_types'; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/logging/logger.ts b/lib/logging/logger.ts index 94fe8e583..b959d6d16 100644 --- a/lib/logging/logger.ts +++ b/lib/logging/logger.ts @@ -18,7 +18,6 @@ import { OptimizelyError } from '../error/optimizly_error'; import { MessageResolver } from '../message/message_resolver'; import { sprintf } from '../utils/fns' -export const __platforms: Platform[] = ['__universal__']; export enum LogLevel { Debug, @@ -168,3 +167,5 @@ export class OptimizelyLogger implements LoggerFacade { this.handleLog(level, resolvedMessage, args); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/logging/logger_factory.ts b/lib/logging/logger_factory.ts index 00cd1f7d5..807802d55 100644 --- a/lib/logging/logger_factory.ts +++ b/lib/logging/logger_factory.ts @@ -18,7 +18,6 @@ import { ConsoleLogHandler, LogHandler, LogLevel, OptimizelyLogger } from './log import { errorResolver, infoResolver, MessageResolver } from '../message/message_resolver'; import { Maybe } from '../utils/type'; -export const __platforms: Platform[] = ['__universal__']; export const INVALID_LOG_HANDLER = 'Invalid log handler'; export const INVALID_LEVEL_PRESET = 'Invalid level preset'; @@ -130,3 +129,5 @@ export const extractLogger = (logger: Maybe): Maybe; }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/message/error_message.ts b/lib/message/error_message.ts index 2d5b5eb6e..6a1e02c3c 100644 --- a/lib/message/error_message.ts +++ b/lib/message/error_message.ts @@ -15,7 +15,6 @@ import { Platform } from './../platform_support'; * See the License for the specific language governing permissions and * limitations under the License. */ -export const __platforms: Platform[] = ['__universal__']; export const NOTIFICATION_LISTENER_EXCEPTION = 'Notification listener for (%s) threw exception: %s'; export const CONDITION_EVALUATOR_ERROR = 'Error evaluating audience condition of type %s: %s'; @@ -103,3 +102,5 @@ export const SERVICE_NOT_RUNNING = "%s not running"; export const EVENT_STORE_FULL = 'Event store is full. Not saving event with id %d.'; export const messages: string[] = []; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/message/log_message.ts b/lib/message/log_message.ts index d6e8cea3f..1f4066c74 100644 --- a/lib/message/log_message.ts +++ b/lib/message/log_message.ts @@ -16,7 +16,6 @@ import { Platform } from './../platform_support'; * limitations under the License. */ -export const __platforms: Platform[] = ['__universal__']; export const FEATURE_ENABLED_FOR_USER = 'Feature %s is enabled for user %s.'; export const FEATURE_NOT_ENABLED_FOR_USER = 'Feature %s is not enabled for user %s.'; @@ -72,3 +71,5 @@ export const CMAB_CACHE_ATTRIBUTES_MISMATCH = 'CMAB cache attributes mismatch fo export const CMAB_CACHE_MISS = 'Cache miss for user %s and rule %s.'; export const messages: string[] = []; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/message/message_resolver.ts b/lib/message/message_resolver.ts index f4817298e..cc04d0aa4 100644 --- a/lib/message/message_resolver.ts +++ b/lib/message/message_resolver.ts @@ -2,7 +2,6 @@ import { Platform } from './../platform_support'; import { messages as infoMessages } from 'log_message'; import { messages as errorMessages } from 'error_message'; -export const __platforms: Platform[] = ['__universal__']; export interface MessageResolver { resolve(baseMessage: string): string; @@ -21,3 +20,5 @@ export const errorResolver: MessageResolver = { return errorMessages[messageNum] || baseMessage; } }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/notification_center/index.ts b/lib/notification_center/index.ts index e31f5d9f8..089cfcddb 100644 --- a/lib/notification_center/index.ts +++ b/lib/notification_center/index.ts @@ -25,7 +25,6 @@ import { NOTIFICATION_LISTENER_EXCEPTION } from 'error_message'; import { ErrorReporter } from '../error/error_reporter'; import { ErrorNotifier } from '../error/error_notifier'; -export const __platforms: Platform[] = ['__universal__']; interface NotificationCenterOptions { logger?: LoggerFacade; @@ -165,3 +164,5 @@ export class DefaultNotificationCenter implements NotificationCenter, Notificati export function createNotificationCenter(options: NotificationCenterOptions): DefaultNotificationCenter { return new DefaultNotificationCenter(options); } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/notification_center/type.ts b/lib/notification_center/type.ts index 8ed9b24d4..81b0d9839 100644 --- a/lib/notification_center/type.ts +++ b/lib/notification_center/type.ts @@ -28,7 +28,6 @@ import { DecisionSource } from '../utils/enums'; import { Nullable } from '../utils/type'; import { Platform } from '../platform_support'; -export const __platforms: Platform[] = ['__universal__']; export type UserEventListenerPayload = { userId: string; @@ -153,3 +152,5 @@ export const NOTIFICATION_TYPES: NotificationTypeValues = { OPTIMIZELY_CONFIG_UPDATE: 'OPTIMIZELY_CONFIG_UPDATE', TRACK: 'TRACK', }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/constant.ts b/lib/odp/constant.ts index 88f53f7dd..2f60082f3 100644 --- a/lib/odp/constant.ts +++ b/lib/odp/constant.ts @@ -16,7 +16,6 @@ import { Platform } from './../platform_support'; * limitations under the License. */ -export const __platforms: Platform[] = ['__universal__']; export enum ODP_USER_KEY { VUID = 'vuid', @@ -30,3 +29,5 @@ export enum ODP_EVENT_ACTION { } export const ODP_DEFAULT_EVENT_TYPE = 'fullstack'; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/event_manager/odp_event.ts b/lib/odp/event_manager/odp_event.ts index dc2f3e0df..aed60ccb5 100644 --- a/lib/odp/event_manager/odp_event.ts +++ b/lib/odp/event_manager/odp_event.ts @@ -16,7 +16,6 @@ import { Platform } from './../../platform_support'; * limitations under the License. */ -export const __platforms: Platform[] = ['__universal__']; export class OdpEvent { /** @@ -53,3 +52,5 @@ export class OdpEvent { this.data = data ?? new Map(); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/event_manager/odp_event_api_manager.ts b/lib/odp/event_manager/odp_event_api_manager.ts index b6ad55cda..2ef3e1af9 100644 --- a/lib/odp/event_manager/odp_event_api_manager.ts +++ b/lib/odp/event_manager/odp_event_api_manager.ts @@ -20,7 +20,6 @@ import { OdpEvent } from './odp_event'; import { HttpMethod, RequestHandler } from '../../utils/http_request_handler/http'; import { OdpConfig } from '../odp_config'; -export const __platforms: Platform[] = ['__universal__']; export type EventDispatchResponse = { statusCode?: number; @@ -117,3 +116,5 @@ export const eventApiRequestGenerator: EventRequestGenerator = (odpConfig: OdpCo }), }; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/event_manager/odp_event_manager.ts b/lib/odp/event_manager/odp_event_manager.ts index d0ea30d2f..f9f77a6dc 100644 --- a/lib/odp/event_manager/odp_event_manager.ts +++ b/lib/odp/event_manager/odp_event_manager.ts @@ -39,7 +39,6 @@ import { SERVICE_STOPPED_BEFORE_RUNNING } from '../../service'; import { sprintf } from '../../utils/fns'; import { Platform } from '../../platform_support'; -export const __platforms: Platform[] = ['__universal__']; export interface OdpEventManager extends Service { updateConfig(odpIntegrationConfig: OdpIntegrationConfig): void; @@ -249,3 +248,5 @@ export class DefaultOdpEventManager extends BaseService implements OdpEventManag } } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/odp_config.ts b/lib/odp/odp_config.ts index 16254694d..e8982d74d 100644 --- a/lib/odp/odp_config.ts +++ b/lib/odp/odp_config.ts @@ -17,7 +17,6 @@ import { Platform } from './../platform_support'; import { checkArrayEquality } from '../utils/fns'; -export const __platforms: Platform[] = ['__universal__']; export class OdpConfig { /** @@ -84,3 +83,5 @@ export const odpIntegrationsAreEqual = (config1: OdpIntegrationConfig, config2: } export type OdpIntegrationConfig = OdpNotIntegratedConfig | OdpIntegratedConfig; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/odp_manager.ts b/lib/odp/odp_manager.ts index 2d6eabb19..5fe26a5fe 100644 --- a/lib/odp/odp_manager.ts +++ b/lib/odp/odp_manager.ts @@ -33,7 +33,6 @@ import { Maybe } from '../utils/type'; import { sprintf } from '../utils/fns'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; -export const __platforms: Platform[] = ['__universal__']; export interface OdpManager extends Service { updateConfig(odpIntegrationConfig: OdpIntegrationConfig): boolean; @@ -266,3 +265,5 @@ export class DefaultOdpManager extends BaseService implements OdpManager { }); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/odp_manager_factory.browser.ts b/lib/odp/odp_manager_factory.browser.ts index 7909f1295..dc8f2eabc 100644 --- a/lib/odp/odp_manager_factory.browser.ts +++ b/lib/odp/odp_manager_factory.browser.ts @@ -19,7 +19,6 @@ import { BrowserRequestHandler } from '../utils/http_request_handler/request_han import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __platforms: Platform[] = ['browser']; export const BROWSER_DEFAULT_API_TIMEOUT = 10_000; export const BROWSER_DEFAULT_BATCH_SIZE = 10; @@ -43,3 +42,5 @@ export const createOdpManager = (options: OdpManagerOptions = {}): OpaqueOdpMana eventRequestGenerator: eventApiRequestGenerator, }); }; + +export const __platforms: Platform[] = ['browser']; diff --git a/lib/odp/odp_manager_factory.node.ts b/lib/odp/odp_manager_factory.node.ts index 548a4d450..5b2ef97cf 100644 --- a/lib/odp/odp_manager_factory.node.ts +++ b/lib/odp/odp_manager_factory.node.ts @@ -19,7 +19,6 @@ import { NodeRequestHandler } from '../utils/http_request_handler/request_handle import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __platforms: Platform[] = ['node']; export const NODE_DEFAULT_API_TIMEOUT = 10_000; export const NODE_DEFAULT_BATCH_SIZE = 10; @@ -43,3 +42,5 @@ export const createOdpManager = (options: OdpManagerOptions = {}): OpaqueOdpMana eventRequestGenerator: eventApiRequestGenerator, }); }; + +export const __platforms: Platform[] = ['node']; diff --git a/lib/odp/odp_manager_factory.react_native.ts b/lib/odp/odp_manager_factory.react_native.ts index a746b1cc3..167cc54f4 100644 --- a/lib/odp/odp_manager_factory.react_native.ts +++ b/lib/odp/odp_manager_factory.react_native.ts @@ -20,7 +20,6 @@ import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager' import { OdpManager } from './odp_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __platforms: Platform[] = ['react_native']; export const RN_DEFAULT_API_TIMEOUT = 10_000; export const RN_DEFAULT_BATCH_SIZE = 10; @@ -44,3 +43,5 @@ export const createOdpManager = (options: OdpManagerOptions = {}): OpaqueOdpMana eventRequestGenerator: eventApiRequestGenerator, }); }; + +export const __platforms: Platform[] = ['react_native']; diff --git a/lib/odp/odp_manager_factory.ts b/lib/odp/odp_manager_factory.ts index 7efd88a00..9b6fc271f 100644 --- a/lib/odp/odp_manager_factory.ts +++ b/lib/odp/odp_manager_factory.ts @@ -27,7 +27,6 @@ import { DefaultOdpSegmentApiManager } from "./segment_manager/odp_segment_api_m import { DefaultOdpSegmentManager, OdpSegmentManager } from "./segment_manager/odp_segment_manager"; import { UserAgentParser } from "./ua_parser/user_agent_parser"; -export const __platforms: Platform[] = ['__universal__']; export const DEFAULT_CACHE_SIZE = 10_000; export const DEFAULT_CACHE_TIMEOUT = 600_000; @@ -139,3 +138,5 @@ export const extractOdpManager = (manager: Maybe): Maybe; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/odp_manager_factory.universal.ts b/lib/odp/odp_manager_factory.universal.ts index 4ab5c8de9..9fea52040 100644 --- a/lib/odp/odp_manager_factory.universal.ts +++ b/lib/odp/odp_manager_factory.universal.ts @@ -20,7 +20,6 @@ import { validateRequestHandler } from '../utils/http_request_handler/request_ha import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; -export const __platforms: Platform[] = ['__universal__']; export const DEFAULT_API_TIMEOUT = 10_000; export const DEFAULT_BATCH_SIZE = 1; @@ -41,3 +40,5 @@ export const createOdpManager = (options: UniversalOdpManagerOptions): OpaqueOdp eventRequestGenerator: eventApiRequestGenerator, }); }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/odp_types.ts b/lib/odp/odp_types.ts index ffee41832..34807a80b 100644 --- a/lib/odp/odp_types.ts +++ b/lib/odp/odp_types.ts @@ -19,7 +19,6 @@ import { Platform } from './../platform_support'; /** * Wrapper around valid data and error responses */ -export const __platforms: Platform[] = ['__universal__']; export interface Response { data: Data; @@ -87,3 +86,5 @@ export interface Node { name: string; state: string; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/segment_manager/odp_response_schema.ts b/lib/odp/segment_manager/odp_response_schema.ts index e4c375b79..5041d0d93 100644 --- a/lib/odp/segment_manager/odp_response_schema.ts +++ b/lib/odp/segment_manager/odp_response_schema.ts @@ -20,7 +20,6 @@ import { JSONSchema4 } from 'json-schema'; /** * JSON Schema used to validate the ODP GraphQL response */ -export const __platforms: Platform[] = ['__universal__']; export const OdpResponseSchema = { @@ -188,3 +187,5 @@ export const OdpResponseSchema = { }, examples: [], } as JSONSchema4; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/segment_manager/odp_segment_api_manager.ts b/lib/odp/segment_manager/odp_segment_api_manager.ts index 3af496b8b..386d44884 100644 --- a/lib/odp/segment_manager/odp_segment_api_manager.ts +++ b/lib/odp/segment_manager/odp_segment_api_manager.ts @@ -25,7 +25,6 @@ import { log } from 'console'; /** * Expected value for a qualified/valid segment */ -export const __platforms: Platform[] = ['__universal__']; const QUALIFIED = 'qualified'; @@ -200,3 +199,5 @@ export class DefaultOdpSegmentApiManager implements OdpSegmentApiManager { return EMPTY_JSON_RESPONSE; } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/segment_manager/odp_segment_manager.ts b/lib/odp/segment_manager/odp_segment_manager.ts index 65bb5d8a3..af2602fb9 100644 --- a/lib/odp/segment_manager/odp_segment_manager.ts +++ b/lib/odp/segment_manager/odp_segment_manager.ts @@ -23,7 +23,6 @@ import { ODP_USER_KEY } from '../constant'; import { LoggerFacade } from '../../logging/logger'; import { ODP_CONFIG_NOT_AVAILABLE, ODP_NOT_INTEGRATED } from 'error_message'; -export const __platforms: Platform[] = ['__universal__']; export interface OdpSegmentManager { fetchQualifiedSegments( @@ -131,3 +130,5 @@ export class DefaultOdpSegmentManager implements OdpSegmentManager { this.segmentsCache.reset(); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/segment_manager/optimizely_segment_option.ts b/lib/odp/segment_manager/optimizely_segment_option.ts index 51ec6c371..7dda97e70 100644 --- a/lib/odp/segment_manager/optimizely_segment_option.ts +++ b/lib/odp/segment_manager/optimizely_segment_option.ts @@ -17,9 +17,10 @@ import { Platform } from './../../platform_support'; */ // Options for defining behavior of OdpSegmentManager's caching mechanism when calling fetchSegments() -export const __platforms: Platform[] = ['__universal__']; export enum OptimizelySegmentOption { IGNORE_CACHE = 'IGNORE_CACHE', RESET_CACHE = 'RESET_CACHE', } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/ua_parser/ua_parser.ts b/lib/odp/ua_parser/ua_parser.ts index 7494ed118..61d44f34f 100644 --- a/lib/odp/ua_parser/ua_parser.ts +++ b/lib/odp/ua_parser/ua_parser.ts @@ -18,7 +18,6 @@ import { UAParser } from 'ua-parser-js'; import { UserAgentInfo } from './user_agent_info'; import { UserAgentParser } from './user_agent_parser'; -export const __platforms: Platform[] = ['__universal__']; const userAgentParser: UserAgentParser = { parseUserAgentInfo(): UserAgentInfo { @@ -32,3 +31,5 @@ const userAgentParser: UserAgentParser = { export function getUserAgentParser(): UserAgentParser { return userAgentParser; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/ua_parser/user_agent_info.ts b/lib/odp/ua_parser/user_agent_info.ts index 76efe298d..929c2d468 100644 --- a/lib/odp/ua_parser/user_agent_info.ts +++ b/lib/odp/ua_parser/user_agent_info.ts @@ -16,7 +16,6 @@ import { Platform } from './../../platform_support'; * limitations under the License. */ -export const __platforms: Platform[] = ['__universal__']; export type UserAgentInfo = { os: { @@ -28,3 +27,5 @@ export type UserAgentInfo = { model?: string, } }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/odp/ua_parser/user_agent_parser.ts b/lib/odp/ua_parser/user_agent_parser.ts index aef124627..793367cc0 100644 --- a/lib/odp/ua_parser/user_agent_parser.ts +++ b/lib/odp/ua_parser/user_agent_parser.ts @@ -17,8 +17,9 @@ import { Platform } from './../../platform_support'; import { UserAgentInfo } from "./user_agent_info"; -export const __platforms: Platform[] = ['__universal__']; export interface UserAgentParser { parseUserAgentInfo(): UserAgentInfo, } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/optimizely/index.ts b/lib/optimizely/index.ts index 499cb6e37..bb579d0c0 100644 --- a/lib/optimizely/index.ts +++ b/lib/optimizely/index.ts @@ -48,7 +48,6 @@ import { buildImpressionEvent, buildConversionEvent } from '../event_processor/e import { isSafeInteger } from '../utils/fns'; import { Platform } from '../platform_support'; -export const __platforms: Platform[] = ['__universal__']; import { validate } from '../utils/attributes_validator'; import * as eventTagsValidator from '../utils/event_tags_validator'; import * as projectConfig from '../project_config/project_config'; @@ -1803,3 +1802,5 @@ export default class Optimizely extends BaseService implements Client { return this.vuidManager.getVuid(); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/optimizely_decision/index.ts b/lib/optimizely_decision/index.ts index 53c41641a..73d221a97 100644 --- a/lib/optimizely_decision/index.ts +++ b/lib/optimizely_decision/index.ts @@ -16,7 +16,6 @@ import { Platform } from './../platform_support'; import { OptimizelyUserContext, OptimizelyDecision } from '../shared_types'; -export const __platforms: Platform[] = ['__universal__']; export function newErrorDecision(key: string, user: OptimizelyUserContext, reasons: string[]): OptimizelyDecision { return { @@ -29,3 +28,5 @@ export function newErrorDecision(key: string, user: OptimizelyUserContext, reaso reasons: reasons, }; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/optimizely_user_context/index.ts b/lib/optimizely_user_context/index.ts index 303661a2a..3b7856a08 100644 --- a/lib/optimizely_user_context/index.ts +++ b/lib/optimizely_user_context/index.ts @@ -26,7 +26,6 @@ import { import { OptimizelySegmentOption } from '../odp/segment_manager/optimizely_segment_option'; import { Platform } from '../platform_support'; -export const __platforms: Platform[] = ['__universal__']; export const FORCED_DECISION_NULL_RULE_KEY = '$opt_null_rule_key'; @@ -298,3 +297,5 @@ export default class OptimizelyUserContext implements IOptimizelyUserContext { return this._qualifiedSegments.indexOf(segment) > -1; } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/platform_support.ts b/lib/platform_support.ts index ad5470945..43ad57ba8 100644 --- a/lib/platform_support.ts +++ b/lib/platform_support.ts @@ -8,9 +8,10 @@ /** * Valid platform identifiers */ +import type { Platform } from './platform_support'; + export type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; -export const __platforms: Platform[] = ['__universal__']; /** * Platform support declaration @@ -56,3 +57,5 @@ export type RequirePlatformDeclaration = T extends { __platforms: readonly Pl export function isUniversal(platforms: readonly Platform[]): boolean { return platforms.length === 1 && platforms[0] === '__universal__'; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/project_config/config_manager_factory.browser.ts b/lib/project_config/config_manager_factory.browser.ts index 30093ab43..6b1e9a541 100644 --- a/lib/project_config/config_manager_factory.browser.ts +++ b/lib/project_config/config_manager_factory.browser.ts @@ -18,7 +18,6 @@ import { Platform } from './../platform_support'; import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; -export const __platforms: Platform[] = ['browser']; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { @@ -27,3 +26,5 @@ export const createPollingProjectConfigManager = (config: PollingConfigManagerCo }; return getOpaquePollingConfigManager({ ...defaultConfig, ...config }); }; + +export const __platforms: Platform[] = ['browser']; diff --git a/lib/project_config/config_manager_factory.node.ts b/lib/project_config/config_manager_factory.node.ts index 05f09ab40..953d1cea1 100644 --- a/lib/project_config/config_manager_factory.node.ts +++ b/lib/project_config/config_manager_factory.node.ts @@ -18,7 +18,6 @@ import { Platform } from './../platform_support'; import { NodeRequestHandler } from "../utils/http_request_handler/request_handler.node"; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; -export const __platforms: Platform[] = ['node']; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { @@ -27,3 +26,5 @@ export const createPollingProjectConfigManager = (config: PollingConfigManagerCo }; return getOpaquePollingConfigManager({ ...defaultConfig, ...config }); }; + +export const __platforms: Platform[] = ['node']; diff --git a/lib/project_config/config_manager_factory.react_native.ts b/lib/project_config/config_manager_factory.react_native.ts index f03bec63a..9c1ddf023 100644 --- a/lib/project_config/config_manager_factory.react_native.ts +++ b/lib/project_config/config_manager_factory.react_native.ts @@ -19,7 +19,6 @@ import { AsyncStorageCache } from "../utils/cache/async_storage_cache.react_nati import { BrowserRequestHandler } from "../utils/http_request_handler/request_handler.browser"; import { getOpaquePollingConfigManager, PollingConfigManagerConfig, OpaqueConfigManager } from "./config_manager_factory"; -export const __platforms: Platform[] = ['react_native']; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { @@ -30,3 +29,5 @@ export const createPollingProjectConfigManager = (config: PollingConfigManagerCo return getOpaquePollingConfigManager({ ...defaultConfig, ...config }); }; + +export const __platforms: Platform[] = ['react_native']; diff --git a/lib/project_config/config_manager_factory.ts b/lib/project_config/config_manager_factory.ts index 9cf44152c..48dbd50dd 100644 --- a/lib/project_config/config_manager_factory.ts +++ b/lib/project_config/config_manager_factory.ts @@ -28,7 +28,6 @@ import { LogLevel } from '../logging/logger' import { Store } from "../utils/cache/store"; import { validateStore } from "../utils/cache/store_validator"; -export const __platforms: Platform[] = ['__universal__']; export const INVALID_CONFIG_MANAGER = "Invalid config manager"; @@ -132,3 +131,5 @@ export const extractConfigManager = (opaqueConfigManager: OpaqueConfigManager): return opaqueConfigManager[configManagerSymbol] as ProjectConfigManager; }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/project_config/config_manager_factory.universal.ts b/lib/project_config/config_manager_factory.universal.ts index 3eb18dcf5..3e662f4c0 100644 --- a/lib/project_config/config_manager_factory.universal.ts +++ b/lib/project_config/config_manager_factory.universal.ts @@ -19,7 +19,6 @@ import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManage import { RequestHandler } from "../utils/http_request_handler/http"; import { validateRequestHandler } from "../utils/http_request_handler/request_handler_validator"; -export const __platforms: Platform[] = ['__universal__']; export type UniversalPollingConfigManagerConfig = PollingConfigManagerConfig & { requestHandler: RequestHandler; @@ -32,3 +31,5 @@ export const createPollingProjectConfigManager = (config: UniversalPollingConfig }; return getOpaquePollingConfigManager({ ...defaultConfig, ...config }); }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/project_config/constant.ts b/lib/project_config/constant.ts index 038bd0c8a..632a55598 100644 --- a/lib/project_config/constant.ts +++ b/lib/project_config/constant.ts @@ -16,7 +16,6 @@ import { Platform } from './../platform_support'; * limitations under the License. */ -export const __platforms: Platform[] = ['__universal__']; const DEFAULT_UPDATE_INTERVAL_MINUTES = 5; /** Standard interval (5 minutes in milliseconds) for polling datafile updates.; */ @@ -35,3 +34,5 @@ export const DEFAULT_AUTHENTICATED_URL_TEMPLATE = `https://config.optimizely.com export const BACKOFF_BASE_WAIT_SECONDS_BY_ERROR_COUNT = [0, 8, 16, 32, 64, 128, 256, 512]; export const REQUEST_TIMEOUT_MS = 60 * 1000; // 1 minute + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/project_config/datafile_manager.ts b/lib/project_config/datafile_manager.ts index 04154220f..8447e1829 100644 --- a/lib/project_config/datafile_manager.ts +++ b/lib/project_config/datafile_manager.ts @@ -21,7 +21,6 @@ import { Fn, Consumer } from '../utils/type'; import { Repeater } from '../utils/repeater/repeater'; import { LoggerFacade } from '../logging/logger'; -export const __platforms: Platform[] = ['__universal__']; export interface DatafileManager extends Service { get(): string | undefined; @@ -42,3 +41,5 @@ export type DatafileManagerConfig = { logger?: LoggerFacade; startupLogs?: StartupLog[]; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/project_config/optimizely_config.ts b/lib/project_config/optimizely_config.ts index 93066fe68..36a369ea9 100644 --- a/lib/project_config/optimizely_config.ts +++ b/lib/project_config/optimizely_config.ts @@ -38,7 +38,6 @@ import { import { DATAFILE_VERSIONS } from '../utils/enums'; import { Platform } from '../platform_support'; -export const __platforms: Platform[] = ['__universal__']; interface FeatureVariablesMap { [key: string]: FeatureVariable[]; } @@ -486,3 +485,5 @@ export class OptimizelyConfig { export function createOptimizelyConfig(configObj: ProjectConfig, datafile: string, logger?: LoggerFacade): OptimizelyConfig { return new OptimizelyConfig(configObj, datafile, logger); } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/project_config/polling_datafile_manager.ts b/lib/project_config/polling_datafile_manager.ts index 7d11d8c1c..f4e939e60 100644 --- a/lib/project_config/polling_datafile_manager.ts +++ b/lib/project_config/polling_datafile_manager.ts @@ -42,7 +42,6 @@ import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; import { Platform } from '../platform_support'; -export const __platforms: Platform[] = ['__universal__']; export const FAILED_TO_FETCH_DATAFILE = 'Failed to fetch datafile'; export class PollingDatafileManager extends BaseService implements DatafileManager { @@ -269,3 +268,5 @@ export class PollingDatafileManager extends BaseService implements DatafileManag } } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/project_config/project_config.ts b/lib/project_config/project_config.ts index e4cd855a5..34c9802b3 100644 --- a/lib/project_config/project_config.ts +++ b/lib/project_config/project_config.ts @@ -55,7 +55,6 @@ import { OptimizelyError } from '../error/optimizly_error'; import { Platform } from '../platform_support'; -export const __platforms: Platform[] = ['__universal__']; interface TryCreatingProjectConfigConfig { // TODO[OASIS-6649]: Don't use object type // eslint-disable-next-line @typescript-eslint/ban-types @@ -979,3 +978,5 @@ export default { tryCreatingProjectConfig, getTrafficAllocation, }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/project_config/project_config_manager.ts b/lib/project_config/project_config_manager.ts index 345dcabb5..f15c0065f 100644 --- a/lib/project_config/project_config_manager.ts +++ b/lib/project_config/project_config_manager.ts @@ -33,7 +33,6 @@ export const GOT_INVALID_DATAFILE = 'got invalid datafile'; import { sprintf } from '../utils/fns'; import { Platform } from '../platform_support'; -export const __platforms: Platform[] = ['__universal__']; interface ProjectConfigManagerConfig { datafile?: string | Record; jsonSchemaValidator?: Transformer, @@ -238,3 +237,5 @@ export class ProjectConfigManagerImpl extends BaseService implements ProjectConf }); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/project_config/project_config_schema.ts b/lib/project_config/project_config_schema.ts index 0ac978a37..947f86305 100644 --- a/lib/project_config/project_config_schema.ts +++ b/lib/project_config/project_config_schema.ts @@ -20,7 +20,6 @@ import { Platform } from './../platform_support'; import { JSONSchema4 } from 'json-schema'; -export const __platforms: Platform[] = ['__universal__']; var schemaDefinition = { $schema: 'http://json-schema.org/draft-04/schema#', @@ -319,3 +318,5 @@ var schemaDefinition = { const schema = schemaDefinition as JSONSchema4 export default schema + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/service.ts b/lib/service.ts index 114258c6a..a9f83f60c 100644 --- a/lib/service.ts +++ b/lib/service.ts @@ -18,7 +18,6 @@ import { Platform } from './platform_support'; import { LoggerFacade, LogLevel, LogLevelToLower } from './logging/logger' import { resolvablePromise, ResolvablePromise } from "./utils/promise/resolvablePromise"; -export const __platforms: Platform[] = ['__universal__']; export const SERVICE_FAILED_TO_START = '%s failed to start, reason: %s'; export const SERVICE_STOPPED_BEFORE_RUNNING = '%s stopped before running'; @@ -135,3 +134,5 @@ export abstract class BaseService implements Service { abstract stop(): void; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/shared_types.ts b/lib/shared_types.ts index ab16743e9..f250404f4 100644 --- a/lib/shared_types.ts +++ b/lib/shared_types.ts @@ -48,7 +48,6 @@ import { OpaqueOdpManager } from './odp/odp_manager_factory'; import { OpaqueVuidManager } from './vuid/vuid_manager_factory'; import { CacheWithRemove } from './utils/cache/cache'; -export const __platforms: Platform[] = ['__universal__']; export { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; export { EventProcessor } from './event_processor/event_processor'; @@ -537,3 +536,5 @@ export { OdpEventManager, OdpManager, }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/attributes_validator/index.ts b/lib/utils/attributes_validator/index.ts index c4495289e..1a7950f8f 100644 --- a/lib/utils/attributes_validator/index.ts +++ b/lib/utils/attributes_validator/index.ts @@ -27,7 +27,6 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @throws If the attributes are not valid */ -export const __platforms: Platform[] = ['__universal__']; export function validate(attributes: unknown): boolean { if (typeof attributes === 'object' && !Array.isArray(attributes) && attributes !== null) { @@ -56,3 +55,5 @@ export function isAttributeValid(attributeKey: unknown, attributeValue: unknown) (fns.isNumber(attributeValue) && fns.isSafeInteger(attributeValue))) ); } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/cache/async_storage_cache.react_native.ts b/lib/utils/cache/async_storage_cache.react_native.ts index a75e7e881..544437364 100644 --- a/lib/utils/cache/async_storage_cache.react_native.ts +++ b/lib/utils/cache/async_storage_cache.react_native.ts @@ -19,7 +19,6 @@ import { Maybe } from "../type"; import { AsyncStore } from "./store"; import { getDefaultAsyncStorage } from "../import.react_native/@react-native-async-storage/async-storage"; -export const __platforms: Platform[] = ['react_native']; export class AsyncStorageCache implements AsyncStore { public readonly operation = 'async'; @@ -51,3 +50,5 @@ export class AsyncStorageCache implements AsyncStore { return items.map(([key, value]) => value ? JSON.parse(value) : undefined); } } + +export const __platforms: Platform[] = ['react_native']; diff --git a/lib/utils/cache/cache.ts b/lib/utils/cache/cache.ts index 27b8e720f..c291e3d22 100644 --- a/lib/utils/cache/cache.ts +++ b/lib/utils/cache/cache.ts @@ -17,7 +17,6 @@ import { OpType, OpValue } from '../../utils/type'; import { Transformer } from '../../utils/type'; import { Platform } from '../../platform_support'; -export const __platforms: Platform[] = ['__universal__']; export interface OpCache { @@ -73,3 +72,5 @@ export const transformCache = ( return transformedCache as CacheWithRemove; }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/cache/in_memory_lru_cache.ts b/lib/utils/cache/in_memory_lru_cache.ts index 506dc3319..a61867c43 100644 --- a/lib/utils/cache/in_memory_lru_cache.ts +++ b/lib/utils/cache/in_memory_lru_cache.ts @@ -18,7 +18,6 @@ import { Platform } from './../../platform_support'; import { Maybe } from "../type"; import { SyncCacheWithRemove } from "./cache"; -export const __platforms: Platform[] = ['__universal__']; type CacheElement = { value: V; @@ -75,3 +74,5 @@ export class InMemoryLruCache implements SyncCacheWithRemove { return Array.from(this.data.keys()); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/cache/local_storage_cache.browser.ts b/lib/utils/cache/local_storage_cache.browser.ts index f91dd4d0c..32df3fea9 100644 --- a/lib/utils/cache/local_storage_cache.browser.ts +++ b/lib/utils/cache/local_storage_cache.browser.ts @@ -18,7 +18,6 @@ import { Platform } from './../../platform_support'; import { Maybe } from "../type"; import { SyncStore } from "./store"; -export const __platforms: Platform[] = ['browser']; export class LocalStorageCache implements SyncStore { public readonly operation = 'sync'; @@ -55,3 +54,5 @@ export class LocalStorageCache implements SyncStore { return keys.map((k) => this.get(k)); } } + +export const __platforms: Platform[] = ['browser']; diff --git a/lib/utils/cache/store.ts b/lib/utils/cache/store.ts index e58eb3b5a..9302c671d 100644 --- a/lib/utils/cache/store.ts +++ b/lib/utils/cache/store.ts @@ -19,7 +19,6 @@ import { Transformer } from '../../utils/type'; import { Maybe } from '../../utils/type'; import { OpType, OpValue } from '../../utils/type'; -export const __platforms: Platform[] = ['__universal__']; export interface OpStore { operation: OP; @@ -177,3 +176,5 @@ export class AsyncPrefixStore implements AsyncStore { return values.map((value) => value ? this.transformGet(value) : undefined); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/cache/store_validator.ts b/lib/utils/cache/store_validator.ts index 9875b8cfe..fa15c50ff 100644 --- a/lib/utils/cache/store_validator.ts +++ b/lib/utils/cache/store_validator.ts @@ -16,7 +16,6 @@ import { Platform } from './../../platform_support'; * See the License for the specific language governing permissions and * limitations under the License. */ -export const __platforms: Platform[] = ['__universal__']; export const INVALID_STORE = 'Invalid store'; export const INVALID_STORE_METHOD = 'Invalid store method %s'; @@ -38,3 +37,5 @@ export const validateStore = (store: any): void => { throw new Error(errors.join(', ')); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/config_validator/index.ts b/lib/utils/config_validator/index.ts index 47a6b2d0d..b5114034d 100644 --- a/lib/utils/config_validator/index.ts +++ b/lib/utils/config_validator/index.ts @@ -24,7 +24,6 @@ import { import { OptimizelyError } from '../../error/optimizly_error'; import { Platform } from '../../platform_support'; -export const __platforms: Platform[] = ['__universal__']; const SUPPORTED_VERSIONS = [DATAFILE_VERSIONS.V2, DATAFILE_VERSIONS.V3, DATAFILE_VERSIONS.V4]; @@ -64,3 +63,5 @@ export const validateDatafile = function(datafile: unknown): any { export default { validateDatafile: validateDatafile, } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/enums/index.ts b/lib/utils/enums/index.ts index 9a9e50f02..d49aa2d34 100644 --- a/lib/utils/enums/index.ts +++ b/lib/utils/enums/index.ts @@ -19,7 +19,6 @@ import { Platform } from './../../platform_support'; /** * Contains global enums used throughout the library */ -export const __platforms: Platform[] = ['__universal__']; export const LOG_LEVEL = { NOTSET: 0, @@ -110,3 +109,5 @@ export const DEFAULT_CMAB_CACHE_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes export const DEFAULT_CMAB_CACHE_SIZE = 10_000; export const DEFAULT_CMAB_RETRIES = 1; export const DEFAULT_CMAB_BACKOFF_MS = 100; // 100 milliseconds + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/event_emitter/event_emitter.ts b/lib/utils/event_emitter/event_emitter.ts index 398b2fd72..a03caba60 100644 --- a/lib/utils/event_emitter/event_emitter.ts +++ b/lib/utils/event_emitter/event_emitter.ts @@ -17,7 +17,6 @@ import { Platform } from './../../platform_support'; import { Fn } from "../type"; -export const __platforms: Platform[] = ['__universal__']; type Consumer = (arg: T) => void; @@ -58,3 +57,5 @@ export class EventEmitter { this.listeners = {}; } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/event_tag_utils/index.ts b/lib/utils/event_tag_utils/index.ts index 01d1ea26c..e56a02f91 100644 --- a/lib/utils/event_tag_utils/index.ts +++ b/lib/utils/event_tag_utils/index.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export const __platforms: Platform[] = ['__universal__']; import { Platform } from './../../platform_support'; import { @@ -84,3 +83,5 @@ export function getEventValue(eventTags: EventTags, logger?: LoggerFacade): numb return null; } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/event_tags_validator/index.ts b/lib/utils/event_tags_validator/index.ts index d79efcfe7..0f280d9fd 100644 --- a/lib/utils/event_tags_validator/index.ts +++ b/lib/utils/event_tags_validator/index.ts @@ -27,7 +27,6 @@ import { INVALID_EVENT_TAGS } from 'error_message'; * @return {boolean} true if event tags are valid * @throws If event tags are not valid */ -export const __platforms: Platform[] = ['__universal__']; export function validate(eventTags: unknown): boolean { @@ -37,3 +36,5 @@ export function validate(eventTags: unknown): boolean { throw new OptimizelyError(INVALID_EVENT_TAGS); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/executor/backoff_retry_runner.ts b/lib/utils/executor/backoff_retry_runner.ts index ec6307e5c..033aa42d5 100644 --- a/lib/utils/executor/backoff_retry_runner.ts +++ b/lib/utils/executor/backoff_retry_runner.ts @@ -5,7 +5,6 @@ import { resolvablePromise, ResolvablePromise } from "../promise/resolvablePromi import { BackoffController } from "../repeater/repeater"; import { AsyncProducer, Fn } from "../type"; -export const __platforms: Platform[] = ['__universal__']; export type RunResult = { result: Promise; @@ -55,3 +54,5 @@ export const runWithRetry = ( runTask(task, returnPromise, cancelSignal, backoff, maxRetries); return { cancelRetry, result: returnPromise.promise }; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/executor/serial_runner.ts b/lib/utils/executor/serial_runner.ts index 2d85a666f..f2bf5b776 100644 --- a/lib/utils/executor/serial_runner.ts +++ b/lib/utils/executor/serial_runner.ts @@ -17,7 +17,6 @@ import { Platform } from './../../platform_support'; import { AsyncProducer } from "../type"; -export const __platforms: Platform[] = ['__universal__']; class SerialRunner { private waitPromise: Promise = Promise.resolve(); @@ -37,3 +36,5 @@ class SerialRunner { } export { SerialRunner }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/fns/index.ts b/lib/utils/fns/index.ts index 55ebd9a68..da60ffaba 100644 --- a/lib/utils/fns/index.ts +++ b/lib/utils/fns/index.ts @@ -16,7 +16,6 @@ import { Platform } from './../../platform_support'; import { v4 } from 'uuid'; -export const __platforms: Platform[] = ['__universal__']; const MAX_SAFE_INTEGER_LIMIT = Math.pow(2, 53); @@ -133,3 +132,5 @@ export default { find, sprintf, }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/http_request_handler/http.ts b/lib/utils/http_request_handler/http.ts index 80f737c49..4c973b402 100644 --- a/lib/utils/http_request_handler/http.ts +++ b/lib/utils/http_request_handler/http.ts @@ -19,7 +19,6 @@ import { Platform } from './../../platform_support'; /** * List of key-value pairs to be used in an HTTP requests */ -export const __platforms: Platform[] = ['__universal__']; export interface Headers { [header: string]: string | undefined; @@ -50,3 +49,5 @@ export type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' export interface RequestHandler { makeRequest(requestUrl: string, headers: Headers, method: HttpMethod, data?: string): AbortableRequest; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/http_request_handler/http_util.ts b/lib/utils/http_request_handler/http_util.ts index 8e03e2fe2..e499bf092 100644 --- a/lib/utils/http_request_handler/http_util.ts +++ b/lib/utils/http_request_handler/http_util.ts @@ -1,8 +1,9 @@ import { Platform } from './../../platform_support'; -export const __platforms: Platform[] = ['__universal__']; export const isSuccessStatusCode = (statusCode: number): boolean => { return statusCode >= 200 && statusCode < 400; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/http_request_handler/request_handler.browser.ts b/lib/utils/http_request_handler/request_handler.browser.ts index d99027096..492cbd897 100644 --- a/lib/utils/http_request_handler/request_handler.browser.ts +++ b/lib/utils/http_request_handler/request_handler.browser.ts @@ -15,7 +15,6 @@ */ // This implementation works in both browser and react_native environments -export const __platforms: Platform[] = ['browser', 'react_native']; import { Platform } from './../../platform_support'; import { AbortableRequest, Headers, RequestHandler, Response } from './http'; @@ -134,3 +133,5 @@ export class BrowserRequestHandler implements RequestHandler { return headers; } } + +export const __platforms: Platform[] = ['browser']; diff --git a/lib/utils/http_request_handler/request_handler.node.ts b/lib/utils/http_request_handler/request_handler.node.ts index a4c12d0bf..21327548a 100644 --- a/lib/utils/http_request_handler/request_handler.node.ts +++ b/lib/utils/http_request_handler/request_handler.node.ts @@ -27,7 +27,6 @@ import { OptimizelyError } from '../../error/optimizly_error'; /** * Handles sending requests and receiving responses over HTTP via NodeJS http module */ -export const __platforms: Platform[] = ['node']; export class NodeRequestHandler implements RequestHandler { @@ -188,3 +187,5 @@ export class NodeRequestHandler implements RequestHandler { return { abort, responsePromise }; } } + +export const __platforms: Platform[] = ['node']; diff --git a/lib/utils/http_request_handler/request_handler_validator.ts b/lib/utils/http_request_handler/request_handler_validator.ts index 8ebd4b909..8e82f6e30 100644 --- a/lib/utils/http_request_handler/request_handler_validator.ts +++ b/lib/utils/http_request_handler/request_handler_validator.ts @@ -16,7 +16,6 @@ import { Platform } from './../../platform_support'; import { RequestHandler } from './http'; -export const __platforms: Platform[] = ['__universal__']; export const INVALID_REQUEST_HANDLER = 'Invalid request handler'; @@ -29,3 +28,5 @@ export const validateRequestHandler = (requestHandler: RequestHandler): void => throw new Error(INVALID_REQUEST_HANDLER); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/id_generator/index.ts b/lib/utils/id_generator/index.ts index 3e70404f5..508ee5af0 100644 --- a/lib/utils/id_generator/index.ts +++ b/lib/utils/id_generator/index.ts @@ -16,7 +16,6 @@ import { Platform } from './../../platform_support'; * limitations under the License. */ -export const __platforms: Platform[] = ['__universal__']; const idSuffixBase = 10_000; @@ -33,3 +32,5 @@ export class IdGenerator { return `${timestamp}${idSuffix}`; } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts index 64d9cd0af..399133028 100644 --- a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts +++ b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts @@ -17,7 +17,6 @@ import { Platform } from './../../../platform_support'; import type { AsyncStorageStatic } from '@react-native-async-storage/async-storage' -export const __platforms: Platform[] = ['__universal__']; export const MODULE_NOT_FOUND_REACT_NATIVE_ASYNC_STORAGE = 'Module not found: @react-native-async-storage/async-storage'; @@ -29,3 +28,5 @@ export const getDefaultAsyncStorage = (): AsyncStorageStatic => { throw new Error(MODULE_NOT_FOUND_REACT_NATIVE_ASYNC_STORAGE); } }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/json_schema_validator/index.ts b/lib/utils/json_schema_validator/index.ts index 844580312..243eb9bad 100644 --- a/lib/utils/json_schema_validator/index.ts +++ b/lib/utils/json_schema_validator/index.ts @@ -27,7 +27,6 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @param {boolean} shouldThrowOnError Should validation throw if invalid JSON object * @return {boolean} true if the given object is valid; throws or false if invalid */ -export const __platforms: Platform[] = ['__universal__']; export function validate( @@ -56,3 +55,5 @@ export function validate( throw new OptimizelyError(INVALID_JSON); } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/microtask/index.ts b/lib/utils/microtask/index.ts index cb9ff7c6b..57ae25999 100644 --- a/lib/utils/microtask/index.ts +++ b/lib/utils/microtask/index.ts @@ -18,7 +18,6 @@ import { Platform } from './../../platform_support'; type Callback = () => void; -export const __platforms: Platform[] = ['__universal__']; export const scheduleMicrotask = (callback: Callback): void => { if (typeof queueMicrotask === 'function') { @@ -27,3 +26,5 @@ export const scheduleMicrotask = (callback: Callback): void => { Promise.resolve().then(callback); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/promise/operation_value.ts b/lib/utils/promise/operation_value.ts index 895b0a6ef..0912a29dd 100644 --- a/lib/utils/promise/operation_value.ts +++ b/lib/utils/promise/operation_value.ts @@ -4,7 +4,6 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { OpType, OpValue } from '../type'; -export const __platforms: Platform[] = ['__universal__']; const isPromise = (val: any): boolean => { return val && typeof val.then === 'function'; @@ -51,3 +50,5 @@ export class Value { return new Value(op, Promise.resolve(val) as OpValue); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/promise/resolvablePromise.ts b/lib/utils/promise/resolvablePromise.ts index a3422b479..e998d1762 100644 --- a/lib/utils/promise/resolvablePromise.ts +++ b/lib/utils/promise/resolvablePromise.ts @@ -16,7 +16,6 @@ import { Platform } from './../../platform_support'; * limitations under the License. */ -export const __platforms: Platform[] = ['__universal__']; const noop = () => {}; @@ -36,3 +35,5 @@ export function resolvablePromise(): ResolvablePromise { }); return { promise, resolve, reject, then: promise.then.bind(promise) }; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/repeater/repeater.ts b/lib/utils/repeater/repeater.ts index aed251b33..716194781 100644 --- a/lib/utils/repeater/repeater.ts +++ b/lib/utils/repeater/repeater.ts @@ -25,7 +25,6 @@ import { scheduleMicrotask } from "../microtask"; // If the retuned promise resolves, the repeater will assume the task succeeded, // and will reset the failure count. If the promise is rejected, the repeater will // assume the task failed and will increase the current consecutive failure count. -export const __platforms: Platform[] = ['__universal__']; export interface Repeater { @@ -158,3 +157,5 @@ export class IntervalRepeater implements Repeater { this.task = task; } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/semantic_version/index.ts b/lib/utils/semantic_version/index.ts index 9469ee880..dec9fc68a 100644 --- a/lib/utils/semantic_version/index.ts +++ b/lib/utils/semantic_version/index.ts @@ -24,7 +24,6 @@ import { VERSION_TYPE } from '../enums'; * @return {boolean} true if the string is number only * */ -export const __platforms: Platform[] = ['__universal__']; function isNumber(content: string): boolean { @@ -184,3 +183,5 @@ export function compareVersion(conditionsVersion: string, userProvidedVersion: s return 0; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/string_value_validator/index.ts b/lib/utils/string_value_validator/index.ts index 46bf7f846..9d4940e20 100644 --- a/lib/utils/string_value_validator/index.ts +++ b/lib/utils/string_value_validator/index.ts @@ -21,8 +21,9 @@ import { Platform } from './../../platform_support'; * @param {unknown} input * @return {boolean} true for non-empty string, false otherwise */ -export const __platforms: Platform[] = ['__universal__']; export function validate(input: unknown): boolean { return typeof input === 'string' && input !== ''; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/type.ts b/lib/utils/type.ts index fc0a1f51b..7d572d1b4 100644 --- a/lib/utils/type.ts +++ b/lib/utils/type.ts @@ -16,7 +16,6 @@ import { Platform } from './../platform_support'; * limitations under the License. */ -export const __platforms: Platform[] = ['__universal__']; export type Fn = () => unknown; export type AsyncFn = () => Promise; @@ -41,3 +40,5 @@ export type OrNull = T | null; export type Nullable = { [P in keyof T]: P extends K ? OrNull : T[P]; } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/user_profile_service_validator/index.ts b/lib/utils/user_profile_service_validator/index.ts index 6cdb51887..a1faf649c 100644 --- a/lib/utils/user_profile_service_validator/index.ts +++ b/lib/utils/user_profile_service_validator/index.ts @@ -31,7 +31,6 @@ import { OptimizelyError } from '../../error/optimizly_error'; * @throws If the instance is not valid */ -export const __platforms: Platform[] = ['__universal__']; export function validate(userProfileServiceInstance: unknown): boolean { if (typeof userProfileServiceInstance === 'object' && userProfileServiceInstance !== null) { @@ -44,3 +43,5 @@ export function validate(userProfileServiceInstance: unknown): boolean { } throw new OptimizelyError(INVALID_USER_PROFILE_SERVICE, 'Not an object'); } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/vuid/vuid.ts b/lib/vuid/vuid.ts index 872f546eb..f8974c88a 100644 --- a/lib/vuid/vuid.ts +++ b/lib/vuid/vuid.ts @@ -17,7 +17,6 @@ import { Platform } from './../platform_support'; import { v4 as uuidV4 } from 'uuid'; -export const __platforms: Platform[] = ['__universal__']; export const VUID_PREFIX = `vuid_`; export const VUID_MAX_LENGTH = 32; @@ -32,3 +31,5 @@ export const makeVuid = (): string => { return vuidFull.length <= VUID_MAX_LENGTH ? vuidFull : vuidFull.substring(0, VUID_MAX_LENGTH); }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/vuid/vuid_manager.ts b/lib/vuid/vuid_manager.ts index a7f5a8200..396d123f6 100644 --- a/lib/vuid/vuid_manager.ts +++ b/lib/vuid/vuid_manager.ts @@ -19,7 +19,6 @@ import { Store } from '../utils/cache/store'; import { AsyncProducer, Maybe } from '../utils/type'; import { isVuid, makeVuid } from './vuid'; -export const __platforms: Platform[] = ['__universal__']; export interface VuidManager { getVuid(): Maybe; @@ -133,3 +132,5 @@ export class DefaultVuidManager implements VuidManager { this.vuid = await this.vuidCacheManager.load(); } } + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/vuid/vuid_manager_factory.browser.ts b/lib/vuid/vuid_manager_factory.browser.ts index 60260b2bf..dfb449ea4 100644 --- a/lib/vuid/vuid_manager_factory.browser.ts +++ b/lib/vuid/vuid_manager_factory.browser.ts @@ -18,7 +18,6 @@ import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manage import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; -export const __platforms: Platform[] = ['browser']; export const vuidCacheManager = new VuidCacheManager(); @@ -29,3 +28,5 @@ export const createVuidManager = (options: VuidManagerOptions = {}): OpaqueVuidM enableVuid: options.enableVuid })); }; + +export const __platforms: Platform[] = ['browser']; diff --git a/lib/vuid/vuid_manager_factory.node.ts b/lib/vuid/vuid_manager_factory.node.ts index 1348c705a..c43ebf8f5 100644 --- a/lib/vuid/vuid_manager_factory.node.ts +++ b/lib/vuid/vuid_manager_factory.node.ts @@ -16,8 +16,9 @@ import { Platform } from './../platform_support'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; -export const __platforms: Platform[] = ['node']; export const createVuidManager = (options: VuidManagerOptions = {}): OpaqueVuidManager => { return wrapVuidManager(undefined); }; + +export const __platforms: Platform[] = ['node']; diff --git a/lib/vuid/vuid_manager_factory.react_native.ts b/lib/vuid/vuid_manager_factory.react_native.ts index 262845b29..231b106fe 100644 --- a/lib/vuid/vuid_manager_factory.react_native.ts +++ b/lib/vuid/vuid_manager_factory.react_native.ts @@ -18,7 +18,6 @@ import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manage import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; -export const __platforms: Platform[] = ['react_native']; export const vuidCacheManager = new VuidCacheManager(); @@ -29,3 +28,5 @@ export const createVuidManager = (options: VuidManagerOptions = {}): OpaqueVuidM enableVuid: options.enableVuid })); }; + +export const __platforms: Platform[] = ['react_native']; diff --git a/lib/vuid/vuid_manager_factory.ts b/lib/vuid/vuid_manager_factory.ts index c451fcd97..a3263bb78 100644 --- a/lib/vuid/vuid_manager_factory.ts +++ b/lib/vuid/vuid_manager_factory.ts @@ -19,7 +19,6 @@ import { Store } from '../utils/cache/store'; import { Maybe } from '../utils/type'; import { VuidManager } from './vuid_manager'; -export const __platforms: Platform[] = ['__universal__']; export type VuidManagerOptions = { vuidCache?: Store; @@ -45,3 +44,5 @@ export const wrapVuidManager = (vuidManager: Maybe): OpaqueVuidMana [vuidManagerSymbol]: vuidManager } }; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/package-lock.json b/package-lock.json index 14162e7ba..82f14cc6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "karma-mocha": "^2.0.1", "karma-webpack": "^5.0.1", "lodash": "^4.17.11", + "minimatch": "^9.0.5", "mocha": "^10.2.0", "mocha-lcov-reporter": "^1.3.0", "nise": "^1.4.10", @@ -3114,6 +3115,19 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/js": { "version": "8.49.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", @@ -3155,6 +3169,19 @@ "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -5022,15 +5049,6 @@ "vitest": "2.1.9" } }, - "node_modules/@vitest/coverage-istanbul/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/@vitest/coverage-istanbul/node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -5115,21 +5133,6 @@ "node": ">=10" } }, - "node_modules/@vitest/coverage-istanbul/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@vitest/coverage-istanbul/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -7580,6 +7583,19 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -8330,6 +8346,19 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/globals": { "version": "13.22.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", @@ -9452,30 +9481,6 @@ "webpack": "^5.0.0" } }, - "node_modules/karma-webpack/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/karma-webpack/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/karma/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -9487,6 +9492,19 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/karma/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/karma/node_modules/ua-parser-js": { "version": "0.7.38", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.38.tgz", @@ -10573,15 +10591,29 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/minimist": { @@ -10927,6 +10959,20 @@ "node": ">= 0.10.5" } }, + "node_modules/node-dir/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -13598,6 +13644,19 @@ "node": ">=8" } }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/text-encoding": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", diff --git a/package.json b/package.json index b411352f5..1014756ae 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,7 @@ "clean": "rm -rf dist", "clean:win": "(if exist dist rd /s/q dist)", "lint": "tsc --noEmit && eslint 'lib/**/*.js' 'lib/**/*.ts'", - "validate-platform-isolation": "node scripts/validate-platform-isolation.js", - "validate-platform-isolation-ts": "node scripts/validate-platform-isolation-ts.js", + "validate-platform-isolation": "node scripts/validate-platform-isolation-ts.js", "test-platform-isolation": "node scripts/test-validator.js", "add-platform-exports": "node scripts/add-platform-exports.js", "test-vitest": "vitest run", @@ -130,6 +129,7 @@ "karma-mocha": "^2.0.1", "karma-webpack": "^5.0.1", "lodash": "^4.17.11", + "minimatch": "^9.0.5", "mocha": "^10.2.0", "mocha-lcov-reporter": "^1.3.0", "nise": "^1.4.10", diff --git a/scripts/README.md b/scripts/README.md index 31963250c..cd2758e71 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -2,15 +2,17 @@ This directory contains build and validation scripts for the JavaScript SDK. -## validate-platform-isolation.js +## validate-platform-isolation-ts.js -Validates that platform-specific code is properly isolated to prevent runtime errors when building for different platforms (Browser, Node.js, React Native). +The main platform isolation validator that ensures platform-specific code is properly isolated to prevent runtime errors when building for different platforms (Browser, Node.js, React Native). + +**Configuration:** File patterns to include/exclude are configured in `.platform-isolation.config.js` at the workspace root. ### Usage ```bash # Run manually -node scripts/validate-platform-isolation.js +node scripts/validate-platform-isolation-ts.js # Run via npm script npm run validate-platform-isolation diff --git a/scripts/add-platform-exports.js b/scripts/add-platform-exports.js index ecb43ec9b..8aee8a3ae 100644 --- a/scripts/add-platform-exports.js +++ b/scripts/add-platform-exports.js @@ -4,17 +4,37 @@ * Auto-add __platforms to files * * This script automatically adds __platforms export to files that don't have it. + * Uses TypeScript parser to analyze files and add proper type annotations. * * Strategy: * 1. Files with platform-specific naming (.browser.ts, .node.ts, .react_native.ts) get their specific platform(s) * 2. All other files are assumed to be universal and get ['__universal__'] + * 3. Adds Platform type import and type annotation + * 4. Inserts __platforms export at the end of the file */ const fs = require('fs'); const path = require('path'); +const ts = require('typescript'); +const { minimatch } = require('minimatch'); +const { extractPlatformsFromAST } = require('./platform-utils'); +const WORKSPACE_ROOT = path.join(__dirname, '..'); const PLATFORMS = ['browser', 'node', 'react_native']; -const LIB_DIR = path.join(__dirname, '..', 'lib'); + +// Load configuration +const configPath = path.join(WORKSPACE_ROOT, '.platform-isolation.config.js'); +const config = fs.existsSync(configPath) + ? require(configPath) + : { + include: ['lib/**/*.ts', 'lib/**/*.js'], + exclude: [ + '**/*.spec.ts', '**/*.test.ts', '**/*.tests.ts', + '**/*.test.js', '**/*.spec.js', '**/*.tests.js', + '**/*.umdtests.js', '**/*.test-d.ts', '**/*.gen.ts', + '**/*.d.ts', '**/__mocks__/**', '**/tests/**' + ] + }; function getPlatformFromFilename(filename) { const platforms = []; @@ -26,130 +46,321 @@ function getPlatformFromFilename(filename) { return platforms.length > 0 ? platforms : null; } -function hasSupportedPlatformsExport(content) { - return /export\s+(?:const|let|var)\s+__platforms/.test(content); +/** + * Check if file matches any pattern using minimatch + */ +function matchesPattern(filePath, patterns) { + const relativePath = path.relative(WORKSPACE_ROOT, filePath).replace(/\\/g, '/'); + + return patterns.some(pattern => minimatch(relativePath, pattern, { dot: true })); } -function findSourceFiles(dir, files = []) { - const entries = fs.readdirSync(dir, { withFileTypes: true }); +/** + * Calculate relative import path for Platform type + */ +function getRelativeImportPath(filePath) { + const platformSupportPath = path.join(WORKSPACE_ROOT, 'lib', 'platform_support.ts'); + const fileDir = path.dirname(filePath); + let relativePath = path.relative(fileDir, platformSupportPath); - for (const entry of entries) { - const fullPath = path.join(dir, entry.name); - - if (entry.isDirectory()) { - if (!entry.name.startsWith('.') && - entry.name !== 'node_modules' && - entry.name !== 'dist' && - entry.name !== 'coverage' && - entry.name !== 'tests') { - findSourceFiles(fullPath, files); - } - } else if (entry.isFile()) { - if ((entry.name.endsWith('.ts') || entry.name.endsWith('.js')) && - !entry.name.endsWith('.spec.ts') && - !entry.name.endsWith('.test.ts') && - !entry.name.endsWith('.tests.ts') && - !entry.name.endsWith('.tests.js') && - !entry.name.endsWith('.test-d.ts') && - !entry.name.endsWith('.d.ts')) { - files.push(fullPath); - } - } + // Normalize to forward slashes and remove .ts extension + relativePath = relativePath.replace(/\\/g, '/').replace(/\.ts$/, ''); + + // Ensure it starts with ./ + if (!relativePath.startsWith('.')) { + relativePath = './' + relativePath; } - return files; + return relativePath; } -function addSupportedPlatforms(filePath) { - const content = fs.readFileSync(filePath, 'utf-8'); +/** + * Find or add Platform import in the file + * Returns the updated content and whether import was added + */ +function ensurePlatformImport(content, filePath) { + const sourceFile = ts.createSourceFile( + filePath, + content, + ts.ScriptTarget.Latest, + true + ); + + // Check if Platform import already exists + let hasPlatformImport = false; + let lastImportEnd = 0; - // Skip if already has __platforms - if (hasSupportedPlatformsExport(content)) { - return { skipped: true, reason: 'already has export' }; + function visit(node) { + if (ts.isImportDeclaration(node)) { + const moduleSpecifier = node.moduleSpecifier; + if (ts.isStringLiteral(moduleSpecifier)) { + // Check if this import is from platform_support + if (moduleSpecifier.text.includes('platform_support')) { + // Check if it imports Platform type + if (node.importClause && node.importClause.namedBindings) { + const namedBindings = node.importClause.namedBindings; + if (ts.isNamedImports(namedBindings)) { + for (const element of namedBindings.elements) { + if (element.name.text === 'Platform') { + hasPlatformImport = true; + break; + } + } + } + } + } + } + lastImportEnd = node.end; + } } - // Determine platforms - const platformsFromFilename = getPlatformFromFilename(filePath); - const platforms = platformsFromFilename || ['__universal__']; + ts.forEachChild(sourceFile, visit); - // Format the export statement - const platformsStr = platforms.map(p => `'${p}'`).join(', '); - const exportStatement = `export const __platforms = [${platformsStr}] as const;\n`; + if (hasPlatformImport) { + return { content, added: false }; + } - // Find where to insert (after imports, before first export or code) - const lines = content.split('\n'); - let insertIndex = 0; - let inComment = false; - let foundImports = false; + // Add Platform import + const importPath = getRelativeImportPath(filePath); + const importStatement = `import type { Platform } from '${importPath}';\n`; - for (let i = 0; i < lines.length; i++) { - const line = lines[i].trim(); - - // Track multi-line comments - if (line.startsWith('/*')) inComment = true; - if (line.endsWith('*/')) inComment = false; + if (lastImportEnd > 0) { + // Add after last import + const lines = content.split('\n'); + let insertLine = 0; + let currentPos = 0; - // Skip empty lines and comments at the start - if (inComment || line.startsWith('//') || line.startsWith('*') || line === '') { - insertIndex = i + 1; - continue; + for (let i = 0; i < lines.length; i++) { + currentPos += lines[i].length + 1; // +1 for newline + if (currentPos >= lastImportEnd) { + insertLine = i + 1; + break; + } } - // Track imports - if (line.startsWith('import ') || line.includes(' import ')) { - foundImports = true; - insertIndex = i + 1; - continue; - } + lines.splice(insertLine, 0, importStatement.trim()); + return { content: lines.join('\n'), added: true }; + } else { + // Add at the beginning (after shebang/comments if any) + const lines = content.split('\n'); + let insertLine = 0; - // If we've seen imports and now see something else, insert before it - if (foundImports && !line.startsWith('import')) { - // Add a blank line after imports if not already there - if (lines[i - 1].trim() !== '') { - lines.splice(i, 0, ''); - i++; + // Skip shebang and leading comments + for (let i = 0; i < lines.length; i++) { + const trimmed = lines[i].trim(); + if (trimmed.startsWith('#!') || trimmed.startsWith('//') || + trimmed.startsWith('/*') || trimmed.startsWith('*') || trimmed === '') { + insertLine = i + 1; + } else { + break; } - break; } - // If no imports found, insert after copyright/header - if (!foundImports && (line.startsWith('export ') || line.startsWith('const ') || - line.startsWith('function ') || line.startsWith('class '))) { - break; + lines.splice(insertLine, 0, importStatement.trim(), ''); + return { content: lines.join('\n'), added: true }; + } +} + +/** + * Remove existing __platforms export from the content + */ +function removeExistingPlatformExport(content, filePath) { + const sourceFile = ts.createSourceFile( + filePath, + content, + ts.ScriptTarget.Latest, + true + ); + + const lines = content.split('\n'); + const linesToRemove = new Set(); + + function visit(node) { + if (ts.isVariableStatement(node)) { + const hasExport = node.modifiers?.some( + mod => mod.kind === ts.SyntaxKind.ExportKeyword + ); + + if (hasExport) { + for (const declaration of node.declarationList.declarations) { + if (ts.isVariableDeclaration(declaration) && + ts.isIdentifier(declaration.name) && + declaration.name.text === '__platforms') { + // Mark this line for removal + const startLine = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line; + const endLine = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line; + + for (let i = startLine; i <= endLine; i++) { + linesToRemove.add(i); + } + } + } + } + } + } + + ts.forEachChild(sourceFile, visit); + + if (linesToRemove.size === 0) { + return { content, removed: false }; + } + + const filteredLines = lines.filter((_, index) => !linesToRemove.has(index)); + return { content: filteredLines.join('\n'), removed: true }; +} + +/** + * Add __platforms export at the end of the file + */ +function addPlatformExport(content, platforms) { + const platformsStr = platforms.map(p => `'${p}'`).join(', '); + const exportStatement = `\n\nexport const __platforms: Platform[] = [${platformsStr}];\n`; + + // Trim trailing whitespace and ensure we end with the export (with blank line before) + return content.trimEnd() + exportStatement; +} + +/** + * Process a single file + */ +function processFile(filePath) { + let content = fs.readFileSync(filePath, 'utf-8'); + + // Use TypeScript parser to check for existing __platforms + const existingPlatforms = extractPlatformsFromAST( + ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true) + ); + + // Determine platforms for this file + // If file already has platforms, use those (preserve existing values) + // Otherwise, determine from filename or default to universal + let platforms; + if (existingPlatforms === null) { + // No __platforms export, determine from filename + const platformsFromFilename = getPlatformFromFilename(filePath); + platforms = platformsFromFilename || ['__universal__']; + } else if (Array.isArray(existingPlatforms)) { + // Has valid __platforms, preserve the existing values + platforms = existingPlatforms; + } else { + // Has issues (NOT_CONST, NOT_LITERALS), determine from filename + const platformsFromFilename = getPlatformFromFilename(filePath); + platforms = platformsFromFilename || ['__universal__']; + } + + let modified = false; + let action = 'skipped'; + + if (existingPlatforms === null) { + // No __platforms export, add it + action = 'added'; + modified = true; + } else if (Array.isArray(existingPlatforms)) { + // Has __platforms but might need to be moved or updated + // Remove existing and re-add at the end + const removed = removeExistingPlatformExport(content, filePath); + if (removed.removed) { + content = removed.content; + action = 'moved'; + modified = true; + } else { + return { skipped: true, reason: 'already has export at end' }; } + } else { + // Has issues (NOT_CONST, NOT_LITERALS), fix them + const removed = removeExistingPlatformExport(content, filePath); + content = removed.content; + action = 'fixed'; + modified = true; } - // Insert the export statement - lines.splice(insertIndex, 0, exportStatement); + if (modified) { + // Ensure Platform import exists + const importResult = ensurePlatformImport(content, filePath); + content = importResult.content; + + // Add __platforms export at the end + content = addPlatformExport(content, platforms); + + // Write back to file + fs.writeFileSync(filePath, content, 'utf-8'); + + return { skipped: false, action, platforms, addedImport: importResult.added }; + } - // Write back to file - fs.writeFileSync(filePath, lines.join('\n'), 'utf-8'); + return { skipped: true, reason: 'no changes needed' }; +} + +/** + * Recursively find all files matching include patterns and not matching exclude patterns + */ +function findSourceFiles(dir, files = []) { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + + // Skip hidden directories, node_modules, dist, and coverage + if (entry.isDirectory()) { + if (!entry.name.startsWith('.') && + entry.name !== 'node_modules' && + entry.name !== 'dist' && + entry.name !== 'coverage') { + findSourceFiles(fullPath, files); + } + } else if (entry.isFile()) { + // Check if file matches include patterns + if (matchesPattern(fullPath, config.include)) { + // Check if file is NOT excluded + if (!matchesPattern(fullPath, config.exclude)) { + files.push(fullPath); + } + } + } + } - return { skipped: false, platforms }; + return files; } function main() { - console.log('🔧 Adding __platforms to files...\n'); + console.log('🔧 Processing __platforms exports...\n'); + console.log(`📋 Configuration: ${path.relative(WORKSPACE_ROOT, configPath) || '.platform-isolation.config.js'}\n`); - const files = findSourceFiles(LIB_DIR); + const files = findSourceFiles(WORKSPACE_ROOT); let added = 0; + let moved = 0; + let fixed = 0; let skipped = 0; for (const file of files) { - const result = addSupportedPlatforms(file); + const result = processFile(file); const relativePath = path.relative(process.cwd(), file); if (result.skipped) { skipped++; } else { - added++; - console.log(`✅ ${relativePath} → [${result.platforms.join(', ')}]`); + switch (result.action) { + case 'added': + added++; + console.log(`➕ ${relativePath} → [${result.platforms.join(', ')}]${result.addedImport ? ' (added import)' : ''}`); + break; + case 'moved': + moved++; + console.log(`📍 ${relativePath} → moved to end [${result.platforms.join(', ')}]${result.addedImport ? ' (added import)' : ''}`); + break; + case 'fixed': + fixed++; + console.log(`🔧 ${relativePath} → fixed [${result.platforms.join(', ')}]${result.addedImport ? ' (added import)' : ''}`); + break; + } } } console.log(`\n📊 Summary:`); console.log(` Added: ${added} files`); - console.log(` Skipped: ${skipped} files (already had export)`); + console.log(` Moved to end: ${moved} files`); + console.log(` Fixed: ${fixed} files`); + console.log(` Skipped: ${skipped} files`); console.log(` Total: ${files.length} files\n`); console.log('✅ Done! Run npm run validate-platform-isolation to verify.\n'); @@ -159,4 +370,4 @@ if (require.main === module) { main(); } -module.exports = { addSupportedPlatforms }; +module.exports = { processFile }; diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index a51b491e1..5366ca8f9 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -25,11 +25,25 @@ const fs = require('fs'); const path = require('path'); const ts = require('typescript'); +const { minimatch } = require('minimatch'); const { getValidPlatforms, extractPlatformsFromAST } = require('./platform-utils'); -const LIB_DIR = path.join(__dirname, '..', 'lib'); const WORKSPACE_ROOT = path.join(__dirname, '..'); +// Load configuration +const configPath = path.join(WORKSPACE_ROOT, '.platform-isolation.config.js'); +const config = fs.existsSync(configPath) + ? require(configPath) + : { + include: ['lib/**/*.ts', 'lib/**/*.js'], + exclude: [ + '**/*.spec.ts', '**/*.test.ts', '**/*.tests.ts', + '**/*.test.js', '**/*.spec.js', '**/*.tests.js', + '**/*.umdtests.js', '**/*.test-d.ts', '**/*.gen.ts', + '**/*.d.ts', '**/__mocks__/**', '**/tests/**' + ] + }; + // Cache for __platforms exports const platformCache = new Map(); @@ -432,7 +446,16 @@ function validateFile(filePath) { } /** - * Recursively find all TypeScript/JavaScript files in a directory + * Check if file matches any pattern using minimatch + */ +function matchesPattern(filePath, patterns) { + const relativePath = path.relative(WORKSPACE_ROOT, filePath).replace(/\\/g, '/'); + + return patterns.some(pattern => minimatch(relativePath, pattern, { dot: true })); +} + +/** + * Recursively find all files matching include patterns and not matching exclude patterns */ function findSourceFiles(dir, files = []) { const entries = fs.readdirSync(dir, { withFileTypes: true }); @@ -440,27 +463,21 @@ function findSourceFiles(dir, files = []) { for (const entry of entries) { const fullPath = path.join(dir, entry.name); + // Skip hidden directories, node_modules, dist, and coverage if (entry.isDirectory()) { - // Skip test directories and node_modules if (!entry.name.startsWith('.') && entry.name !== 'node_modules' && entry.name !== 'dist' && - entry.name !== 'coverage' && - entry.name !== 'tests') { + entry.name !== 'coverage') { findSourceFiles(fullPath, files); } } else if (entry.isFile()) { - // Only include TypeScript and JavaScript files, skip test files and generated files - if ((entry.name.endsWith('.ts') || entry.name.endsWith('.js')) && - !entry.name.endsWith('.spec.ts') && - !entry.name.endsWith('.test.ts') && - !entry.name.endsWith('.tests.ts') && - !entry.name.endsWith('.tests.js') && - !entry.name.endsWith('.umdtests.js') && - !entry.name.endsWith('.test-d.ts') && - !entry.name.endsWith('.gen.ts') && - !entry.name.endsWith('.d.ts')) { - files.push(fullPath); + // Check if file matches include patterns + if (matchesPattern(fullPath, config.include)) { + // Check if file is NOT excluded + if (!matchesPattern(fullPath, config.exclude)) { + files.push(fullPath); + } } } } @@ -473,8 +490,9 @@ function findSourceFiles(dir, files = []) { */ function main() { console.log('🔍 Validating platform isolation (using TypeScript parser)...\n'); + console.log(`📋 Configuration: ${path.relative(WORKSPACE_ROOT, configPath) || '.platform-isolation.config.js'}\n`); - const files = findSourceFiles(LIB_DIR); + const files = findSourceFiles(WORKSPACE_ROOT); // Load valid platforms first const validPlatforms = getValidPlatformsFromSource(); diff --git a/scripts/validate-platform-isolation.js b/scripts/validate-platform-isolation.js deleted file mode 100644 index 551ced47a..000000000 --- a/scripts/validate-platform-isolation.js +++ /dev/null @@ -1,464 +0,0 @@ -#!/usr/bin/env node - -/** - * Platform Isolation Validator - * - * This script ensures that platform-specific entry points only import - * from universal or compatible platform files. - * - * Platform Detection: - * - ALL source files (except tests) MUST export __platforms array - * - Universal files use: export const __platforms = ['__universal__']; - * - Platform-specific files use: export const __platforms = ['browser', 'node']; - * - Legacy naming convention (.browser.ts, etc.) is deprecated - * - * Rules: - * - Platform-specific files can only import from: - * - Universal files (marked with '__universal__') - * - Files supporting the same platforms - * - External packages (node_modules) - * - * Usage: node scripts/validate-platform-isolation.js - */ - -const fs = require('fs'); -const path = require('path'); - -const PLATFORMS = ['browser', 'node', 'react_native']; -const LIB_DIR = path.join(__dirname, '..', 'lib'); - -// Cache for __platforms exports -const platformCache = new Map(); - -// Track files missing __platforms export -const filesWithoutExport = []; - -/** - * Extracts the platform from a filename using naming convention - */ -function getPlatformFromFilename(filename) { - for (const platform of PLATFORMS) { - if (filename.includes(`.${platform}.`)) { - return platform; - } - } - return null; -} - -/** - * Extracts __platforms array from file content - */ -function extractSupportedPlatforms(content) { - // Match: export const __platforms = ['browser', 'react_native']; - // or: export const __platforms: Platform[] = ['browser', 'react_native']; - // or with satisfies: export const __platforms = ['browser'] satisfies Platform[]; - // or universal: export const __platforms = ['__universal__']; - const regex = /export\s+(?:const|let|var)\s+__platforms\s*(?::\s*[^=]+)?\s*=\s*\[([^\]]+)\](?:\s+satisfies\s+[^;]+)?/; - const match = content.match(regex); - - if (!match) { - return null; - } - - // Extract platform names from the array - const platformsStr = match[1]; - - // Check for __universal__ marker - if (platformsStr.includes(`'__universal__'`) || platformsStr.includes(`"__universal__"`)) { - return ['__universal__']; - } - - const platforms = []; - - for (const platform of PLATFORMS) { - if (platformsStr.includes(`'${platform}'`) || platformsStr.includes(`"${platform}"`)) { - platforms.push(platform); - } - } - - return platforms.length > 0 ? platforms : null; -} - -/** - * Check if file content has __platforms export - */ -function hasSupportedPlatformsExport(content) { - return /export\s+(?:const|let|var)\s+__platforms/.test(content); -} - -/** - * Gets the supported platforms for a file (with caching) - * Returns: - * - string[] (platforms from __platforms) - * - 'MISSING' (file is missing __platforms export) - * - * Note: ALL files must have __platforms export - */ -function getSupportedPlatforms(filePath) { - // Check cache first - if (platformCache.has(filePath)) { - return platformCache.get(filePath); - } - - let result; - - try { - const content = fs.readFileSync(filePath, 'utf-8'); - - // Check for __platforms export - const supportedPlatforms = extractSupportedPlatforms(content); - - if (supportedPlatforms) { - result = supportedPlatforms; - platformCache.set(filePath, result); - return result; - } - - // File exists but missing __platforms export - result = 'MISSING'; - platformCache.set(filePath, result); - filesWithoutExport.push(filePath); - return result; - - } catch (error) { - // If file doesn't exist or can't be read, return MISSING - result = 'MISSING'; - platformCache.set(filePath, result); - return result; - } -} - -/** - * Gets a human-readable platform name - */ -function getPlatformName(platform) { - const names = { - 'browser': 'Browser', - 'node': 'Node.js', - 'react_native': 'React Native' - }; - return names[platform] || platform; -} - -/** - * Formats platform info for display - */ -function formatPlatforms(platforms) { - if (platforms === 'MISSING') return 'MISSING __platforms'; - if (!platforms || platforms.length === 0) return 'Unknown'; - if (Array.isArray(platforms) && platforms.length === 1 && platforms[0] === '__universal__') return 'Universal (all platforms)'; - if (typeof platforms === 'string') return getPlatformName(platforms); - return platforms.map(p => getPlatformName(p)).join(' + '); -} - -/** - * Checks if platforms represent universal (all platforms) - */ -function isUniversal(platforms) { - return Array.isArray(platforms) && - platforms.length === 1 && - platforms[0] === '__universal__'; -} - -/** - * Checks if a platform is compatible with target platforms - * - * Rules: - * - If either file is MISSING __platforms, not compatible - * - Universal files (all 3 platforms) are compatible with any file - * - The import must support ALL platforms that the file supports - */ -function isPlatformCompatible(filePlatforms, importPlatforms) { - // If either is missing __platforms, not compatible - if (filePlatforms === 'MISSING' || importPlatforms === 'MISSING') { - return false; - } - - // Universal imports are always compatible - if (isUniversal(importPlatforms)) { - return true; - } - - // Convert to arrays for consistent handling - const fileArray = Array.isArray(filePlatforms) ? filePlatforms : [filePlatforms]; - const importArray = Array.isArray(importPlatforms) ? importPlatforms : [importPlatforms]; - - // The import must support ALL platforms that the file supports - // Check if every platform in fileArray is present in importArray - return fileArray.every(fp => importArray.includes(fp)); -} - -/** - * Extract import statements from a TypeScript/JavaScript file - * Skips commented imports - */ -function extractImports(content) { - const imports = []; - const lines = content.split('\n'); - - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - const trimmed = line.trim(); - - // Skip lines that are comments - if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*')) { - continue; - } - - // Match: import ... from '...' - const importMatch = /import\s+(?:(?:[\w*\s{},]*)\s+from\s+)?['"]([^'"]+)['"]/.exec(line); - if (importMatch) { - imports.push({ type: 'import', path: importMatch[1], line: i + 1 }); - continue; - } - - // Match: require('...') - const requireMatch = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/.exec(line); - if (requireMatch) { - imports.push({ type: 'require', path: requireMatch[1], line: i + 1 }); - continue; - } - - // Match: import('...') - dynamic imports - const dynamicImportMatch = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/.exec(line); - if (dynamicImportMatch) { - imports.push({ type: 'dynamic-import', path: dynamicImportMatch[1], line: i + 1 }); - } - } - - return imports; -} - -/** - * Resolve import path relative to current file - */ -function resolveImportPath(importPath, currentFilePath) { - // External imports (node_modules) - return as-is - if (!importPath.startsWith('.') && !importPath.startsWith('/')) { - return { isExternal: true, resolved: importPath }; - } - - const currentDir = path.dirname(currentFilePath); - let resolved = path.resolve(currentDir, importPath); - - // Check if it's a directory - if so, look for index file - if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) { - const extensions = ['.ts', '.js', '.tsx', '.jsx']; - for (const ext of extensions) { - const indexFile = path.join(resolved, `index${ext}`); - if (fs.existsSync(indexFile)) { - return { isExternal: false, resolved: indexFile }; - } - } - // Directory exists but no index file found - return { isExternal: false, resolved }; - } - - // Check if file exists as-is (with extension already) - if (fs.existsSync(resolved)) { - return { isExternal: false, resolved }; - } - - // Try different extensions - const extensions = ['.ts', '.js', '.tsx', '.jsx']; - for (const ext of extensions) { - const withExt = resolved + ext; - if (fs.existsSync(withExt)) { - return { isExternal: false, resolved: withExt }; - } - } - - // Try index files (for cases where the directory doesn't exist yet) - for (const ext of extensions) { - const indexFile = path.join(resolved, `index${ext}`); - if (fs.existsSync(indexFile)) { - return { isExternal: false, resolved: indexFile }; - } - } - - // Return the resolved path even if it doesn't exist - // (getSupportedPlatforms will handle it) - return { isExternal: false, resolved }; -} - -/** - * Validate a single file - */ -function validateFile(filePath) { - const filePlatforms = getSupportedPlatforms(filePath); - - // If file is missing __platforms, that's a validation error handled separately - if (filePlatforms === 'MISSING') { - return { valid: true, errors: [] }; // Reported separately - } - - const content = fs.readFileSync(filePath, 'utf-8'); - const imports = extractImports(content); - const errors = []; - - for (const importInfo of imports) { - const { isExternal, resolved } = resolveImportPath(importInfo.path, filePath); - - // External imports are always allowed - if (isExternal) { - continue; - } - - const importPlatforms = getSupportedPlatforms(resolved); - - // Check compatibility - if (!isPlatformCompatible(filePlatforms, importPlatforms)) { - const message = importPlatforms === 'MISSING' - ? `Import is missing __platforms export: "${importInfo.path}"` - : `${formatPlatforms(filePlatforms)} file cannot import from ${formatPlatforms(importPlatforms)}-only file: "${importInfo.path}"`; - - errors.push({ - line: importInfo.line, - importPath: importInfo.path, - filePlatforms, - importPlatforms, - message - }); - } - } - - return { valid: errors.length === 0, errors }; -} - -/** - * Recursively find all TypeScript/JavaScript files in a directory - */ -function findSourceFiles(dir, files = []) { - const entries = fs.readdirSync(dir, { withFileTypes: true }); - - for (const entry of entries) { - const fullPath = path.join(dir, entry.name); - - if (entry.isDirectory()) { - // Skip test directories and node_modules - if (!entry.name.startsWith('.') && - entry.name !== 'node_modules' && - entry.name !== 'dist' && - entry.name !== 'coverage' && - entry.name !== 'tests') { - findSourceFiles(fullPath, files); - } - } else if (entry.isFile()) { - // Only include TypeScript and JavaScript files, skip test files - if ((entry.name.endsWith('.ts') || entry.name.endsWith('.js')) && - !entry.name.endsWith('.spec.ts') && - !entry.name.endsWith('.test.ts') && - !entry.name.endsWith('.tests.ts') && - !entry.name.endsWith('.tests.js') && - !entry.name.endsWith('.umdtests.js') && - !entry.name.endsWith('.test-d.ts') && - !entry.name.endsWith('.d.ts')) { - files.push(fullPath); - } - } - } - - return files; -} - -/** - * Main validation function - */ -function main() { - console.log('🔍 Validating platform isolation...\n'); - - const files = findSourceFiles(LIB_DIR); - - // First pass: check for __platforms export - console.log(`Found ${files.length} source files\n`); - console.log('Checking for __platforms exports...\n'); - - files.forEach(f => getSupportedPlatforms(f)); // Populate cache and filesWithoutExport - - // Report files missing __platforms - if (filesWithoutExport.length > 0) { - console.error(`❌ Found ${filesWithoutExport.length} file(s) missing __platforms export:\n`); - - for (const file of filesWithoutExport) { - const relativePath = path.relative(process.cwd(), file); - console.error(` 📄 ${relativePath}`); - } - - console.error('\n'); - console.error('REQUIRED: Every source file must export __platforms array'); - console.error(''); - console.error('Examples:'); - console.error(' // Platform-specific file'); - console.error(' export const __platforms = [\'browser\', \'react_native\'];'); - console.error(''); - console.error(' // Universal file (all platforms)'); - console.error(' export const __platforms = [\'browser\', \'node\', \'react_native\'];'); - console.error(''); - console.error('See lib/platform_support.ts for type definitions.\n'); - - process.exit(1); - } - - console.log('✅ All files have __platforms export\n'); - - // Second pass: validate platform isolation - console.log('Validating platform compatibility...\n'); - - let totalErrors = 0; - const filesWithErrors = []; - - for (const file of files) { - const result = validateFile(file); - - if (!result.valid) { - totalErrors += result.errors.length; - filesWithErrors.push({ file, errors: result.errors }); - } - } - - if (totalErrors === 0) { - console.log('✅ All files are properly isolated!\n'); - process.exit(0); - } else { - console.error(`❌ Found ${totalErrors} platform isolation violation(s) in ${filesWithErrors.length} file(s):\n`); - - for (const { file, errors } of filesWithErrors) { - const relativePath = path.relative(process.cwd(), file); - const filePlatforms = getSupportedPlatforms(file); - console.error(`\n📄 ${relativePath} [${formatPlatforms(filePlatforms)}]`); - - for (const error of errors) { - console.error(` Line ${error.line}: ${error.message}`); - } - } - - console.error('\n'); - console.error('Platform isolation rules:'); - console.error(' - Files can only import from files supporting ALL their platforms'); - console.error(' - Universal files ([browser, node, react_native]) can be imported by any file'); - console.error(' - All files must have __platforms export\n'); - - process.exit(1); - } -} - -// Run the validator -if (require.main === module) { - try { - main(); - } catch (error) { - console.error('❌ Validation failed with error:', error.message); - console.error(error.stack); - process.exit(1); - } -} - -module.exports = { - validateFile, - getSupportedPlatforms, - extractImports, - extractSupportedPlatforms, - isPlatformCompatible, - isUniversal, - hasSupportedPlatformsExport -}; From 2a3342f3c94411261cde46d3fac58828e14b98e2 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 20:27:01 +0600 Subject: [PATCH 16/40] fix failed validations --- .../event_dispatcher/default_dispatcher.browser.ts | 2 +- lib/utils/http_request_handler/request_handler.browser.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts index d55b4a7a4..3b4200ce1 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts @@ -25,4 +25,4 @@ const eventDispatcher: EventDispatcher = new DefaultEventDispatcher(new BrowserR export default eventDispatcher; -export const __platforms: Platform[] = ['browser']; +export const __platforms: Platform[] = ['browser', 'react_native']; diff --git a/lib/utils/http_request_handler/request_handler.browser.ts b/lib/utils/http_request_handler/request_handler.browser.ts index 492cbd897..4cff94858 100644 --- a/lib/utils/http_request_handler/request_handler.browser.ts +++ b/lib/utils/http_request_handler/request_handler.browser.ts @@ -134,4 +134,4 @@ export class BrowserRequestHandler implements RequestHandler { } } -export const __platforms: Platform[] = ['browser']; +export const __platforms: Platform[] = ['browser', 'react_native']; From 37b368eb7059cea611f6fa31209b71ca8e9bc4d1 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 20:58:47 +0600 Subject: [PATCH 17/40] update file matching --- scripts/add-platform-exports.js | 13 ++++++++----- scripts/validate-platform-isolation-ts.js | 13 ++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/scripts/add-platform-exports.js b/scripts/add-platform-exports.js index 8aee8a3ae..6304c8fac 100644 --- a/scripts/add-platform-exports.js +++ b/scripts/add-platform-exports.js @@ -300,12 +300,15 @@ function findSourceFiles(dir, files = []) { for (const entry of entries) { const fullPath = path.join(dir, entry.name); - // Skip hidden directories, node_modules, dist, and coverage if (entry.isDirectory()) { - if (!entry.name.startsWith('.') && - entry.name !== 'node_modules' && - entry.name !== 'dist' && - entry.name !== 'coverage') { + // Check if this directory path could potentially contain files matching include patterns + // Use minimatch with partial mode to test if pattern could match files under this directory + const relativePath = path.relative(WORKSPACE_ROOT, fullPath).replace(/\\/g, '/'); + const couldMatch = config.include.some(pattern => { + return minimatch(relativePath, pattern, { partial: true }); + }); + + if (couldMatch) { findSourceFiles(fullPath, files); } } else if (entry.isFile()) { diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index 5366ca8f9..4fabe02e4 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -463,12 +463,15 @@ function findSourceFiles(dir, files = []) { for (const entry of entries) { const fullPath = path.join(dir, entry.name); - // Skip hidden directories, node_modules, dist, and coverage if (entry.isDirectory()) { - if (!entry.name.startsWith('.') && - entry.name !== 'node_modules' && - entry.name !== 'dist' && - entry.name !== 'coverage') { + // Check if this directory path could potentially contain files matching include patterns + // Use minimatch with partial mode to test if pattern could match files under this directory + const relativePath = path.relative(WORKSPACE_ROOT, fullPath).replace(/\\/g, '/'); + const couldMatch = config.include.some(pattern => { + return minimatch(relativePath, pattern, { partial: true }); + }); + + if (couldMatch) { findSourceFiles(fullPath, files); } } else if (entry.isFile()) { From 2faebf646553d16b6da56a4e2b5ae69346c4cf10 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 21:03:03 +0600 Subject: [PATCH 18/40] update --- scripts/add-platform-exports.js | 2 +- scripts/validate-platform-isolation-ts.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/add-platform-exports.js b/scripts/add-platform-exports.js index 6304c8fac..347e7c262 100644 --- a/scripts/add-platform-exports.js +++ b/scripts/add-platform-exports.js @@ -52,7 +52,7 @@ function getPlatformFromFilename(filename) { function matchesPattern(filePath, patterns) { const relativePath = path.relative(WORKSPACE_ROOT, filePath).replace(/\\/g, '/'); - return patterns.some(pattern => minimatch(relativePath, pattern, { dot: true })); + return patterns.some(pattern => minimatch(relativePath, pattern)); } /** diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index 4fabe02e4..9019c5f76 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -451,7 +451,7 @@ function validateFile(filePath) { function matchesPattern(filePath, patterns) { const relativePath = path.relative(WORKSPACE_ROOT, filePath).replace(/\\/g, '/'); - return patterns.some(pattern => minimatch(relativePath, pattern, { dot: true })); + return patterns.some(pattern => minimatch(relativePath, pattern)); } /** From cfa67a0e36d89b593c3d99c5e8f4b5d8da2e5a5e Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 23:44:30 +0600 Subject: [PATCH 19/40] update --- lib/platform_support.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/platform_support.ts b/lib/platform_support.ts index 43ad57ba8..82b6b7678 100644 --- a/lib/platform_support.ts +++ b/lib/platform_support.ts @@ -8,8 +8,6 @@ /** * Valid platform identifiers */ -import type { Platform } from './platform_support'; - export type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; From c772ab9cfe0f55c2ea07d2840dc85ac1d52bfaa3 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 25 Nov 2025 23:51:37 +0600 Subject: [PATCH 20/40] update platform_support file --- .eslintrc.js | 1 + .platform-isolation.config.js | 3 ++ lib/platform_support.ts | 60 ++++++----------------------------- 3 files changed, 13 insertions(+), 51 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 950c67337..4114a7859 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -31,6 +31,7 @@ module.exports = { { 'files': ['lib/**/*.ts', 'src/**/*.ts'], 'excludedFiles': [ + '**/platform_support.ts', '**/*.spec.ts', '**/*.test.ts', '**/*.tests.ts', diff --git a/.platform-isolation.config.js b/.platform-isolation.config.js index 413130cd3..e7a1de0a4 100644 --- a/.platform-isolation.config.js +++ b/.platform-isolation.config.js @@ -13,6 +13,9 @@ module.exports = { // Files and patterns to exclude from validation exclude: [ + // Platform definition file (this file defines Platform type, doesn't need __platforms) + '**/platform_support.ts', + // Test files '**/*.spec.ts', '**/*.test.ts', diff --git a/lib/platform_support.ts b/lib/platform_support.ts index 82b6b7678..e8e5834a4 100644 --- a/lib/platform_support.ts +++ b/lib/platform_support.ts @@ -1,59 +1,17 @@ -/** - * Platform Support Type Definitions - * - * Every source file (except tests) must export __platforms to declare - * which platforms it supports. This is enforced at both type-level and runtime. - */ - -/** - * Valid platform identifiers - */ -export type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; - - -/** - * Platform support declaration - * - * Every file must export this to declare which platforms it supports: - * - Specific platforms: export const __platforms = ['browser', 'node']; - * - All platforms (universal): export const __platforms = ['__universal__']; - * - * @example - * // Browser and React Native only - * export const __platforms = ['browser', 'react_native'] satisfies Platform[]; - * - * @example - * // Node.js only - * export const __platforms = ['node'] satisfies Platform[]; - * - * @example - * // Universal (all platforms) - * export const __platforms = ['__universal__'] satisfies Platform[]; - */ -export type SupportedPlatforms = readonly Platform[]; /** - * Helper type to enforce that a module exports __platforms - * - * Usage in your module: - * ```typescript - * import type { RequirePlatformDeclaration, Platform } from './platform_support'; + * ⚠️ WARNING: DO NOT MOVE, DELETE, OR RENAME THIS FILE * - * export const __platforms = ['browser'] satisfies Platform[]; + * This file is used by the build system and validation scripts: + * - scripts/validate-platform-isolation-ts.js + * - scripts/platform-utils.js + * - eslint-local-rules/require-platform-declaration.js * - * // This type check ensures __platforms is exported - * // type _Check = RequirePlatformDeclaration; - * ``` + * These tools parse this file at build time to extract the Platform type definition. + * Moving or renaming this file will break the build. */ -export type RequirePlatformDeclaration = T extends { __platforms: readonly Platform[] } - ? T - : never & { error: '__platforms export is required' }; /** - * Utility to check if a file is universal (supports all platforms) + * Valid platform identifiers */ -export function isUniversal(platforms: readonly Platform[]): boolean { - return platforms.length === 1 && platforms[0] === '__universal__'; -} - -export const __platforms: Platform[] = ['__universal__']; +export type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; From fde779cec274de2d553ec34ecadd71d3c8c71715 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Wed, 26 Nov 2025 01:36:06 +0600 Subject: [PATCH 21/40] updates --- .eslintrc.js | 2 + .platform-isolation.config.js | 3 + lib/common_exports.ts | 2 - lib/export_types.ts | 2 - scripts/platform-utils.js | 140 ++++++++--- scripts/validate-platform-isolation-ts.js | 280 +++++++++------------- 6 files changed, 226 insertions(+), 203 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 4114a7859..93c696808 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,6 +32,8 @@ module.exports = { 'files': ['lib/**/*.ts', 'src/**/*.ts'], 'excludedFiles': [ '**/platform_support.ts', + '**/common_exports.ts', + '**/export_types.ts', '**/*.spec.ts', '**/*.test.ts', '**/*.tests.ts', diff --git a/.platform-isolation.config.js b/.platform-isolation.config.js index e7a1de0a4..7cca39db5 100644 --- a/.platform-isolation.config.js +++ b/.platform-isolation.config.js @@ -15,6 +15,9 @@ module.exports = { exclude: [ // Platform definition file (this file defines Platform type, doesn't need __platforms) '**/platform_support.ts', + + '**/common_exports.ts', + '**/export_types.ts', // Test files '**/*.spec.ts', diff --git a/lib/common_exports.ts b/lib/common_exports.ts index b3cbd940c..c269df653 100644 --- a/lib/common_exports.ts +++ b/lib/common_exports.ts @@ -38,5 +38,3 @@ export { export { NOTIFICATION_TYPES, DECISION_NOTIFICATION_TYPES } from './notification_center/type'; export { OptimizelyDecideOption } from './shared_types'; - -export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/export_types.ts b/lib/export_types.ts index 86aba4aca..80c949c2f 100644 --- a/lib/export_types.ts +++ b/lib/export_types.ts @@ -106,5 +106,3 @@ export type { NotificationCenter, OptimizelySegmentOption, } from './shared_types'; - -export const __platforms: Platform[] = ['__universal__']; diff --git a/scripts/platform-utils.js b/scripts/platform-utils.js index 7ce90b39a..a8d12ed67 100644 --- a/scripts/platform-utils.js +++ b/scripts/platform-utils.js @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable no-inner-declarations */ /** * Platform Utilities * @@ -55,6 +57,9 @@ function getValidPlatforms(workspaceRoot) { platforms.push(type.literal.text); } } + } else if (ts.isLiteralTypeNode(node.type) && ts.isStringLiteral(node.type.literal)) { + // Handle single literal type: type Platform = 'browser'; + platforms.push(node.type.literal.text); } } @@ -77,21 +82,27 @@ function getValidPlatforms(workspaceRoot) { } /** - * Extracts __platforms array from TypeScript AST + * Extracts __platforms array from TypeScript AST with detailed error reporting * - * Returns: - * - string[] if valid platforms array found - * - 'NOT_CONST' if __platforms is not declared as const - * - 'NOT_LITERALS' if array contains non-literal values - * - null if __platforms export not found + * Returns an object with: + * - success: boolean - whether extraction was successful + * - platforms: string[] - array of platform values (if successful) + * - error: object - detailed error information (if unsuccessful) + * - type: 'MISSING' | 'NOT_CONST' | 'NOT_ARRAY' | 'EMPTY_ARRAY' | 'NOT_LITERALS' | 'INVALID_VALUES' + * - message: string - human-readable error message + * - invalidValues: string[] - list of invalid platform values (for INVALID_VALUES type) * * @param {ts.SourceFile} sourceFile - TypeScript source file AST - * @returns {string[] | 'NOT_CONST' | 'NOT_LITERALS' | null} + * @param {string} filePath - File path for context in error messages + * @param {string[]} validPlatforms - Array of valid platform values + * @returns {Object} */ -function extractPlatformsFromAST(sourceFile) { - let platforms = null; +function extractPlatformsFromAST(sourceFile, filePath, validPlatforms) { + let found = false; + let isConst = false; + let isArray = false; + let platforms = []; let hasNonStringLiteral = false; - let isNotConst = false; function visit(node) { // Look for: export const __platforms = [...] @@ -102,17 +113,15 @@ function extractPlatformsFromAST(sourceFile) { ); if (hasExport) { - // Check if declaration is const - const isConst = (node.declarationList.flags & ts.NodeFlags.Const) !== 0; + const isConstDecl = (node.declarationList.flags & ts.NodeFlags.Const) !== 0; for (const declaration of node.declarationList.declarations) { if (ts.isVariableDeclaration(declaration) && ts.isIdentifier(declaration.name) && declaration.name.text === '__platforms') { - if (!isConst) { - isNotConst = true; - } + found = true; + isConst = isConstDecl; let initializer = declaration.initializer; @@ -126,9 +135,11 @@ function extractPlatformsFromAST(sourceFile) { initializer = initializer.expression; } - // Extract array elements + // Check if it's an array if (initializer && ts.isArrayLiteralExpression(initializer)) { - platforms = []; + isArray = true; + + // Extract array elements for (const element of initializer.elements) { if (ts.isStringLiteral(element)) { platforms.push(element.text); @@ -137,8 +148,9 @@ function extractPlatformsFromAST(sourceFile) { hasNonStringLiteral = true; } } - return; // Found it, stop visiting } + + return; // Found it, stop visiting } } } @@ -149,26 +161,88 @@ function extractPlatformsFromAST(sourceFile) { visit(sourceFile); - if (platforms !== null) { - if (isNotConst) { - return 'NOT_CONST'; - } - if (hasNonStringLiteral) { - return 'NOT_LITERALS'; + // Detailed error reporting + if (!found) { + return { + success: false, + error: { + type: 'MISSING', + message: `File does not export '__platforms' constant` + } + }; + } + + if (!isConst) { + return { + success: false, + error: { + type: 'NOT_CONST', + message: `'__platforms' must be declared with 'const', found non-const declaration` + } + }; + } + + if (!isArray) { + return { + success: false, + error: { + type: 'NOT_ARRAY', + message: `'__platforms' must be an array literal, found ${platforms.length === 0 ? 'non-array value' : 'other type'}` + } + }; + } + + if (hasNonStringLiteral) { + return { + success: false, + error: { + type: 'NOT_LITERALS', + message: `'__platforms' must only contain string literals, found non-literal values` + } + }; + } + + if (platforms.length === 0) { + return { + success: false, + error: { + type: 'EMPTY_ARRAY', + message: `'__platforms' array is empty, must contain at least one platform` + } + }; + } + + // Validate platform values if validPlatforms provided + if (validPlatforms) { + const invalidPlatforms = platforms.filter(p => !validPlatforms.includes(p)); + if (invalidPlatforms.length > 0) { + return { + success: false, + error: { + type: 'INVALID_VALUES', + message: `Invalid platform values found`, + invalidValues: invalidPlatforms + } + }; } } - return platforms; + return { + success: true, + platforms: platforms + }; } /** - * Extract platforms from a file path + * Extract platforms from a file path with detailed error reporting * * @param {string} filePath - Absolute path to the file - * @returns {string[] | 'NOT_CONST' | 'NOT_LITERALS' | null} + * @param {string} workspaceRoot - Workspace root for resolving valid platforms + * @returns {Object} Result object with success, platforms, and error information */ -function extractPlatformsFromFile(filePath) { +function extractPlatformsFromFile(filePath, workspaceRoot) { try { + const validPlatforms = workspaceRoot ? getValidPlatforms(workspaceRoot) : null; const content = fs.readFileSync(filePath, 'utf-8'); const sourceFile = ts.createSourceFile( filePath, @@ -176,9 +250,15 @@ function extractPlatformsFromFile(filePath) { ts.ScriptTarget.Latest, true ); - return extractPlatformsFromAST(sourceFile); + return extractPlatformsFromAST(sourceFile, filePath, validPlatforms); } catch (error) { - return null; + return { + success: false, + error: { + type: 'READ_ERROR', + message: `Failed to read or parse file: ${error.message}` + } + }; } } diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index 9019c5f76..2b271fe8e 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -26,7 +26,7 @@ const fs = require('fs'); const path = require('path'); const ts = require('typescript'); const { minimatch } = require('minimatch'); -const { getValidPlatforms, extractPlatformsFromAST } = require('./platform-utils'); +const { getValidPlatforms, extractPlatformsFromFile } = require('./platform-utils'); const WORKSPACE_ROOT = path.join(__dirname, '..'); @@ -80,51 +80,13 @@ function getPlatformFromFilename(filename) { } // Track files missing __platforms export -const filesWithoutExport = []; - -// Track files with invalid platform values -const filesWithInvalidPlatforms = []; - -// Track files with non-const declaration -const filesWithNonConst = []; - -// Track files with non-literal values -const filesWithNonLiterals = []; - -/** - * Validates that platform values are valid according to Platform type - */ -function validatePlatformValues(platforms, filePath) { - if (!platforms || platforms.length === 0) { - return { valid: false, invalidValues: [] }; - } - - const validPlatforms = getValidPlatformsFromSource(); - const invalidValues = []; - - for (const platform of platforms) { - if (!validPlatforms.includes(platform)) { - invalidValues.push(platform); - } - } - - if (invalidValues.length > 0) { - filesWithInvalidPlatforms.push({ - filePath, - platforms, - invalidValues - }); - return { valid: false, invalidValues }; - } - - return { valid: true, invalidValues: [] }; -} +const fileErrors = new Map(); /** * Gets the supported platforms for a file (with caching) * Returns: * - string[] (platforms from __platforms) - * - 'MISSING' (file is missing __platforms export) + * - null (file has errors) * * Note: ALL files must have __platforms export */ @@ -134,62 +96,27 @@ function getSupportedPlatforms(filePath) { return platformCache.get(filePath); } - let result; - try { - const content = fs.readFileSync(filePath, 'utf-8'); - - // Parse with TypeScript - const sourceFile = ts.createSourceFile( - filePath, - content, - ts.ScriptTarget.Latest, - true - ); + // Extract platforms from file with detailed error reporting + const result = extractPlatformsFromFile(filePath, WORKSPACE_ROOT); - // Extract platforms from AST - const supportedPlatforms = extractPlatformsFromAST(sourceFile); - - if (supportedPlatforms === 'NOT_CONST') { - filesWithNonConst.push(filePath); - result = 'NOT_CONST'; - platformCache.set(filePath, result); - return result; - } - - if (supportedPlatforms === 'NOT_LITERALS') { - filesWithNonLiterals.push(filePath); - result = 'NOT_LITERALS'; - platformCache.set(filePath, result); - return result; + if (result.success) { + platformCache.set(filePath, result.platforms); + return result.platforms; + } else { + // Store error for this file + fileErrors.set(filePath, result.error); + platformCache.set(filePath, null); + return null; } - - if (supportedPlatforms && supportedPlatforms.length > 0) { - // Validate platform values - const validation = validatePlatformValues(supportedPlatforms, filePath); - if (!validation.valid) { - // Still cache it but it will be reported as error - result = supportedPlatforms; - platformCache.set(filePath, result); - return result; - } - - result = supportedPlatforms; - platformCache.set(filePath, result); - return result; - } - - // File exists but missing __platforms export - result = 'MISSING'; - platformCache.set(filePath, result); - filesWithoutExport.push(filePath); - return result; - } catch (error) { - // If file doesn't exist or can't be read, return MISSING - result = 'MISSING'; - platformCache.set(filePath, result); - return result; + // Store read error + fileErrors.set(filePath, { + type: 'READ_ERROR', + message: `Failed to read file: ${error.message}` + }); + platformCache.set(filePath, null); + return null; } } @@ -241,16 +168,14 @@ function isUniversal(platforms) { * Checks if a platform is compatible with target platforms * * Rules: - * - If either file is MISSING __platforms, not compatible + * - If either file has errors, not compatible * - Import must support ALL platforms that the importing file runs on * - Universal imports can be used by any file (they support all platforms) * - Platform-specific files can only import from universal or files supporting all their platforms */ function isPlatformCompatible(filePlatforms, importPlatforms) { - // If either has any error state, not compatible - if (filePlatforms === 'MISSING' || importPlatforms === 'MISSING' || - filePlatforms === 'NOT_CONST' || importPlatforms === 'NOT_CONST' || - filePlatforms === 'NOT_LITERALS' || importPlatforms === 'NOT_LITERALS') { + // If either has errors, not compatible + if (!filePlatforms || !importPlatforms) { return false; } @@ -408,8 +333,8 @@ function resolveImportPath(importPath, currentFilePath) { function validateFile(filePath) { const filePlatforms = getSupportedPlatforms(filePath); - // If file is missing __platforms, that's a validation error handled separately - if (filePlatforms === 'MISSING') { + // If file has errors, that's handled separately + if (!filePlatforms) { return { valid: true, errors: [] }; // Reported separately } @@ -424,12 +349,18 @@ function validateFile(filePath) { continue; } + // Skip excluded files (e.g., platform_support.ts) + if (matchesPattern(resolved, config.exclude)) { + continue; + } + const importPlatforms = getSupportedPlatforms(resolved); // Check compatibility if (!isPlatformCompatible(filePlatforms, importPlatforms)) { - const message = importPlatforms === 'MISSING' - ? `Import is missing __platforms export: "${importInfo.path}"` + const importError = fileErrors.get(resolved); + const message = importError + ? `Import has __platforms error: "${importInfo.path}" - ${importError.message}` : `${formatPlatforms(filePlatforms)} file cannot import from ${formatPlatforms(importPlatforms)}-only file: "${importInfo.path}"`; errors.push({ @@ -505,88 +436,100 @@ function main() { console.log(`Found ${files.length} source files\n`); console.log('Checking for __platforms exports...\n'); - files.forEach(f => getSupportedPlatforms(f)); // Populate cache and filesWithoutExport + files.forEach(f => getSupportedPlatforms(f)); // Populate cache and fileErrors + + // Group errors by type + const errorsByType = { + MISSING: [], + NOT_CONST: [], + NOT_ARRAY: [], + EMPTY_ARRAY: [], + NOT_LITERALS: [], + INVALID_VALUES: [], + READ_ERROR: [] + }; - // Report files missing __platforms - if (filesWithoutExport.length > 0) { - console.error(`❌ Found ${filesWithoutExport.length} file(s) missing __platforms export:\n`); - - for (const file of filesWithoutExport) { - const relativePath = path.relative(process.cwd(), file); - console.error(` 📄 ${relativePath}`); + for (const [filePath, error] of fileErrors) { + if (errorsByType[error.type]) { + errorsByType[error.type].push({ filePath, error }); } - - console.error('\n'); - console.error('REQUIRED: Every source file must export __platforms array'); - console.error(''); - console.error('Examples:'); - console.error(' // Platform-specific file'); - console.error(' export const __platforms = [\'browser\', \'react_native\'];'); - console.error(''); - console.error(' // Universal file (all platforms)'); - console.error(' export const __platforms = [\'__universal__\'];'); - console.error(''); - console.error('See lib/platform_support.ts for type definitions.\n'); - - process.exit(1); } - console.log('✅ All files have __platforms export\n'); + // Report errors by type + let hasErrors = false; - // Report files with non-const declaration - if (filesWithNonConst.length > 0) { - console.error(`❌ Found ${filesWithNonConst.length} file(s) with __platforms not declared as const:\n`); - - for (const file of filesWithNonConst) { - const relativePath = path.relative(process.cwd(), file); - console.error(` 📄 ${relativePath}`); + if (errorsByType.MISSING.length > 0) { + hasErrors = true; + console.error(`❌ Found ${errorsByType.MISSING.length} file(s) missing __platforms export:\n`); + for (const { filePath, error } of errorsByType.MISSING) { + console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); } - - console.error('\n'); - console.error('REQUIRED: __platforms must be declared as const'); - console.error('Use: export const __platforms: Platform[] = [...]\n'); - - process.exit(1); + console.error(`\n${errorsByType.MISSING[0].error.message}\n`); } - // Report files with non-literal values - if (filesWithNonLiterals.length > 0) { - console.error(`❌ Found ${filesWithNonLiterals.length} file(s) with __platforms containing non-literal values:\n`); - - for (const file of filesWithNonLiterals) { - const relativePath = path.relative(process.cwd(), file); - console.error(` 📄 ${relativePath}`); + if (errorsByType.NOT_CONST.length > 0) { + hasErrors = true; + console.error(`❌ Found ${errorsByType.NOT_CONST.length} file(s) with __platforms not declared as const:\n`); + for (const { filePath, error } of errorsByType.NOT_CONST) { + console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); } - - console.error('\n'); - console.error('REQUIRED: __platforms array must contain only string literals'); - console.error('Use: export const __platforms: Platform[] = [\'browser\', \'node\']'); - console.error('Do NOT use variables, computed values, or spread operators\n'); - - process.exit(1); + console.error(`\n${errorsByType.NOT_CONST[0].error.message}\n`); + } + + if (errorsByType.NOT_ARRAY.length > 0) { + hasErrors = true; + console.error(`❌ Found ${errorsByType.NOT_ARRAY.length} file(s) with __platforms not declared as an array:\n`); + for (const { filePath, error } of errorsByType.NOT_ARRAY) { + console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); + } + console.error(`\n${errorsByType.NOT_ARRAY[0].error.message}\n`); } - console.log('✅ All __platforms declarations are const with string literals\n'); + if (errorsByType.EMPTY_ARRAY.length > 0) { + hasErrors = true; + console.error(`❌ Found ${errorsByType.EMPTY_ARRAY.length} file(s) with empty __platforms array:\n`); + for (const { filePath, error } of errorsByType.EMPTY_ARRAY) { + console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); + } + console.error(`\n${errorsByType.EMPTY_ARRAY[0].error.message}\n`); + } - // Report files with invalid platform values - if (filesWithInvalidPlatforms.length > 0) { - console.error(`❌ Found ${filesWithInvalidPlatforms.length} file(s) with invalid platform values:\n`); - - for (const { filePath, platforms, invalidValues } of filesWithInvalidPlatforms) { - const relativePath = path.relative(process.cwd(), filePath); - console.error(` 📄 ${relativePath}`); - console.error(` Declared: [${platforms.map(p => `'${p}'`).join(', ')}]`); - console.error(` Invalid values: [${invalidValues.map(p => `'${p}'`).join(', ')}]`); + if (errorsByType.NOT_LITERALS.length > 0) { + hasErrors = true; + console.error(`❌ Found ${errorsByType.NOT_LITERALS.length} file(s) with __platforms containing non-literal values:\n`); + for (const { filePath, error} of errorsByType.NOT_LITERALS) { + console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); } - - console.error('\n'); - console.error(`Valid platform values: ${validPlatforms.map(p => `'${p}'`).join(', ')}`); - console.error('See lib/platform_support.ts for Platform type definition.\n'); - + console.error(`\n${errorsByType.NOT_LITERALS[0].error.message}\n`); + } + + if (errorsByType.INVALID_VALUES.length > 0) { + hasErrors = true; + console.error(`❌ Found ${errorsByType.INVALID_VALUES.length} file(s) with invalid platform values:\n`); + for (const { filePath, error } of errorsByType.INVALID_VALUES) { + const invalidValuesStr = error.invalidValues ? error.invalidValues.map(v => `'${v}'`).join(', ') : ''; + console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); + console.error(` Invalid values: ${invalidValuesStr}`); + console.error(` Valid platforms: ${validPlatforms.join(', ')}`); + } + console.error(''); + } + + if (errorsByType.READ_ERROR.length > 0) { + hasErrors = true; + console.error(`❌ Found ${errorsByType.READ_ERROR.length} file(s) with read errors:\n`); + for (const { filePath, error } of errorsByType.READ_ERROR) { + console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); + console.error(` ${error.message}`); + } + console.error(''); + } + + if (hasErrors) { process.exit(1); } - console.log('✅ All __platforms arrays have valid values\n'); + console.log('✅ All files have valid __platforms exports\n'); // Second pass: validate platform isolation console.log('Validating platform compatibility...\n'); @@ -633,7 +576,6 @@ function main() { if (typeof module !== 'undefined' && module.exports) { module.exports = { isPlatformCompatible, - extractPlatformsFromAST, getSupportedPlatforms, extractImports, }; From bdb7f255735196adea3919048c627753b50971a7 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Wed, 26 Nov 2025 19:02:55 +0600 Subject: [PATCH 22/40] fix add platform script --- .platform-isolation.config.js | 4 +- lib/common_exports.ts | 2 + .../default_dispatcher.browser.ts | 1 + lib/export_types.ts | 2 + .../request_handler.browser.ts | 2 +- scripts/add-platform-exports.js | 164 +++++++++++++----- 6 files changed, 126 insertions(+), 49 deletions(-) diff --git a/.platform-isolation.config.js b/.platform-isolation.config.js index 7cca39db5..63c9255e6 100644 --- a/.platform-isolation.config.js +++ b/.platform-isolation.config.js @@ -16,8 +16,8 @@ module.exports = { // Platform definition file (this file defines Platform type, doesn't need __platforms) '**/platform_support.ts', - '**/common_exports.ts', - '**/export_types.ts', + // '**/common_exports.ts', + // '**/export_types.ts', // Test files '**/*.spec.ts', diff --git a/lib/common_exports.ts b/lib/common_exports.ts index c269df653..b3cbd940c 100644 --- a/lib/common_exports.ts +++ b/lib/common_exports.ts @@ -38,3 +38,5 @@ export { export { NOTIFICATION_TYPES, DECISION_NOTIFICATION_TYPES } from './notification_center/type'; export { OptimizelyDecideOption } from './shared_types'; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts index 3b4200ce1..e0e5f8b04 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts @@ -21,6 +21,7 @@ import { BrowserRequestHandler } from "../../utils/http_request_handler/request_ import { EventDispatcher } from './event_dispatcher'; import { DefaultEventDispatcher } from './default_dispatcher'; + const eventDispatcher: EventDispatcher = new DefaultEventDispatcher(new BrowserRequestHandler()); export default eventDispatcher; diff --git a/lib/export_types.ts b/lib/export_types.ts index 80c949c2f..86aba4aca 100644 --- a/lib/export_types.ts +++ b/lib/export_types.ts @@ -106,3 +106,5 @@ export type { NotificationCenter, OptimizelySegmentOption, } from './shared_types'; + +export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/utils/http_request_handler/request_handler.browser.ts b/lib/utils/http_request_handler/request_handler.browser.ts index 4cff94858..492cbd897 100644 --- a/lib/utils/http_request_handler/request_handler.browser.ts +++ b/lib/utils/http_request_handler/request_handler.browser.ts @@ -134,4 +134,4 @@ export class BrowserRequestHandler implements RequestHandler { } } -export const __platforms: Platform[] = ['browser', 'react_native']; +export const __platforms: Platform[] = ['browser']; diff --git a/scripts/add-platform-exports.js b/scripts/add-platform-exports.js index 347e7c262..0883150a2 100644 --- a/scripts/add-platform-exports.js +++ b/scripts/add-platform-exports.js @@ -17,7 +17,7 @@ const fs = require('fs'); const path = require('path'); const ts = require('typescript'); const { minimatch } = require('minimatch'); -const { extractPlatformsFromAST } = require('./platform-utils'); +const { extractPlatformsFromAST, getValidPlatforms } = require('./platform-utils'); const WORKSPACE_ROOT = path.join(__dirname, '..'); const PLATFORMS = ['browser', 'node', 'react_native']; @@ -162,9 +162,57 @@ function ensurePlatformImport(content, filePath) { } /** - * Remove existing __platforms export from the content + * Check if __platforms export is at the end of the file */ -function removeExistingPlatformExport(content, filePath) { +function isPlatformExportAtEnd(content, filePath) { + const sourceFile = ts.createSourceFile( + filePath, + content, + ts.ScriptTarget.Latest, + true + ); + + let platformExportEnd = -1; + let lastStatementEnd = -1; + + function visit(node) { + // Track the last statement + if (ts.isStatement(node) && node.parent === sourceFile) { + lastStatementEnd = Math.max(lastStatementEnd, node.end); + } + + // Find __platforms export + if (ts.isVariableStatement(node)) { + const hasExport = node.modifiers?.some( + mod => mod.kind === ts.SyntaxKind.ExportKeyword + ); + + if (hasExport) { + for (const declaration of node.declarationList.declarations) { + if (ts.isVariableDeclaration(declaration) && + ts.isIdentifier(declaration.name) && + declaration.name.text === '__platforms') { + platformExportEnd = node.end; + } + } + } + } + } + + ts.forEachChild(sourceFile, visit); + + if (platformExportEnd === -1) { + return false; // No export found + } + + // Check if __platforms is the last statement (allowing for trailing whitespace/newlines) + return platformExportEnd === lastStatementEnd; +} + +/** + * Extract the existing __platforms export statement as-is + */ +function extractExistingPlatformExport(content, filePath) { const sourceFile = ts.createSourceFile( filePath, content, @@ -173,6 +221,7 @@ function removeExistingPlatformExport(content, filePath) { ); const lines = content.split('\n'); + let exportStatement = null; const linesToRemove = new Set(); function visit(node) { @@ -186,13 +235,16 @@ function removeExistingPlatformExport(content, filePath) { if (ts.isVariableDeclaration(declaration) && ts.isIdentifier(declaration.name) && declaration.name.text === '__platforms') { - // Mark this line for removal + // Extract the full statement const startLine = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line; const endLine = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line; + const statementLines = []; for (let i = startLine; i <= endLine; i++) { linesToRemove.add(i); + statementLines.push(lines[i]); } + exportStatement = statementLines.join('\n'); } } } @@ -201,23 +253,29 @@ function removeExistingPlatformExport(content, filePath) { ts.forEachChild(sourceFile, visit); - if (linesToRemove.size === 0) { - return { content, removed: false }; + if (!exportStatement) { + return { content, statement: null, removed: false }; } const filteredLines = lines.filter((_, index) => !linesToRemove.has(index)); - return { content: filteredLines.join('\n'), removed: true }; + return { content: filteredLines.join('\n'), statement: exportStatement, removed: true }; } /** * Add __platforms export at the end of the file */ +function addPlatformExportStatement(content, statement) { + // Trim trailing whitespace and add the statement (with blank line before) + return content.trimEnd() + '\n\n' + statement + '\n'; +} + +/** + * Add __platforms export at the end of the file (when creating new) + */ function addPlatformExport(content, platforms) { const platformsStr = platforms.map(p => `'${p}'`).join(', '); - const exportStatement = `\n\nexport const __platforms: Platform[] = [${platformsStr}];\n`; - - // Trim trailing whitespace and ensure we end with the export (with blank line before) - return content.trimEnd() + exportStatement; + const exportStatement = `export const __platforms: Platform[] = [${platformsStr}];`; + return addPlatformExportStatement(content, exportStatement); } /** @@ -226,66 +284,80 @@ function addPlatformExport(content, platforms) { function processFile(filePath) { let content = fs.readFileSync(filePath, 'utf-8'); + // Get valid platforms for validation + const validPlatforms = getValidPlatforms(WORKSPACE_ROOT); + // Use TypeScript parser to check for existing __platforms - const existingPlatforms = extractPlatformsFromAST( - ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true) + const result = extractPlatformsFromAST( + ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true), + filePath, + validPlatforms // Validate platform values ); + // Extract platforms and error info from result + const existingPlatforms = result.success ? result.platforms : null; + const needsFixing = result.error && ['MISSING', 'NOT_CONST', 'NOT_ARRAY', 'EMPTY_ARRAY', 'NOT_LITERALS', 'INVALID_VALUES'].includes(result.error.type); + // Determine platforms for this file - // If file already has platforms, use those (preserve existing values) + // If file already has valid platforms, use those (preserve existing values) // Otherwise, determine from filename or default to universal let platforms; - if (existingPlatforms === null) { - // No __platforms export, determine from filename + if (!existingPlatforms || needsFixing) { + // No __platforms export or has errors, determine from filename const platformsFromFilename = getPlatformFromFilename(filePath); platforms = platformsFromFilename || ['__universal__']; - } else if (Array.isArray(existingPlatforms)) { + } else { // Has valid __platforms, preserve the existing values platforms = existingPlatforms; - } else { - // Has issues (NOT_CONST, NOT_LITERALS), determine from filename - const platformsFromFilename = getPlatformFromFilename(filePath); - platforms = platformsFromFilename || ['__universal__']; } let modified = false; let action = 'skipped'; - if (existingPlatforms === null) { - // No __platforms export, add it - action = 'added'; - modified = true; - } else if (Array.isArray(existingPlatforms)) { - // Has __platforms but might need to be moved or updated - // Remove existing and re-add at the end - const removed = removeExistingPlatformExport(content, filePath); - if (removed.removed) { - content = removed.content; - action = 'moved'; - modified = true; - } else { - return { skipped: true, reason: 'already has export at end' }; + if (needsFixing) { + // Has issues (MISSING, NOT_CONST, NOT_LITERALS, INVALID_VALUES, etc.), fix them + const extracted = extractExistingPlatformExport(content, filePath); + if (extracted.removed) { + content = extracted.content; } - } else { - // Has issues (NOT_CONST, NOT_LITERALS), fix them - const removed = removeExistingPlatformExport(content, filePath); - content = removed.content; action = 'fixed'; modified = true; - } - - if (modified) { + // Ensure Platform import exists const importResult = ensurePlatformImport(content, filePath); content = importResult.content; // Add __platforms export at the end content = addPlatformExport(content, platforms); + } else if (existingPlatforms) { + // Has valid __platforms structure - check if it's already at the end + if (isPlatformExportAtEnd(content, filePath)) { + return { skipped: true, reason: 'already has export at end' }; + } + // Extract it and move to end without modification + const extracted = extractExistingPlatformExport(content, filePath); + if (extracted.removed) { + content = extracted.content; + + // Ensure Platform import exists + const importResult = ensurePlatformImport(content, filePath); + content = importResult.content; + + // Add the original statement at the end + content = addPlatformExportStatement(content, extracted.statement); + action = 'moved'; + modified = true; + } else { + return { skipped: true, reason: 'could not extract export' }; + } + } + + if (modified) { // Write back to file fs.writeFileSync(filePath, content, 'utf-8'); - return { skipped: false, action, platforms, addedImport: importResult.added }; + return { skipped: false, action, platforms }; } return { skipped: true, reason: 'no changes needed' }; @@ -345,15 +417,15 @@ function main() { switch (result.action) { case 'added': added++; - console.log(`➕ ${relativePath} → [${result.platforms.join(', ')}]${result.addedImport ? ' (added import)' : ''}`); + console.log(`➕ ${relativePath} → [${result.platforms.join(', ')}]`); break; case 'moved': moved++; - console.log(`📍 ${relativePath} → moved to end [${result.platforms.join(', ')}]${result.addedImport ? ' (added import)' : ''}`); + console.log(`📍 ${relativePath} → moved to end [${result.platforms.join(', ')}]`); break; case 'fixed': fixed++; - console.log(`🔧 ${relativePath} → fixed [${result.platforms.join(', ')}]${result.addedImport ? ' (added import)' : ''}`); + console.log(`🔧 ${relativePath} → fixed [${result.platforms.join(', ')}]`); break; } } From 65f160729426f729756fb16152a3fc31c6d6c4eb Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Wed, 26 Nov 2025 19:32:13 +0600 Subject: [PATCH 23/40] remove const declaration requirement --- .eslintrc.js | 2 -- .platform-isolation.config.js | 3 --- .../require-platform-declaration.js | 24 +++++++------------ .../request_handler.browser.ts | 2 +- message_generator.ts | 2 +- scripts/add-platform-exports.js | 4 ++-- scripts/platform-utils.js | 14 ----------- scripts/validate-platform-isolation-ts.js | 10 -------- 8 files changed, 12 insertions(+), 49 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 93c696808..4114a7859 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,8 +32,6 @@ module.exports = { 'files': ['lib/**/*.ts', 'src/**/*.ts'], 'excludedFiles': [ '**/platform_support.ts', - '**/common_exports.ts', - '**/export_types.ts', '**/*.spec.ts', '**/*.test.ts', '**/*.tests.ts', diff --git a/.platform-isolation.config.js b/.platform-isolation.config.js index 63c9255e6..e7a1de0a4 100644 --- a/.platform-isolation.config.js +++ b/.platform-isolation.config.js @@ -15,9 +15,6 @@ module.exports = { exclude: [ // Platform definition file (this file defines Platform type, doesn't need __platforms) '**/platform_support.ts', - - // '**/common_exports.ts', - // '**/export_types.ts', // Test files '**/*.spec.ts', diff --git a/eslint-local-rules/require-platform-declaration.js b/eslint-local-rules/require-platform-declaration.js index 549822239..4e1bad347 100644 --- a/eslint-local-rules/require-platform-declaration.js +++ b/eslint-local-rules/require-platform-declaration.js @@ -46,9 +46,10 @@ module.exports = { }, messages: { missingPlatformDeclaration: 'File must export __platforms to declare which platforms it supports. Example: export const __platforms = [\'__universal__\'];', - invalidPlatformDeclaration: '__platforms must be exported as a const array. Example: export const __platforms = [\'browser\', \'node\'];', - invalidPlatformValue: '__platforms contains invalid platform value "{{value}}". Valid platforms are: {{validPlatforms}}', - emptyPlatformArray: '__platforms array cannot be empty. Specify at least one platform or use [\'__universal__\']', + notArray: '__platforms must be an array literal. Example: export const __platforms = [\'browser\', \'node\'];', + emptyArray: '__platforms array cannot be empty. Specify at least one platform or use [\'__universal__\'].', + notLiterals: '__platforms must only contain string literals. Do NOT use variables, computed values, or spread operators.', + invalidValues: 'Invalid platform value "{{value}}". Valid platforms are: {{validPlatforms}}', }, schema: [], }, @@ -69,15 +70,6 @@ module.exports = { hasPlatformExport = true; - // Validate it's a const - if (node.declaration.kind !== 'const') { - context.report({ - node: declarator, - messageId: 'invalidPlatformDeclaration', - }); - return; - } - // Validate it's an array expression let init = declarator.init; @@ -94,7 +86,7 @@ module.exports = { if (!init || init.type !== 'ArrayExpression') { context.report({ node: declarator, - messageId: 'invalidPlatformDeclaration', + messageId: 'notArray', }); return; } @@ -103,7 +95,7 @@ module.exports = { if (init.elements.length === 0) { context.report({ node: init, - messageId: 'emptyPlatformArray', + messageId: 'emptyArray', }); return; } @@ -114,7 +106,7 @@ module.exports = { if (!VALID_PLATFORMS.includes(element.value)) { context.report({ node: element, - messageId: 'invalidPlatformValue', + messageId: 'invalidValues', data: { value: element.value, validPlatforms: VALID_PLATFORMS.map(p => `'${p}'`).join(', ') @@ -125,7 +117,7 @@ module.exports = { // Not a string literal context.report({ node: element || init, - messageId: 'invalidPlatformDeclaration', + messageId: 'notLiterals', }); } } diff --git a/lib/utils/http_request_handler/request_handler.browser.ts b/lib/utils/http_request_handler/request_handler.browser.ts index 492cbd897..4cff94858 100644 --- a/lib/utils/http_request_handler/request_handler.browser.ts +++ b/lib/utils/http_request_handler/request_handler.browser.ts @@ -134,4 +134,4 @@ export class BrowserRequestHandler implements RequestHandler { } } -export const __platforms: Platform[] = ['browser']; +export const __platforms: Platform[] = ['browser', 'react_native']; diff --git a/message_generator.ts b/message_generator.ts index fae725a1c..5571f84e7 100644 --- a/message_generator.ts +++ b/message_generator.ts @@ -18,7 +18,7 @@ const generate = async () => { let genOut = ''; Object.keys(exports).forEach((key, i) => { - if (key === 'messages') return; + if (key === 'messages' || key === '__platforms') return; genOut += `export const ${key} = '${i}';\n`; messages.push(exports[key]) }); diff --git a/scripts/add-platform-exports.js b/scripts/add-platform-exports.js index 0883150a2..b57c0c230 100644 --- a/scripts/add-platform-exports.js +++ b/scripts/add-platform-exports.js @@ -296,7 +296,7 @@ function processFile(filePath) { // Extract platforms and error info from result const existingPlatforms = result.success ? result.platforms : null; - const needsFixing = result.error && ['MISSING', 'NOT_CONST', 'NOT_ARRAY', 'EMPTY_ARRAY', 'NOT_LITERALS', 'INVALID_VALUES'].includes(result.error.type); + const needsFixing = result.error && ['MISSING', 'NOT_ARRAY', 'EMPTY_ARRAY', 'NOT_LITERALS', 'INVALID_VALUES'].includes(result.error.type); // Determine platforms for this file // If file already has valid platforms, use those (preserve existing values) @@ -315,7 +315,7 @@ function processFile(filePath) { let action = 'skipped'; if (needsFixing) { - // Has issues (MISSING, NOT_CONST, NOT_LITERALS, INVALID_VALUES, etc.), fix them + // Has issues (MISSING, NOT_ARRAY, NOT_LITERALS, INVALID_VALUES, etc.), fix them const extracted = extractExistingPlatformExport(content, filePath); if (extracted.removed) { content = extracted.content; diff --git a/scripts/platform-utils.js b/scripts/platform-utils.js index a8d12ed67..b3029ea8e 100644 --- a/scripts/platform-utils.js +++ b/scripts/platform-utils.js @@ -99,7 +99,6 @@ function getValidPlatforms(workspaceRoot) { */ function extractPlatformsFromAST(sourceFile, filePath, validPlatforms) { let found = false; - let isConst = false; let isArray = false; let platforms = []; let hasNonStringLiteral = false; @@ -113,15 +112,12 @@ function extractPlatformsFromAST(sourceFile, filePath, validPlatforms) { ); if (hasExport) { - const isConstDecl = (node.declarationList.flags & ts.NodeFlags.Const) !== 0; - for (const declaration of node.declarationList.declarations) { if (ts.isVariableDeclaration(declaration) && ts.isIdentifier(declaration.name) && declaration.name.text === '__platforms') { found = true; - isConst = isConstDecl; let initializer = declaration.initializer; @@ -172,16 +168,6 @@ function extractPlatformsFromAST(sourceFile, filePath, validPlatforms) { }; } - if (!isConst) { - return { - success: false, - error: { - type: 'NOT_CONST', - message: `'__platforms' must be declared with 'const', found non-const declaration` - } - }; - } - if (!isArray) { return { success: false, diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index 2b271fe8e..632384c15 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -441,7 +441,6 @@ function main() { // Group errors by type const errorsByType = { MISSING: [], - NOT_CONST: [], NOT_ARRAY: [], EMPTY_ARRAY: [], NOT_LITERALS: [], @@ -467,15 +466,6 @@ function main() { console.error(`\n${errorsByType.MISSING[0].error.message}\n`); } - if (errorsByType.NOT_CONST.length > 0) { - hasErrors = true; - console.error(`❌ Found ${errorsByType.NOT_CONST.length} file(s) with __platforms not declared as const:\n`); - for (const { filePath, error } of errorsByType.NOT_CONST) { - console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); - } - console.error(`\n${errorsByType.NOT_CONST[0].error.message}\n`); - } - if (errorsByType.NOT_ARRAY.length > 0) { hasErrors = true; console.error(`❌ Found ${errorsByType.NOT_ARRAY.length} file(s) with __platforms not declared as an array:\n`); From 1a9203fa9d234e91fb6bacab5e5abcf2424ba3f4 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Thu, 27 Nov 2025 00:33:36 +0600 Subject: [PATCH 24/40] updates in platform-utils --- scripts/platform-utils.js | 121 ++++++++++++---------- scripts/validate-platform-isolation-ts.js | 5 +- 2 files changed, 70 insertions(+), 56 deletions(-) diff --git a/scripts/platform-utils.js b/scripts/platform-utils.js index b3029ea8e..9b029f627 100644 --- a/scripts/platform-utils.js +++ b/scripts/platform-utils.js @@ -1,12 +1,29 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -/* eslint-disable no-inner-declarations */ +/** + * Copyright 2025, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /** * Platform Utilities * - * Shared utilities for platform isolation validation used by both - * the validation script and ESLint rule. + * Shared utilities for platform isolation validation */ + +/* eslint-disable @typescript-eslint/no-var-requires */ +/* eslint-disable no-inner-declarations */ + const fs = require('fs'); const path = require('path'); const ts = require('typescript'); @@ -26,58 +43,52 @@ function getValidPlatforms(workspaceRoot) { return validPlatformsCache; } - try { - const platformSupportPath = path.join(workspaceRoot, 'lib', 'platform_support.ts'); - - if (!fs.existsSync(platformSupportPath)) { - throw new Error(`platform_support.ts not found at ${platformSupportPath}`); - } + const platformSupportPath = path.join(workspaceRoot, 'lib', 'platform_support.ts'); + + if (!fs.existsSync(platformSupportPath)) { + throw new Error(`platform_support.ts not found at ${platformSupportPath}`); + } - const content = fs.readFileSync(platformSupportPath, 'utf8'); - const sourceFile = ts.createSourceFile( - platformSupportPath, - content, - ts.ScriptTarget.Latest, - true - ); - - const platforms = []; - - // Visit all nodes in the AST - function visit(node) { - // Look for: export type Platform = 'browser' | 'node' | ... - if (ts.isTypeAliasDeclaration(node) && - node.name.text === 'Platform' && - node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) { - - // Parse the union type - if (ts.isUnionTypeNode(node.type)) { - for (const type of node.type.types) { - if (ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal)) { - platforms.push(type.literal.text); - } + const content = fs.readFileSync(platformSupportPath, 'utf8'); + const sourceFile = ts.createSourceFile( + platformSupportPath, + content, + ts.ScriptTarget.Latest, + true + ); + + const platforms = []; + + // Visit all nodes in the AST + function visit(node) { + // Look for: export type Platform = 'browser' | 'node' | ... + if (ts.isTypeAliasDeclaration(node) && + node.name.text === 'Platform' && + node.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword)) { + + // Parse the union type + if (ts.isUnionTypeNode(node.type)) { + for (const type of node.type.types) { + if (ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal)) { + platforms.push(type.literal.text); } - } else if (ts.isLiteralTypeNode(node.type) && ts.isStringLiteral(node.type.literal)) { - // Handle single literal type: type Platform = 'browser'; - platforms.push(node.type.literal.text); } + } else if (ts.isLiteralTypeNode(node.type) && ts.isStringLiteral(node.type.literal)) { + // Handle single literal type: type Platform = 'browser'; + platforms.push(node.type.literal.text); } - - ts.forEachChild(node, visit); } - visit(sourceFile); - - if (platforms.length > 0) { - validPlatformsCache = platforms; - return validPlatformsCache; - } - } catch (error) { - console.warn('Could not parse platform_support.ts, using fallback values:', error.message); + ts.forEachChild(node, visit); + } + + visit(sourceFile); + + if (platforms.length === 0) { + throw new Error(`Could not extract Platform type from ${platformSupportPath}`); } - // Fallback to default platforms - validPlatformsCache = ['browser', 'node', 'react_native', '__universal__']; + validPlatformsCache = platforms; return validPlatformsCache; } @@ -93,11 +104,10 @@ function getValidPlatforms(workspaceRoot) { * - invalidValues: string[] - list of invalid platform values (for INVALID_VALUES type) * * @param {ts.SourceFile} sourceFile - TypeScript source file AST - * @param {string} filePath - File path for context in error messages * @param {string[]} validPlatforms - Array of valid platform values * @returns {Object} */ -function extractPlatformsFromAST(sourceFile, filePath, validPlatforms) { +function extractPlatformsFromAST(sourceFile, validPlatforms) { let found = false; let isArray = false; let platforms = []; @@ -222,21 +232,22 @@ function extractPlatformsFromAST(sourceFile, filePath, validPlatforms) { /** * Extract platforms from a file path with detailed error reporting * - * @param {string} filePath - Absolute path to the file - * @param {string} workspaceRoot - Workspace root for resolving valid platforms + * @param {string} filePath - Relative path to the file (from workspaceRoot) + * @param {string} workspaceRoot - Workspace root directory * @returns {Object} Result object with success, platforms, and error information */ function extractPlatformsFromFile(filePath, workspaceRoot) { try { const validPlatforms = workspaceRoot ? getValidPlatforms(workspaceRoot) : null; - const content = fs.readFileSync(filePath, 'utf-8'); + const absolutePath = path.resolve(workspaceRoot, filePath); + const content = fs.readFileSync(absolutePath, 'utf-8'); const sourceFile = ts.createSourceFile( - filePath, + absolutePath, content, ts.ScriptTarget.Latest, true ); - return extractPlatformsFromAST(sourceFile, filePath, validPlatforms); + return extractPlatformsFromAST(sourceFile, validPlatforms); } catch (error) { return { success: false, diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index 632384c15..264c5ac7c 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -97,8 +97,11 @@ function getSupportedPlatforms(filePath) { } try { + // Convert absolute path to relative path + const relativePath = path.relative(WORKSPACE_ROOT, filePath); + // Extract platforms from file with detailed error reporting - const result = extractPlatformsFromFile(filePath, WORKSPACE_ROOT); + const result = extractPlatformsFromFile(relativePath, WORKSPACE_ROOT); if (result.success) { platformCache.set(filePath, result.platforms); From 70aa9a7b3930449f90e1bbc9159f4d744203af20 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Thu, 27 Nov 2025 22:38:24 +0600 Subject: [PATCH 25/40] refactor validator --- .../require-platform-declaration.js | 19 +- scripts/add-platform-exports.js | 2 +- scripts/platform-utils.js | 132 ++++----- scripts/validate-platform-isolation-ts.js | 260 +++++++----------- 4 files changed, 170 insertions(+), 243 deletions(-) diff --git a/eslint-local-rules/require-platform-declaration.js b/eslint-local-rules/require-platform-declaration.js index 4e1bad347..f9bb51e0e 100644 --- a/eslint-local-rules/require-platform-declaration.js +++ b/eslint-local-rules/require-platform-declaration.js @@ -17,25 +17,8 @@ * // Not exported as const array */ -const path = require('path'); const { getValidPlatforms } = require('../scripts/platform-utils'); -// Cache for valid platforms per workspace -const validPlatformsCache = new Map(); - -function getValidPlatformsForContext(context) { - const filename = context.getFilename(); - const workspaceRoot = filename.split('/lib/')[0]; - - if (validPlatformsCache.has(workspaceRoot)) { - return validPlatformsCache.get(workspaceRoot); - } - - const platforms = getValidPlatforms(workspaceRoot); - validPlatformsCache.set(workspaceRoot, platforms); - return platforms; -} - module.exports = { meta: { type: 'problem', @@ -55,7 +38,7 @@ module.exports = { }, create(context) { - const VALID_PLATFORMS = getValidPlatformsForContext(context); + const VALID_PLATFORMS = getValidPlatforms(); let hasPlatformExport = false; return { diff --git a/scripts/add-platform-exports.js b/scripts/add-platform-exports.js index b57c0c230..cd9cf2545 100644 --- a/scripts/add-platform-exports.js +++ b/scripts/add-platform-exports.js @@ -285,7 +285,7 @@ function processFile(filePath) { let content = fs.readFileSync(filePath, 'utf-8'); // Get valid platforms for validation - const validPlatforms = getValidPlatforms(WORKSPACE_ROOT); + const validPlatforms = getValidPlatforms(); // Use TypeScript parser to check for existing __platforms const result = extractPlatformsFromAST( diff --git a/scripts/platform-utils.js b/scripts/platform-utils.js index 9b029f627..1e6ee3943 100644 --- a/scripts/platform-utils.js +++ b/scripts/platform-utils.js @@ -31,18 +31,21 @@ const ts = require('typescript'); // Cache for valid platforms let validPlatformsCache = null; +// Cache for file platforms +const filePlatformCache = new Map(); + /** * Extract valid platform values from Platform type definition in platform_support.ts * Parses: type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; * - * @param {string} workspaceRoot - The root directory of the workspace * @returns {string[]} Array of valid platform identifiers */ -function getValidPlatforms(workspaceRoot) { +function getValidPlatforms() { if (validPlatformsCache) { return validPlatformsCache; } + const workspaceRoot = path.join(__dirname, '..'); const platformSupportPath = path.join(workspaceRoot, 'lib', 'platform_support.ts'); if (!fs.existsSync(platformSupportPath)) { @@ -59,8 +62,8 @@ function getValidPlatforms(workspaceRoot) { const platforms = []; - // Visit all nodes in the AST - function visit(node) { + // Visit only top-level statements since Platform type must be exported at top level + for (const node of sourceFile.statements) { // Look for: export type Platform = 'browser' | 'node' | ... if (ts.isTypeAliasDeclaration(node) && node.name.text === 'Platform' && @@ -77,13 +80,11 @@ function getValidPlatforms(workspaceRoot) { // Handle single literal type: type Platform = 'browser'; platforms.push(node.type.literal.text); } + + break; // Found it, stop searching } - - ts.forEachChild(node, visit); } - visit(sourceFile); - if (platforms.length === 0) { throw new Error(`Could not extract Platform type from ${platformSupportPath}`); } @@ -113,59 +114,58 @@ function extractPlatformsFromAST(sourceFile, validPlatforms) { let platforms = []; let hasNonStringLiteral = false; - function visit(node) { + // Visit only top-level children since __platforms must be exported at top level + for (const node of sourceFile.statements) { // Look for: export const __platforms = [...] - if (ts.isVariableStatement(node)) { - // Check if it has export modifier - const hasExport = node.modifiers?.some( - mod => mod.kind === ts.SyntaxKind.ExportKeyword - ); + if (!ts.isVariableStatement(node)) continue; + + // Check if it has export modifier + const hasExport = node.modifiers?.some( + mod => mod.kind === ts.SyntaxKind.ExportKeyword + ); + if (!hasExport) continue; - if (hasExport) { - for (const declaration of node.declarationList.declarations) { - if (ts.isVariableDeclaration(declaration) && - ts.isIdentifier(declaration.name) && - declaration.name.text === '__platforms') { - - found = true; - - let initializer = declaration.initializer; - - // Handle "as const" assertion: [...] as const - if (initializer && ts.isAsExpression(initializer)) { - initializer = initializer.expression; - } - - // Handle type assertion: [...] - if (initializer && ts.isTypeAssertionExpression(initializer)) { - initializer = initializer.expression; - } - - // Check if it's an array - if (initializer && ts.isArrayLiteralExpression(initializer)) { - isArray = true; - - // Extract array elements - for (const element of initializer.elements) { - if (ts.isStringLiteral(element)) { - platforms.push(element.text); - } else { - // Non-string literal found (variable, computed value, etc.) - hasNonStringLiteral = true; - } - } - } - - return; // Found it, stop visiting + for (const declaration of node.declarationList.declarations) { + if (!ts.isVariableDeclaration(declaration) || + !ts.isIdentifier(declaration.name) || + declaration.name.text !== '__platforms') { + continue; + } + + found = true; + + let initializer = declaration.initializer; + + // Handle "as const" assertion: [...] as const + if (initializer && ts.isAsExpression(initializer)) { + initializer = initializer.expression; + } + + // Handle type assertion: [...] + if (initializer && ts.isTypeAssertionExpression(initializer)) { + initializer = initializer.expression; + } + + // Check if it's an array + if (initializer && ts.isArrayLiteralExpression(initializer)) { + isArray = true; + + // Extract array elements + for (const element of initializer.elements) { + if (ts.isStringLiteral(element)) { + platforms.push(element.text); + } else { + // Non-string literal found (variable, computed value, etc.) + hasNonStringLiteral = true; } } } + + break; // Found it, stop searching } - - ts.forEachChild(node, visit); + + if (found) break; } - - visit(sourceFile); // Detailed error reporting if (!found) { @@ -173,7 +173,7 @@ function extractPlatformsFromAST(sourceFile, validPlatforms) { success: false, error: { type: 'MISSING', - message: `File does not export '__platforms' constant` + message: `File does not export '__platforms' array` } }; } @@ -231,15 +231,20 @@ function extractPlatformsFromAST(sourceFile, validPlatforms) { /** * Extract platforms from a file path with detailed error reporting + * Uses caching to avoid re-parsing the same file multiple times. * - * @param {string} filePath - Relative path to the file (from workspaceRoot) - * @param {string} workspaceRoot - Workspace root directory + * @param {string} absolutePath - Absolute path to the file * @returns {Object} Result object with success, platforms, and error information */ -function extractPlatformsFromFile(filePath, workspaceRoot) { +function extractPlatformsFromFile(absolutePath) { + // Check cache first + if (filePlatformCache.has(absolutePath)) { + return filePlatformCache.get(absolutePath); + } + + let result; try { - const validPlatforms = workspaceRoot ? getValidPlatforms(workspaceRoot) : null; - const absolutePath = path.resolve(workspaceRoot, filePath); + const validPlatforms = getValidPlatforms(); const content = fs.readFileSync(absolutePath, 'utf-8'); const sourceFile = ts.createSourceFile( absolutePath, @@ -247,9 +252,9 @@ function extractPlatformsFromFile(filePath, workspaceRoot) { ts.ScriptTarget.Latest, true ); - return extractPlatformsFromAST(sourceFile, validPlatforms); + result = extractPlatformsFromAST(sourceFile, validPlatforms); } catch (error) { - return { + result = { success: false, error: { type: 'READ_ERROR', @@ -257,6 +262,9 @@ function extractPlatformsFromFile(filePath, workspaceRoot) { } }; } + + filePlatformCache.set(absolutePath, result); + return result; } module.exports = { diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index 264c5ac7c..f3890e57d 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -1,5 +1,20 @@ #!/usr/bin/env node -/* eslint-disable @typescript-eslint/no-var-requires */ + +/** + * Copyright 2025, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** * Platform Isolation Validator @@ -22,6 +37,7 @@ * Usage: node scripts/validate-platform-isolation-ts.js */ +/* eslint-disable @typescript-eslint/no-var-requires */ const fs = require('fs'); const path = require('path'); const ts = require('typescript'); @@ -44,46 +60,11 @@ const config = fs.existsSync(configPath) ] }; -// Cache for __platforms exports -const platformCache = new Map(); - -// Valid platforms (loaded dynamically) -let VALID_PLATFORMS = null; -let ALL_CONCRETE_PLATFORMS = null; - -/** - * Get valid platforms from source - */ -function getValidPlatformsFromSource() { - if (VALID_PLATFORMS !== null) { - return VALID_PLATFORMS; - } - - VALID_PLATFORMS = getValidPlatforms(WORKSPACE_ROOT); - ALL_CONCRETE_PLATFORMS = VALID_PLATFORMS.filter(p => p !== '__universal__'); - return VALID_PLATFORMS; -} - -/** - * Gets a human-readable platform name - */ -function getPlatformFromFilename(filename) { - const validPlatforms = getValidPlatformsFromSource(); - const concretePlatforms = validPlatforms.filter(p => p !== '__universal__'); - - for (const platform of concretePlatforms) { - if (filename.includes(`.${platform}.`)) { - return platform; - } - } - return null; -} - -// Track files missing __platforms export +// Track files with errrors in __platforms export const fileErrors = new Map(); /** - * Gets the supported platforms for a file (with caching) + * Gets the supported platforms for a file * Returns: * - string[] (platforms from __platforms) * - null (file has errors) @@ -91,34 +72,14 @@ const fileErrors = new Map(); * Note: ALL files must have __platforms export */ function getSupportedPlatforms(filePath) { - // Check cache first - if (platformCache.has(filePath)) { - return platformCache.get(filePath); - } + // Extract platforms from file with detailed error reporting (uses cache internally) + const result = extractPlatformsFromFile(filePath); - try { - // Convert absolute path to relative path - const relativePath = path.relative(WORKSPACE_ROOT, filePath); - - // Extract platforms from file with detailed error reporting - const result = extractPlatformsFromFile(relativePath, WORKSPACE_ROOT); - - if (result.success) { - platformCache.set(filePath, result.platforms); - return result.platforms; - } else { - // Store error for this file - fileErrors.set(filePath, result.error); - platformCache.set(filePath, null); - return null; - } - } catch (error) { - // Store read error - fileErrors.set(filePath, { - type: 'READ_ERROR', - message: `Failed to read file: ${error.message}` - }); - platformCache.set(filePath, null); + if (result.success) { + return result.platforms; + } else { + // Store error for this file + fileErrors.set(filePath, result.error); return null; } } @@ -137,12 +98,11 @@ function getPlatformName(platform) { /** * Formats platform info for display + * + * Note: Assumes platforms is a valid array (after first validation pass) */ function formatPlatforms(platforms) { - if (platforms === 'MISSING') return 'MISSING __platforms'; - if (!platforms || platforms.length === 0) return 'Unknown'; - if (Array.isArray(platforms) && platforms.length === 1 && platforms[0] === '__universal__') return 'Universal (all platforms)'; - if (typeof platforms === 'string') return getPlatformName(platforms); + if (isUniversal(platforms)) return 'Universal (all platforms)'; return platforms.map(p => getPlatformName(p)).join(' + '); } @@ -171,17 +131,13 @@ function isUniversal(platforms) { * Checks if a platform is compatible with target platforms * * Rules: - * - If either file has errors, not compatible * - Import must support ALL platforms that the importing file runs on * - Universal imports can be used by any file (they support all platforms) * - Platform-specific files can only import from universal or files supporting all their platforms + * + * Note: This function assumes both parameters are valid platform arrays (non-null) */ function isPlatformCompatible(filePlatforms, importPlatforms) { - // If either has errors, not compatible - if (!filePlatforms || !importPlatforms) { - return false; - } - // If import is universal, always compatible (universal supports all platforms) if (isUniversal(importPlatforms)) { return true; @@ -335,12 +291,6 @@ function resolveImportPath(importPath, currentFilePath) { */ function validateFile(filePath) { const filePlatforms = getSupportedPlatforms(filePath); - - // If file has errors, that's handled separately - if (!filePlatforms) { - return { valid: true, errors: [] }; // Reported separately - } - const imports = extractImports(filePath); const errors = []; @@ -361,10 +311,7 @@ function validateFile(filePath) { // Check compatibility if (!isPlatformCompatible(filePlatforms, importPlatforms)) { - const importError = fileErrors.get(resolved); - const message = importError - ? `Import has __platforms error: "${importInfo.path}" - ${importError.message}` - : `${formatPlatforms(filePlatforms)} file cannot import from ${formatPlatforms(importPlatforms)}-only file: "${importInfo.path}"`; + const message = `${formatPlatforms(filePlatforms)} file cannot import from ${formatPlatforms(importPlatforms)}-only file: "${importInfo.path}"`; errors.push({ line: importInfo.line, @@ -382,10 +329,64 @@ function validateFile(filePath) { /** * Check if file matches any pattern using minimatch */ -function matchesPattern(filePath, patterns) { +function matchesPattern(filePath, patterns, options = {}) { const relativePath = path.relative(WORKSPACE_ROOT, filePath).replace(/\\/g, '/'); - return patterns.some(pattern => minimatch(relativePath, pattern)); + return patterns.some(pattern => minimatch(relativePath, pattern, options)); +} + +/** + * Report platform export errors by type + */ +function reportPlatformErrors(errorsByType, validPlatforms) { + let hasErrors = false; + + const errorConfig = { + MISSING: { + message: (count) => `❌ Found ${count} file(s) missing __platforms export:\n` + }, + NOT_ARRAY: { + message: (count) => `❌ Found ${count} file(s) with __platforms not declared as an array:\n` + }, + EMPTY_ARRAY: { + message: (count) => `❌ Found ${count} file(s) with empty __platforms array:\n` + }, + NOT_LITERALS: { + message: (count) => `❌ Found ${count} file(s) with __platforms containing non-literal values:\n` + }, + INVALID_VALUES: { + message: (count) => `❌ Found ${count} file(s) with invalid platform values:\n`, + customHandler: (error) => { + const invalidValuesStr = error.invalidValues ? error.invalidValues.map(v => `${v}`).join(', ') : ''; + console.error(` Invalid values: ${invalidValuesStr}`); + console.error(` Valid platforms: ${validPlatforms.join(', ')}`); + } + }, + READ_ERROR: { + message: (count) => `❌ Found ${count} file(s) with read errors:\n`, + customHandler: (error) => { + console.error(` ${error.message}`); + } + } + }; + + for (const [errorType, config] of Object.entries(errorConfig)) { + const errors = errorsByType[errorType]; + if (errors.length === 0) continue; + + hasErrors = true; + console.error(config.message(errors.length)); + + for (const { filePath, error } of errors) { + console.error(` 📄 ${path.relative(WORKSPACE_ROOT, filePath)}`); + if (config.customHandler) { + config.customHandler(error); + } + } + console.error('\n'); + } + + return hasErrors; } /** @@ -399,22 +400,14 @@ function findSourceFiles(dir, files = []) { if (entry.isDirectory()) { // Check if this directory path could potentially contain files matching include patterns - // Use minimatch with partial mode to test if pattern could match files under this directory - const relativePath = path.relative(WORKSPACE_ROOT, fullPath).replace(/\\/g, '/'); - const couldMatch = config.include.some(pattern => { - return minimatch(relativePath, pattern, { partial: true }); - }); - - if (couldMatch) { + if (matchesPattern(fullPath, config.include, { partial: true })) { findSourceFiles(fullPath, files); } } else if (entry.isFile()) { - // Check if file matches include patterns - if (matchesPattern(fullPath, config.include)) { - // Check if file is NOT excluded - if (!matchesPattern(fullPath, config.exclude)) { - files.push(fullPath); - } + // Check if file matches include patterns and is NOT excluded + if (matchesPattern(fullPath, config.include) + && !matchesPattern(fullPath, config.exclude)) { + files.push(fullPath); } } } @@ -426,13 +419,13 @@ function findSourceFiles(dir, files = []) { * Main validation function */ function main() { - console.log('🔍 Validating platform isolation (using TypeScript parser)...\n'); + console.log('🔍 Validating platform isolation...\n'); console.log(`📋 Configuration: ${path.relative(WORKSPACE_ROOT, configPath) || '.platform-isolation.config.js'}\n`); const files = findSourceFiles(WORKSPACE_ROOT); // Load valid platforms first - const validPlatforms = getValidPlatformsFromSource(); + const validPlatforms = getValidPlatforms(); console.log(`Valid platforms: ${validPlatforms.join(', ')}\n`); // First pass: check for __platforms export @@ -458,65 +451,7 @@ function main() { } // Report errors by type - let hasErrors = false; - - if (errorsByType.MISSING.length > 0) { - hasErrors = true; - console.error(`❌ Found ${errorsByType.MISSING.length} file(s) missing __platforms export:\n`); - for (const { filePath, error } of errorsByType.MISSING) { - console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); - } - console.error(`\n${errorsByType.MISSING[0].error.message}\n`); - } - - if (errorsByType.NOT_ARRAY.length > 0) { - hasErrors = true; - console.error(`❌ Found ${errorsByType.NOT_ARRAY.length} file(s) with __platforms not declared as an array:\n`); - for (const { filePath, error } of errorsByType.NOT_ARRAY) { - console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); - } - console.error(`\n${errorsByType.NOT_ARRAY[0].error.message}\n`); - } - - if (errorsByType.EMPTY_ARRAY.length > 0) { - hasErrors = true; - console.error(`❌ Found ${errorsByType.EMPTY_ARRAY.length} file(s) with empty __platforms array:\n`); - for (const { filePath, error } of errorsByType.EMPTY_ARRAY) { - console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); - } - console.error(`\n${errorsByType.EMPTY_ARRAY[0].error.message}\n`); - } - - if (errorsByType.NOT_LITERALS.length > 0) { - hasErrors = true; - console.error(`❌ Found ${errorsByType.NOT_LITERALS.length} file(s) with __platforms containing non-literal values:\n`); - for (const { filePath, error} of errorsByType.NOT_LITERALS) { - console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); - } - console.error(`\n${errorsByType.NOT_LITERALS[0].error.message}\n`); - } - - if (errorsByType.INVALID_VALUES.length > 0) { - hasErrors = true; - console.error(`❌ Found ${errorsByType.INVALID_VALUES.length} file(s) with invalid platform values:\n`); - for (const { filePath, error } of errorsByType.INVALID_VALUES) { - const invalidValuesStr = error.invalidValues ? error.invalidValues.map(v => `'${v}'`).join(', ') : ''; - console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); - console.error(` Invalid values: ${invalidValuesStr}`); - console.error(` Valid platforms: ${validPlatforms.join(', ')}`); - } - console.error(''); - } - - if (errorsByType.READ_ERROR.length > 0) { - hasErrors = true; - console.error(`❌ Found ${errorsByType.READ_ERROR.length} file(s) with read errors:\n`); - for (const { filePath, error } of errorsByType.READ_ERROR) { - console.error(` 📄 ${path.relative(process.cwd(), filePath)}`); - console.error(` ${error.message}`); - } - console.error(''); - } + const hasErrors = reportPlatformErrors(errorsByType, validPlatforms); if (hasErrors) { process.exit(1); @@ -525,6 +460,7 @@ function main() { console.log('✅ All files have valid __platforms exports\n'); // Second pass: validate platform isolation + // At this point, all files are guaranteed to have valid __platforms exports console.log('Validating platform compatibility...\n'); let totalErrors = 0; @@ -546,7 +482,7 @@ function main() { console.error(`❌ Found ${totalErrors} platform isolation violation(s) in ${filesWithErrors.length} file(s):\n`); for (const { file, errors } of filesWithErrors) { - const relativePath = path.relative(process.cwd(), file); + const relativePath = path.relative(WORKSPACE_ROOT, file); const filePlatforms = getSupportedPlatforms(file); console.error(`\n📄 ${relativePath} [${formatPlatforms(filePlatforms)}]`); From 17ffe489b31d1f2fe9df90ddc5d422f0ddd858ee Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Thu, 27 Nov 2025 23:53:39 +0600 Subject: [PATCH 26/40] update module resolution --- scripts/validate-platform-isolation-ts.js | 60 ++++++++--------------- 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index f3890e57d..6f591c9ed 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -46,6 +46,15 @@ const { getValidPlatforms, extractPlatformsFromFile } = require('./platform-util const WORKSPACE_ROOT = path.join(__dirname, '..'); +// Load tsconfig to get module resolution settings +const tsconfigPath = path.join(WORKSPACE_ROOT, 'tsconfig.json'); +const tsconfigContent = fs.readFileSync(tsconfigPath, 'utf-8'); +const tsconfig = ts.parseConfigFileTextToJson(tsconfigPath, tsconfigContent).config; +const compilerOptions = ts.convertCompilerOptionsFromJson( + tsconfig.compilerOptions, + WORKSPACE_ROOT +).options; + // Load configuration const configPath = path.join(WORKSPACE_ROOT, '.platform-isolation.config.js'); const config = fs.existsSync(configPath) @@ -235,7 +244,7 @@ function extractImports(filePath) { } /** - * Resolve import path relative to current file + * Resolve import path relative to current file using TypeScript's module resolution */ function resolveImportPath(importPath, currentFilePath) { // External imports (node_modules) - return as-is @@ -243,47 +252,20 @@ function resolveImportPath(importPath, currentFilePath) { return { isExternal: true, resolved: importPath }; } - const currentDir = path.dirname(currentFilePath); - let resolved = path.resolve(currentDir, importPath); - - // Check if it's a directory - if so, look for index file - if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) { - const extensions = ['.ts', '.js', '.tsx', '.jsx']; - for (const ext of extensions) { - const indexFile = path.join(resolved, `index${ext}`); - if (fs.existsSync(indexFile)) { - return { isExternal: false, resolved: indexFile }; - } - } - // Directory exists but no index file found - return { isExternal: false, resolved }; - } - - // Check if file exists as-is (with extension already) - if (fs.existsSync(resolved)) { - return { isExternal: false, resolved }; - } - - // Try different extensions - const extensions = ['.ts', '.js', '.tsx', '.jsx']; - for (const ext of extensions) { - const withExt = resolved + ext; - if (fs.existsSync(withExt)) { - return { isExternal: false, resolved: withExt }; - } - } + // Use TypeScript's module resolution with settings from tsconfig + const result = ts.resolveModuleName( + importPath, + currentFilePath, + compilerOptions, + ts.sys + ); - // Try index files (for cases where the directory doesn't exist yet) - for (const ext of extensions) { - const indexFile = path.join(resolved, `index${ext}`); - if (fs.existsSync(indexFile)) { - return { isExternal: false, resolved: indexFile }; - } + if (result.resolvedModule) { + return { isExternal: false, resolved: result.resolvedModule.resolvedFileName }; } - // Return the resolved path even if it doesn't exist - // (getSupportedPlatforms will handle it) - return { isExternal: false, resolved }; + // If TypeScript can't resolve, throw an error + throw new Error(`Cannot resolve import "${importPath}" from ${path.relative(WORKSPACE_ROOT, currentFilePath)}`); } /** From a6617e8606f35aaccc22916c0539adc81495c6ee Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Fri, 28 Nov 2025 00:22:39 +0600 Subject: [PATCH 27/40] dry --- scripts/add-platform-exports.js | 74 ++------------------- scripts/platform-utils.js | 80 +++++++++++++++++++++++ scripts/validate-platform-isolation-ts.js | 42 ++---------- 3 files changed, 88 insertions(+), 108 deletions(-) diff --git a/scripts/add-platform-exports.js b/scripts/add-platform-exports.js index cd9cf2545..2418da597 100644 --- a/scripts/add-platform-exports.js +++ b/scripts/add-platform-exports.js @@ -16,26 +16,11 @@ const fs = require('fs'); const path = require('path'); const ts = require('typescript'); -const { minimatch } = require('minimatch'); -const { extractPlatformsFromAST, getValidPlatforms } = require('./platform-utils'); +const { extractPlatformsFromFile, findSourceFiles } = require('./platform-utils'); const WORKSPACE_ROOT = path.join(__dirname, '..'); const PLATFORMS = ['browser', 'node', 'react_native']; -// Load configuration -const configPath = path.join(WORKSPACE_ROOT, '.platform-isolation.config.js'); -const config = fs.existsSync(configPath) - ? require(configPath) - : { - include: ['lib/**/*.ts', 'lib/**/*.js'], - exclude: [ - '**/*.spec.ts', '**/*.test.ts', '**/*.tests.ts', - '**/*.test.js', '**/*.spec.js', '**/*.tests.js', - '**/*.umdtests.js', '**/*.test-d.ts', '**/*.gen.ts', - '**/*.d.ts', '**/__mocks__/**', '**/tests/**' - ] - }; - function getPlatformFromFilename(filename) { const platforms = []; for (const platform of PLATFORMS) { @@ -46,15 +31,6 @@ function getPlatformFromFilename(filename) { return platforms.length > 0 ? platforms : null; } -/** - * Check if file matches any pattern using minimatch - */ -function matchesPattern(filePath, patterns) { - const relativePath = path.relative(WORKSPACE_ROOT, filePath).replace(/\\/g, '/'); - - return patterns.some(pattern => minimatch(relativePath, pattern)); -} - /** * Calculate relative import path for Platform type */ @@ -284,15 +260,8 @@ function addPlatformExport(content, platforms) { function processFile(filePath) { let content = fs.readFileSync(filePath, 'utf-8'); - // Get valid platforms for validation - const validPlatforms = getValidPlatforms(); - - // Use TypeScript parser to check for existing __platforms - const result = extractPlatformsFromAST( - ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true), - filePath, - validPlatforms // Validate platform values - ); + // Use extractPlatformsFromFile which validates platform values + const result = extractPlatformsFromFile(filePath); // Extract platforms and error info from result const existingPlatforms = result.success ? result.platforms : null; @@ -363,45 +332,10 @@ function processFile(filePath) { return { skipped: true, reason: 'no changes needed' }; } -/** - * Recursively find all files matching include patterns and not matching exclude patterns - */ -function findSourceFiles(dir, files = []) { - const entries = fs.readdirSync(dir, { withFileTypes: true }); - - for (const entry of entries) { - const fullPath = path.join(dir, entry.name); - - if (entry.isDirectory()) { - // Check if this directory path could potentially contain files matching include patterns - // Use minimatch with partial mode to test if pattern could match files under this directory - const relativePath = path.relative(WORKSPACE_ROOT, fullPath).replace(/\\/g, '/'); - const couldMatch = config.include.some(pattern => { - return minimatch(relativePath, pattern, { partial: true }); - }); - - if (couldMatch) { - findSourceFiles(fullPath, files); - } - } else if (entry.isFile()) { - // Check if file matches include patterns - if (matchesPattern(fullPath, config.include)) { - // Check if file is NOT excluded - if (!matchesPattern(fullPath, config.exclude)) { - files.push(fullPath); - } - } - } - } - - return files; -} - function main() { console.log('🔧 Processing __platforms exports...\n'); - console.log(`📋 Configuration: ${path.relative(WORKSPACE_ROOT, configPath) || '.platform-isolation.config.js'}\n`); - const files = findSourceFiles(WORKSPACE_ROOT); + const files = findSourceFiles(); let added = 0; let moved = 0; let fixed = 0; diff --git a/scripts/platform-utils.js b/scripts/platform-utils.js index 1e6ee3943..278eaa153 100644 --- a/scripts/platform-utils.js +++ b/scripts/platform-utils.js @@ -27,6 +27,7 @@ const fs = require('fs'); const path = require('path'); const ts = require('typescript'); +const { minimatch } = require('minimatch'); // Cache for valid platforms let validPlatformsCache = null; @@ -34,6 +35,37 @@ let validPlatformsCache = null; // Cache for file platforms const filePlatformCache = new Map(); +// Cache for config +let configCache = null; + +/** + * Load platform isolation configuration + * + * @returns {Object} Configuration object with include/exclude patterns + */ +function loadConfig() { + if (configCache) { + return configCache; + } + + const workspaceRoot = path.join(__dirname, '..'); + const configPath = path.join(workspaceRoot, '.platform-isolation.config.js'); + + configCache = fs.existsSync(configPath) + ? require(configPath) + : { + include: ['lib/**/*.ts', 'lib/**/*.js'], + exclude: [ + '**/*.spec.ts', '**/*.test.ts', '**/*.tests.ts', + '**/*.test.js', '**/*.spec.js', '**/*.tests.js', + '**/*.umdtests.js', '**/*.test-d.ts', '**/*.gen.ts', + '**/*.d.ts', '**/__mocks__/**', '**/tests/**' + ] + }; + + return configCache; +} + /** * Extract valid platform values from Platform type definition in platform_support.ts * Parses: type Platform = 'browser' | 'node' | 'react_native' | '__universal__'; @@ -267,8 +299,56 @@ function extractPlatformsFromFile(absolutePath) { return result; } +/** + * Find all source files matching include/exclude patterns from config + * + * @returns {string[]} Array of absolute file paths + */ +function findSourceFiles() { + const workspaceRoot = path.join(__dirname, '..'); + const config = loadConfig(); + + /** + * Check if file matches any pattern using minimatch + */ + function matchesPattern(filePath, patterns, options = {}) { + const relativePath = path.relative(workspaceRoot, filePath).replace(/\\/g, '/'); + return patterns.some(pattern => minimatch(relativePath, pattern, options)); + } + + /** + * Recursively find all files matching include patterns and not matching exclude patterns + */ + function findFilesRecursive(dir, files = []) { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + + if (entry.isDirectory()) { + // Check if this directory path could potentially contain files matching include patterns + if (matchesPattern(fullPath, config.include, { partial: true })) { + findFilesRecursive(fullPath, files); + } + } else if (entry.isFile()) { + // Check if file matches include patterns and is NOT excluded + if (matchesPattern(fullPath, config.include) + && !matchesPattern(fullPath, config.exclude)) { + files.push(fullPath); + } + } + } + + return files; + } + + return findFilesRecursive(workspaceRoot); +} + module.exports = { getValidPlatforms, extractPlatformsFromAST, extractPlatformsFromFile, + findSourceFiles, + loadConfig, }; diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation-ts.js index 6f591c9ed..1985b0d4f 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation-ts.js @@ -42,7 +42,7 @@ const fs = require('fs'); const path = require('path'); const ts = require('typescript'); const { minimatch } = require('minimatch'); -const { getValidPlatforms, extractPlatformsFromFile } = require('./platform-utils'); +const { getValidPlatforms, extractPlatformsFromFile, findSourceFiles, loadConfig } = require('./platform-utils'); const WORKSPACE_ROOT = path.join(__dirname, '..'); @@ -56,18 +56,8 @@ const compilerOptions = ts.convertCompilerOptionsFromJson( ).options; // Load configuration +const config = loadConfig(); const configPath = path.join(WORKSPACE_ROOT, '.platform-isolation.config.js'); -const config = fs.existsSync(configPath) - ? require(configPath) - : { - include: ['lib/**/*.ts', 'lib/**/*.js'], - exclude: [ - '**/*.spec.ts', '**/*.test.ts', '**/*.tests.ts', - '**/*.test.js', '**/*.spec.js', '**/*.tests.js', - '**/*.umdtests.js', '**/*.test-d.ts', '**/*.gen.ts', - '**/*.d.ts', '**/__mocks__/**', '**/tests/**' - ] - }; // Track files with errrors in __platforms export const fileErrors = new Map(); @@ -371,31 +361,7 @@ function reportPlatformErrors(errorsByType, validPlatforms) { return hasErrors; } -/** - * Recursively find all files matching include patterns and not matching exclude patterns - */ -function findSourceFiles(dir, files = []) { - const entries = fs.readdirSync(dir, { withFileTypes: true }); - - for (const entry of entries) { - const fullPath = path.join(dir, entry.name); - - if (entry.isDirectory()) { - // Check if this directory path could potentially contain files matching include patterns - if (matchesPattern(fullPath, config.include, { partial: true })) { - findSourceFiles(fullPath, files); - } - } else if (entry.isFile()) { - // Check if file matches include patterns and is NOT excluded - if (matchesPattern(fullPath, config.include) - && !matchesPattern(fullPath, config.exclude)) { - files.push(fullPath); - } - } - } - - return files; -} + /** * Main validation function @@ -404,7 +370,7 @@ function main() { console.log('🔍 Validating platform isolation...\n'); console.log(`📋 Configuration: ${path.relative(WORKSPACE_ROOT, configPath) || '.platform-isolation.config.js'}\n`); - const files = findSourceFiles(WORKSPACE_ROOT); + const files = findSourceFiles(); // Load valid platforms first const validPlatforms = getValidPlatforms(); From 901cc4a220a1349c88323236ed35704112d5f505 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Fri, 28 Nov 2025 00:26:13 +0600 Subject: [PATCH 28/40] update platform extraction from file name --- scripts/add-platform-exports.js | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/scripts/add-platform-exports.js b/scripts/add-platform-exports.js index 2418da597..88f78bcfa 100644 --- a/scripts/add-platform-exports.js +++ b/scripts/add-platform-exports.js @@ -23,11 +23,28 @@ const PLATFORMS = ['browser', 'node', 'react_native']; function getPlatformFromFilename(filename) { const platforms = []; - for (const platform of PLATFORMS) { - if (filename.includes(`.${platform}.`)) { - platforms.push(platform); + const basename = path.basename(filename); + + // Extract all parts before the extension + // e.g., "file.browser.node.ts" -> ["file", "browser", "node"] + const parts = basename.split('.'); + + // Skip the last part (extension) + if (parts.length < 2) { + return null; + } + + // Check parts from right to left (excluding extension) for platform names + for (let i = parts.length - 2; i >= 0; i--) { + const part = parts[i]; + if (PLATFORMS.includes(part)) { + platforms.unshift(part); // Add to beginning to maintain order + } else { + // Stop when we encounter a non-platform part + break; } } + return platforms.length > 0 ? platforms : null; } From 2f93e2d47235a49fd82fe6e66ff5fefd58b55c03 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Fri, 28 Nov 2025 20:11:24 +0600 Subject: [PATCH 29/40] finalize scripts --- package.json | 2 +- scripts/add-platform-exports.js | 157 ++++++++++-------- scripts/test-validator.js | 64 +++++-- ...n-ts.js => validate-platform-isolation.js} | 6 +- 4 files changed, 134 insertions(+), 95 deletions(-) rename scripts/{validate-platform-isolation-ts.js => validate-platform-isolation.js} (98%) diff --git a/package.json b/package.json index 1014756ae..b598508e2 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "clean": "rm -rf dist", "clean:win": "(if exist dist rd /s/q dist)", "lint": "tsc --noEmit && eslint 'lib/**/*.js' 'lib/**/*.ts'", - "validate-platform-isolation": "node scripts/validate-platform-isolation-ts.js", + "validate-platform-isolation": "node scripts/validate-platform-isolation.js", "test-platform-isolation": "node scripts/test-validator.js", "add-platform-exports": "node scripts/add-platform-exports.js", "test-vitest": "vitest run", diff --git a/scripts/add-platform-exports.js b/scripts/add-platform-exports.js index 88f78bcfa..8f29150d8 100644 --- a/scripts/add-platform-exports.js +++ b/scripts/add-platform-exports.js @@ -1,5 +1,21 @@ #!/usr/bin/env node +/** + * Copyright 2025, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /** * Auto-add __platforms to files * @@ -13,6 +29,7 @@ * 4. Inserts __platforms export at the end of the file */ +/* eslint-disable @typescript-eslint/no-var-requires */ const fs = require('fs'); const path = require('path'); const ts = require('typescript'); @@ -84,26 +101,27 @@ function ensurePlatformImport(content, filePath) { let lastImportEnd = 0; function visit(node) { - if (ts.isImportDeclaration(node)) { - const moduleSpecifier = node.moduleSpecifier; - if (ts.isStringLiteral(moduleSpecifier)) { - // Check if this import is from platform_support - if (moduleSpecifier.text.includes('platform_support')) { - // Check if it imports Platform type - if (node.importClause && node.importClause.namedBindings) { - const namedBindings = node.importClause.namedBindings; - if (ts.isNamedImports(namedBindings)) { - for (const element of namedBindings.elements) { - if (element.name.text === 'Platform') { - hasPlatformImport = true; - break; - } - } - } - } - } + if (!ts.isImportDeclaration(node)) return; + + lastImportEnd = node.end; + + const moduleSpecifier = node.moduleSpecifier; + if (!ts.isStringLiteral(moduleSpecifier)) return; + + // Check if this import is from platform_support + if (!moduleSpecifier.text.includes('platform_support')) return; + + // Check if it imports Platform type + if (!node.importClause?.namedBindings) return; + + const namedBindings = node.importClause.namedBindings; + if (!ts.isNamedImports(namedBindings)) return; + + for (const element of namedBindings.elements) { + if (element.name.text === 'Platform') { + hasPlatformImport = true; + break; } - lastImportEnd = node.end; } } @@ -175,19 +193,19 @@ function isPlatformExportAtEnd(content, filePath) { } // Find __platforms export - if (ts.isVariableStatement(node)) { - const hasExport = node.modifiers?.some( - mod => mod.kind === ts.SyntaxKind.ExportKeyword - ); - - if (hasExport) { - for (const declaration of node.declarationList.declarations) { - if (ts.isVariableDeclaration(declaration) && - ts.isIdentifier(declaration.name) && - declaration.name.text === '__platforms') { - platformExportEnd = node.end; - } - } + if (!ts.isVariableStatement(node)) return; + + const hasExport = node.modifiers?.some( + mod => mod.kind === ts.SyntaxKind.ExportKeyword + ); + + if (!hasExport) return; + + for (const declaration of node.declarationList.declarations) { + if (ts.isVariableDeclaration(declaration) && + ts.isIdentifier(declaration.name) && + declaration.name.text === '__platforms') { + platformExportEnd = node.end; } } } @@ -218,40 +236,42 @@ function extractExistingPlatformExport(content, filePath) { const linesToRemove = new Set(); function visit(node) { - if (ts.isVariableStatement(node)) { - const hasExport = node.modifiers?.some( - mod => mod.kind === ts.SyntaxKind.ExportKeyword - ); + if (!ts.isVariableStatement(node)) return; + + const hasExport = node.modifiers?.some( + mod => mod.kind === ts.SyntaxKind.ExportKeyword + ); + + if (!hasExport) return; + + for (const declaration of node.declarationList.declarations) { + if (!ts.isVariableDeclaration(declaration) || + !ts.isIdentifier(declaration.name) || + declaration.name.text !== '__platforms') { + continue; + } - if (hasExport) { - for (const declaration of node.declarationList.declarations) { - if (ts.isVariableDeclaration(declaration) && - ts.isIdentifier(declaration.name) && - declaration.name.text === '__platforms') { - // Extract the full statement - const startLine = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line; - const endLine = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line; - - const statementLines = []; - for (let i = startLine; i <= endLine; i++) { - linesToRemove.add(i); - statementLines.push(lines[i]); - } - exportStatement = statementLines.join('\n'); - } - } + // Extract the full statement + const startLine = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line; + const endLine = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line; + + const statementLines = []; + for (let i = startLine; i <= endLine; i++) { + linesToRemove.add(i); + statementLines.push(lines[i]); } + exportStatement = statementLines.join('\n'); } } ts.forEachChild(sourceFile, visit); if (!exportStatement) { - return { content, statement: null, removed: false }; + return { restContent: content, platformExportStatement: null }; } const filteredLines = lines.filter((_, index) => !linesToRemove.has(index)); - return { content: filteredLines.join('\n'), statement: exportStatement, removed: true }; + return { restContent: filteredLines.join('\n'), platformExportStatement: exportStatement }; } /** @@ -288,7 +308,7 @@ function processFile(filePath) { // If file already has valid platforms, use those (preserve existing values) // Otherwise, determine from filename or default to universal let platforms; - if (!existingPlatforms || needsFixing) { + if (needsFixing) { // No __platforms export or has errors, determine from filename const platformsFromFilename = getPlatformFromFilename(filePath); platforms = platformsFromFilename || ['__universal__']; @@ -303,9 +323,8 @@ function processFile(filePath) { if (needsFixing) { // Has issues (MISSING, NOT_ARRAY, NOT_LITERALS, INVALID_VALUES, etc.), fix them const extracted = extractExistingPlatformExport(content, filePath); - if (extracted.removed) { - content = extracted.content; - } + content = extracted.restContent; + action = 'fixed'; modified = true; @@ -323,20 +342,12 @@ function processFile(filePath) { // Extract it and move to end without modification const extracted = extractExistingPlatformExport(content, filePath); - if (extracted.removed) { - content = extracted.content; - - // Ensure Platform import exists - const importResult = ensurePlatformImport(content, filePath); - content = importResult.content; - - // Add the original statement at the end - content = addPlatformExportStatement(content, extracted.statement); - action = 'moved'; - modified = true; - } else { - return { skipped: true, reason: 'could not extract export' }; - } + content = extracted.restContent; + + // Add the original statement at the end + content = addPlatformExportStatement(content, extracted.platformExportStatement); + action = 'moved'; + modified = true; } if (modified) { diff --git a/scripts/test-validator.js b/scripts/test-validator.js index e0e0ccfe1..8c6d6dc03 100644 --- a/scripts/test-validator.js +++ b/scripts/test-validator.js @@ -1,11 +1,29 @@ #!/usr/bin/env node +/** + * Copyright 2025, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /** * Comprehensive test suite for platform isolation validator * * This test documents and validates all the compatibility rules */ + +/* eslint-disable @typescript-eslint/no-var-requires */ const assert = require('assert'); const validator = require('./validate-platform-isolation.js'); @@ -35,19 +53,43 @@ test('Node file can import universal', validator.isPlatformCompatible(['node'], ['__universal__']), true); test('Multi-platform file can import universal', validator.isPlatformCompatible(['browser', 'react_native'], ['__universal__']), true); +test('Universal file can import universal', + validator.isPlatformCompatible(['__universal__'], ['__universal__']), true); + console.log('\n2. SINGLE PLATFORM FILES'); console.log('-'.repeat(70)); test('Browser file can import from browser file', validator.isPlatformCompatible(['browser'], ['browser']), true); +test('Browser file can import from universal file', + validator.isPlatformCompatible(['browser'], ['__universal__']), true); test('Browser file CANNOT import from node file', validator.isPlatformCompatible(['browser'], ['node']), false); test('Node file can import from node file', validator.isPlatformCompatible(['node'], ['node']), true); +test('Node file can import from universal file', + validator.isPlatformCompatible(['node'], ['__universal__']), true); test('React Native file can import from react_native file', validator.isPlatformCompatible(['react_native'], ['react_native']), true); +test('React Native file can import from universal file', + validator.isPlatformCompatible(['react_native'], ['__universal__']), true); + + +console.log('\n3. UNIVERSAL IMPORTING FROM NON-UNIVERSAL'); +console.log('-'.repeat(70)); +test('Universal file CANNOT import from browser file', + validator.isPlatformCompatible(['__universal__'], ['browser']), false); +test('Universal file CANNOT import from node file', + validator.isPlatformCompatible(['__universal__'], ['node']), false); +test('Universal file CANNOT import from react_native file', + validator.isPlatformCompatible(['__universal__'], ['react_native']), false); +test('Universal file CANNOT import from [browser, react_native] file', + validator.isPlatformCompatible(['__universal__'], ['browser', 'react_native']), false); +test('Universal file CANNOT import from [browser, node] file', + validator.isPlatformCompatible(['__universal__'], ['browser', 'node']), false); + -console.log('\n3. SINGLE PLATFORM IMPORTING FROM MULTI-PLATFORM'); +console.log('\n4. SINGLE PLATFORM IMPORTING FROM MULTI-PLATFORM'); console.log('-'.repeat(70)); test('Browser file CAN import from [browser, react_native] file', validator.isPlatformCompatible(['browser'], ['browser', 'react_native']), true); @@ -56,10 +98,12 @@ test('React Native file CAN import from [browser, react_native] file', test('Node file CANNOT import from [browser, react_native] file', validator.isPlatformCompatible(['node'], ['browser', 'react_native']), false); -console.log('\n4. MULTI-PLATFORM FILES (strictest rules)'); +console.log('\n5. MULTI-PLATFORM FILES (strictest rules)'); console.log('-'.repeat(70)); test('[browser, react_native] file CAN import from [browser, react_native] file', validator.isPlatformCompatible(['browser', 'react_native'], ['browser', 'react_native']), true); +test('[browser, react_native] file CAN import from universal file', + validator.isPlatformCompatible(['browser', 'react_native'], ['__universal__']), true); test('[browser, react_native] file CANNOT import from browser-only file', validator.isPlatformCompatible(['browser', 'react_native'], 'browser'), false); test('[browser, react_native] file CANNOT import from react_native-only file', @@ -67,22 +111,6 @@ test('[browser, react_native] file CANNOT import from react_native-only file', test('[browser, react_native] file CANNOT import from node file', validator.isPlatformCompatible(['browser', 'react_native'], 'node'), false); -console.log('\n5. SUPPORTED PLATFORMS EXTRACTION'); -console.log('-'.repeat(70)); -const testExport1 = `export const __platforms = ['browser', 'react_native'];`; -const platforms1 = validator.extractSupportedPlatforms(testExport1); -test('Extract __platforms array', - JSON.stringify(platforms1), JSON.stringify(['browser', 'react_native'])); - -const testExport2 = `export const __platforms: string[] = ["browser", "node"];`; -const platforms2 = validator.extractSupportedPlatforms(testExport2); -test('Extract __platforms with type annotation', - JSON.stringify(platforms2), JSON.stringify(['browser', 'node'])); - -const testExport3 = `export const __platforms = ['__universal__'];`; -const platforms3 = validator.extractSupportedPlatforms(testExport3); -test('Extract __universal__ marker', - JSON.stringify(platforms3), JSON.stringify(['__universal__'])); console.log('\n' + '='.repeat(70)); console.log(`\nResults: ${passed} passed, ${failed} failed`); diff --git a/scripts/validate-platform-isolation-ts.js b/scripts/validate-platform-isolation.js similarity index 98% rename from scripts/validate-platform-isolation-ts.js rename to scripts/validate-platform-isolation.js index 1985b0d4f..157983f60 100644 --- a/scripts/validate-platform-isolation-ts.js +++ b/scripts/validate-platform-isolation.js @@ -19,13 +19,13 @@ /** * Platform Isolation Validator * - * This script ensures that platform-specific entry points only import - * from universal or compatible platform files. + * This script ensures that platform-specific modules only import + * from universal or compatible platform modules. * * Platform Detection: * - ALL source files (except tests) MUST export __platforms array * - Universal files use: export const __platforms = ['__universal__']; - * - Platform-specific files use: export const __platforms = ['browser', 'node']; + * - Platform-specific files use platforn names, e.g: export const __platforms = ['browser', 'node']; * - Valid platform values are dynamically read from Platform type in platform_support.ts * * Rules: From 246a9e971c8d222946ea9a1b6deadd2667f92902 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Fri, 28 Nov 2025 20:23:51 +0600 Subject: [PATCH 30/40] update script readme --- package.json | 2 +- scripts/README.md | 57 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index b598508e2..c47c78d44 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "clean:win": "(if exist dist rd /s/q dist)", "lint": "tsc --noEmit && eslint 'lib/**/*.js' 'lib/**/*.ts'", "validate-platform-isolation": "node scripts/validate-platform-isolation.js", - "test-platform-isolation": "node scripts/test-validator.js", + "test-isolation-rules": "node scripts/test-validator.js", "add-platform-exports": "node scripts/add-platform-exports.js", "test-vitest": "vitest run", "test-mocha": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register -r tsconfig-paths/register -r lib/tests/exit_on_unhandled_rejection.js 'lib/**/*.tests.ts' 'lib/**/*.tests.js'", diff --git a/scripts/README.md b/scripts/README.md index cd2758e71..966327ffe 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -2,7 +2,7 @@ This directory contains build and validation scripts for the JavaScript SDK. -## validate-platform-isolation-ts.js +## validate-platform-isolation.js The main platform isolation validator that ensures platform-specific code is properly isolated to prevent runtime errors when building for different platforms (Browser, Node.js, React Native). @@ -12,7 +12,7 @@ The main platform isolation validator that ensures platform-specific code is pro ```bash # Run manually -node scripts/validate-platform-isolation-ts.js +node scripts/validate-platform-isolation.js # Run via npm script npm run validate-platform-isolation @@ -24,33 +24,68 @@ npm run build ### How It Works The script: -1. Scans all TypeScript/JavaScript files in the `lib/` directory -2. **Requires every non-test file to export `__platforms` array** declaring supported platforms +1. Scans all TypeScript/JavaScript files configured in the in the `.platform-isolation.config.js` config file. +2. **Requires every file to export `__platforms` array** declaring supported platforms 3. **Validates platform values** by reading the Platform type definition from `platform_support.ts` 4. Parses import statements (ES6 imports, require(), dynamic imports) using TypeScript AST 5. **Validates import compatibility**: For each import, ensures the imported file supports ALL platforms that the importing file runs on 6. Fails with exit code 1 if any violations are found, if `__platforms` export is missing, or if invalid platform values are used **Import Rule**: When file A imports file B, file B must support ALL platforms that file A runs on. -- Example: A universal file can only import from universal files (or files supporting all platforms) +- Example: A universal file can only import from universal files. - Example: A browser file can import from universal files or any file that supports browser -**Note:** The validator can theoretically support file naming conventions (`.browser.ts`, etc.) in addition to `__platforms` exports, but currently enforces only the `__platforms` export. File naming is not validated and is considered redundant. +**Note:** The validator can be updated to support file naming conventions (`.browser.ts`, etc.) in addition to `__platforms` exports, but currently enforces only the `__platforms` export. File naming is not validated and is used for convenience. -### Exit Codes -- `0`: All platform-specific files are properly isolated -- `1`: Violations found or script error +## add-platform-exports.js + +Auto-fix script that adds or updates `__platforms` exports in files. This script helps maintain platform isolation by automatically adding the required `__platforms` export to files that are missing it or have invalid exports. + +### Usage + +```bash +# Run via npm script +npm run add-platform-exports + +# Or run directly +node scripts/add-platform-exports.js +``` + +### How It Works + +The script: +1. Scans all TypeScript/JavaScript files configured in `.platform-isolation.config.js` +2. For each file, checks if it has a valid `__platforms` export +3. **Determines platform from filename**: Files with platform-specific naming (`.browser.ts`, `.node.ts`, `.react_native.ts`) get their specific platform(s) +4. **Defaults to universal**: Files without platform-specific naming get `['__universal__']` +5. **Adds Platform type import**: Calculates correct relative path and ensures `import type { Platform } from '../path/to/platform_support'` exists +6. **Moves export to end**: Places `__platforms` export at the end of the file for consistency +7. Preserves existing platform values for files that already have valid `__platforms` exports + +### Actions + +- **Fixed**: File had missing, invalid, or incorrectly formatted `__platforms` export - now corrected +- **Moved**: File had valid `__platforms` export but not at the end - moved to end +- **Skipped**: File already has valid `__platforms` export at the end + +### Platform Detection + +- `file.browser.ts` → `['browser']` +- `file.node.ts` → `['node']` +- `file.react_native.ts` → `['react_native']` +- `file.browser.node.ts` → `['browser', 'node']` +- `file.ts` → `['__universal__']` ## test-validator.js -Comprehensive test suite for the platform isolation validator. Documents and validates all compatibility rules. +Comprehensive test suite for the platform isolation rules. Documents and validates all compatibility rules. ### Usage ```bash # Run via npm script -npm run test-platform-isolation +npm run test-isolation-rules # Or run directly node scripts/test-validator.js From 99f710be572a3a105b49b9b46b63ea266192967c Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Fri, 28 Nov 2025 20:40:16 +0600 Subject: [PATCH 31/40] lint doc updates --- eslint-local-rules/README.md | 50 +++++++++---------- eslint-local-rules/index.js | 16 ++++++ .../require-platform-declaration.js | 18 +++++++ 3 files changed, 59 insertions(+), 25 deletions(-) diff --git a/eslint-local-rules/README.md b/eslint-local-rules/README.md index 7df08767e..6a166c615 100644 --- a/eslint-local-rules/README.md +++ b/eslint-local-rules/README.md @@ -6,53 +6,53 @@ This directory contains custom ESLint rules specific to this project. ### `require-platform-declaration` -**Purpose:** **Enforces that every non-test source file exports `__platforms`** to declare which platforms it supports. +**Purpose:** **Enforces that every configured source file exports `__platforms`** to declare which platforms it supports. -**Why:** This is a mandatory requirement for platform isolation. The rule catches missing declarations at lint time, before build or runtime. +**Why:** This is a mandatory requirement for platform isolation. The rule catches missing declarations at lint time. -**Requirement:** Every `.ts`/`.js` file in `lib/` (except tests) MUST export `__platforms` array with valid platform values. - -**Enforcement:** -- ✅ Enabled for all `.ts` files in `lib/` directory -- ❌ Disabled for test files (`.spec.ts`, `.test.ts`, etc.) -- ❌ Disabled for `__mocks__` and `tests` directories +**Requirement:** Every configured source file MUST export `__platforms` array with valid platform values. **Valid Examples:** ```typescript // Universal file (all platforms) -export const __platforms = ['__universal__'] as const; +export const __platforms: Platform[] = ['__universal__']; // Platform-specific file -export const __platforms = ['browser', 'node'] as const; +export const __platforms: Platform[] = ['browser', 'node']; -// With type annotation -export const __platforms: Platform[] = ['react_native'] as const; +// Single platform +export const __platforms: Platform[] = ['react_native']; ``` **Invalid:** ```typescript // Missing __platforms export -// ESLint Error: File must export __platforms to declare which platforms it supports +// ESLint Error: File must export __platforms to declare which platforms it supports. Example: export const __platforms = ['__universal__']; + +// Not an array +export const __platforms: Platform[] = 'browser'; +// ESLint Error: __platforms must be an array literal. Example: export const __platforms = ['browser', 'node']; + +// Empty array +export const __platforms: Platform[] = []; +// ESLint Error: __platforms array cannot be empty. Specify at least one platform or use ['__universal__']. + +// Using variables or computed values +const myPlatform = 'browser'; +export const __platforms: Platform[] = [myPlatform]; +// ESLint Error: __platforms must only contain string literals. Do NOT use variables, computed values, or spread operators. + +// Invalid platform value +export const __platforms: Platform[] = ['desktop']; +// ESLint Error: Invalid platform value "desktop". Valid platforms are: 'browser', 'node', 'react_native', '__universal__' ``` ## Configuration The rules are loaded via `eslint-plugin-local-rules` and configured in `.eslintrc.js`: -```javascript -{ - plugins: ['local-rules'], - overrides: [{ - files: ['*.ts', '!*.spec.ts', '!*.test.ts'], - rules: { - 'local-rules/require-platform-declaration': 'error' - } - }] -} -``` - ## Adding New Rules 1. Create a new rule file in this directory (e.g., `my-rule.js`) diff --git a/eslint-local-rules/index.js b/eslint-local-rules/index.js index 587ccd041..04ae81c0c 100644 --- a/eslint-local-rules/index.js +++ b/eslint-local-rules/index.js @@ -1,3 +1,19 @@ +/** + * Copyright 2025, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /** * Local ESLint Rules * diff --git a/eslint-local-rules/require-platform-declaration.js b/eslint-local-rules/require-platform-declaration.js index f9bb51e0e..c48adea18 100644 --- a/eslint-local-rules/require-platform-declaration.js +++ b/eslint-local-rules/require-platform-declaration.js @@ -1,3 +1,19 @@ +/** + * Copyright 2025, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + /** * ESLint Rule: require-platform-declaration * @@ -17,6 +33,8 @@ * // Not exported as const array */ +/* eslint-disable @typescript-eslint/no-var-requires */ + const { getValidPlatforms } = require('../scripts/platform-utils'); module.exports = { From c4ffd43de236af500bb26a989c112ea5f81db096 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Fri, 28 Nov 2025 21:42:54 +0600 Subject: [PATCH 32/40] refactor eslint rule --- .../require-platform-declaration.js | 110 +++++++++--------- 1 file changed, 53 insertions(+), 57 deletions(-) diff --git a/eslint-local-rules/require-platform-declaration.js b/eslint-local-rules/require-platform-declaration.js index c48adea18..1e3f5f7fd 100644 --- a/eslint-local-rules/require-platform-declaration.js +++ b/eslint-local-rules/require-platform-declaration.js @@ -42,7 +42,7 @@ module.exports = { type: 'problem', docs: { description: 'Require __platforms export with valid platform values in all source files', - category: 'Best Practices', + category: 'Possible Problems', recommended: true, }, messages: { @@ -62,66 +62,62 @@ module.exports = { return { ExportNamedDeclaration(node) { // Check for: export const __platforms = [...] - if (node.declaration && - node.declaration.type === 'VariableDeclaration') { + if (!node.declaration || node.declaration.type !== 'VariableDeclaration') return; + + for (const declarator of node.declaration.declarations) { + if (declarator.id.type !== 'Identifier' || declarator.id.name !== '__platforms') continue; - for (const declarator of node.declaration.declarations) { - if (declarator.id.type === 'Identifier' && - declarator.id.name === '__platforms') { - - hasPlatformExport = true; - - // Validate it's an array expression - let init = declarator.init; - - // Handle TSAsExpression: [...] as const - if (init && init.type === 'TSAsExpression') { - init = init.expression; - } - - // Handle TSTypeAssertion: [...] - if (init && init.type === 'TSTypeAssertion') { - init = init.expression; - } - - if (!init || init.type !== 'ArrayExpression') { - context.report({ - node: declarator, - messageId: 'notArray', - }); - return; - } - - // Check if array is empty - if (init.elements.length === 0) { + hasPlatformExport = true; + + // Validate it's an array expression + let init = declarator.init; + + // Handle TSAsExpression: [...] as const + if (init && init.type === 'TSAsExpression') { + init = init.expression; + } + + // Handle TSTypeAssertion: [...] + if (init && init.type === 'TSTypeAssertion') { + init = init.expression; + } + + if (!init || init.type !== 'ArrayExpression') { + context.report({ + node: declarator, + messageId: 'notArray', + }); + return; + } + + // Check if array is empty + if (init.elements.length === 0) { + context.report({ + node: init, + messageId: 'emptyArray', + }); + return; + } + + // Validate each array element is a valid platform string + for (const element of init.elements) { + if (element && element.type === 'Literal' && typeof element.value === 'string') { + if (!VALID_PLATFORMS.includes(element.value)) { context.report({ - node: init, - messageId: 'emptyArray', - }); - return; - } - - // Validate each array element is a valid platform string - for (const element of init.elements) { - if (element && element.type === 'Literal' && typeof element.value === 'string') { - if (!VALID_PLATFORMS.includes(element.value)) { - context.report({ - node: element, - messageId: 'invalidValues', - data: { - value: element.value, - validPlatforms: VALID_PLATFORMS.map(p => `'${p}'`).join(', ') - } - }); + node: element, + messageId: 'invalidValues', + data: { + value: element.value, + validPlatforms: VALID_PLATFORMS.map(p => `'${p}'`).join(', ') } - } else { - // Not a string literal - context.report({ - node: element || init, - messageId: 'notLiterals', - }); - } + }); } + } else { + // Not a string literal + context.report({ + node: element || init, + messageId: 'notLiterals', + }); } } } From 94cbeec4bf068ac50a682f01f3dab3f8a16f89b0 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Sat, 29 Nov 2025 00:53:22 +0600 Subject: [PATCH 33/40] update doc --- docs/PLATFORM_ISOLATION.md | 133 +++++++++++++++++-------------------- scripts/README.md | 2 +- 2 files changed, 61 insertions(+), 74 deletions(-) diff --git a/docs/PLATFORM_ISOLATION.md b/docs/PLATFORM_ISOLATION.md index a3b718d55..47ba93adc 100644 --- a/docs/PLATFORM_ISOLATION.md +++ b/docs/PLATFORM_ISOLATION.md @@ -24,14 +24,14 @@ export const __platforms = ['node']; // Node.js only export const __platforms = ['react_native']; // React Native only ``` -**For multi-platform files (but not all):** +**For multi-platform files:** ```typescript // lib/utils/web-features.ts export const __platforms = ['browser', 'react_native']; // Your code that works on both browser and react_native -export function getWindowSize() { +export function makeHttpRequest() { // Implementation that works on both platforms } ``` @@ -42,7 +42,7 @@ Valid platform identifiers: `'browser'`, `'node'`, `'react_native'`, `'__univers ### File Naming Convention (Optional) -While not enforced, you may optionally use file name suffixes for clarity: +While not enforced, you should use file name suffixes for clarity: - `.browser.ts` - Typically browser-specific - `.node.ts` - Typically Node.js-specific - `.react_native.ts` - Typically React Native-specific @@ -107,21 +107,22 @@ import { NodeRequestHandler } from './utils/http_request_handler/request_handler ``` ```typescript -// In lib/index.react_native.ts (React Native platform only) -import { Config } from './shared_types'; // ✅ Universal file - -// If web-features.ts has: __platforms = ['browser', 'react_native'] -import { getWindowSize } from './utils/web-features'; // ✅ Compatible (supports react_native) +// In lib/vuid/vuid_manager_factory.react_native.ts (React Native platform only) +import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; // ✅ Compatible (react_native only) ``` -```typescript -// In lib/utils/web-api.ts -// export const __platforms = ['browser', 'react_native']; -import { Config } from './shared_types'; // ✅ Universal file -// If dom-helpers.ts has: __platforms = ['browser', 'react_native'] -import { helpers } from './dom-helpers'; // ✅ Compatible (supports BOTH browser and react_native) +```typescript +// In lib/event_processor/event_processor_factory.browser.ts (Browser platform only) +import { Config } from '../shared_types'; // ✅ Universal file +import defaultEventDispatcher from './event_dispatcher/default_dispatcher.browser'; // ✅ Compatible (browser + react_native, includes browser) +``` + +```typescript +// In lib/event_processor/event_dispatcher/default_dispatcher.browser.ts (Multi-platform: browser + react_native) +import { Config } from '../../shared_types'; // ✅ Universal file +import { BrowserRequestHandler } from '../../utils/http_request_handler/request_handler.browser'; // ✅ Compatible (also browser + react_native) ``` ❌ **Invalid Imports** @@ -133,24 +134,17 @@ import { NodeRequestHandler } from './utils/http_request_handler/request_handler ```typescript // In lib/index.node.ts (Node platform only) -// If web-features.ts has: __platforms = ['browser', 'react_native'] -import { getWindowSize } from './utils/web-features'; // ❌ Not compatible with Node +import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; // ❌ browser + react_native, doesn't support node ``` ```typescript // In lib/shared_types.ts (Universal file) -// export const __platforms = ['__universal__']; - -import { helper } from './helper.browser'; // ❌ Browser-only, universal file needs imports that work everywhere +import { AsyncStorageCache } from './utils/cache/async_storage_cache.react_native'; // ❌ React Native only, universal file needs imports that work everywhere ``` ```typescript -// In lib/utils/web-api.ts -// export const __platforms = ['browser', 'react_native']; - -// If helper.browser.ts is browser-only -import { helper } from './helper.browser'; // ❌ Browser-only, doesn't support react_native - +// In lib/event_processor/event_dispatcher/default_dispatcher.browser.ts +import { NodeRequestHandler } from '../../utils/http_request_handler/request_handler.node'; // ❌ Node-only, doesn't support browser or react_native // This file needs imports that work in BOTH browser AND react_native ``` @@ -172,7 +166,7 @@ npm run build The validation script (`scripts/validate-platform-isolation-ts.js`): -1. Scans all source files in the `lib/` directory (excluding tests) +1. Scans all TypeScript/JavaScript files configured in the in the `.platform-isolation.config.js` config file. 2. **Verifies every file has a `__platforms` export** - fails immediately if any file is missing this 3. **Validates all platform values** - ensures values in `__platforms` arrays are valid (read from Platform type) 4. Parses import statements using TypeScript AST (ES6 imports, require, dynamic imports) @@ -199,21 +193,44 @@ If platform isolation is violated, the build will fail with a detailed error mes - What platform the file belongs to - What platform it's incorrectly importing from -## Creating New Platform-Specific Code -When creating new platform-specific implementations: -### Single Platform +## Creating New Modules + +### Universal Code + +For code that works across all platforms, use `['__universal__']`: + +**Example: Universal utility function** + +```typescript +// lib/utils/string-helpers.ts +export const __platforms = ['__universal__']; + +// Pure JavaScript that works everywhere +export function capitalize(str: string): string { + return str.charAt(0).toUpperCase() + str.slice(1); +} + +export function sanitizeKey(key: string): string { + return key.replace(/[^a-zA-Z0-9_]/g, '_'); +} +``` + +### Platform-Specific Code + +**Single Platform** 1. **Add `__platforms` export** declaring the platform (e.g., `export const __platforms = ['browser'];`) -2. Optionally name the file with a platform suffix for clarity (e.g., `my-feature.browser.ts`) +2. Name the file with a platform suffix for clarity (e.g., `my-feature.browser.ts`) 3. Only import from universal or compatible platform files -4. Create a universal factory or interface if multiple platforms need different implementations **Example:** ```typescript // lib/features/my-feature.ts (universal interface) +export const __platforms = ['__universal__']; + export interface MyFeature { doSomething(): void; } @@ -235,64 +252,34 @@ export class NodeMyFeature implements MyFeature { // Node.js-specific implementation } } - -// lib/features/factory.browser.ts -import { BrowserMyFeature } from './my-feature.browser'; -export const createMyFeature = () => new BrowserMyFeature(); - -// lib/features/factory.node.ts -import { NodeMyFeature } from './my-feature.node'; -export const createMyFeature = () => new NodeMyFeature(); ``` -### Multiple Platforms (But Not All) +**Multiple Platforms (But Not Universal)** -For code that works on multiple platforms but not all, use the `__platforms` export: +For code that works on multiple platforms but is not universal, use the `__platforms` export to decalre the list of supported platforms: **Example: Browser + React Native only** ```typescript -// lib/utils/dom-helpers.ts +// lib/utils/http-helpers.ts export const __platforms = ['browser', 'react_native']; // This code works on both browser and react_native, but not node -export function getElementById(id: string): Element | null { - if (typeof document !== 'undefined') { - return document.getElementById(id); - } - // React Native polyfill or alternative - return null; +export function makeRequest(url: string): Promise { + // XMLHttpRequest is available in both browser and react_native + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.onload = () => resolve(xhr.responseText); + xhr.onerror = () => reject(new Error('Request failed')); + xhr.send(); + }); } ``` -**Example: Node + React Native only** - -```typescript -// lib/utils/native-crypto.ts -export const __platforms = ['node', 'react_native']; - -import crypto from 'crypto'; // Available in both Node and React Native - -export function generateHash(data: string): string { - return crypto.createHash('sha256').update(data).digest('hex'); -} -``` - -## Troubleshooting - -If you encounter a platform isolation error: - -1. **Check the error message** - It will tell you which file and line has the violation -2. **Identify the issue** - Look at the import statement on that line -3. **Fix the import**: - - If the code should be universal, remove the platform suffix from the imported file - - If the code must be platform-specific, create separate implementations for each platform - - Use factory patterns to abstract platform-specific instantiation - ## Benefits - ✅ Prevents runtime errors from platform-incompatible code - ✅ Catches issues at build time, not in production - ✅ Makes platform boundaries explicit and maintainable - ✅ Ensures each bundle only includes relevant code -- ✅ Works independently of linting tools (ESLint, Biome, etc.) diff --git a/scripts/README.md b/scripts/README.md index 966327ffe..17cb48019 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -25,7 +25,7 @@ npm run build The script: 1. Scans all TypeScript/JavaScript files configured in the in the `.platform-isolation.config.js` config file. -2. **Requires every file to export `__platforms` array** declaring supported platforms +2. **Requires every configured file to export `__platforms` array** declaring supported platforms 3. **Validates platform values** by reading the Platform type definition from `platform_support.ts` 4. Parses import statements (ES6 imports, require(), dynamic imports) using TypeScript AST 5. **Validates import compatibility**: For each import, ensures the imported file supports ALL platforms that the importing file runs on From 823e2c177583eb969299c0efc47a394ce4507b85 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Mon, 1 Dec 2025 15:14:11 +0600 Subject: [PATCH 34/40] copilot review updates --- README.md | 2 +- lib/index.browser.umdtests.js | 2 -- lib/project_config/optimizely_config.ts | 2 -- scripts/validate-platform-isolation.js | 4 ++-- vitest.config.mts | 10 ---------- 5 files changed, 3 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ebed7c3b8..3e22b28e3 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ If you're updating your SDK version, please check the appropriate migration guid ### Platform Isolation -The SDK supports multiple JavaScript platforms (Browser, Node.js, React Native) with a unified codebase. To prevent runtime errors from platform-specific code being bundled incorrectly, we enforce **platform isolation** constraints: +The SDK supports multiple JavaScript platforms (Browser, Node.js, React Native and universal) with a unified codebase. To prevent runtime errors from platform-specific code being bundled incorrectly, we enforce **platform isolation** constraints: - Every source file must declare which platforms it supports using `export const __platforms: Platform[] = [...]` - Files can only import from other files that support all their declared platforms diff --git a/lib/index.browser.umdtests.js b/lib/index.browser.umdtests.js index a9cbdeed2..a13f5046b 100644 --- a/lib/index.browser.umdtests.js +++ b/lib/index.browser.umdtests.js @@ -25,8 +25,6 @@ import packageJSON from '../package.json'; import eventDispatcher from './plugins/event_dispatcher/index.browser'; import { INVALID_CONFIG_OR_SOMETHING } from './exception_messages'; -export const __platforms = ['browser'] as const; - describe('javascript-sdk', function() { describe('APIs', function() { describe('createInstance', function() { diff --git a/lib/project_config/optimizely_config.ts b/lib/project_config/optimizely_config.ts index 36a369ea9..c74b0344b 100644 --- a/lib/project_config/optimizely_config.ts +++ b/lib/project_config/optimizely_config.ts @@ -34,8 +34,6 @@ import { VariationVariable, } from '../shared_types'; - -import { DATAFILE_VERSIONS } from '../utils/enums'; import { Platform } from '../platform_support'; interface FeatureVariablesMap { diff --git a/scripts/validate-platform-isolation.js b/scripts/validate-platform-isolation.js index 157983f60..262c57d36 100644 --- a/scripts/validate-platform-isolation.js +++ b/scripts/validate-platform-isolation.js @@ -25,7 +25,7 @@ * Platform Detection: * - ALL source files (except tests) MUST export __platforms array * - Universal files use: export const __platforms = ['__universal__']; - * - Platform-specific files use platforn names, e.g: export const __platforms = ['browser', 'node']; + * - Platform-specific files use platform names, e.g: export const __platforms = ['browser', 'node']; * - Valid platform values are dynamically read from Platform type in platform_support.ts * * Rules: @@ -59,7 +59,7 @@ const compilerOptions = ts.convertCompilerOptionsFromJson( const config = loadConfig(); const configPath = path.join(WORKSPACE_ROOT, '.platform-isolation.config.js'); -// Track files with errrors in __platforms export +// Track files with errors in __platforms export const fileErrors = new Map(); /** diff --git a/vitest.config.mts b/vitest.config.mts index cc25cd3c2..1bce36eb0 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -31,15 +31,5 @@ export default defineConfig({ enabled: true, tsconfig: 'tsconfig.spec.json', }, - coverage: { - provider: 'istanbul', - include: ['lib/**/*.ts'], - exclude: [ - '**/tests/**', - '**/*.spec.ts', - '**/*.gen.ts', - '**/*d.ts', - ], - }, }, }); From a6b69fd21c4795728befbbc1f4a055fca0bc06c6 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Mon, 1 Dec 2025 15:48:47 +0600 Subject: [PATCH 35/40] update add script --- lib/project_config/optimizely_config.ts | 2 +- scripts/add-platform-exports.js | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/project_config/optimizely_config.ts b/lib/project_config/optimizely_config.ts index c74b0344b..74027765d 100644 --- a/lib/project_config/optimizely_config.ts +++ b/lib/project_config/optimizely_config.ts @@ -484,4 +484,4 @@ export function createOptimizelyConfig(configObj: ProjectConfig, datafile: strin return new OptimizelyConfig(configObj, datafile, logger); } -export const __platforms: Platform[] = ['__universal__']; +export const __platforms: Platform[] = [ '__universal__' ]; diff --git a/scripts/add-platform-exports.js b/scripts/add-platform-exports.js index 8f29150d8..9b039a808 100644 --- a/scripts/add-platform-exports.js +++ b/scripts/add-platform-exports.js @@ -260,6 +260,12 @@ function extractExistingPlatformExport(content, filePath) { linesToRemove.add(i); statementLines.push(lines[i]); } + + // If the next line is whitespace, extract it as well + if (endLine + 1 < lines.length && lines[endLine + 1].trim() === '') { + linesToRemove.add(endLine + 1); + } + exportStatement = statementLines.join('\n'); } } @@ -325,7 +331,13 @@ function processFile(filePath) { const extracted = extractExistingPlatformExport(content, filePath); content = extracted.restContent; - action = 'fixed'; + if (extracted.platformExportStatement) { + // There was an existing export statement, we removed it + // so we will be replacing it with a fixed one + action = 'fixed'; + } else { + action = 'added'; + } modified = true; // Ensure Platform import exists From c7be4d47c5a28118ef1e6fc4e88a1bcf52407045 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Mon, 1 Dec 2025 16:11:24 +0600 Subject: [PATCH 36/40] rm --- ESLINT_TROUBLESHOOTING.md | 84 --------------------------------------- 1 file changed, 84 deletions(-) delete mode 100644 ESLINT_TROUBLESHOOTING.md diff --git a/ESLINT_TROUBLESHOOTING.md b/ESLINT_TROUBLESHOOTING.md deleted file mode 100644 index 1731c5e71..000000000 --- a/ESLINT_TROUBLESHOOTING.md +++ /dev/null @@ -1,84 +0,0 @@ -# ESLint Rule Troubleshooting - -## The Rule is Working! - -The `require-platform-declaration` rule **is** working correctly from the command line: - -```bash -$ npx eslint lib/core/custom_attribute_condition_evaluator/index.ts - -lib/core/custom_attribute_condition_evaluator/index.ts - 16:1 error File must export __platforms to declare which platforms - it supports. Example: export const __platforms = ['__universal__'] as const; -``` - -## VSCode Not Showing Errors? - -If VSCode isn't showing the ESLint errors, try these steps: - -### 1. Restart ESLint Server -- Open Command Palette: `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Windows/Linux) -- Type: `ESLint: Restart ESLint Server` -- Press Enter - -### 2. Check ESLint Extension is Installed -- Open Extensions panel: `Cmd+Shift+X` (Mac) or `Ctrl+Shift+X` (Windows/Linux) -- Search for "ESLint" by Microsoft -- Make sure it's installed and enabled - -### 3. Check ESLint Output -- Open Output panel: `Cmd+Shift+U` (Mac) or `Ctrl+Shift+U` (Windows/Linux) -- Select "ESLint" from the dropdown -- Look for any error messages - -### 4. Reload VSCode Window -- Open Command Palette: `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Windows/Linux) -- Type: `Developer: Reload Window` -- Press Enter - -### 5. Check File is Being Linted -The rule only applies to: -- ✅ Files in `lib/` or `src/` directory -- ✅ TypeScript files (`.ts`) -- ❌ Test files (`.spec.ts`, `.test.ts`, etc.) -- ❌ Declaration files (`.d.ts`) - -### 6. Verify ESLint Configuration -Check that `.eslintrc.js` has the parser set: -```javascript -parser: '@typescript-eslint/parser', -``` - -And that the rule is in the overrides: -```javascript -overrides: [{ - files: ['*.ts', '!*.spec.ts', '!*.test.ts', '!*.tests.ts', '!*.test-d.ts'], - rules: { - 'local-rules/require-platform-declaration': 'error', - } -}] -``` - -## Manual Verification - -You can always verify the rule works by running: - -```bash -# Check a specific file -npx eslint lib/service.ts - -# Check all lib files (shows only errors) -npx eslint lib/**/*.ts --quiet -``` - -## Adding __platforms - -To fix the error, add this export to your file (after imports): - -```typescript -// Universal file (all platforms) -export const __platforms = ['__universal__'] as const; - -// OR platform-specific file -export const __platforms = ['browser', 'node'] as const; -``` From b62aad88bab125c24e85e63e8e8acffa9d56c073 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 2 Dec 2025 21:45:51 +0600 Subject: [PATCH 37/40] fix imports --- lib/client_factory.ts | 3 +-- lib/common_exports.ts | 4 ++-- lib/core/audience_evaluator/index.ts | 3 +-- .../odp_segment_condition_evaluator/index.ts | 3 +-- lib/core/bucketer/bucket_value_generator.ts | 3 +-- lib/core/bucketer/index.ts | 3 +-- lib/core/condition_tree_evaluator/index.ts | 4 ++-- lib/core/custom_attribute_condition_evaluator/index.ts | 3 +-- lib/core/decision/index.ts | 3 +-- lib/core/decision_service/cmab/cmab_client.ts | 3 +-- lib/core/decision_service/cmab/cmab_service.ts | 2 -- lib/core/decision_service/index.ts | 2 -- lib/error/error_handler.ts | 4 ++-- lib/error/error_notifier.ts | 3 +-- lib/error/error_notifier_factory.ts | 3 +-- lib/error/error_reporter.ts | 3 +-- lib/error/optimizly_error.ts | 3 +-- lib/event_processor/batch_event_processor.react_native.ts | 3 +-- lib/event_processor/batch_event_processor.ts | 3 +-- lib/event_processor/event_builder/log_event.ts | 3 +-- lib/event_processor/event_builder/user_event.ts | 2 -- .../event_dispatcher/default_dispatcher.browser.ts | 3 +-- .../event_dispatcher/default_dispatcher.node.ts | 3 +-- lib/event_processor/event_dispatcher/default_dispatcher.ts | 3 +-- lib/event_processor/event_dispatcher/event_dispatcher.ts | 3 +-- .../event_dispatcher/event_dispatcher_factory.ts | 3 +-- .../event_dispatcher/send_beacon_dispatcher.browser.ts | 3 +-- lib/event_processor/event_processor.ts | 3 +-- lib/event_processor/event_processor_factory.browser.ts | 2 -- lib/event_processor/event_processor_factory.node.ts | 2 -- lib/event_processor/event_processor_factory.react_native.ts | 2 -- lib/event_processor/event_processor_factory.ts | 3 +-- lib/event_processor/event_processor_factory.universal.ts | 4 +--- lib/event_processor/event_store.ts | 2 -- lib/event_processor/forwarding_event_processor.ts | 3 +-- lib/export_types.ts | 4 ++-- lib/feature_toggle.ts | 4 ++-- lib/index.browser.ts | 1 - lib/index.node.ts | 1 - lib/index.react_native.ts | 1 - lib/index.universal.ts | 2 -- lib/logging/logger.ts | 3 +-- lib/logging/logger_factory.ts | 3 +-- lib/message/error_message.ts | 4 ++-- lib/message/log_message.ts | 4 ++-- lib/message/message_resolver.ts | 3 +-- lib/notification_center/index.ts | 3 +-- lib/notification_center/type.ts | 2 -- lib/odp/constant.ts | 4 ++-- lib/odp/event_manager/odp_event.ts | 4 ++-- lib/odp/event_manager/odp_event_api_manager.ts | 3 +-- lib/odp/event_manager/odp_event_manager.ts | 2 -- lib/odp/odp_config.ts | 3 +-- lib/odp/odp_manager.ts | 3 +-- lib/odp/odp_manager_factory.browser.ts | 3 +-- lib/odp/odp_manager_factory.node.ts | 3 +-- lib/odp/odp_manager_factory.react_native.ts | 3 +-- lib/odp/odp_manager_factory.ts | 3 +-- lib/odp/odp_manager_factory.universal.ts | 3 +-- lib/odp/odp_types.ts | 4 ++-- lib/odp/segment_manager/odp_response_schema.ts | 3 +-- lib/odp/segment_manager/odp_segment_api_manager.ts | 2 +- lib/odp/segment_manager/odp_segment_manager.ts | 3 +-- lib/odp/segment_manager/optimizely_segment_option.ts | 4 ++-- lib/odp/ua_parser/ua_parser.ts | 3 +-- lib/odp/ua_parser/user_agent_info.ts | 4 ++-- lib/odp/ua_parser/user_agent_parser.ts | 3 +-- lib/optimizely/index.ts | 4 +--- lib/optimizely_decision/index.ts | 3 +-- lib/optimizely_user_context/index.ts | 2 -- lib/project_config/config_manager_factory.browser.ts | 3 +-- lib/project_config/config_manager_factory.node.ts | 3 +-- lib/project_config/config_manager_factory.react_native.ts | 3 +-- lib/project_config/config_manager_factory.ts | 3 +-- lib/project_config/config_manager_factory.universal.ts | 3 +-- lib/project_config/constant.ts | 4 ++-- lib/project_config/datafile_manager.ts | 3 +-- lib/project_config/optimizely_config.ts | 2 -- lib/project_config/polling_datafile_manager.ts | 2 -- lib/project_config/project_config.ts | 2 -- lib/project_config/project_config_manager.ts | 1 - lib/project_config/project_config_schema.ts | 3 +-- lib/service.ts | 3 +-- lib/shared_types.ts | 3 +-- lib/utils/attributes_validator/index.ts | 3 +-- lib/utils/cache/async_storage_cache.react_native.ts | 3 +-- lib/utils/cache/cache.ts | 2 -- lib/utils/cache/in_memory_lru_cache.ts | 3 +-- lib/utils/cache/local_storage_cache.browser.ts | 3 +-- lib/utils/cache/store.ts | 3 +-- lib/utils/cache/store_validator.ts | 4 ++-- lib/utils/config_validator/index.ts | 2 -- lib/utils/enums/index.ts | 4 ++-- lib/utils/event_emitter/event_emitter.ts | 3 +-- lib/utils/event_tag_utils/index.ts | 3 +-- lib/utils/event_tags_validator/index.ts | 3 +-- lib/utils/executor/backoff_retry_runner.ts | 3 +-- lib/utils/executor/serial_runner.ts | 3 +-- lib/utils/fns/index.ts | 3 +-- lib/utils/http_request_handler/http.ts | 4 ++-- lib/utils/http_request_handler/http_util.ts | 2 +- lib/utils/http_request_handler/request_handler.browser.ts | 3 +-- lib/utils/http_request_handler/request_handler.node.ts | 3 +-- lib/utils/http_request_handler/request_handler_validator.ts | 3 +-- lib/utils/id_generator/index.ts | 4 ++-- .../@react-native-async-storage/async-storage.ts | 3 +-- lib/utils/json_schema_validator/index.ts | 3 +-- lib/utils/microtask/index.ts | 4 ++-- lib/utils/promise/operation_value.ts | 3 +-- lib/utils/promise/resolvablePromise.ts | 4 ++-- lib/utils/repeater/repeater.ts | 3 +-- lib/utils/semantic_version/index.ts | 3 +-- lib/utils/string_value_validator/index.ts | 4 ++-- lib/utils/type.ts | 4 ++-- lib/utils/user_profile_service_validator/index.ts | 3 +-- lib/vuid/vuid.ts | 3 +-- lib/vuid/vuid_manager.ts | 3 +-- lib/vuid/vuid_manager_factory.browser.ts | 3 +-- lib/vuid/vuid_manager_factory.node.ts | 3 +-- lib/vuid/vuid_manager_factory.react_native.ts | 3 +-- lib/vuid/vuid_manager_factory.ts | 3 +-- 121 files changed, 122 insertions(+), 238 deletions(-) diff --git a/lib/client_factory.ts b/lib/client_factory.ts index 5f4bac98a..6601a5739 100644 --- a/lib/client_factory.ts +++ b/lib/client_factory.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './platform_support'; import { Config } from "./shared_types"; import { extractLogger } from "./logging/logger_factory"; import { extractErrorNotifier } from "./error/error_notifier_factory"; @@ -29,7 +28,7 @@ import { CmabCacheValue, DefaultCmabService } from "./core/decision_service/cmab import { InMemoryLruCache } from "./utils/cache/in_memory_lru_cache"; import { transformCache, CacheWithRemove } from "./utils/cache/cache"; import { ConstantBackoff } from "./utils/repeater/repeater"; - +import { Platform } from './platform_support'; export type OptimizelyFactoryConfig = Config & { requestHandler: RequestHandler; diff --git a/lib/common_exports.ts b/lib/common_exports.ts index b3cbd940c..a8950d0ff 100644 --- a/lib/common_exports.ts +++ b/lib/common_exports.ts @@ -1,5 +1,3 @@ -import { Platform } from './platform_support'; - /** * Copyright 2023-2025 Optimizely * @@ -17,6 +15,8 @@ import { Platform } from './platform_support'; */ +import { Platform } from './platform_support'; + export { createStaticProjectConfigManager } from './project_config/config_manager_factory'; export { LogLevel } from './logging/logger'; diff --git a/lib/core/audience_evaluator/index.ts b/lib/core/audience_evaluator/index.ts index 335e61fd0..cc59b3f56 100644 --- a/lib/core/audience_evaluator/index.ts +++ b/lib/core/audience_evaluator/index.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import * as conditionTreeEvaluator from '../condition_tree_evaluator'; import * as customAttributeConditionEvaluator from '../custom_attribute_condition_evaluator'; import * as odpSegmentsConditionEvaluator from './odp_segment_condition_evaluator'; @@ -21,7 +20,7 @@ import { Audience, Condition, OptimizelyUserContext } from '../../shared_types'; import { CONDITION_EVALUATOR_ERROR, UNKNOWN_CONDITION_TYPE } from 'error_message'; import { AUDIENCE_EVALUATION_RESULT, EVALUATING_AUDIENCE} from 'log_message'; import { LoggerFacade } from '../../logging/logger'; - +import { Platform } from '../../platform_support'; export class AudienceEvaluator { private logger?: LoggerFacade; diff --git a/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts b/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts index ab9a8d004..b090e9dce 100644 --- a/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts +++ b/lib/core/audience_evaluator/odp_segment_condition_evaluator/index.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * * limitations under the License. * ***************************************************************************/ -import { Platform } from './../../../platform_support'; import { UNKNOWN_MATCH_TYPE } from 'error_message'; import { LoggerFacade } from '../../../logging/logger'; import { Condition, OptimizelyUserContext } from '../../../shared_types'; - +import { Platform } from '../../../platform_support'; const QUALIFIED_MATCH_TYPE = 'qualified'; diff --git a/lib/core/bucketer/bucket_value_generator.ts b/lib/core/bucketer/bucket_value_generator.ts index 17a08c14d..bec776994 100644 --- a/lib/core/bucketer/bucket_value_generator.ts +++ b/lib/core/bucketer/bucket_value_generator.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import murmurhash from 'murmurhash'; import { INVALID_BUCKETING_ID } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; - +import { Platform } from '../../platform_support'; const HASH_SEED = 1; const MAX_HASH_VALUE = Math.pow(2, 32); diff --git a/lib/core/bucketer/index.ts b/lib/core/bucketer/index.ts index 58d10ee92..479ad17c5 100644 --- a/lib/core/bucketer/index.ts +++ b/lib/core/bucketer/index.ts @@ -17,7 +17,6 @@ /** * Bucketer API for determining the variation id from the specified parameters */ -import { Platform } from './../../platform_support'; import { LoggerFacade } from '../../logging/logger'; import { DecisionResponse, @@ -29,7 +28,7 @@ import { INVALID_GROUP_ID } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; import { generateBucketValue } from './bucket_value_generator'; import { DecisionReason } from '../decision_service'; - +import { Platform } from '../../platform_support'; export const USER_NOT_IN_ANY_EXPERIMENT = 'User %s is not in any experiment of group %s.'; export const USER_NOT_BUCKETED_INTO_EXPERIMENT_IN_GROUP = 'User %s is not in experiment %s of group %s.'; diff --git a/lib/core/condition_tree_evaluator/index.ts b/lib/core/condition_tree_evaluator/index.ts index 434fd5d0a..9efd4008a 100644 --- a/lib/core/condition_tree_evaluator/index.ts +++ b/lib/core/condition_tree_evaluator/index.ts @@ -1,5 +1,3 @@ -import { Platform } from './../../platform_support'; - /**************************************************************************** * Copyright 2018, 2021, Optimizely, Inc. and contributors * * * @@ -17,6 +15,8 @@ import { Platform } from './../../platform_support'; ***************************************************************************/ +import { Platform } from '../../platform_support'; + const AND_CONDITION = 'and'; const OR_CONDITION = 'or'; const NOT_CONDITION = 'not'; diff --git a/lib/core/custom_attribute_condition_evaluator/index.ts b/lib/core/custom_attribute_condition_evaluator/index.ts index 936350159..44ba02b2c 100644 --- a/lib/core/custom_attribute_condition_evaluator/index.ts +++ b/lib/core/custom_attribute_condition_evaluator/index.ts @@ -14,7 +14,6 @@ * limitations under the License. * ***************************************************************************/ -import { Platform } from './../../platform_support'; import { Condition, OptimizelyUserContext } from '../../shared_types'; import fns from '../../utils/fns'; @@ -30,7 +29,7 @@ import { UNKNOWN_MATCH_TYPE } from 'error_message'; import { LoggerFacade } from '../../logging/logger'; - +import { Platform } from '../../platform_support'; const EXACT_MATCH_TYPE = 'exact'; const EXISTS_MATCH_TYPE = 'exists'; const GREATER_OR_EQUAL_THAN_MATCH_TYPE = 'ge'; diff --git a/lib/core/decision/index.ts b/lib/core/decision/index.ts index 9e2b67eda..0be3e1d7a 100644 --- a/lib/core/decision/index.ts +++ b/lib/core/decision/index.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { DecisionObj } from '../decision_service'; - +import { Platform } from '../../platform_support'; /** * Get experiment key from the provided decision object * @param {DecisionObj} decisionObj Object representing decision diff --git a/lib/core/decision_service/cmab/cmab_client.ts b/lib/core/decision_service/cmab/cmab_client.ts index 7528936e1..b5e90b343 100644 --- a/lib/core/decision_service/cmab/cmab_client.ts +++ b/lib/core/decision_service/cmab/cmab_client.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Platform } from './../../../platform_support'; import { OptimizelyError } from "../../../error/optimizly_error"; import { CMAB_FETCH_FAILED, INVALID_CMAB_FETCH_RESPONSE } from "../../../message/error_message"; import { UserAttributes } from "../../../shared_types"; @@ -24,7 +23,7 @@ import { RequestHandler } from "../../../utils/http_request_handler/http"; import { isSuccessStatusCode } from "../../../utils/http_request_handler/http_util"; import { BackoffController } from "../../../utils/repeater/repeater"; import { Producer } from "../../../utils/type"; - +import { Platform } from '../../../platform_support'; export interface CmabClient { fetchDecision( diff --git a/lib/core/decision_service/cmab/cmab_service.ts b/lib/core/decision_service/cmab/cmab_service.ts index 6e739e79b..004a146c0 100644 --- a/lib/core/decision_service/cmab/cmab_service.ts +++ b/lib/core/decision_service/cmab/cmab_service.ts @@ -33,8 +33,6 @@ import { RESET_CMAB_CACHE, } from 'log_message'; import { Platform } from '../../../platform_support'; - - export type CmabDecision = { variationId: string, cmabUuid: string, diff --git a/lib/core/decision_service/index.ts b/lib/core/decision_service/index.ts index 1041de7c4..099156b39 100644 --- a/lib/core/decision_service/index.ts +++ b/lib/core/decision_service/index.ts @@ -75,10 +75,8 @@ import { OptimizelyError } from '../../error/optimizly_error'; import { CmabService } from './cmab/cmab_service'; import { Maybe, OpType, OpValue } from '../../utils/type'; import { Value } from '../../utils/promise/operation_value'; - import { Platform } from '../../platform_support'; - export const EXPERIMENT_NOT_RUNNING = 'Experiment %s is not running.'; export const RETURNING_STORED_VARIATION = 'Returning previously activated variation "%s" of experiment "%s" for user "%s" from user profile.'; diff --git a/lib/error/error_handler.ts b/lib/error/error_handler.ts index 4bad9ecfa..b04ff4a8c 100644 --- a/lib/error/error_handler.ts +++ b/lib/error/error_handler.ts @@ -1,5 +1,3 @@ -import { Platform } from './../platform_support'; - /** * Copyright 2019, 2025, Optimizely * @@ -20,6 +18,8 @@ import { Platform } from './../platform_support'; * @interface ErrorHandler */ +import { Platform } from '../platform_support'; + export interface ErrorHandler { /** * @param {Error} exception diff --git a/lib/error/error_notifier.ts b/lib/error/error_notifier.ts index 807052591..303431655 100644 --- a/lib/error/error_notifier.ts +++ b/lib/error/error_notifier.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { MessageResolver } from "../message/message_resolver"; import { ErrorHandler } from "./error_handler"; import { OptimizelyError } from "./optimizly_error"; - +import { Platform } from '../platform_support'; export interface ErrorNotifier { notify(error: Error): void; diff --git a/lib/error/error_notifier_factory.ts b/lib/error/error_notifier_factory.ts index 6122ea36e..a7ee9fd39 100644 --- a/lib/error/error_notifier_factory.ts +++ b/lib/error/error_notifier_factory.ts @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { errorResolver } from "../message/message_resolver"; import { Maybe } from "../utils/type"; import { ErrorHandler } from "./error_handler"; import { DefaultErrorNotifier } from "./error_notifier"; - +import { Platform } from '../platform_support'; export const INVALID_ERROR_HANDLER = 'Invalid error handler'; diff --git a/lib/error/error_reporter.ts b/lib/error/error_reporter.ts index b1d909a0a..c465c0b9d 100644 --- a/lib/error/error_reporter.ts +++ b/lib/error/error_reporter.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { LoggerFacade } from "../logging/logger"; import { ErrorNotifier } from "./error_notifier"; import { OptimizelyError } from "./optimizly_error"; - +import { Platform } from '../platform_support'; export class ErrorReporter { private logger?: LoggerFacade; diff --git a/lib/error/optimizly_error.ts b/lib/error/optimizly_error.ts index 7081daa01..656aae456 100644 --- a/lib/error/optimizly_error.ts +++ b/lib/error/optimizly_error.ts @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { MessageResolver } from "../message/message_resolver"; import { sprintf } from "../utils/fns"; - +import { Platform } from '../platform_support'; export class OptimizelyError extends Error { baseMessage: string; diff --git a/lib/event_processor/batch_event_processor.react_native.ts b/lib/event_processor/batch_event_processor.react_native.ts index f1a770f78..e4cb2476a 100644 --- a/lib/event_processor/batch_event_processor.react_native.ts +++ b/lib/event_processor/batch_event_processor.react_native.ts @@ -14,12 +14,11 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { NetInfoState, addEventListener } from '@react-native-community/netinfo'; import { BatchEventProcessor, BatchEventProcessorConfig } from './batch_event_processor'; import { Fn } from '../utils/type'; - +import { Platform } from '../platform_support'; export class ReactNativeNetInfoEventProcessor extends BatchEventProcessor { private isInternetReachable = true; diff --git a/lib/event_processor/batch_event_processor.ts b/lib/event_processor/batch_event_processor.ts index 22f56e99c..81547352f 100644 --- a/lib/event_processor/batch_event_processor.ts +++ b/lib/event_processor/batch_event_processor.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { EventProcessor, ProcessableEvent } from "./event_processor"; import { getBatchedAsync, getBatchedSync, Store } from "../utils/cache/store"; import { EventDispatcher, EventDispatcherResponse, LogEvent } from "./event_dispatcher/event_dispatcher"; @@ -32,7 +31,7 @@ import { FAILED_TO_DISPATCH_EVENTS, SERVICE_NOT_RUNNING } from "error_message"; import { OptimizelyError } from "../error/optimizly_error"; import { sprintf } from "../utils/fns"; import { SERVICE_STOPPED_BEFORE_RUNNING } from "../service"; - +import { Platform } from '../platform_support'; export const DEFAULT_MIN_BACKOFF = 1000; export const DEFAULT_MAX_BACKOFF = 32000; diff --git a/lib/event_processor/event_builder/log_event.ts b/lib/event_processor/event_builder/log_event.ts index ef137fc1f..1d13bb5fb 100644 --- a/lib/event_processor/event_builder/log_event.ts +++ b/lib/event_processor/event_builder/log_event.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import { ConversionEvent, ImpressionEvent, UserEvent } from './user_event'; import { CONTROL_ATTRIBUTES } from '../../utils/enums'; @@ -21,7 +20,7 @@ import { CONTROL_ATTRIBUTES } from '../../utils/enums'; import { LogEvent } from '../event_dispatcher/event_dispatcher'; import { EventTags } from '../../shared_types'; import { Region } from '../../project_config/project_config'; - +import { Platform } from '../../platform_support'; const ACTIVATE_EVENT_KEY = 'campaign_activated' const CUSTOM_ATTRIBUTE_FEATURE_TYPE = 'custom' diff --git a/lib/event_processor/event_builder/user_event.ts b/lib/event_processor/event_builder/user_event.ts index 04d0e256f..76879db35 100644 --- a/lib/event_processor/event_builder/user_event.ts +++ b/lib/event_processor/event_builder/user_event.ts @@ -30,8 +30,6 @@ import { EventTags, UserAttributes } from '../../shared_types'; import { LoggerFacade } from '../../logging/logger'; import { DECISION_SOURCES } from '../../common_exports'; import { Platform } from '../../platform_support'; - - export type VisitorAttribute = { entityId: string key: string diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts index e0e5f8b04..9da3b132e 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.browser.ts @@ -16,11 +16,10 @@ // This implementation works in both browser and react_native environments -import { Platform } from './../../platform_support'; import { BrowserRequestHandler } from "../../utils/http_request_handler/request_handler.browser"; import { EventDispatcher } from './event_dispatcher'; import { DefaultEventDispatcher } from './default_dispatcher'; - +import { Platform } from '../../platform_support'; const eventDispatcher: EventDispatcher = new DefaultEventDispatcher(new BrowserRequestHandler()); diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.node.ts b/lib/event_processor/event_dispatcher/default_dispatcher.node.ts index a7193dc96..6d9e78dd7 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.node.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.node.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import { EventDispatcher } from './event_dispatcher'; import { NodeRequestHandler } from '../../utils/http_request_handler/request_handler.node'; import { DefaultEventDispatcher } from './default_dispatcher'; - +import { Platform } from '../../platform_support'; const eventDispatcher: EventDispatcher = new DefaultEventDispatcher(new NodeRequestHandler()); diff --git a/lib/event_processor/event_dispatcher/default_dispatcher.ts b/lib/event_processor/event_dispatcher/default_dispatcher.ts index d82659078..769efd554 100644 --- a/lib/event_processor/event_dispatcher/default_dispatcher.ts +++ b/lib/event_processor/event_dispatcher/default_dispatcher.ts @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import { OptimizelyError } from '../../error/optimizly_error'; import { ONLY_POST_REQUESTS_ARE_SUPPORTED } from 'error_message'; import { RequestHandler } from '../../utils/http_request_handler/http'; import { EventDispatcher, EventDispatcherResponse, LogEvent } from './event_dispatcher'; - +import { Platform } from '../../platform_support'; export class DefaultEventDispatcher implements EventDispatcher { private requestHandler: RequestHandler; diff --git a/lib/event_processor/event_dispatcher/event_dispatcher.ts b/lib/event_processor/event_dispatcher/event_dispatcher.ts index d8a1e3973..f9937c6a7 100644 --- a/lib/event_processor/event_dispatcher/event_dispatcher.ts +++ b/lib/event_processor/event_dispatcher/event_dispatcher.ts @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import { EventBatch } from "../event_builder/log_event"; - +import { Platform } from '../../platform_support'; export type EventDispatcherResponse = { statusCode?: number diff --git a/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts b/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts index 08a1e32a0..bf5258543 100644 --- a/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts +++ b/lib/event_processor/event_dispatcher/event_dispatcher_factory.ts @@ -14,13 +14,12 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { RequestHandler } from '../../utils/http_request_handler/http'; import { DefaultEventDispatcher } from './default_dispatcher'; import { EventDispatcher } from './event_dispatcher'; import { validateRequestHandler } from '../../utils/http_request_handler/request_handler_validator'; - +import { Platform } from '../../platform_support'; export const createEventDispatcher = (requestHander: RequestHandler): EventDispatcher => { validateRequestHandler(requestHander); diff --git a/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts b/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts index e5b66f704..a5fe6d496 100644 --- a/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts +++ b/lib/event_processor/event_dispatcher/send_beacon_dispatcher.browser.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { OptimizelyError } from '../../error/optimizly_error'; import { SEND_BEACON_FAILED } from 'error_message'; import { EventDispatcher, EventDispatcherResponse } from './event_dispatcher'; - +import { Platform } from '../../platform_support'; export type Event = { url: string; diff --git a/lib/event_processor/event_processor.ts b/lib/event_processor/event_processor.ts index 153accdf4..22718c841 100644 --- a/lib/event_processor/event_processor.ts +++ b/lib/event_processor/event_processor.ts @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { ConversionEvent, ImpressionEvent } from './event_builder/user_event' import { LogEvent } from './event_dispatcher/event_dispatcher' import { Service } from '../service' import { Consumer, Fn } from '../utils/type'; import { LoggerFacade } from '../logging/logger'; - +import { Platform } from '../platform_support'; export const DEFAULT_FLUSH_INTERVAL = 30000 // Unit is ms - default flush interval is 30s export const DEFAULT_BATCH_SIZE = 10 diff --git a/lib/event_processor/event_processor_factory.browser.ts b/lib/event_processor/event_processor_factory.browser.ts index 22db5492f..b69219a7f 100644 --- a/lib/event_processor/event_processor_factory.browser.ts +++ b/lib/event_processor/event_processor_factory.browser.ts @@ -30,8 +30,6 @@ import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; import { FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory'; import { DEFAULT_MAX_EVENTS_IN_STORE, EventStore } from './event_store'; import { Platform } from '../platform_support'; - - export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 1_000; export const EVENT_MAX_RETRIES_BROWSER = 5; diff --git a/lib/event_processor/event_processor_factory.node.ts b/lib/event_processor/event_processor_factory.node.ts index 57f4c18f7..15fe6f4e9 100644 --- a/lib/event_processor/event_processor_factory.node.ts +++ b/lib/event_processor/event_processor_factory.node.ts @@ -25,8 +25,6 @@ import { getForwardingEventProcessor, } from './event_processor_factory'; import { Platform } from '../platform_support'; - - export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 30_000; export const EVENT_MAX_RETRIES_NODE = 5; diff --git a/lib/event_processor/event_processor_factory.react_native.ts b/lib/event_processor/event_processor_factory.react_native.ts index 018160457..525dd91cf 100644 --- a/lib/event_processor/event_processor_factory.react_native.ts +++ b/lib/event_processor/event_processor_factory.react_native.ts @@ -29,8 +29,6 @@ import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_nati import { ReactNativeNetInfoEventProcessor } from './batch_event_processor.react_native'; import { DEFAULT_MAX_EVENTS_IN_STORE, EventStore } from './event_store'; import { Platform } from '../platform_support'; - - export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 1_000; export const EVENT_MAX_RETRIES_REACT_NATIVE = 5; diff --git a/lib/event_processor/event_processor_factory.ts b/lib/event_processor/event_processor_factory.ts index 139dd965b..7336dfa86 100644 --- a/lib/event_processor/event_processor_factory.ts +++ b/lib/event_processor/event_processor_factory.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { LogLevel } from "../logging/logger"; import { StartupLog } from "../service"; import { AsyncPrefixStore, Store, SyncPrefixStore } from "../utils/cache/store"; @@ -26,7 +25,7 @@ import { EventDispatcher } from "./event_dispatcher/event_dispatcher"; import { EventProcessor } from "./event_processor"; import { EVENT_STORE_PREFIX } from "./event_store"; import { ForwardingEventProcessor } from "./forwarding_event_processor"; - +import { Platform } from '../platform_support'; export const INVALID_EVENT_DISPATCHER = 'Invalid event dispatcher'; diff --git a/lib/event_processor/event_processor_factory.universal.ts b/lib/event_processor/event_processor_factory.universal.ts index 8a522f5f8..c2ec305aa 100644 --- a/lib/event_processor/event_processor_factory.universal.ts +++ b/lib/event_processor/event_processor_factory.universal.ts @@ -24,14 +24,12 @@ import { wrapEventProcessor, getPrefixEventStore, } from './event_processor_factory'; -import { Platform } from '../platform_support'; - export const DEFAULT_EVENT_BATCH_SIZE = 10; export const DEFAULT_EVENT_FLUSH_INTERVAL = 1_000; import { FAILED_EVENT_RETRY_INTERVAL } from './event_processor_factory'; - +import { Platform } from '../platform_support'; export const createForwardingEventProcessor = ( eventDispatcher: EventDispatcher ): OpaqueEventProcessor => { diff --git a/lib/event_processor/event_store.ts b/lib/event_processor/event_store.ts index d4b051a28..90ccf9ff6 100644 --- a/lib/event_processor/event_store.ts +++ b/lib/event_processor/event_store.ts @@ -13,8 +13,6 @@ import { SerialRunner } from "../utils/executor/serial_runner"; import { Maybe } from "../utils/type"; import { EventWithId } from "./batch_event_processor"; import { Platform } from '../platform_support'; - - export type StoredEvent = EventWithId & { _time?: { storedAt: number; diff --git a/lib/event_processor/forwarding_event_processor.ts b/lib/event_processor/forwarding_event_processor.ts index 73532da30..12decd5ab 100644 --- a/lib/event_processor/forwarding_event_processor.ts +++ b/lib/event_processor/forwarding_event_processor.ts @@ -15,7 +15,6 @@ */ -import { Platform } from './../platform_support'; import { LogEvent } from './event_dispatcher/event_dispatcher'; import { EventProcessor, ProcessableEvent } from './event_processor'; @@ -26,7 +25,7 @@ import { EventEmitter } from '../utils/event_emitter/event_emitter'; import { Consumer, Fn } from '../utils/type'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; import { sprintf } from '../utils/fns'; - +import { Platform } from '../platform_support'; export class ForwardingEventProcessor extends BaseService implements EventProcessor { private dispatcher: EventDispatcher; diff --git a/lib/export_types.ts b/lib/export_types.ts index 86aba4aca..51109d933 100644 --- a/lib/export_types.ts +++ b/lib/export_types.ts @@ -1,5 +1,3 @@ -import { Platform } from './platform_support'; - /** * Copyright 2022-2024, Optimizely * @@ -18,6 +16,8 @@ import { Platform } from './platform_support'; // config manager related types +import { Platform } from './platform_support'; + export type { StaticConfigManagerConfig, PollingConfigManagerConfig, diff --git a/lib/feature_toggle.ts b/lib/feature_toggle.ts index 0e2a6fad9..c567c46a3 100644 --- a/lib/feature_toggle.ts +++ b/lib/feature_toggle.ts @@ -1,5 +1,3 @@ -import { Platform } from './platform_support'; - /** * Copyright 2025, Optimizely * @@ -37,6 +35,8 @@ import { Platform } from './platform_support'; // export const wipFeat = () => false as const; +import { Platform } from './platform_support'; + export type IfActive boolean, Y, N = unknown> = ReturnType extends true ? Y : N; export const __platforms: Platform[] = ['__universal__']; diff --git a/lib/index.browser.ts b/lib/index.browser.ts index b7498f007..79e5968bf 100644 --- a/lib/index.browser.ts +++ b/lib/index.browser.ts @@ -20,7 +20,6 @@ import { EventDispatcher } from './event_processor/event_dispatcher/event_dispat import { JAVASCRIPT_CLIENT_ENGINE } from './utils/enums'; import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; import { Platform } from './platform_support'; - /** * Creates an instance of the Optimizely class * @param {Config} config diff --git a/lib/index.node.ts b/lib/index.node.ts index 48b99c159..0f3c0adbf 100644 --- a/lib/index.node.ts +++ b/lib/index.node.ts @@ -19,7 +19,6 @@ import { getOptimizelyInstance, OptimizelyFactoryConfig } from './client_factory import { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; import { NodeRequestHandler } from './utils/http_request_handler/request_handler.node'; import { Platform } from './platform_support'; - /** * Creates an instance of the Optimizely class * @param {Config} config diff --git a/lib/index.react_native.ts b/lib/index.react_native.ts index ee3afdd8a..6914a5f97 100644 --- a/lib/index.react_native.ts +++ b/lib/index.react_native.ts @@ -22,7 +22,6 @@ import { REACT_NATIVE_JS_CLIENT_ENGINE } from './utils/enums'; import { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; import { Platform } from './platform_support'; - /** * Creates an instance of the Optimizely class * @param {Config} config diff --git a/lib/index.universal.ts b/lib/index.universal.ts index b3f5f90de..3d731aa50 100644 --- a/lib/index.universal.ts +++ b/lib/index.universal.ts @@ -19,8 +19,6 @@ import { JAVASCRIPT_CLIENT_ENGINE } from './utils/enums'; import { RequestHandler } from './utils/http_request_handler/http'; import { Platform } from './platform_support'; - - export type UniversalConfig = Config & { requestHandler: RequestHandler; } diff --git a/lib/logging/logger.ts b/lib/logging/logger.ts index b959d6d16..c11b1250d 100644 --- a/lib/logging/logger.ts +++ b/lib/logging/logger.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { OptimizelyError } from '../error/optimizly_error'; import { MessageResolver } from '../message/message_resolver'; import { sprintf } from '../utils/fns' - +import { Platform } from '../platform_support'; export enum LogLevel { Debug, diff --git a/lib/logging/logger_factory.ts b/lib/logging/logger_factory.ts index 807802d55..57af2f50f 100644 --- a/lib/logging/logger_factory.ts +++ b/lib/logging/logger_factory.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { ConsoleLogHandler, LogHandler, LogLevel, OptimizelyLogger } from './logger'; import { errorResolver, infoResolver, MessageResolver } from '../message/message_resolver'; import { Maybe } from '../utils/type'; - +import { Platform } from '../platform_support'; export const INVALID_LOG_HANDLER = 'Invalid log handler'; export const INVALID_LEVEL_PRESET = 'Invalid level preset'; diff --git a/lib/message/error_message.ts b/lib/message/error_message.ts index 6a1e02c3c..624e4085d 100644 --- a/lib/message/error_message.ts +++ b/lib/message/error_message.ts @@ -1,5 +1,3 @@ -import { Platform } from './../platform_support'; - /** * Copyright 2024-2025, Optimizely * @@ -16,6 +14,8 @@ import { Platform } from './../platform_support'; * limitations under the License. */ +import { Platform } from '../platform_support'; + export const NOTIFICATION_LISTENER_EXCEPTION = 'Notification listener for (%s) threw exception: %s'; export const CONDITION_EVALUATOR_ERROR = 'Error evaluating audience condition of type %s: %s'; export const EXPERIMENT_KEY_NOT_IN_DATAFILE = 'Experiment key %s is not in datafile.'; diff --git a/lib/message/log_message.ts b/lib/message/log_message.ts index 1f4066c74..2e9180535 100644 --- a/lib/message/log_message.ts +++ b/lib/message/log_message.ts @@ -1,5 +1,3 @@ -import { Platform } from './../platform_support'; - /** * Copyright 2024, Optimizely * @@ -17,6 +15,8 @@ import { Platform } from './../platform_support'; */ +import { Platform } from '../platform_support'; + export const FEATURE_ENABLED_FOR_USER = 'Feature %s is enabled for user %s.'; export const FEATURE_NOT_ENABLED_FOR_USER = 'Feature %s is not enabled for user %s.'; export const FAILED_TO_PARSE_VALUE = 'Failed to parse event value "%s" from event tags.'; diff --git a/lib/message/message_resolver.ts b/lib/message/message_resolver.ts index cc04d0aa4..050207fc0 100644 --- a/lib/message/message_resolver.ts +++ b/lib/message/message_resolver.ts @@ -1,7 +1,6 @@ -import { Platform } from './../platform_support'; import { messages as infoMessages } from 'log_message'; import { messages as errorMessages } from 'error_message'; - +import { Platform } from '../platform_support'; export interface MessageResolver { resolve(baseMessage: string): string; diff --git a/lib/notification_center/index.ts b/lib/notification_center/index.ts index 089cfcddb..ee792179b 100644 --- a/lib/notification_center/index.ts +++ b/lib/notification_center/index.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { LoggerFacade } from '../logging/logger'; import { objectValues } from '../utils/fns'; @@ -24,7 +23,7 @@ import { EventEmitter } from '../utils/event_emitter/event_emitter'; import { NOTIFICATION_LISTENER_EXCEPTION } from 'error_message'; import { ErrorReporter } from '../error/error_reporter'; import { ErrorNotifier } from '../error/error_notifier'; - +import { Platform } from '../platform_support'; interface NotificationCenterOptions { logger?: LoggerFacade; diff --git a/lib/notification_center/type.ts b/lib/notification_center/type.ts index 81b0d9839..1d423b13d 100644 --- a/lib/notification_center/type.ts +++ b/lib/notification_center/type.ts @@ -27,8 +27,6 @@ import { import { DecisionSource } from '../utils/enums'; import { Nullable } from '../utils/type'; import { Platform } from '../platform_support'; - - export type UserEventListenerPayload = { userId: string; attributes?: UserAttributes; diff --git a/lib/odp/constant.ts b/lib/odp/constant.ts index 2f60082f3..178e940ed 100644 --- a/lib/odp/constant.ts +++ b/lib/odp/constant.ts @@ -1,5 +1,3 @@ -import { Platform } from './../platform_support'; - /** * Copyright 2024, Optimizely * @@ -17,6 +15,8 @@ import { Platform } from './../platform_support'; */ +import { Platform } from '../platform_support'; + export enum ODP_USER_KEY { VUID = 'vuid', FS_USER_ID = 'fs_user_id', diff --git a/lib/odp/event_manager/odp_event.ts b/lib/odp/event_manager/odp_event.ts index aed60ccb5..b237b8fc7 100644 --- a/lib/odp/event_manager/odp_event.ts +++ b/lib/odp/event_manager/odp_event.ts @@ -1,5 +1,3 @@ -import { Platform } from './../../platform_support'; - /** * Copyright 2022-2024, Optimizely * @@ -17,6 +15,8 @@ import { Platform } from './../../platform_support'; */ +import { Platform } from '../../platform_support'; + export class OdpEvent { /** * Type of event (typically "fullstack") diff --git a/lib/odp/event_manager/odp_event_api_manager.ts b/lib/odp/event_manager/odp_event_api_manager.ts index 2ef3e1af9..c7b4165f5 100644 --- a/lib/odp/event_manager/odp_event_api_manager.ts +++ b/lib/odp/event_manager/odp_event_api_manager.ts @@ -14,12 +14,11 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { LoggerFacade } from '../../logging/logger'; import { OdpEvent } from './odp_event'; import { HttpMethod, RequestHandler } from '../../utils/http_request_handler/http'; import { OdpConfig } from '../odp_config'; - +import { Platform } from '../../platform_support'; export type EventDispatchResponse = { statusCode?: number; diff --git a/lib/odp/event_manager/odp_event_manager.ts b/lib/odp/event_manager/odp_event_manager.ts index f9f77a6dc..67a22a9dc 100644 --- a/lib/odp/event_manager/odp_event_manager.ts +++ b/lib/odp/event_manager/odp_event_manager.ts @@ -38,8 +38,6 @@ import { LoggerFacade } from '../../logging/logger'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../../service'; import { sprintf } from '../../utils/fns'; import { Platform } from '../../platform_support'; - - export interface OdpEventManager extends Service { updateConfig(odpIntegrationConfig: OdpIntegrationConfig): void; sendEvent(event: OdpEvent): void; diff --git a/lib/odp/odp_config.ts b/lib/odp/odp_config.ts index e8982d74d..f858bbbd9 100644 --- a/lib/odp/odp_config.ts +++ b/lib/odp/odp_config.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { checkArrayEquality } from '../utils/fns'; - +import { Platform } from '../platform_support'; export class OdpConfig { /** diff --git a/lib/odp/odp_manager.ts b/lib/odp/odp_manager.ts index 5fe26a5fe..7525d0efb 100644 --- a/lib/odp/odp_manager.ts +++ b/lib/odp/odp_manager.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { v4 as uuidV4} from 'uuid'; import { LoggerFacade } from '../logging/logger'; @@ -32,7 +31,7 @@ import { isVuid } from '../vuid/vuid'; import { Maybe } from '../utils/type'; import { sprintf } from '../utils/fns'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; - +import { Platform } from '../platform_support'; export interface OdpManager extends Service { updateConfig(odpIntegrationConfig: OdpIntegrationConfig): boolean; diff --git a/lib/odp/odp_manager_factory.browser.ts b/lib/odp/odp_manager_factory.browser.ts index dc8f2eabc..30b7479e9 100644 --- a/lib/odp/odp_manager_factory.browser.ts +++ b/lib/odp/odp_manager_factory.browser.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; - +import { Platform } from '../platform_support'; export const BROWSER_DEFAULT_API_TIMEOUT = 10_000; export const BROWSER_DEFAULT_BATCH_SIZE = 10; diff --git a/lib/odp/odp_manager_factory.node.ts b/lib/odp/odp_manager_factory.node.ts index 5b2ef97cf..d40757b88 100644 --- a/lib/odp/odp_manager_factory.node.ts +++ b/lib/odp/odp_manager_factory.node.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { NodeRequestHandler } from '../utils/http_request_handler/request_handler.node'; import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; - +import { Platform } from '../platform_support'; export const NODE_DEFAULT_API_TIMEOUT = 10_000; export const NODE_DEFAULT_BATCH_SIZE = 10; diff --git a/lib/odp/odp_manager_factory.react_native.ts b/lib/odp/odp_manager_factory.react_native.ts index 167cc54f4..ca9a39c57 100644 --- a/lib/odp/odp_manager_factory.react_native.ts +++ b/lib/odp/odp_manager_factory.react_native.ts @@ -14,12 +14,11 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { OdpManager } from './odp_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; - +import { Platform } from '../platform_support'; export const RN_DEFAULT_API_TIMEOUT = 10_000; export const RN_DEFAULT_BATCH_SIZE = 10; diff --git a/lib/odp/odp_manager_factory.ts b/lib/odp/odp_manager_factory.ts index 9b6fc271f..f61c25209 100644 --- a/lib/odp/odp_manager_factory.ts +++ b/lib/odp/odp_manager_factory.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { RequestHandler } from "../shared_types"; import { Cache } from "../utils/cache/cache"; import { InMemoryLruCache } from "../utils/cache/in_memory_lru_cache"; @@ -26,7 +25,7 @@ import { DefaultOdpManager, OdpManager } from "./odp_manager"; import { DefaultOdpSegmentApiManager } from "./segment_manager/odp_segment_api_manager"; import { DefaultOdpSegmentManager, OdpSegmentManager } from "./segment_manager/odp_segment_manager"; import { UserAgentParser } from "./ua_parser/user_agent_parser"; - +import { Platform } from '../platform_support'; export const DEFAULT_CACHE_SIZE = 10_000; export const DEFAULT_CACHE_TIMEOUT = 600_000; diff --git a/lib/odp/odp_manager_factory.universal.ts b/lib/odp/odp_manager_factory.universal.ts index 9fea52040..5b8ddec1e 100644 --- a/lib/odp/odp_manager_factory.universal.ts +++ b/lib/odp/odp_manager_factory.universal.ts @@ -14,12 +14,11 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { RequestHandler } from '../utils/http_request_handler/http'; import { validateRequestHandler } from '../utils/http_request_handler/request_handler_validator'; import { eventApiRequestGenerator } from './event_manager/odp_event_api_manager'; import { getOpaqueOdpManager, OdpManagerOptions, OpaqueOdpManager } from './odp_manager_factory'; - +import { Platform } from '../platform_support'; export const DEFAULT_API_TIMEOUT = 10_000; export const DEFAULT_BATCH_SIZE = 1; diff --git a/lib/odp/odp_types.ts b/lib/odp/odp_types.ts index 34807a80b..7ddf847a8 100644 --- a/lib/odp/odp_types.ts +++ b/lib/odp/odp_types.ts @@ -1,5 +1,3 @@ -import { Platform } from './../platform_support'; - /** * Copyright 2022-2024, Optimizely * @@ -20,6 +18,8 @@ import { Platform } from './../platform_support'; * Wrapper around valid data and error responses */ +import { Platform } from '../platform_support'; + export interface Response { data: Data; errors: Error[]; diff --git a/lib/odp/segment_manager/odp_response_schema.ts b/lib/odp/segment_manager/odp_response_schema.ts index 5041d0d93..cdf82493b 100644 --- a/lib/odp/segment_manager/odp_response_schema.ts +++ b/lib/odp/segment_manager/odp_response_schema.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { JSONSchema4 } from 'json-schema'; - +import { Platform } from '../../platform_support'; /** * JSON Schema used to validate the ODP GraphQL response */ diff --git a/lib/odp/segment_manager/odp_segment_api_manager.ts b/lib/odp/segment_manager/odp_segment_api_manager.ts index 386d44884..59032013c 100644 --- a/lib/odp/segment_manager/odp_segment_api_manager.ts +++ b/lib/odp/segment_manager/odp_segment_api_manager.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { LoggerFacade } from '../../logging/logger'; import { validate } from '../../utils/json_schema_validator'; import { OdpResponseSchema } from './odp_response_schema'; @@ -22,6 +21,7 @@ import { ODP_USER_KEY } from '../constant'; import { RequestHandler } from '../../utils/http_request_handler/http'; import { Response as GraphQLResponse } from '../odp_types'; import { log } from 'console'; +import { Platform } from '../../platform_support'; /** * Expected value for a qualified/valid segment */ diff --git a/lib/odp/segment_manager/odp_segment_manager.ts b/lib/odp/segment_manager/odp_segment_manager.ts index af2602fb9..7a6fae790 100644 --- a/lib/odp/segment_manager/odp_segment_manager.ts +++ b/lib/odp/segment_manager/odp_segment_manager.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { Cache } from '../../utils/cache/cache'; import { OdpSegmentApiManager } from './odp_segment_api_manager'; import { OdpIntegrationConfig } from '../odp_config'; @@ -22,7 +21,7 @@ import { OptimizelySegmentOption } from './optimizely_segment_option'; import { ODP_USER_KEY } from '../constant'; import { LoggerFacade } from '../../logging/logger'; import { ODP_CONFIG_NOT_AVAILABLE, ODP_NOT_INTEGRATED } from 'error_message'; - +import { Platform } from '../../platform_support'; export interface OdpSegmentManager { fetchQualifiedSegments( diff --git a/lib/odp/segment_manager/optimizely_segment_option.ts b/lib/odp/segment_manager/optimizely_segment_option.ts index 7dda97e70..a28b82854 100644 --- a/lib/odp/segment_manager/optimizely_segment_option.ts +++ b/lib/odp/segment_manager/optimizely_segment_option.ts @@ -1,5 +1,3 @@ -import { Platform } from './../../platform_support'; - /** * Copyright 2022, 2024, Optimizely * @@ -18,6 +16,8 @@ import { Platform } from './../../platform_support'; // Options for defining behavior of OdpSegmentManager's caching mechanism when calling fetchSegments() +import { Platform } from '../../platform_support'; + export enum OptimizelySegmentOption { IGNORE_CACHE = 'IGNORE_CACHE', RESET_CACHE = 'RESET_CACHE', diff --git a/lib/odp/ua_parser/ua_parser.ts b/lib/odp/ua_parser/ua_parser.ts index 61d44f34f..bf0bceae7 100644 --- a/lib/odp/ua_parser/ua_parser.ts +++ b/lib/odp/ua_parser/ua_parser.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import { UAParser } from 'ua-parser-js'; import { UserAgentInfo } from './user_agent_info'; import { UserAgentParser } from './user_agent_parser'; - +import { Platform } from '../../platform_support'; const userAgentParser: UserAgentParser = { parseUserAgentInfo(): UserAgentInfo { diff --git a/lib/odp/ua_parser/user_agent_info.ts b/lib/odp/ua_parser/user_agent_info.ts index 929c2d468..813683db7 100644 --- a/lib/odp/ua_parser/user_agent_info.ts +++ b/lib/odp/ua_parser/user_agent_info.ts @@ -1,5 +1,3 @@ -import { Platform } from './../../platform_support'; - /** * Copyright 2023, Optimizely * @@ -17,6 +15,8 @@ import { Platform } from './../../platform_support'; */ +import { Platform } from '../../platform_support'; + export type UserAgentInfo = { os: { name?: string, diff --git a/lib/odp/ua_parser/user_agent_parser.ts b/lib/odp/ua_parser/user_agent_parser.ts index 793367cc0..0ff1c6eb2 100644 --- a/lib/odp/ua_parser/user_agent_parser.ts +++ b/lib/odp/ua_parser/user_agent_parser.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { UserAgentInfo } from "./user_agent_info"; - +import { Platform } from '../../platform_support'; export interface UserAgentParser { parseUserAgentInfo(): UserAgentInfo, diff --git a/lib/optimizely/index.ts b/lib/optimizely/index.ts index bb579d0c0..c9a82b551 100644 --- a/lib/optimizely/index.ts +++ b/lib/optimizely/index.ts @@ -46,8 +46,6 @@ import { createDecisionService, DecisionService, DecisionObj } from '../core/dec import { buildLogEvent } from '../event_processor/event_builder/log_event'; import { buildImpressionEvent, buildConversionEvent } from '../event_processor/event_builder/user_event'; import { isSafeInteger } from '../utils/fns'; -import { Platform } from '../platform_support'; - import { validate } from '../utils/attributes_validator'; import * as eventTagsValidator from '../utils/event_tags_validator'; import * as projectConfig from '../project_config/project_config'; @@ -105,7 +103,7 @@ import { ErrorReporter } from '../error/error_reporter'; import { OptimizelyError } from '../error/optimizly_error'; import { Value } from '../utils/promise/operation_value'; import { CmabService } from '../core/decision_service/cmab/cmab_service'; - +import { Platform } from '../platform_support'; const DEFAULT_ONREADY_TIMEOUT = 30000; // TODO: Make feature_key, user_id, variable_key, experiment_key, event_key camelCase diff --git a/lib/optimizely_decision/index.ts b/lib/optimizely_decision/index.ts index 73d221a97..28d99b704 100644 --- a/lib/optimizely_decision/index.ts +++ b/lib/optimizely_decision/index.ts @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * * limitations under the License. * ***************************************************************************/ -import { Platform } from './../platform_support'; import { OptimizelyUserContext, OptimizelyDecision } from '../shared_types'; - +import { Platform } from '../platform_support'; export function newErrorDecision(key: string, user: OptimizelyUserContext, reasons: string[]): OptimizelyDecision { return { diff --git a/lib/optimizely_user_context/index.ts b/lib/optimizely_user_context/index.ts index 3b7856a08..0fbe2bec0 100644 --- a/lib/optimizely_user_context/index.ts +++ b/lib/optimizely_user_context/index.ts @@ -25,8 +25,6 @@ import { } from '../shared_types'; import { OptimizelySegmentOption } from '../odp/segment_manager/optimizely_segment_option'; import { Platform } from '../platform_support'; - - export const FORCED_DECISION_NULL_RULE_KEY = '$opt_null_rule_key'; interface OptimizelyUserContextConfig { diff --git a/lib/project_config/config_manager_factory.browser.ts b/lib/project_config/config_manager_factory.browser.ts index 6b1e9a541..8d23ffb82 100644 --- a/lib/project_config/config_manager_factory.browser.ts +++ b/lib/project_config/config_manager_factory.browser.ts @@ -14,10 +14,9 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { BrowserRequestHandler } from '../utils/http_request_handler/request_handler.browser'; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; - +import { Platform } from '../platform_support'; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { diff --git a/lib/project_config/config_manager_factory.node.ts b/lib/project_config/config_manager_factory.node.ts index 953d1cea1..6ec76ccc3 100644 --- a/lib/project_config/config_manager_factory.node.ts +++ b/lib/project_config/config_manager_factory.node.ts @@ -14,10 +14,9 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { NodeRequestHandler } from "../utils/http_request_handler/request_handler.node"; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; - +import { Platform } from '../platform_support'; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { diff --git a/lib/project_config/config_manager_factory.react_native.ts b/lib/project_config/config_manager_factory.react_native.ts index 9c1ddf023..747720be3 100644 --- a/lib/project_config/config_manager_factory.react_native.ts +++ b/lib/project_config/config_manager_factory.react_native.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { AsyncStorageCache } from "../utils/cache/async_storage_cache.react_native"; import { BrowserRequestHandler } from "../utils/http_request_handler/request_handler.browser"; import { getOpaquePollingConfigManager, PollingConfigManagerConfig, OpaqueConfigManager } from "./config_manager_factory"; - +import { Platform } from '../platform_support'; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): OpaqueConfigManager => { const defaultConfig = { diff --git a/lib/project_config/config_manager_factory.ts b/lib/project_config/config_manager_factory.ts index 48dbd50dd..ed27ee234 100644 --- a/lib/project_config/config_manager_factory.ts +++ b/lib/project_config/config_manager_factory.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { RequestHandler } from "../utils/http_request_handler/http"; import { Maybe, Transformer } from "../utils/type"; import { DatafileManagerConfig } from "./datafile_manager"; @@ -27,7 +26,7 @@ import { MIN_UPDATE_INTERVAL, UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE } from './co import { LogLevel } from '../logging/logger' import { Store } from "../utils/cache/store"; import { validateStore } from "../utils/cache/store_validator"; - +import { Platform } from '../platform_support'; export const INVALID_CONFIG_MANAGER = "Invalid config manager"; diff --git a/lib/project_config/config_manager_factory.universal.ts b/lib/project_config/config_manager_factory.universal.ts index 3e662f4c0..9f2b6e21b 100644 --- a/lib/project_config/config_manager_factory.universal.ts +++ b/lib/project_config/config_manager_factory.universal.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { getOpaquePollingConfigManager, OpaqueConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; import { RequestHandler } from "../utils/http_request_handler/http"; import { validateRequestHandler } from "../utils/http_request_handler/request_handler_validator"; - +import { Platform } from '../platform_support'; export type UniversalPollingConfigManagerConfig = PollingConfigManagerConfig & { requestHandler: RequestHandler; diff --git a/lib/project_config/constant.ts b/lib/project_config/constant.ts index 632a55598..1fa922bcf 100644 --- a/lib/project_config/constant.ts +++ b/lib/project_config/constant.ts @@ -1,5 +1,3 @@ -import { Platform } from './../platform_support'; - /** * Copyright 2022-2023, Optimizely * @@ -17,6 +15,8 @@ import { Platform } from './../platform_support'; */ +import { Platform } from '../platform_support'; + const DEFAULT_UPDATE_INTERVAL_MINUTES = 5; /** Standard interval (5 minutes in milliseconds) for polling datafile updates.; */ export const DEFAULT_UPDATE_INTERVAL = DEFAULT_UPDATE_INTERVAL_MINUTES * 60 * 1000; diff --git a/lib/project_config/datafile_manager.ts b/lib/project_config/datafile_manager.ts index 8447e1829..28df93375 100644 --- a/lib/project_config/datafile_manager.ts +++ b/lib/project_config/datafile_manager.ts @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { Service, StartupLog } from '../service'; import { Store } from '../utils/cache/store'; import { RequestHandler } from '../utils/http_request_handler/http'; import { Fn, Consumer } from '../utils/type'; import { Repeater } from '../utils/repeater/repeater'; import { LoggerFacade } from '../logging/logger'; - +import { Platform } from '../platform_support'; export interface DatafileManager extends Service { get(): string | undefined; diff --git a/lib/project_config/optimizely_config.ts b/lib/project_config/optimizely_config.ts index 74027765d..6965657fb 100644 --- a/lib/project_config/optimizely_config.ts +++ b/lib/project_config/optimizely_config.ts @@ -33,9 +33,7 @@ import { Variation, VariationVariable, } from '../shared_types'; - import { Platform } from '../platform_support'; - interface FeatureVariablesMap { [key: string]: FeatureVariable[]; } diff --git a/lib/project_config/polling_datafile_manager.ts b/lib/project_config/polling_datafile_manager.ts index f4e939e60..d4f02e37e 100644 --- a/lib/project_config/polling_datafile_manager.ts +++ b/lib/project_config/polling_datafile_manager.ts @@ -39,9 +39,7 @@ import { LoggerFacade } from '../logging/logger'; export const LOGGER_NAME = 'PollingDatafileManager'; import { SERVICE_STOPPED_BEFORE_RUNNING } from '../service'; - import { Platform } from '../platform_support'; - export const FAILED_TO_FETCH_DATAFILE = 'Failed to fetch datafile'; export class PollingDatafileManager extends BaseService implements DatafileManager { diff --git a/lib/project_config/project_config.ts b/lib/project_config/project_config.ts index 34c9802b3..2627abe09 100644 --- a/lib/project_config/project_config.ts +++ b/lib/project_config/project_config.ts @@ -52,9 +52,7 @@ import { } from 'error_message'; import { SKIPPING_JSON_VALIDATION, VALID_DATAFILE } from 'log_message'; import { OptimizelyError } from '../error/optimizly_error'; - import { Platform } from '../platform_support'; - interface TryCreatingProjectConfigConfig { // TODO[OASIS-6649]: Don't use object type // eslint-disable-next-line @typescript-eslint/ban-types diff --git a/lib/project_config/project_config_manager.ts b/lib/project_config/project_config_manager.ts index f15c0065f..4a2191102 100644 --- a/lib/project_config/project_config_manager.ts +++ b/lib/project_config/project_config_manager.ts @@ -32,7 +32,6 @@ export const GOT_INVALID_DATAFILE = 'got invalid datafile'; import { sprintf } from '../utils/fns'; import { Platform } from '../platform_support'; - interface ProjectConfigManagerConfig { datafile?: string | Record; jsonSchemaValidator?: Transformer, diff --git a/lib/project_config/project_config_schema.ts b/lib/project_config/project_config_schema.ts index 947f86305..d940bbf2e 100644 --- a/lib/project_config/project_config_schema.ts +++ b/lib/project_config/project_config_schema.ts @@ -17,9 +17,8 @@ /** * Project Config JSON Schema file used to validate the project json datafile */ -import { Platform } from './../platform_support'; import { JSONSchema4 } from 'json-schema'; - +import { Platform } from '../platform_support'; var schemaDefinition = { $schema: 'http://json-schema.org/draft-04/schema#', diff --git a/lib/service.ts b/lib/service.ts index a9f83f60c..cb2de2269 100644 --- a/lib/service.ts +++ b/lib/service.ts @@ -14,10 +14,9 @@ * limitations under the License. */ -import { Platform } from './platform_support'; import { LoggerFacade, LogLevel, LogLevelToLower } from './logging/logger' import { resolvablePromise, ResolvablePromise } from "./utils/promise/resolvablePromise"; - +import { Platform } from './platform_support'; export const SERVICE_FAILED_TO_START = '%s failed to start, reason: %s'; export const SERVICE_STOPPED_BEFORE_RUNNING = '%s stopped before running'; diff --git a/lib/shared_types.ts b/lib/shared_types.ts index f250404f4..00943811a 100644 --- a/lib/shared_types.ts +++ b/lib/shared_types.ts @@ -20,7 +20,6 @@ */ // import { ErrorHandler, LogHandler, LogLevel, LoggerFacade } from './modules/logging'; -import { Platform } from './platform_support'; import { LoggerFacade, LogLevel } from './logging/logger'; import { ErrorHandler } from './error/error_handler'; @@ -47,7 +46,7 @@ import { OpaqueEventProcessor } from './event_processor/event_processor_factory' import { OpaqueOdpManager } from './odp/odp_manager_factory'; import { OpaqueVuidManager } from './vuid/vuid_manager_factory'; import { CacheWithRemove } from './utils/cache/cache'; - +import { Platform } from './platform_support'; export { EventDispatcher } from './event_processor/event_dispatcher/event_dispatcher'; export { EventProcessor } from './event_processor/event_processor'; diff --git a/lib/utils/attributes_validator/index.ts b/lib/utils/attributes_validator/index.ts index 1a7950f8f..f72e6347b 100644 --- a/lib/utils/attributes_validator/index.ts +++ b/lib/utils/attributes_validator/index.ts @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import { ObjectWithUnknownProperties } from '../../shared_types'; import fns from '../../utils/fns'; import { INVALID_ATTRIBUTES, UNDEFINED_ATTRIBUTE } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; - +import { Platform } from '../../platform_support'; /** * Validates user's provided attributes * @param {unknown} attributes diff --git a/lib/utils/cache/async_storage_cache.react_native.ts b/lib/utils/cache/async_storage_cache.react_native.ts index 544437364..049633fd6 100644 --- a/lib/utils/cache/async_storage_cache.react_native.ts +++ b/lib/utils/cache/async_storage_cache.react_native.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { Maybe } from "../type"; import { AsyncStore } from "./store"; import { getDefaultAsyncStorage } from "../import.react_native/@react-native-async-storage/async-storage"; - +import { Platform } from '../../platform_support'; export class AsyncStorageCache implements AsyncStore { public readonly operation = 'async'; diff --git a/lib/utils/cache/cache.ts b/lib/utils/cache/cache.ts index c291e3d22..dd05ab63c 100644 --- a/lib/utils/cache/cache.ts +++ b/lib/utils/cache/cache.ts @@ -17,8 +17,6 @@ import { OpType, OpValue } from '../../utils/type'; import { Transformer } from '../../utils/type'; import { Platform } from '../../platform_support'; - - export interface OpCache { operation: OP; save(key: string, value: V): OpValue; diff --git a/lib/utils/cache/in_memory_lru_cache.ts b/lib/utils/cache/in_memory_lru_cache.ts index a61867c43..36c4d4b56 100644 --- a/lib/utils/cache/in_memory_lru_cache.ts +++ b/lib/utils/cache/in_memory_lru_cache.ts @@ -14,10 +14,9 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { Maybe } from "../type"; import { SyncCacheWithRemove } from "./cache"; - +import { Platform } from '../../platform_support'; type CacheElement = { value: V; diff --git a/lib/utils/cache/local_storage_cache.browser.ts b/lib/utils/cache/local_storage_cache.browser.ts index 32df3fea9..3e5ede910 100644 --- a/lib/utils/cache/local_storage_cache.browser.ts +++ b/lib/utils/cache/local_storage_cache.browser.ts @@ -14,10 +14,9 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { Maybe } from "../type"; import { SyncStore } from "./store"; - +import { Platform } from '../../platform_support'; export class LocalStorageCache implements SyncStore { public readonly operation = 'sync'; diff --git a/lib/utils/cache/store.ts b/lib/utils/cache/store.ts index 9302c671d..c3a3b1980 100644 --- a/lib/utils/cache/store.ts +++ b/lib/utils/cache/store.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { Transformer } from '../../utils/type'; import { Maybe } from '../../utils/type'; import { OpType, OpValue } from '../../utils/type'; - +import { Platform } from '../../platform_support'; export interface OpStore { operation: OP; diff --git a/lib/utils/cache/store_validator.ts b/lib/utils/cache/store_validator.ts index fa15c50ff..1746d9c69 100644 --- a/lib/utils/cache/store_validator.ts +++ b/lib/utils/cache/store_validator.ts @@ -1,5 +1,3 @@ -import { Platform } from './../../platform_support'; - /** * Copyright 2025, Optimizely @@ -17,6 +15,8 @@ import { Platform } from './../../platform_support'; * limitations under the License. */ +import { Platform } from '../../platform_support'; + export const INVALID_STORE = 'Invalid store'; export const INVALID_STORE_METHOD = 'Invalid store method %s'; diff --git a/lib/utils/config_validator/index.ts b/lib/utils/config_validator/index.ts index b5114034d..4b59b066d 100644 --- a/lib/utils/config_validator/index.ts +++ b/lib/utils/config_validator/index.ts @@ -23,8 +23,6 @@ import { } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; import { Platform } from '../../platform_support'; - - const SUPPORTED_VERSIONS = [DATAFILE_VERSIONS.V2, DATAFILE_VERSIONS.V3, DATAFILE_VERSIONS.V4]; /** diff --git a/lib/utils/enums/index.ts b/lib/utils/enums/index.ts index d49aa2d34..6657848b4 100644 --- a/lib/utils/enums/index.ts +++ b/lib/utils/enums/index.ts @@ -1,5 +1,3 @@ -import { Platform } from './../../platform_support'; - /** * Copyright 2016-2025, Optimizely * @@ -20,6 +18,8 @@ import { Platform } from './../../platform_support'; * Contains global enums used throughout the library */ +import { Platform } from '../../platform_support'; + export const LOG_LEVEL = { NOTSET: 0, DEBUG: 1, diff --git a/lib/utils/event_emitter/event_emitter.ts b/lib/utils/event_emitter/event_emitter.ts index a03caba60..a98f09572 100644 --- a/lib/utils/event_emitter/event_emitter.ts +++ b/lib/utils/event_emitter/event_emitter.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { Fn } from "../type"; - +import { Platform } from '../../platform_support'; type Consumer = (arg: T) => void; diff --git a/lib/utils/event_tag_utils/index.ts b/lib/utils/event_tag_utils/index.ts index e56a02f91..1254d5572 100644 --- a/lib/utils/event_tag_utils/index.ts +++ b/lib/utils/event_tag_utils/index.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { FAILED_TO_PARSE_REVENUE, FAILED_TO_PARSE_VALUE, @@ -25,7 +24,7 @@ import { LoggerFacade } from '../../logging/logger'; import { RESERVED_EVENT_KEYWORDS } from '../enums'; import { EventTags } from '../../shared_types'; - +import { Platform } from '../../platform_support'; /** * Provides utility method for parsing event tag values */ diff --git a/lib/utils/event_tags_validator/index.ts b/lib/utils/event_tags_validator/index.ts index 0f280d9fd..eb69bb82d 100644 --- a/lib/utils/event_tags_validator/index.ts +++ b/lib/utils/event_tags_validator/index.ts @@ -17,10 +17,9 @@ /** * Provides utility method for validating that event tags user has provided are valid */ -import { Platform } from './../../platform_support'; import { OptimizelyError } from '../../error/optimizly_error'; import { INVALID_EVENT_TAGS } from 'error_message'; - +import { Platform } from '../../platform_support'; /** * Validates user's provided event tags * @param {unknown} eventTags diff --git a/lib/utils/executor/backoff_retry_runner.ts b/lib/utils/executor/backoff_retry_runner.ts index 033aa42d5..7de9376c7 100644 --- a/lib/utils/executor/backoff_retry_runner.ts +++ b/lib/utils/executor/backoff_retry_runner.ts @@ -1,10 +1,9 @@ -import { Platform } from './../../platform_support'; import { OptimizelyError } from "../../error/optimizly_error"; import { RETRY_CANCELLED } from "error_message"; import { resolvablePromise, ResolvablePromise } from "../promise/resolvablePromise"; import { BackoffController } from "../repeater/repeater"; import { AsyncProducer, Fn } from "../type"; - +import { Platform } from '../../platform_support'; export type RunResult = { result: Promise; diff --git a/lib/utils/executor/serial_runner.ts b/lib/utils/executor/serial_runner.ts index f2bf5b776..a4b9a5109 100644 --- a/lib/utils/executor/serial_runner.ts +++ b/lib/utils/executor/serial_runner.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { AsyncProducer } from "../type"; - +import { Platform } from '../../platform_support'; class SerialRunner { private waitPromise: Promise = Promise.resolve(); diff --git a/lib/utils/fns/index.ts b/lib/utils/fns/index.ts index da60ffaba..413d5f9d7 100644 --- a/lib/utils/fns/index.ts +++ b/lib/utils/fns/index.ts @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import { v4 } from 'uuid'; - +import { Platform } from '../../platform_support'; const MAX_SAFE_INTEGER_LIMIT = Math.pow(2, 53); diff --git a/lib/utils/http_request_handler/http.ts b/lib/utils/http_request_handler/http.ts index 4c973b402..4bc4154f3 100644 --- a/lib/utils/http_request_handler/http.ts +++ b/lib/utils/http_request_handler/http.ts @@ -1,5 +1,3 @@ -import { Platform } from './../../platform_support'; - /** * Copyright 2019-2020, 2022, 2024 Optimizely * @@ -20,6 +18,8 @@ import { Platform } from './../../platform_support'; * List of key-value pairs to be used in an HTTP requests */ +import { Platform } from '../../platform_support'; + export interface Headers { [header: string]: string | undefined; } diff --git a/lib/utils/http_request_handler/http_util.ts b/lib/utils/http_request_handler/http_util.ts index e499bf092..bf0ef27d9 100644 --- a/lib/utils/http_request_handler/http_util.ts +++ b/lib/utils/http_request_handler/http_util.ts @@ -1,6 +1,6 @@ -import { Platform } from './../../platform_support'; +import { Platform } from '../../platform_support'; export const isSuccessStatusCode = (statusCode: number): boolean => { return statusCode >= 200 && statusCode < 400; diff --git a/lib/utils/http_request_handler/request_handler.browser.ts b/lib/utils/http_request_handler/request_handler.browser.ts index 4cff94858..578002d17 100644 --- a/lib/utils/http_request_handler/request_handler.browser.ts +++ b/lib/utils/http_request_handler/request_handler.browser.ts @@ -16,13 +16,12 @@ // This implementation works in both browser and react_native environments -import { Platform } from './../../platform_support'; import { AbortableRequest, Headers, RequestHandler, Response } from './http'; import { LoggerFacade, LogLevel } from '../../logging/logger'; import { REQUEST_TIMEOUT_MS } from '../enums'; import { REQUEST_ERROR, REQUEST_TIMEOUT, UNABLE_TO_PARSE_AND_SKIPPED_HEADER } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; - +import { Platform } from '../../platform_support'; /** * Handles sending requests and receiving responses over HTTP via XMLHttpRequest */ diff --git a/lib/utils/http_request_handler/request_handler.node.ts b/lib/utils/http_request_handler/request_handler.node.ts index 21327548a..b972c0526 100644 --- a/lib/utils/http_request_handler/request_handler.node.ts +++ b/lib/utils/http_request_handler/request_handler.node.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import http from 'http'; import https from 'https'; import url from 'url'; @@ -23,7 +22,7 @@ import { LoggerFacade } from '../../logging/logger'; import { REQUEST_TIMEOUT_MS } from '../enums'; import { NO_STATUS_CODE_IN_RESPONSE, REQUEST_ERROR, REQUEST_TIMEOUT, UNSUPPORTED_PROTOCOL } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; - +import { Platform } from '../../platform_support'; /** * Handles sending requests and receiving responses over HTTP via NodeJS http module */ diff --git a/lib/utils/http_request_handler/request_handler_validator.ts b/lib/utils/http_request_handler/request_handler_validator.ts index 8e82f6e30..a3373e561 100644 --- a/lib/utils/http_request_handler/request_handler_validator.ts +++ b/lib/utils/http_request_handler/request_handler_validator.ts @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import { RequestHandler } from './http'; - +import { Platform } from '../../platform_support'; export const INVALID_REQUEST_HANDLER = 'Invalid request handler'; diff --git a/lib/utils/id_generator/index.ts b/lib/utils/id_generator/index.ts index 508ee5af0..d73052026 100644 --- a/lib/utils/id_generator/index.ts +++ b/lib/utils/id_generator/index.ts @@ -1,5 +1,3 @@ -import { Platform } from './../../platform_support'; - /** * Copyright 2022-2024, Optimizely * @@ -17,6 +15,8 @@ import { Platform } from './../../platform_support'; */ +import { Platform } from '../../platform_support'; + const idSuffixBase = 10_000; export class IdGenerator { diff --git a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts index 399133028..dcf394ae0 100644 --- a/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts +++ b/lib/utils/import.react_native/@react-native-async-storage/async-storage.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { Platform } from './../../../platform_support'; import type { AsyncStorageStatic } from '@react-native-async-storage/async-storage' - +import { Platform } from '../../../platform_support'; export const MODULE_NOT_FOUND_REACT_NATIVE_ASYNC_STORAGE = 'Module not found: @react-native-async-storage/async-storage'; diff --git a/lib/utils/json_schema_validator/index.ts b/lib/utils/json_schema_validator/index.ts index 243eb9bad..8c5760f59 100644 --- a/lib/utils/json_schema_validator/index.ts +++ b/lib/utils/json_schema_validator/index.ts @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import { JSONSchema4, validate as jsonSchemaValidator } from 'json-schema'; import schema from '../../project_config/project_config_schema'; import { INVALID_DATAFILE, INVALID_JSON, NO_JSON_PROVIDED } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; - +import { Platform } from '../../platform_support'; /** * Validate the given json object against the specified schema * @param {unknown} jsonObject The object to validate against the schema diff --git a/lib/utils/microtask/index.ts b/lib/utils/microtask/index.ts index 57ae25999..015b2f691 100644 --- a/lib/utils/microtask/index.ts +++ b/lib/utils/microtask/index.ts @@ -1,5 +1,3 @@ -import { Platform } from './../../platform_support'; - /** * Copyright 2024, Optimizely * @@ -16,6 +14,8 @@ import { Platform } from './../../platform_support'; * limitations under the License. */ +import { Platform } from '../../platform_support'; + type Callback = () => void; diff --git a/lib/utils/promise/operation_value.ts b/lib/utils/promise/operation_value.ts index 0912a29dd..fd656e4e8 100644 --- a/lib/utils/promise/operation_value.ts +++ b/lib/utils/promise/operation_value.ts @@ -1,8 +1,7 @@ -import { Platform } from './../../platform_support'; import { PROMISE_NOT_ALLOWED } from '../../message/error_message'; import { OptimizelyError } from '../../error/optimizly_error'; import { OpType, OpValue } from '../type'; - +import { Platform } from '../../platform_support'; const isPromise = (val: any): boolean => { diff --git a/lib/utils/promise/resolvablePromise.ts b/lib/utils/promise/resolvablePromise.ts index e998d1762..87cff7224 100644 --- a/lib/utils/promise/resolvablePromise.ts +++ b/lib/utils/promise/resolvablePromise.ts @@ -1,5 +1,3 @@ -import { Platform } from './../../platform_support'; - /** * Copyright 2024, Optimizely * @@ -17,6 +15,8 @@ import { Platform } from './../../platform_support'; */ +import { Platform } from '../../platform_support'; + const noop = () => {}; export type ResolvablePromise = { diff --git a/lib/utils/repeater/repeater.ts b/lib/utils/repeater/repeater.ts index 716194781..cdb9ec9f3 100644 --- a/lib/utils/repeater/repeater.ts +++ b/lib/utils/repeater/repeater.ts @@ -14,10 +14,9 @@ * limitations under the License. */ -import { Platform } from './../../platform_support'; import { AsyncTransformer } from "../type"; import { scheduleMicrotask } from "../microtask"; - +import { Platform } from '../../platform_support'; // A repeater will invoke the task repeatedly. The time at which the task is invoked // is determined by the implementation. // The task is a function that takes a number as an argument and returns a promise. diff --git a/lib/utils/semantic_version/index.ts b/lib/utils/semantic_version/index.ts index dec9fc68a..39a50df76 100644 --- a/lib/utils/semantic_version/index.ts +++ b/lib/utils/semantic_version/index.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../../platform_support'; import { UNKNOWN_MATCH_TYPE } from 'error_message'; import { LoggerFacade } from '../../logging/logger'; import { VERSION_TYPE } from '../enums'; - +import { Platform } from '../../platform_support'; /** * Evaluate if provided string is number only * @param {unknown} content diff --git a/lib/utils/string_value_validator/index.ts b/lib/utils/string_value_validator/index.ts index 9d4940e20..b0fb85b03 100644 --- a/lib/utils/string_value_validator/index.ts +++ b/lib/utils/string_value_validator/index.ts @@ -1,5 +1,3 @@ -import { Platform } from './../../platform_support'; - /** * Copyright 2018, 2020, Optimizely * @@ -22,6 +20,8 @@ import { Platform } from './../../platform_support'; * @return {boolean} true for non-empty string, false otherwise */ +import { Platform } from '../../platform_support'; + export function validate(input: unknown): boolean { return typeof input === 'string' && input !== ''; } diff --git a/lib/utils/type.ts b/lib/utils/type.ts index 7d572d1b4..ecc9916be 100644 --- a/lib/utils/type.ts +++ b/lib/utils/type.ts @@ -1,5 +1,3 @@ -import { Platform } from './../platform_support'; - /** * Copyright 2024-2025, Optimizely * @@ -17,6 +15,8 @@ import { Platform } from './../platform_support'; */ +import { Platform } from '../platform_support'; + export type Fn = () => unknown; export type AsyncFn = () => Promise; export type AsyncTransformer = (arg: A) => Promise; diff --git a/lib/utils/user_profile_service_validator/index.ts b/lib/utils/user_profile_service_validator/index.ts index a1faf649c..3bb1b3e4e 100644 --- a/lib/utils/user_profile_service_validator/index.ts +++ b/lib/utils/user_profile_service_validator/index.ts @@ -18,12 +18,11 @@ * Provides utility method for validating that the given user profile service implementation is valid. */ -import { Platform } from './../../platform_support'; import { ObjectWithUnknownProperties } from '../../shared_types'; import { INVALID_USER_PROFILE_SERVICE } from 'error_message'; import { OptimizelyError } from '../../error/optimizly_error'; - +import { Platform } from '../../platform_support'; /** * Validates user's provided user profile service instance * @param {unknown} userProfileServiceInstance diff --git a/lib/vuid/vuid.ts b/lib/vuid/vuid.ts index f8974c88a..19e8cbdca 100644 --- a/lib/vuid/vuid.ts +++ b/lib/vuid/vuid.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { v4 as uuidV4 } from 'uuid'; - +import { Platform } from '../platform_support'; export const VUID_PREFIX = `vuid_`; export const VUID_MAX_LENGTH = 32; diff --git a/lib/vuid/vuid_manager.ts b/lib/vuid/vuid_manager.ts index 396d123f6..1415b8ee4 100644 --- a/lib/vuid/vuid_manager.ts +++ b/lib/vuid/vuid_manager.ts @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { LoggerFacade } from '../logging/logger'; import { Store } from '../utils/cache/store'; import { AsyncProducer, Maybe } from '../utils/type'; import { isVuid, makeVuid } from './vuid'; - +import { Platform } from '../platform_support'; export interface VuidManager { getVuid(): Maybe; diff --git a/lib/vuid/vuid_manager_factory.browser.ts b/lib/vuid/vuid_manager_factory.browser.ts index dfb449ea4..eace086b3 100644 --- a/lib/vuid/vuid_manager_factory.browser.ts +++ b/lib/vuid/vuid_manager_factory.browser.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manager'; import { LocalStorageCache } from '../utils/cache/local_storage_cache.browser'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; - +import { Platform } from '../platform_support'; export const vuidCacheManager = new VuidCacheManager(); diff --git a/lib/vuid/vuid_manager_factory.node.ts b/lib/vuid/vuid_manager_factory.node.ts index c43ebf8f5..9fbec12a4 100644 --- a/lib/vuid/vuid_manager_factory.node.ts +++ b/lib/vuid/vuid_manager_factory.node.ts @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; - +import { Platform } from '../platform_support'; export const createVuidManager = (options: VuidManagerOptions = {}): OpaqueVuidManager => { return wrapVuidManager(undefined); diff --git a/lib/vuid/vuid_manager_factory.react_native.ts b/lib/vuid/vuid_manager_factory.react_native.ts index 231b106fe..57b2e9d3a 100644 --- a/lib/vuid/vuid_manager_factory.react_native.ts +++ b/lib/vuid/vuid_manager_factory.react_native.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Platform } from './../platform_support'; import { DefaultVuidManager, VuidCacheManager, VuidManager } from './vuid_manager'; import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; import { OpaqueVuidManager, VuidManagerOptions, wrapVuidManager } from './vuid_manager_factory'; - +import { Platform } from '../platform_support'; export const vuidCacheManager = new VuidCacheManager(); diff --git a/lib/vuid/vuid_manager_factory.ts b/lib/vuid/vuid_manager_factory.ts index a3263bb78..a4fa30100 100644 --- a/lib/vuid/vuid_manager_factory.ts +++ b/lib/vuid/vuid_manager_factory.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import { Platform } from './../platform_support'; import { Store } from '../utils/cache/store'; import { Maybe } from '../utils/type'; import { VuidManager } from './vuid_manager'; - +import { Platform } from '../platform_support'; export type VuidManagerOptions = { vuidCache?: Store; From 81527c7123b1d8ad5563c2b42c216b3cc29d69c0 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 2 Dec 2025 21:48:41 +0600 Subject: [PATCH 38/40] updates --- docs/PLATFORM_ISOLATION.md | 4 +- package-lock.json | 164 ------------------------------------- package.json | 1 - 3 files changed, 2 insertions(+), 167 deletions(-) diff --git a/docs/PLATFORM_ISOLATION.md b/docs/PLATFORM_ISOLATION.md index 47ba93adc..78e179da7 100644 --- a/docs/PLATFORM_ISOLATION.md +++ b/docs/PLATFORM_ISOLATION.md @@ -164,7 +164,7 @@ npm run build ### How It Works -The validation script (`scripts/validate-platform-isolation-ts.js`): +The validation script (`scripts/validate-platform-isolation.js`): 1. Scans all TypeScript/JavaScript files configured in the in the `.platform-isolation.config.js` config file. 2. **Verifies every file has a `__platforms` export** - fails immediately if any file is missing this @@ -256,7 +256,7 @@ export class NodeMyFeature implements MyFeature { **Multiple Platforms (But Not Universal)** -For code that works on multiple platforms but is not universal, use the `__platforms` export to decalre the list of supported platforms: +For code that works on multiple platforms but is not universal, use the `__platforms` export to declare the list of supported platforms: **Example: Browser + React Native only** diff --git a/package-lock.json b/package-lock.json index 82f14cc6b..ace39b5be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "uuid": "^10.0.0" }, "devDependencies": { - "@biomejs/biome": "^2.3.7", "@react-native-async-storage/async-storage": "^2", "@react-native-community/netinfo": "^11.3.2", "@rollup/plugin-commonjs": "^11.0.2", @@ -2487,169 +2486,6 @@ "node": ">=6.9.0" } }, - "node_modules/@biomejs/biome": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.7.tgz", - "integrity": "sha512-CTbAS/jNAiUc6rcq94BrTB8z83O9+BsgWj2sBCQg9rD6Wkh2gjfR87usjx0Ncx0zGXP1NKgT7JNglay5Zfs9jw==", - "dev": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.3.7", - "@biomejs/cli-darwin-x64": "2.3.7", - "@biomejs/cli-linux-arm64": "2.3.7", - "@biomejs/cli-linux-arm64-musl": "2.3.7", - "@biomejs/cli-linux-x64": "2.3.7", - "@biomejs/cli-linux-x64-musl": "2.3.7", - "@biomejs/cli-win32-arm64": "2.3.7", - "@biomejs/cli-win32-x64": "2.3.7" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.7.tgz", - "integrity": "sha512-LirkamEwzIUULhXcf2D5b+NatXKeqhOwilM+5eRkbrnr6daKz9rsBL0kNZ16Hcy4b8RFq22SG4tcLwM+yx/wFA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.7.tgz", - "integrity": "sha512-Q4TO633kvrMQkKIV7wmf8HXwF0dhdTD9S458LGE24TYgBjSRbuhvio4D5eOQzirEYg6eqxfs53ga/rbdd8nBKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.7.tgz", - "integrity": "sha512-inHOTdlstUBzgjDcx0ge71U4SVTbwAljmkfi3MC5WzsYCRhancqfeL+sa4Ke6v2ND53WIwCFD5hGsYExoI3EZQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.7.tgz", - "integrity": "sha512-/afy8lto4CB8scWfMdt+NoCZtatBUF62Tk3ilWH2w8ENd5spLhM77zKlFZEvsKJv9AFNHknMl03zO67CiklL2Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.7.tgz", - "integrity": "sha512-fJMc3ZEuo/NaMYo5rvoWjdSS5/uVSW+HPRQujucpZqm2ZCq71b8MKJ9U4th9yrv2L5+5NjPF0nqqILCl8HY/fg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.7.tgz", - "integrity": "sha512-CQUtgH1tIN6e5wiYSJqzSwJumHYolNtaj1dwZGCnZXm2PZU1jOJof9TsyiP3bXNDb+VOR7oo7ZvY01If0W3iFQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.7.tgz", - "integrity": "sha512-aJAE8eCNyRpcfx2JJAtsPtISnELJ0H4xVVSwnxm13bzI8RwbXMyVtxy2r5DV1xT3WiSP+7LxORcApWw0LM8HiA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.7.tgz", - "integrity": "sha512-pulzUshqv9Ed//MiE8MOUeeEkbkSHVDVY5Cz5wVAnH1DUqliCQG3j6s1POaITTFqFfo7AVIx2sWdKpx/GS+Nqw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", diff --git a/package.json b/package.json index c47c78d44..ff86bf9f9 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,6 @@ "uuid": "^10.0.0" }, "devDependencies": { - "@biomejs/biome": "^2.3.7", "@react-native-async-storage/async-storage": "^2", "@react-native-community/netinfo": "^11.3.2", "@rollup/plugin-commonjs": "^11.0.2", From d319e416197b916c3a86410b86e4cc4e2188cede Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 2 Dec 2025 22:36:48 +0600 Subject: [PATCH 39/40] update scripts --- package.json | 6 ++-- scripts/platform-validator.js | 64 +++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) create mode 100755 scripts/platform-validator.js diff --git a/package.json b/package.json index ff86bf9f9..3cc9d765c 100644 --- a/package.json +++ b/package.json @@ -57,9 +57,9 @@ "clean": "rm -rf dist", "clean:win": "(if exist dist rd /s/q dist)", "lint": "tsc --noEmit && eslint 'lib/**/*.js' 'lib/**/*.ts'", - "validate-platform-isolation": "node scripts/validate-platform-isolation.js", - "test-isolation-rules": "node scripts/test-validator.js", - "add-platform-exports": "node scripts/add-platform-exports.js", + "validate-platform-isolation": "./scripts/platform-validator.js --validate", + "fix-platform-isolation": "./scripts/platform-validator.js --fix", + "test-isolation-rules": "./scripts/test-validator.js", "test-vitest": "vitest run", "test-mocha": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register -r tsconfig-paths/register -r lib/tests/exit_on_unhandled_rejection.js 'lib/**/*.tests.ts' 'lib/**/*.tests.js'", "test": "npm run test-mocha && npm run test-vitest", diff --git a/scripts/platform-validator.js b/scripts/platform-validator.js new file mode 100755 index 000000000..aa12a34c3 --- /dev/null +++ b/scripts/platform-validator.js @@ -0,0 +1,64 @@ +#!/usr/bin/env node + +/** + * Copyright 2025, Optimizely + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Platform validator CLI + * + * Provides a unified interface for validating and fixing platform isolation issues. + * + * Usage: + * node platform-validator.js --validate # Validate platform isolation (default) + * node platform-validator.js --fix # Fix platform isolation issues + */ + +/* eslint-disable @typescript-eslint/no-var-requires */ +const { execSync } = require('child_process'); + +function main() { + const args = process.argv.slice(2); + + const hasValidate = args.includes('--validate'); + const hasFix = args.includes('--fix'); + + // Check if both options are provided + if (hasValidate && hasFix) { + console.error('❌ Error: Cannot specify both --validate and --fix options'); + process.exit(1); + } + + // Determine which script to run (default to validate) + const shouldFix = hasFix; + + try { + if (shouldFix) { + console.log('🔧 Running platform isolation fix...\n'); + execSync('node scripts/add-platform-exports.js', { stdio: 'inherit' }); + } else { + console.log('🔍 Running platform isolation validation...\n'); + execSync('node scripts/validate-platform-isolation.js', { stdio: 'inherit' }); + } + } catch (error) { + process.exit(error.status || 1); + } +} + +if (require.main === module) { + main(); +} + +module.exports = { main }; From 9d3deb5f8d92bbe1da205b19004da0821ff78886 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Tue, 2 Dec 2025 22:49:14 +0600 Subject: [PATCH 40/40] upd --- package.json | 2 +- scripts/README.md | 47 ++++++++++++++----- ...form-exports.js => fix-platform-export.js} | 14 ++++-- scripts/platform-validator.js | 16 +++---- 4 files changed, 55 insertions(+), 24 deletions(-) rename scripts/{add-platform-exports.js => fix-platform-export.js} (96%) diff --git a/package.json b/package.json index 3cc9d765c..148ec7e9f 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "clean:win": "(if exist dist rd /s/q dist)", "lint": "tsc --noEmit && eslint 'lib/**/*.js' 'lib/**/*.ts'", "validate-platform-isolation": "./scripts/platform-validator.js --validate", - "fix-platform-isolation": "./scripts/platform-validator.js --fix", + "fix-platform-export": "./scripts/platform-validator.js --fix-export", "test-isolation-rules": "./scripts/test-validator.js", "test-vitest": "vitest run", "test-mocha": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register -r tsconfig-paths/register -r lib/tests/exit_on_unhandled_rejection.js 'lib/**/*.tests.ts' 'lib/**/*.tests.js'", diff --git a/scripts/README.md b/scripts/README.md index 17cb48019..43906185c 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -2,9 +2,28 @@ This directory contains build and validation scripts for the JavaScript SDK. +## platform-validator.js + +Main entry point for platform isolation validation and fixing. Provides a unified CLI interface. + +### Usage + +```bash +# Validate platform isolation (default) +npm run validate-platform-isolation +./scripts/platform-validator.js --validate +./scripts/platform-validator.js # --validate is default + +# Fix platform export issues +npm run fix-platform-export +./scripts/platform-validator.js --fix-export +``` + +**Note:** Cannot specify both `--validate` and `--fix-export` options at the same time. + ## validate-platform-isolation.js -The main platform isolation validator that ensures platform-specific code is properly isolated to prevent runtime errors when building for different platforms (Browser, Node.js, React Native). +The platform isolation validator that ensures platform-specific code is properly isolated to prevent runtime errors when building for different platforms (Browser, Node.js, React Native). **Configuration:** File patterns to include/exclude are configured in `.platform-isolation.config.js` at the workspace root. @@ -38,34 +57,40 @@ The script: **Note:** The validator can be updated to support file naming conventions (`.browser.ts`, etc.) in addition to `__platforms` exports, but currently enforces only the `__platforms` export. File naming is not validated and is used for convenience. -## add-platform-exports.js +## fix-platform-export.js -Auto-fix script that adds or updates `__platforms` exports in files. This script helps maintain platform isolation by automatically adding the required `__platforms` export to files that are missing it or have invalid exports. +Auto-fix script that adds or updates `__platforms` exports in files. This script helps maintain platform isolation by automatically fixing issues with platform export declarations. + +**Important:** This script only fixes `__platforms` export issues. It does not fix import compatibility issues - those must be resolved manually. ### Usage ```bash -# Run via npm script -npm run add-platform-exports +# Run via npm script (recommended) +npm run fix-platform-export + +# Or via platform-validator +./scripts/platform-validator.js --fix-export # Or run directly -node scripts/add-platform-exports.js +./scripts/fix-platform-export.js ``` ### How It Works The script: 1. Scans all TypeScript/JavaScript files configured in `.platform-isolation.config.js` -2. For each file, checks if it has a valid `__platforms` export -3. **Determines platform from filename**: Files with platform-specific naming (`.browser.ts`, `.node.ts`, `.react_native.ts`) get their specific platform(s) -4. **Defaults to universal**: Files without platform-specific naming get `['__universal__']` -5. **Adds Platform type import**: Calculates correct relative path and ensures `import type { Platform } from '../path/to/platform_support'` exists +2. **Ensures correct Platform import**: Normalizes all Platform imports to use the correct path and format +3. For each file, checks if it has a valid `__platforms` export +4. **Determines platform from filename**: Files with platform-specific naming (`.browser.ts`, `.node.ts`, `.react_native.ts`) get their specific platform(s) +5. **Defaults to universal**: Files without platform-specific naming get `['__universal__']` 6. **Moves export to end**: Places `__platforms` export at the end of the file for consistency 7. Preserves existing platform values for files that already have valid `__platforms` exports ### Actions -- **Fixed**: File had missing, invalid, or incorrectly formatted `__platforms` export - now corrected +- **Added**: File was missing `__platforms` export - now added +- **Fixed**: File had invalid or incorrectly formatted `__platforms` export - now corrected - **Moved**: File had valid `__platforms` export but not at the end - moved to end - **Skipped**: File already has valid `__platforms` export at the end diff --git a/scripts/add-platform-exports.js b/scripts/fix-platform-export.js similarity index 96% rename from scripts/add-platform-exports.js rename to scripts/fix-platform-export.js index 9b039a808..0a023fdab 100644 --- a/scripts/add-platform-exports.js +++ b/scripts/fix-platform-export.js @@ -17,15 +17,21 @@ */ /** - * Auto-add __platforms to files + * Fix platform export issues * - * This script automatically adds __platforms export to files that don't have it. - * Uses TypeScript parser to analyze files and add proper type annotations. + * This script automatically fixes __platforms export issues in files: + * - Adds missing __platforms exports + * - Fixes invalid __platforms declarations + * - Moves __platforms to end of file + * - Ensures correct Platform import + * + * Note: This only fixes __platforms export issues. Import compatibility issues + * must be resolved manually. * * Strategy: * 1. Files with platform-specific naming (.browser.ts, .node.ts, .react_native.ts) get their specific platform(s) * 2. All other files are assumed to be universal and get ['__universal__'] - * 3. Adds Platform type import and type annotation + * 3. Adds Platform import and type annotation * 4. Inserts __platforms export at the end of the file */ diff --git a/scripts/platform-validator.js b/scripts/platform-validator.js index aa12a34c3..b0077a837 100755 --- a/scripts/platform-validator.js +++ b/scripts/platform-validator.js @@ -22,8 +22,8 @@ * Provides a unified interface for validating and fixing platform isolation issues. * * Usage: - * node platform-validator.js --validate # Validate platform isolation (default) - * node platform-validator.js --fix # Fix platform isolation issues + * node platform-validator.js --validate # Validate platform isolation (default) + * node platform-validator.js --fix-export # Fix platform export issues */ /* eslint-disable @typescript-eslint/no-var-requires */ @@ -33,21 +33,21 @@ function main() { const args = process.argv.slice(2); const hasValidate = args.includes('--validate'); - const hasFix = args.includes('--fix'); + const hasFixExport = args.includes('--fix-export'); // Check if both options are provided - if (hasValidate && hasFix) { - console.error('❌ Error: Cannot specify both --validate and --fix options'); + if (hasValidate && hasFixExport) { + console.error('❌ Error: Cannot specify both --validate and --fix-export options'); process.exit(1); } // Determine which script to run (default to validate) - const shouldFix = hasFix; + const shouldFix = hasFixExport; try { if (shouldFix) { - console.log('🔧 Running platform isolation fix...\n'); - execSync('node scripts/add-platform-exports.js', { stdio: 'inherit' }); + console.log('🔧 Fixing platform export issues...\n'); + execSync('node scripts/fix-platform-export.js', { stdio: 'inherit' }); } else { console.log('🔍 Running platform isolation validation...\n'); execSync('node scripts/validate-platform-isolation.js', { stdio: 'inherit' });