Skip to content

Commit 96e1b80

Browse files
committed
multi platform
1 parent bfcb365 commit 96e1b80

File tree

7 files changed

+436
-72
lines changed

7 files changed

+436
-72
lines changed

docs/PLATFORM_ISOLATION.md

Lines changed: 115 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,116 @@ This project supports multiple runtime platforms (Browser, Node.js, React Native
66

77
## Naming Convention
88

9-
Platform-specific files use a suffix pattern:
9+
Platform-specific files can be identified in two ways:
10+
11+
### 1. File Naming Convention (Single Platform)
12+
13+
For files specific to a single platform, use a suffix pattern:
1014
- `.browser.ts` - Browser-specific implementation
1115
- `.node.ts` - Node.js-specific implementation
1216
- `.react_native.ts` - React Native-specific implementation
1317
- `.ts` (no suffix) - Universal code (works across all platforms)
1418

19+
### 2. Export Declaration (Multiple Platforms)
20+
21+
For files that support multiple platforms but not all (e.g., Browser + React Native, but not Node.js), export a `__supportedPlatforms` array:
22+
23+
```typescript
24+
// lib/utils/web-features.ts
25+
export const __supportedPlatforms = ['browser', 'react_native'];
26+
27+
// Your code that works on both browser and react_native
28+
export function getWindowSize() {
29+
// Implementation that works on both platforms
30+
}
31+
```
32+
33+
Valid platform identifiers: `'browser'`, `'node'`, `'react_native'`
34+
35+
### Priority
36+
37+
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.
38+
1539
## Import Rules
1640

1741
Each platform-specific file can **only** import from:
1842

19-
1. **Universal files** (no platform suffix)
20-
2. **Same-platform files** (matching platform suffix)
43+
1. **Universal files** (no platform restrictions)
44+
2. **Compatible platform files** (files that support ALL the required platforms)
2145
3. **External packages** (node_modules)
2246

47+
A file is compatible if:
48+
- It's universal (no platform restrictions)
49+
- For single-platform files: The import supports at least that platform
50+
- For multi-platform files: The import supports ALL of those platforms
51+
52+
### Compatibility Examples
53+
54+
**Single Platform File (`.browser.ts` or `__supportedPlatforms = ['browser']`)**
55+
- ✅ Can import from: universal files, `.browser.ts` files, files with `['browser']` or `['browser', 'react_native']`
56+
- ❌ Cannot import from: `.node.ts` files, files with `['node']` or `['react_native']` only
57+
58+
**Multi-Platform File (`__supportedPlatforms = ['browser', 'react_native']`)**
59+
- ✅ Can import from: universal files, files with exactly `['browser', 'react_native']`
60+
- ❌ Cannot import from: `.browser.ts` (browser only), `.react_native.ts` (react_native only), `.node.ts`
61+
- **Why?** A file supporting both platforms needs imports that work in BOTH environments
62+
2363
### Examples
2464

2565
**Valid Imports**
2666

2767
```typescript
28-
// In lib/index.browser.ts
68+
// In lib/index.browser.ts (Browser platform only)
2969
import { Config } from './shared_types'; // ✅ Universal file
30-
import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; //Same platform
70+
import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; //browser + react_native (supports browser)
3171
import { uuid } from 'uuid'; // ✅ External package
3272
```
3373

3474
```typescript
35-
// In lib/index.node.ts
75+
// In lib/index.node.ts (Node platform only)
3676
import { Config } from './shared_types'; // ✅ Universal file
3777
import { NodeRequestHandler } from './utils/http_request_handler/request_handler.node'; // ✅ Same platform
3878
```
3979

80+
```typescript
81+
// In lib/index.react_native.ts (React Native platform only)
82+
import { Config } from './shared_types'; // ✅ Universal file
83+
84+
// If web-features.ts has: __supportedPlatforms = ['browser', 'react_native']
85+
import { getWindowSize } from './utils/web-features'; // ✅ Compatible (supports react_native)
86+
```
87+
88+
```typescript
89+
// In lib/utils/web-api.ts
90+
// export const __supportedPlatforms = ['browser', 'react_native'];
91+
92+
import { Config } from './shared_types'; // ✅ Universal file
93+
94+
// If dom-helpers.ts has: __supportedPlatforms = ['browser', 'react_native']
95+
import { helpers } from './dom-helpers'; // ✅ Compatible (supports BOTH browser and react_native)
96+
```
97+
4098
**Invalid Imports**
4199

42100
```typescript
43-
// In lib/index.browser.ts
44-
import { NodeRequestHandler } from './utils/http_request_handler/request_handler.node'; // ❌ Different platform
101+
// In lib/index.browser.ts (Browser platform only)
102+
import { NodeRequestHandler } from './utils/http_request_handler/request_handler.node'; // ❌ Node-only file
103+
```
104+
105+
```typescript
106+
// In lib/index.node.ts (Node platform only)
107+
// If web-features.ts has: __supportedPlatforms = ['browser', 'react_native']
108+
import { getWindowSize } from './utils/web-features'; // ❌ Not compatible with Node
45109
```
46110

47111
```typescript
48-
// In lib/index.node.ts
49-
import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; // ❌ Different platform
112+
// In lib/utils/web-api.ts
113+
// export const __supportedPlatforms = ['browser', 'react_native'];
114+
115+
// If helper.browser.ts is browser-only (no __supportedPlatforms export)
116+
import { helper } from './helper.browser'; // ❌ Browser-only, doesn't support react_native
117+
118+
// This file needs imports that work in BOTH browser AND react_native
50119
```
51120

52121
## Automatic Validation
@@ -95,11 +164,13 @@ If platform isolation is violated, the build will fail with a detailed error mes
95164

96165
When creating new platform-specific implementations:
97166

167+
### Single Platform
168+
98169
1. Name the file with the appropriate platform suffix (e.g., `my-feature.browser.ts`)
99170
2. Only import from universal or same-platform files
100171
3. Create a universal factory or interface if multiple platforms need different implementations
101172

102-
### Example: Creating a Platform-Specific Feature
173+
**Example:**
103174

104175
```typescript
105176
// lib/features/my-feature.ts (universal interface)
@@ -130,6 +201,39 @@ import { NodeMyFeature } from './my-feature.node';
130201
export const createMyFeature = () => new NodeMyFeature();
131202
```
132203

204+
### Multiple Platforms (But Not All)
205+
206+
For code that works on multiple platforms but not all, use the `__supportedPlatforms` export:
207+
208+
**Example: Browser + React Native only**
209+
210+
```typescript
211+
// lib/utils/dom-helpers.ts
212+
export const __supportedPlatforms = ['browser', 'react_native'];
213+
214+
// This code works on both browser and react_native, but not node
215+
export function getElementById(id: string): Element | null {
216+
if (typeof document !== 'undefined') {
217+
return document.getElementById(id);
218+
}
219+
// React Native polyfill or alternative
220+
return null;
221+
}
222+
```
223+
224+
**Example: Node + React Native only**
225+
226+
```typescript
227+
// lib/utils/native-crypto.ts
228+
export const __supportedPlatforms = ['node', 'react_native'];
229+
230+
import crypto from 'crypto'; // Available in both Node and React Native
231+
232+
export function generateHash(data: string): string {
233+
return crypto.createHash('sha256').update(data).digest('hex');
234+
}
235+
```
236+
133237
## Troubleshooting
134238

135239
If you encounter a platform isolation error:

lib/event_processor/event_dispatcher/default_dispatcher.browser.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
* limitations under the License.
1515
*/
1616

17+
// This implementation works in both browser and react_native environments
18+
export const __supportedPlatforms = ['browser', 'react_native'];
19+
1720
import { BrowserRequestHandler } from "../../utils/http_request_handler/request_handler.browser";
1821
import { EventDispatcher } from './event_dispatcher';
1922
import { DefaultEventDispatcher } from './default_dispatcher';

lib/utils/http_request_handler/request_handler.browser.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
* limitations under the License.
1515
*/
1616

17+
// This implementation works in both browser and react_native environments
18+
export const __supportedPlatforms = ['browser', 'react_native'];
19+
1720
import { AbortableRequest, Headers, RequestHandler, Response } from './http';
1821
import { LoggerFacade, LogLevel } from '../../logging/logger';
1922
import { REQUEST_TIMEOUT_MS } from '../enums';

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"clean:win": "(if exist dist rd /s/q dist)",
5959
"lint": "tsc --noEmit && eslint 'lib/**/*.js' 'lib/**/*.ts'",
6060
"validate-platform-isolation": "node scripts/validate-platform-isolation.js",
61+
"test-platform-isolation": "node scripts/test-validator.js",
6162
"test-vitest": "vitest run",
6263
"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'",
6364
"test": "npm run test-mocha && npm run test-vitest",

scripts/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Scripts
2+
3+
This directory contains build and validation scripts for the JavaScript SDK.
4+
5+
## validate-platform-isolation.js
6+
7+
Validates that platform-specific code is properly isolated to prevent runtime errors when building for different platforms (Browser, Node.js, React Native).
8+
9+
### Usage
10+
11+
```bash
12+
# Run manually
13+
node scripts/validate-platform-isolation.js
14+
15+
# Run via npm script
16+
npm run validate-platform-isolation
17+
18+
# Runs automatically during build
19+
npm run build
20+
```
21+
22+
### How It Works
23+
24+
The script:
25+
1. Scans all TypeScript/JavaScript files in the `lib/` directory
26+
2. Identifies platform-specific files by:
27+
- Naming convention (`.browser.ts`, `.node.ts`, `.react_native.ts`)
28+
- `__supportedPlatforms` export for multi-platform files
29+
3. Parses import statements (ES6 imports, require(), dynamic imports)
30+
4. Validates that each import is compatible with the file's platform
31+
5. Fails with exit code 1 if any violations are found
32+
33+
### Exit Codes
34+
35+
- `0`: All platform-specific files are properly isolated
36+
- `1`: Violations found or script error
37+
38+
## test-validator.js
39+
40+
Comprehensive test suite for the platform isolation validator. Documents and validates all compatibility rules.
41+
42+
### Usage
43+
44+
```bash
45+
# Run via npm script
46+
npm run test-platform-isolation
47+
48+
# Or run directly
49+
node scripts/test-validator.js
50+
```
51+
52+
Tests cover:
53+
- Universal imports (always compatible)
54+
- Single platform file imports
55+
- Single platform importing from multi-platform files
56+
- Multi-platform file imports (strictest rules)
57+
- `__supportedPlatforms` extraction
58+
59+
---
60+
61+
See [../docs/PLATFORM_ISOLATION.md](../docs/PLATFORM_ISOLATION.md) for detailed documentation on platform isolation rules.

scripts/test-validator.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Comprehensive test suite for platform isolation validator
5+
*
6+
* This test documents and validates all the compatibility rules
7+
*/
8+
9+
const assert = require('assert');
10+
const validator = require('./validate-platform-isolation.js');
11+
12+
let passed = 0;
13+
let failed = 0;
14+
15+
function test(description, actual, expected) {
16+
try {
17+
assert.strictEqual(actual, expected);
18+
console.log(`✅ ${description}`);
19+
passed++;
20+
} catch (e) {
21+
console.log(`❌ ${description}`);
22+
console.log(` Expected: ${expected}, Got: ${actual}`);
23+
failed++;
24+
}
25+
}
26+
27+
console.log('Platform Isolation Validator - Comprehensive Test Suite\n');
28+
console.log('=' .repeat(70));
29+
30+
console.log('\n1. UNIVERSAL IMPORTS (always compatible)');
31+
console.log('-'.repeat(70));
32+
test('Browser file can import universal',
33+
validator.isPlatformCompatible('browser', null), true);
34+
test('Node file can import universal',
35+
validator.isPlatformCompatible('node', null), true);
36+
test('Multi-platform file can import universal',
37+
validator.isPlatformCompatible(['browser', 'react_native'], null), true);
38+
39+
console.log('\n2. SINGLE PLATFORM FILES');
40+
console.log('-'.repeat(70));
41+
test('Browser file can import from browser file',
42+
validator.isPlatformCompatible('browser', 'browser'), true);
43+
test('Browser file CANNOT import from node file',
44+
validator.isPlatformCompatible('browser', 'node'), false);
45+
test('Node file can import from node file',
46+
validator.isPlatformCompatible('node', 'node'), true);
47+
test('React Native file can import from react_native file',
48+
validator.isPlatformCompatible('react_native', 'react_native'), true);
49+
50+
console.log('\n3. SINGLE PLATFORM IMPORTING FROM MULTI-PLATFORM');
51+
console.log('-'.repeat(70));
52+
test('Browser file CAN import from [browser, react_native] file',
53+
validator.isPlatformCompatible('browser', ['browser', 'react_native']), true);
54+
test('React Native file CAN import from [browser, react_native] file',
55+
validator.isPlatformCompatible('react_native', ['browser', 'react_native']), true);
56+
test('Node file CANNOT import from [browser, react_native] file',
57+
validator.isPlatformCompatible('node', ['browser', 'react_native']), false);
58+
59+
console.log('\n4. MULTI-PLATFORM FILES (strictest rules)');
60+
console.log('-'.repeat(70));
61+
test('[browser, react_native] file CAN import from [browser, react_native] file',
62+
validator.isPlatformCompatible(['browser', 'react_native'], ['browser', 'react_native']), true);
63+
test('[browser, react_native] file CANNOT import from browser-only file',
64+
validator.isPlatformCompatible(['browser', 'react_native'], 'browser'), false);
65+
test('[browser, react_native] file CANNOT import from react_native-only file',
66+
validator.isPlatformCompatible(['browser', 'react_native'], 'react_native'), false);
67+
test('[browser, react_native] file CANNOT import from node file',
68+
validator.isPlatformCompatible(['browser', 'react_native'], 'node'), false);
69+
70+
console.log('\n5. SUPPORTED PLATFORMS EXTRACTION');
71+
console.log('-'.repeat(70));
72+
const testExport1 = `export const __supportedPlatforms = ['browser', 'react_native'];`;
73+
const platforms1 = validator.extractSupportedPlatforms(testExport1);
74+
test('Extract __supportedPlatforms array',
75+
JSON.stringify(platforms1), JSON.stringify(['browser', 'react_native']));
76+
77+
const testExport2 = `export const __supportedPlatforms: string[] = ["browser", "node"];`;
78+
const platforms2 = validator.extractSupportedPlatforms(testExport2);
79+
test('Extract __supportedPlatforms with type annotation',
80+
JSON.stringify(platforms2), JSON.stringify(['browser', 'node']));
81+
82+
console.log('\n' + '='.repeat(70));
83+
console.log(`\nResults: ${passed} passed, ${failed} failed`);
84+
85+
if (failed > 0) {
86+
process.exit(1);
87+
}
88+
89+
console.log('\n✅ All tests passed!');

0 commit comments

Comments
 (0)