0
# symbol-observable
1
2
Symbol.observable ponyfill for consistent observable symbol usage across JavaScript environments. This package provides a Symbol.observable implementation that ensures all consumers use the same observable symbol value, which is critical for interoperability between observable libraries like RxJS, XStream, and Most.js.
3
4
## Package Information
5
6
- **Package Name**: symbol-observable
7
- **Package Type**: npm
8
- **Language**: JavaScript (with TypeScript support)
9
- **Installation**: `npm install symbol-observable`
10
11
## Core Imports
12
13
```typescript
14
import symbolObservable from "symbol-observable";
15
```
16
17
```javascript
18
// CommonJS
19
const symbolObservable = require("symbol-observable").default;
20
```
21
22
For ponyfill function:
23
24
```typescript
25
import makeObservableSymbol from "symbol-observable/ponyfill";
26
```
27
28
```javascript
29
// CommonJS
30
const makeObservableSymbol = require("symbol-observable/ponyfill").default;
31
```
32
33
## Basic Usage
34
35
```typescript
36
import symbolObservable from "symbol-observable";
37
38
// Use the observable symbol to make objects observable
39
const myObservable = {
40
[symbolObservable]() {
41
return {
42
subscribe(observer) {
43
// Implementation
44
const handler = (data) => observer.next(data);
45
46
// Setup data source
47
someDataSource.addEventListener('data', handler);
48
49
return {
50
unsubscribe() {
51
someDataSource.removeEventListener('data', handler);
52
}
53
};
54
},
55
[symbolObservable]() {
56
return this;
57
}
58
};
59
}
60
};
61
62
// The symbol works consistently across different environments
63
console.log(typeof symbolObservable); // "symbol" in modern environments, "string" in legacy environments
64
console.log(symbolObservable); // Symbol(https://github.com/benlesh/symbol-observable) or "@@observable"
65
```
66
67
## Architecture
68
69
symbol-observable implements a ponyfill strategy with environment detection:
70
71
- **Symbol Detection**: Detects native Symbol support and Symbol.observable availability
72
- **Fallback Strategy**: Uses Symbol.for(), Symbol(), or string fallback based on environment capabilities
73
- **Global Assignment**: Safely attempts to assign Symbol.observable for ecosystem compatibility
74
- **Security Handling**: Gracefully handles frozen Symbol objects in restricted environments
75
76
The package ensures all observable libraries use the same symbol reference, preventing interoperability issues.
77
78
## Capabilities
79
80
### Observable Symbol Export
81
82
Main export providing the Symbol.observable ponyfill that works consistently across all JavaScript environments.
83
84
```typescript { .api }
85
/**
86
* The Symbol.observable ponyfill - ensures consistent observable symbol across environments
87
* Returns Symbol.observable if available, otherwise creates appropriate fallback
88
* Runtime type can be either symbol or string depending on environment capabilities
89
*/
90
declare const symbolObservable: symbol | string;
91
export default symbolObservable;
92
93
// Global Symbol augmentation
94
declare global {
95
interface SymbolConstructor {
96
readonly observable: symbol;
97
}
98
}
99
```
100
101
**Environment Behavior:**
102
- **Native Symbol.observable exists**: Returns the existing Symbol.observable
103
- **Symbol.for available**: Creates `Symbol.for('https://github.com/benlesh/symbol-observable')`
104
- **Only Symbol available**: Creates `Symbol('https://github.com/benlesh/symbol-observable')`
105
- **No Symbol support**: Returns string `'@@observable'`
106
107
The symbol is automatically assigned to `Symbol.observable` when possible, ensuring ecosystem compatibility.
108
109
### Ponyfill Function Export
110
111
Standalone function that creates Symbol.observable for any given root object, useful for custom environments or when you need control over the global assignment.
112
113
```typescript { .api }
114
/**
115
* Creates Symbol.observable ponyfill for the provided root object
116
* @param root - The root/global object to operate on (e.g., window, global, self)
117
* @returns The observable symbol appropriate for the environment
118
* Runtime return type can be either symbol or string depending on environment capabilities
119
*/
120
declare const symbolObservablePonyfill: (root: object) => symbol | string;
121
export default symbolObservablePonyfill;
122
```
123
124
**Parameters:**
125
- **root** (object): The root/global object to inspect for Symbol support and to assign Symbol.observable if possible
126
127
**Returns:**
128
- symbol | string: The observable symbol (if Symbol is supported) or string '@@observable' (in environments without Symbol support)
129
130
**Usage Example:**
131
132
```typescript
133
import makeObservableSymbol from "symbol-observable/ponyfill";
134
135
// Use with custom root object
136
const customRoot = { Symbol: MyCustomSymbolImplementation };
137
const observableSymbol = makeObservableSymbol(customRoot);
138
console.log(typeof observableSymbol); // "symbol" or "string" depending on customRoot.Symbol
139
140
// Use with current global
141
const globalSymbol = makeObservableSymbol(
142
typeof window !== 'undefined' ? window : global
143
);
144
145
// Handle both symbol and string cases in your code
146
function isObservable(obj: any): boolean {
147
return obj != null && typeof obj[globalSymbol] === 'function';
148
}
149
```
150
151
## Types
152
153
```typescript { .api }
154
// Main export type - actual runtime type varies by environment
155
type ObservableSymbol = symbol | string;
156
157
// Ponyfill function signature - returns symbol in modern environments, string in legacy
158
type SymbolObservablePonyfill = (root: object) => symbol | string;
159
160
// Observable interface (for reference - not exported by this package)
161
interface Observable<T> {
162
subscribe(observer: Observer<T>): Subscription;
163
[Symbol.observable](): Observable<T>;
164
}
165
166
interface Observer<T> {
167
next(value: T): void;
168
error?(error: any): void;
169
complete?(): void;
170
}
171
172
interface Subscription {
173
unsubscribe(): void;
174
}
175
```
176
177
## Environment Compatibility
178
179
### JavaScript Environments
180
- **Node.js**: All versions (falls back to string in very old versions without Symbol)
181
- **Browsers**: Modern browsers with Symbol support use native symbols
182
- **Legacy Browsers**: Automatic fallback to `'@@observable'` string identifier
183
- **Web Workers**: Full support with appropriate global detection
184
185
### Module Systems
186
- **ES6 Modules**: Native import/export support
187
- **CommonJS**: Compatible with require() and module.exports
188
- **TypeScript**: Full type definitions included
189
- **UMD**: Works in browser script tags and AMD loaders
190
191
### Security Considerations
192
- **Frozen Symbol**: Gracefully handles frozen Symbol objects in security-restricted environments
193
- **CSP Compliance**: No eval() or unsafe dynamic code execution
194
- **Immutable**: Package only reads from globals, never modifies them unsafely
195
196
The ponyfill ensures that all observable-implementing libraries (RxJS, XStream, Most.js, etc.) use the same symbol reference, preventing interoperability issues that could occur if different libraries used different symbols.