From bfcb365c4c9f271e8944d66f4a95e3b8431a3157 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Mon, 24 Nov 2025 18:16:09 +0600 Subject: [PATCH 01/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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/20] 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__';