osra 0.2.13 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +319 -304
  3. package/build/connections/bidirectional.d.ts +53 -0
  4. package/build/connections/index.d.ts +20 -0
  5. package/build/connections/utils.d.ts +31 -0
  6. package/build/index.d.ts +16 -630
  7. package/build/index.js +918 -634
  8. package/build/index.js.map +1 -1
  9. package/build/revivables/abort-signal.d.ts +20 -0
  10. package/build/revivables/array-buffer.d.ts +12 -0
  11. package/build/revivables/bigint.d.ts +14 -0
  12. package/build/revivables/date.d.ts +9 -0
  13. package/build/revivables/error.d.ts +10 -0
  14. package/build/revivables/event-target.d.ts +29 -0
  15. package/build/revivables/function.d.ts +29 -0
  16. package/build/revivables/headers.d.ts +9 -0
  17. package/build/revivables/identity.d.ts +39 -0
  18. package/build/revivables/index.d.ts +39 -0
  19. package/build/revivables/map.d.ts +11 -0
  20. package/build/revivables/message-port.d.ts +83 -0
  21. package/build/revivables/promise.d.ts +34 -0
  22. package/build/revivables/readable-stream.d.ts +18 -0
  23. package/build/revivables/request.d.ts +23 -0
  24. package/build/revivables/response.d.ts +18 -0
  25. package/build/revivables/set.d.ts +11 -0
  26. package/build/revivables/transfer.d.ts +37 -0
  27. package/build/revivables/typed-array.d.ts +15 -0
  28. package/build/revivables/utils.d.ts +59 -0
  29. package/build/types.d.ts +45 -0
  30. package/build/utils/capable-check.d.ts +44 -0
  31. package/build/utils/event-channel.d.ts +27 -0
  32. package/build/utils/index.d.ts +11 -0
  33. package/build/utils/replace.d.ts +25 -0
  34. package/build/utils/transferable.d.ts +20 -0
  35. package/build/utils/transport.d.ts +56 -0
  36. package/build/utils/type-guards.d.ts +58 -0
  37. package/build/utils/type.d.ts +2 -0
  38. package/build/utils/typed-event-target.d.ts +16 -0
  39. package/build/utils/typed-message-channel.d.ts +19 -0
  40. package/package.json +60 -67
package/README.md CHANGED
@@ -1,305 +1,320 @@
1
- # Osra - Easy Communication Between Workers
2
-
3
- [![npm version](https://img.shields.io/npm/v/osra.svg)](https://www.npmjs.com/package/osra)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
-
6
- Osra is a powerful, type-safe communication library for JavaScript/TypeScript that enables seamless inter-context communication with support for complex data types that normally wouldn't be transferable.
7
-
8
- ## Features
9
-
10
- - **Universal Communication** - Works across Workers, SharedWorkers, ServiceWorkers, Windows, MessagePorts, WebSockets, and Browser Extensions
11
- - **Rich Type Support** - Seamlessly handle Promises, Functions, Streams, Dates, Errors, TypedArrays, and more
12
- - **Full TypeScript Support** - Complete type safety with automatic type inference
13
- - **Intelligent Transport** - Automatically detects platform capabilities and adapts accordingly
14
- - **Zero Dependencies** - Lightweight with no external runtime dependencies
15
- - **Graceful Degradation** - Falls back to JSON-only mode when advanced features aren't supported
16
-
17
- ## Installation
18
-
19
- ```bash
20
- npm install osra
21
- ```
22
-
23
- ## Quick Start
24
-
25
- ### Basic Worker Communication
26
-
27
- **Worker file (`worker.ts`):**
28
- ```typescript
29
- import { expose } from 'osra'
30
-
31
- const api = {
32
- // Simple function
33
- add: async (a: number, b: number) => a + b,
34
-
35
- // Function returning complex objects
36
- getUser: async (id: string) => ({
37
- id,
38
- name: 'John Doe',
39
- createdAt: new Date(),
40
- // Even functions work!
41
- greet: () => `Hello, I'm user ${id}`,
42
- }),
43
-
44
- // Streaming data
45
- streamData: async function* () {
46
- for (let i = 0; i < 10; i++) {
47
- yield i
48
- await new Promise(r => setTimeout(r, 100))
49
- }
50
- }
51
- }
52
-
53
- export type WorkerAPI = typeof api
54
-
55
- // Expose the API through the worker
56
- expose(api, { transport: self })
57
- ```
58
-
59
- **Main thread (`main.ts`):**
60
- ```typescript
61
- import { expose } from 'osra'
62
- import type { WorkerAPI } from './worker'
63
-
64
- const worker = new Worker('./worker.js', { type: 'module' })
65
-
66
- // Connect to the worker with full type safety
67
- const api = await expose<WorkerAPI>({}, { transport: worker })
68
-
69
- // Call functions as if they were local
70
- const sum = await api.add(5, 3) // 8
71
-
72
- // Complex objects work seamlessly
73
- const user = await api.getUser('123')
74
- console.log(user.createdAt) // Date object, not string!
75
- const greeting = await user.greet() // "Hello, I'm user 123"
76
-
77
- // Stream data
78
- for await (const value of api.streamData()) {
79
- console.log(value) // 0, 1, 2, ...
80
- }
81
- ```
82
-
83
- ## Advanced Examples
84
-
85
- ### Window to Window Communication
86
-
87
- ```typescript
88
- // Parent window
89
- import { expose } from 'osra'
90
-
91
- const childWindow = window.open('child.html')
92
-
93
- const parentAPI = {
94
- notifyParent: async (message: string) => {
95
- console.log('Child says:', message)
96
- }
97
- }
98
-
99
- const childAPI = await expose<ChildAPI>(parentAPI, {
100
- transport: childWindow,
101
- origin: 'https://child-domain.com' // Optional: restrict origin
102
- })
103
-
104
- // Child window (child.html)
105
- const childAPI = {
106
- initialize: async () => {
107
- console.log('Child initialized!')
108
- return true
109
- }
110
- }
111
-
112
- expose(childAPI, { transport: window.parent })
113
- ```
114
-
115
- ### SharedWorker Communication
116
-
117
- ```typescript
118
- // Shared Worker
119
- import { expose } from 'osra'
120
-
121
- const connections = new Set<string>()
122
-
123
- const api = {
124
- connect: async (clientId: string) => {
125
- connections.add(clientId)
126
- return {
127
- broadcast: async (message: string) => {
128
- // Broadcast to all connected clients
129
- console.log(`${clientId} broadcasts: ${message}`)
130
- }
131
- }
132
- }
133
- }
134
-
135
- self.addEventListener('connect', (event) => {
136
- const port = event.ports[0]
137
- expose(api, { transport: port })
138
- })
139
-
140
- // Client
141
- const sharedWorker = new SharedWorker('./shared-worker.js')
142
- const api = await expose<SharedWorkerAPI>({}, { transport: sharedWorker })
143
- const connection = await api.connect('client-1')
144
- await connection.broadcast('Hello everyone!')
145
- ```
146
-
147
- ### Browser Extension Communication
148
-
149
- ```typescript
150
- // Background script
151
- import { expose } from 'osra'
152
-
153
- const api = {
154
- fetchData: async (url: string) => {
155
- const response = await fetch(url)
156
- return response.json()
157
- }
158
- }
159
-
160
- expose(api, { transport: chrome.runtime })
161
-
162
- // Content script or popup
163
- const api = await expose<BackgroundAPI>({}, { transport: chrome.runtime })
164
- const data = await api.fetchData('https://api.example.com/data')
165
- ```
166
-
167
- ### Custom Transport
168
-
169
- ```typescript
170
- import { expose } from 'osra'
171
-
172
- // Create custom transport for any communication channel
173
- const customTransport = {
174
- emit: (message: any, transferables?: Transferable[]) => {
175
- // Send message through your custom channel
176
- myCustomChannel.send(message, transferables)
177
- },
178
- receive: (listener: (message: any) => void) => {
179
- // Listen for messages from your custom channel
180
- myCustomChannel.on('message', listener)
181
-
182
- // Return cleanup function
183
- return () => myCustomChannel.off('message', listener)
184
- }
185
- }
186
-
187
- const api = await expose<RemoteAPI>({}, { transport: customTransport })
188
- ```
189
-
190
- ## Supported Types
191
-
192
- Osra automatically handles serialization/deserialization of:
193
-
194
- - **Primitives**: `boolean`, `number`, `string`, `null`, `undefined`, `BigInt`
195
- - **Objects & Arrays**: Including nested structures
196
- - **Built-in Objects**: `Date`, `RegExp`, `Map`, `Set`, `Error`
197
- - **Binary Data**: `ArrayBuffer`, `TypedArray`, `Blob`, `File`
198
- - **Functions**: Callable across contexts with full async support
199
- - **Promises**: Seamlessly await remote promises
200
- - **Streams**: `ReadableStream` support (WritableStream coming soon)
201
- - **Transferables**: `MessagePort`, `ImageBitmap`, `OffscreenCanvas`
202
-
203
- ## API Reference
204
-
205
- ### `expose<T>(value, options)`
206
-
207
- The main function for establishing communication between contexts.
208
-
209
- #### Parameters
210
-
211
- - `value`: The object/value to expose (server-side) or an empty object (client-side)
212
- - `options`: Configuration object
213
- - `transport`: The transport to use (Worker, Window, MessagePort, etc.)
214
- - `name?`: Optional name for this endpoint (default: random UUID)
215
- - `remoteName?`: Name of the remote endpoint to connect to
216
- - `key?`: Optional key for additional security
217
- - `origin?`: Origin restriction for Window communication
218
- - `unregisterSignal?`: AbortSignal to clean up the connection
219
- - `transferAll?`: Automatically transfer all transferables (default: false)
220
-
221
- #### Returns
222
-
223
- Promise resolving to the remote API object with full type safety.
224
-
225
- ### Transfer Optimization
226
-
227
- For large binary data, use the `transfer` helper to transfer instead of clone:
228
-
229
- ```typescript
230
- import { expose, transfer } from 'osra'
231
-
232
- const api = {
233
- processImage: async (imageData: ImageData) => {
234
- // Process image...
235
- return transfer(imageData) // Transfer back instead of cloning
236
- }
237
- }
238
- ```
239
-
240
- ## Protocol Modes
241
-
242
- ### Bidirectional Mode (Default)
243
-
244
- Both sides can expose APIs and call each other:
245
-
246
- ```typescript
247
- // Side A
248
- const remoteAPI = await expose<RemoteAPI>(localAPI, { transport })
249
-
250
- // Side B
251
- const remoteAPI = await expose<RemoteAPI>(localAPI, { transport })
252
- ```
253
-
254
- ### Unidirectional Mode
255
-
256
- One-way communication when only one side needs to call the other:
257
-
258
- ```typescript
259
- // Server (exposes API)
260
- expose(api, { transport })
261
-
262
- // Client (calls API)
263
- const api = await expose<API>({}, { transport })
264
- ```
265
-
266
- ## Platform Capabilities
267
-
268
- Osra automatically detects platform capabilities and adapts its behavior:
269
-
270
- - **MessagePort Transfer**: Can transfer MessagePort objects for function calls
271
- - **ArrayBuffer Transfer**: Can transfer ArrayBuffers instead of cloning
272
- - **Stream Transfer**: Can transfer ReadableStreams directly
273
- - **Structured Clone**: Supports native structured cloning
274
-
275
- When operating in JSON-only mode (WebSockets, Browser Extensions), Osra uses a box/reviver system to serialize complex types like Functions, Promises, Dates, Errors, and TypedArrays into JSON-compatible representations that are automatically revived on the receiving end.
276
-
277
- ## Performance Tips
278
-
279
- 1. **Use Transfer for Large Data**: Transfer ArrayBuffers and TypedArrays instead of cloning
280
- 2. **Batch Operations**: Group multiple calls when possible
281
- 3. **Stream Large Datasets**: Use async generators for large data sets
282
- 4. **Reuse Connections**: Keep connections alive for multiple operations
283
-
284
- ## Browser Compatibility
285
-
286
- - Chrome/Edge 88+
287
- - Firefox 85+
288
- - Safari 15+
289
- - Node.js 16+ (with Worker Threads)
290
-
291
- ## Contributing
292
-
293
- Contributions are welcome! Please feel free to submit a Pull Request.
294
-
295
- ## License
296
-
297
- MIT © [Banou26](https://github.com/Banou26)
298
-
299
- ## Roadmap
300
-
301
- - [ ] WritableStream support
302
- - [ ] Custom revivable plugins for user-defined types
303
- - [ ] Performance optimizations for large object graphs
304
- - [ ] Better error handling and debugging tools
1
+ # Osra - Easy Communication Between Workers
2
+
3
+ [![npm version](https://img.shields.io/npm/v/osra.svg)](https://www.npmjs.com/package/osra)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Osra is a powerful, type-safe communication library for JavaScript/TypeScript that enables seamless inter-context communication with support for complex data types that normally wouldn't be transferable.
7
+
8
+ ## Features
9
+
10
+ - **Universal Communication** - Works across Workers, SharedWorkers, ServiceWorkers, Windows, MessagePorts, WebSockets, and Browser Extensions
11
+ - **Rich Type Support** - Seamlessly handle Promises, Functions, Streams, Dates, Errors, TypedArrays, and more
12
+ - **Full TypeScript Support** - Complete type safety with automatic type inference
13
+ - **Two Transport Modes** - Capable mode for structured-clone transports, JSON mode for string-only channels — selected from the transport itself
14
+ - **Zero Dependencies** - Lightweight with no external runtime dependencies
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install osra
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ### Basic Worker Communication
25
+
26
+ **Worker file (`worker.ts`):**
27
+ ```typescript
28
+ import { expose } from 'osra'
29
+
30
+ const api = {
31
+ // Simple function
32
+ add: async (a: number, b: number) => a + b,
33
+
34
+ // Function returning complex objects
35
+ getUser: async (id: string) => ({
36
+ id,
37
+ name: 'John Doe',
38
+ createdAt: new Date(),
39
+ // Even functions work!
40
+ greet: () => `Hello, I'm user ${id}`,
41
+ }),
42
+
43
+ // Streaming data
44
+ streamData: async function* () {
45
+ for (let i = 0; i < 10; i++) {
46
+ yield i
47
+ await new Promise(r => setTimeout(r, 100))
48
+ }
49
+ }
50
+ }
51
+
52
+ export type WorkerAPI = typeof api
53
+
54
+ // Expose the API through the worker
55
+ expose(api, { transport: self })
56
+ ```
57
+
58
+ **Main thread (`main.ts`):**
59
+ ```typescript
60
+ import { expose } from 'osra'
61
+ import type { WorkerAPI } from './worker'
62
+
63
+ const worker = new Worker('./worker.js', { type: 'module' })
64
+
65
+ // Connect to the worker with full type safety
66
+ const api = await expose<WorkerAPI>({}, { transport: worker })
67
+
68
+ // Call functions as if they were local
69
+ const sum = await api.add(5, 3) // 8
70
+
71
+ // Complex objects work seamlessly
72
+ const user = await api.getUser('123')
73
+ console.log(user.createdAt) // Date object, not string!
74
+ const greeting = await user.greet() // "Hello, I'm user 123"
75
+
76
+ // Stream data
77
+ for await (const value of api.streamData()) {
78
+ console.log(value) // 0, 1, 2, ...
79
+ }
80
+ ```
81
+
82
+ ## Advanced Examples
83
+
84
+ ### Window to Window Communication
85
+
86
+ ```typescript
87
+ // Parent window
88
+ import { expose } from 'osra'
89
+
90
+ const childWindow = window.open('child.html')
91
+
92
+ const parentAPI = {
93
+ notifyParent: async (message: string) => {
94
+ console.log('Child says:', message)
95
+ }
96
+ }
97
+
98
+ const childAPI = await expose<ChildAPI>(parentAPI, {
99
+ transport: childWindow,
100
+ origin: 'https://child-domain.com' // Optional: restrict origin
101
+ })
102
+
103
+ // Child window (child.html)
104
+ const childAPI = {
105
+ initialize: async () => {
106
+ console.log('Child initialized!')
107
+ return true
108
+ }
109
+ }
110
+
111
+ expose(childAPI, { transport: window.parent })
112
+ ```
113
+
114
+ ### SharedWorker Communication
115
+
116
+ ```typescript
117
+ // Shared Worker
118
+ import { expose } from 'osra'
119
+
120
+ const connections = new Set<string>()
121
+
122
+ const api = {
123
+ connect: async (clientId: string) => {
124
+ connections.add(clientId)
125
+ return {
126
+ broadcast: async (message: string) => {
127
+ // Broadcast to all connected clients
128
+ console.log(`${clientId} broadcasts: ${message}`)
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ self.addEventListener('connect', (event) => {
135
+ const port = event.ports[0]
136
+ expose(api, { transport: port })
137
+ })
138
+
139
+ // Client
140
+ const sharedWorker = new SharedWorker('./shared-worker.js')
141
+ const api = await expose<SharedWorkerAPI>({}, { transport: sharedWorker })
142
+ const connection = await api.connect('client-1')
143
+ await connection.broadcast('Hello everyone!')
144
+ ```
145
+
146
+ ### Browser Extension Communication
147
+
148
+ ```typescript
149
+ // Background script
150
+ import { expose } from 'osra'
151
+
152
+ const api = {
153
+ fetchData: async (url: string) => {
154
+ const response = await fetch(url)
155
+ return response.json()
156
+ }
157
+ }
158
+
159
+ expose(api, { transport: chrome.runtime })
160
+
161
+ // Content script or popup
162
+ const api = await expose<BackgroundAPI>({}, { transport: chrome.runtime })
163
+ const data = await api.fetchData('https://api.example.com/data')
164
+ ```
165
+
166
+ ### Custom Transport
167
+
168
+ ```typescript
169
+ import { expose } from 'osra'
170
+
171
+ // Create custom transport for any communication channel
172
+ const customTransport = {
173
+ emit: (message: any, transferables?: Transferable[]) => {
174
+ // Send message through your custom channel
175
+ myCustomChannel.send(message, transferables)
176
+ },
177
+ receive: (listener: (message: any) => void) => {
178
+ // Listen for messages from your custom channel
179
+ myCustomChannel.on('message', listener)
180
+
181
+ // Return cleanup function
182
+ return () => myCustomChannel.off('message', listener)
183
+ }
184
+ }
185
+
186
+ const api = await expose<RemoteAPI>({}, { transport: customTransport })
187
+ ```
188
+
189
+ ## Supported Types
190
+
191
+ Osra automatically handles serialization/deserialization of:
192
+
193
+ - **Primitives**: `boolean`, `number`, `string`, `null`, `undefined`, `BigInt`
194
+ - **Objects & Arrays**: Including nested structures
195
+ - **Built-in Objects**: `Date`, `RegExp`, `Map`, `Set`, `Error`
196
+ - **Binary Data**: `ArrayBuffer`, `TypedArray`, `Blob`, `File`
197
+ - **Functions**: Callable across contexts with full async support
198
+ - **Promises**: Seamlessly await remote promises
199
+ - **Streams**: `ReadableStream` support (WritableStream coming soon)
200
+ - **Transferables**: `MessagePort`, `ImageBitmap`, `OffscreenCanvas`
201
+
202
+ ## API Reference
203
+
204
+ ### `expose<T>(value, options)`
205
+
206
+ The main function for establishing communication between contexts.
207
+
208
+ #### Parameters
209
+
210
+ - `value`: The object/value to expose (server-side) or an empty object (client-side)
211
+ - `options`: Configuration object
212
+ - `transport`: The transport to use (Worker, Window, MessagePort, etc.)
213
+ - `name?`: Optional name for this endpoint (default: random UUID)
214
+ - `remoteName?`: Name of the remote endpoint to connect to
215
+ - `key?`: Optional key for additional security
216
+ - `origin?`: Origin restriction for Window communication
217
+ - `unregisterSignal?`: AbortSignal to clean up the connection
218
+
219
+ #### Returns
220
+
221
+ Promise resolving to the remote API object with full type safety.
222
+
223
+ ### Transfer Optimization
224
+
225
+ Osra copies transferables by default — your buffers stay usable on the
226
+ sender after an RPC. When you want to hand off ownership instead (large
227
+ uploads, one-shot buffers, streams you won't read locally), wrap the value
228
+ in `transfer()`:
229
+
230
+ ```typescript
231
+ import { expose, transfer } from 'osra'
232
+
233
+ const buffer = new Uint8Array(largeData).buffer
234
+
235
+ // Default: copy. `buffer` is still usable after this call.
236
+ await remote.preview(buffer)
237
+
238
+ // Opt-in transfer: `buffer` is neutered on the sender, no copy made.
239
+ await remote.upload(transfer(buffer))
240
+ ```
241
+
242
+ `transfer()` works for `ArrayBuffer`, typed array views, `MessagePort`,
243
+ streams, `ImageBitmap`, and `OffscreenCanvas`. It's idempotent and a no-op
244
+ for primitives and plain objects. Must-transfer types (`MessagePort`,
245
+ streams, `OffscreenCanvas`) are always moved regardless of the wrapper —
246
+ structured clone can't copy them.
247
+
248
+ ## Protocol Modes
249
+
250
+ ### Bidirectional Mode (Default)
251
+
252
+ Both sides can expose APIs and call each other:
253
+
254
+ ```typescript
255
+ // Side A
256
+ const remoteAPI = await expose<RemoteAPI>(localAPI, { transport })
257
+
258
+ // Side B
259
+ const remoteAPI = await expose<RemoteAPI>(localAPI, { transport })
260
+ ```
261
+
262
+ ### Unidirectional Mode
263
+
264
+ One-way communication when only one side needs to call the other:
265
+
266
+ ```typescript
267
+ // Server (exposes API)
268
+ expose(api, { transport })
269
+
270
+ // Client (calls API)
271
+ const api = await expose<API>({}, { transport })
272
+ ```
273
+
274
+ ## Transport Modes
275
+
276
+ Osra picks between two modes based on the transport you hand it:
277
+
278
+ - **Capable mode** — Workers, SharedWorkers, ServiceWorkers, Windows,
279
+ MessagePorts, and any custom transport without `isJson: true`. Uses
280
+ structured clone natively and moves transferables when you opt in with
281
+ `transfer()`.
282
+ - **JSON mode** WebSockets, browser extension runtime/port APIs, and any
283
+ custom transport flagged with `isJson: true`. Complex types (Functions,
284
+ Promises, Dates, Errors, TypedArrays, streams, …) still work: the
285
+ box/reviver system serializes them into JSON-safe representations and
286
+ revives them on the other side.
287
+
288
+ You generally don't need to configure anything — pass your transport and
289
+ osra does the right thing. For a custom transport that tunnels JSON (e.g.
290
+ over a `string`-only channel), set `isJson: true` on it.
291
+
292
+ ## Performance Tips
293
+
294
+ 1. **Use Transfer for Large Data**: Transfer ArrayBuffers and TypedArrays instead of cloning
295
+ 2. **Batch Operations**: Group multiple calls when possible
296
+ 3. **Stream Large Datasets**: Use async generators for large data sets
297
+ 4. **Reuse Connections**: Keep connections alive for multiple operations
298
+
299
+ ## Browser Compatibility
300
+
301
+ - Chrome/Edge 88+
302
+ - Firefox 85+
303
+ - Safari 15+
304
+ - Node.js 16+ (with Worker Threads)
305
+
306
+ ## Contributing
307
+
308
+ Contributions are welcome! Please feel free to submit a Pull Request.
309
+
310
+ ## License
311
+
312
+ MIT © [Banou26](https://github.com/Banou26)
313
+
314
+ ## Roadmap
315
+
316
+ - [ ] WritableStream support
317
+ - [ ] Custom revivable plugins for user-defined types
318
+ - [ ] Performance optimizations for large object graphs
319
+ - [ ] Better error handling and debugging tools
305
320
  - [ ] WebRTC DataChannel transport