Skip to content

Custom revivables

Every type osra knows how to send is implemented as a small module: a guard that recognizes the value, a box function that flattens it into something the wire can carry, and a revive function that rebuilds it on the other side.
The defaults are an ordered list of those modules, and the revivableModules option of expose() hands you that list so you can extend it, reorder it, or replace parts of it.

This is also the way to get your own classes across.
If you try to send an instance of your own class, osra rejects it at compile time, because it has no way to rebuild an arbitrary prototype on the other side.
And even if it went through, structured clone would only keep the instance’s own data fields, so it would arrive as a plain object stripped of its methods.

A custom module fills exactly that gap: you tell osra how to flatten the instance, and how to build a real one back from those fields.

Here is a module for a small Point class:

import type {
type RevivableContext<TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), ... 23 more ..., { ...; }]> = {
transport: Transport;
remoteUuid: Uuid;
sendMessage: (message: MessageFields & Record<string, unknown>) => void;
revivableModules: TModules;
eventTarget: MessageEventTarget<TModules>;
}
RevivableContext
,
type RevivableModule<T extends string = string, T2 = any, T3 extends BoxBase<T> = any, T4 extends MessageFields = MessageFields> = {
readonly type: T;
readonly isType: (value: unknown) => value is T2;
readonly objectsOnly?: boolean;
readonly box: ((value: T2, context: RevivableContext<any>) => T3) | ((...args: any[]) => any);
readonly revive: (value: T3, context: RevivableContext<any>) => T2;
readonly init?: (context: RevivableContext<any>) => void;
readonly Messages?: T4;
}
RevivableModule
} from 'osra'
import {
type BoxBase<T extends string = string> = {
readonly __OSRA_BOX__: "revivable";
} & {
type: T;
}
const BoxBase: {
readonly __OSRA_BOX__: "revivable";
}
BoxBase
} from 'osra'
class
class Point
Point
{
constructor(public
Point.x: number
x
: number, public
Point.y: number
y
: number) {}
Point.distance(): number
distance
() { return
var Math: Math

An intrinsic object that provides basic mathematics functionality and constants.

Math
.
Math.hypot(...values: number[]): number

Returns the square root of the sum of squares of its arguments.

@paramvalues Values to compute the square root for. If no arguments are passed, the result is +0. If there is only one argument, the result is the absolute value. If any argument is +Infinity or -Infinity, the result is +Infinity. If any argument is NaN, the result is NaN. If all arguments are either +0 or −0, the result is +0.

hypot
(this.
Point.x: number
x
, this.
Point.y: number
y
) }
}
const
const point: {
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}
point
= {
type: string
type
: 'point' as
type const = "point"
const
,
objectsOnly?: boolean | undefined

Declare true when isType never claims a primitive (string, number, boolean, bigint, symbol, undefined, null). The walker then skips the module for primitive leaves, which is most of what a large payload is made of. Leaving it off is always safe: the default is to call isType for every value, exactly as before. objectsOnlyFlagsAreHonest in the suite calls every flagged module with a set of primitives and fails if one of them says yes.

objectsOnly
: true,
isType: (value: unknown) => value is any
isType
: (
value: unknown
value
: unknown):
value: unknown
value
is
class Point
Point
=>
value: unknown
value
instanceof
class Point
Point
,
box: ((...args: any[]) => any) | ((value: any, context: RevivableContext<any>) => any)
box
: (
value: Point
value
:
class Point
Point
,
_context: RevivableContext
_context
:
type RevivableContext<TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), ... 23 more ..., { ...; }]> = {
transport: Transport;
remoteUuid: Uuid;
sendMessage: (message: MessageFields & Record<string, unknown>) => void;
revivableModules: TModules;
eventTarget: MessageEventTarget<TModules>;
}
RevivableContext
) => ({
...
const BoxBase: {
readonly __OSRA_BOX__: "revivable";
}
BoxBase
,
type: "point"
type
: 'point' as
type const = "point"
const
,
x: number
x
:
value: Point
value
.
Point.x: number
x
,
y: number
y
:
value: Point
value
.
Point.y: number
y
}),
revive: (value: any, context: RevivableContext<any>) => any
revive
: (
value: {
x: number;
y: number;
}
value
: {
x: number
x
: number,
y: number
y
: number },
_context: RevivableContext
_context
:
type RevivableContext<TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), ... 23 more ..., { ...; }]> = {
transport: Transport;
remoteUuid: Uuid;
sendMessage: (message: MessageFields & Record<string, unknown>) => void;
revivableModules: TModules;
eventTarget: MessageEventTarget<TModules>;
}
RevivableContext
) =>
new
constructor Point(x: number, y: number): Point
Point
(
value: {
x: number;
y: number;
}
value
.
x: number
x
,
value: {
x: number;
y: number;
}
value
.
y: number
y
)
} as
type const = {
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}
const
satisfies
type RevivableModule<T extends string = string, T2 = any, T3 extends BoxBase<T> = any, T4 extends MessageFields = MessageFields> = {
readonly type: T;
readonly isType: (value: unknown) => value is T2;
readonly objectsOnly?: boolean;
readonly box: ((value: T2, context: RevivableContext<any>) => T3) | ((...args: any[]) => any);
readonly revive: (value: T3, context: RevivableContext<any>) => T2;
readonly init?: (context: RevivableContext<any>) => void;
readonly Messages?: T4;
}
RevivableModule
Field
type Names the box on the wire. Keep it unique within the list, reviving picks the first module whose type matches.
isType Decides whether this module handles a value on the way out.
box Flattens the value into something sendable. Spread BoxBase into the result so osra recognizes it as a box, and keep the type field on it.
revive Rebuilds the value on the receiving side, from exactly the fields box produced.
objectsOnly Optional. Set it to true when isType never claims a primitive, so osra skips your module for strings, numbers and the like. Most modules qualify.
init Optional. Runs once when a connection starts, see live values.
Messages Optional. Declares the custom message types your module sends, see live values.

One thing to note is that both box and revive are synchronous.
Osra runs them inline while it serializes and deserializes messages, so they must return their result directly, never a Promise.
If your type needs to keep talking after it was sent, the way functions and streams do, that is what live values are for.

Also keep in mind that whatever box returns is sent as is, osra does not walk into your box looking for more values to convert.
Plain JSON data like the two numbers above is always safe, but a field holding a Date, a function, or another Point needs to be boxed by you, see nested values.

revivableModules is a function that receives the default module list and returns the list you actually want.
Putting your module in front of the defaults is the usual shape, and typing the parameter as DefaultRevivableModules is what lets osra infer the final list from the option:

main.ts
import type {
type DefaultRevivableModules = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]
DefaultRevivableModules
} from 'osra'
import {
const expose: <T = unknown, const TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), ... 16 more ..., {
...;
}], const TTransport extends Transport = Transport, const TValue = Capable<...>, TResult = Remote<...>>(value: TValue extends Contextual<infer U> ? Contextual<CapableCheck<...>> : CapableCheck<...>, options: Omit<StartConnectionsOptions<TModules>, "connection"> & {
transport: TTransport;
connection?: (connected: Connected<Remote<T>>) => TResult;
}) => Exposed<TResult>

Expose a value to whoever connects, and get back what they exposed.

Wrap value in context to build it once per connection, which is what lets one server answer each realm differently (scoped resolvers per app) instead of sharing one object across all of them. A bare function stays a plain exposed endpoint, so the wrapper is what disambiguates the two.

The result is both awaitable and async-iterable: awaiting gives the first peer, iterating gives every peer as it connects. Both hand back the same shape.

const remote = await expose(resolvers, { transport }) // the first peer's value
for await (const remote of expose(resolvers, { transport })) { } // every peer's value

connection decides what that shape is. Omit it and it is the peer's value, which is what expose has always resolved to. Return whatever a connection should mean instead:

const { value, context } = await expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})
for await (const peer of expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})) {
if (!allowed(peer.context.origin)) peer.context.abort?.()
}

A peer's identity is whatever the transport can observe merged over whatever the caller declared in context. Only a window message carries a browser-set origin and source; a MessagePort message carries neither, so a port-based server declares what it learned when it received the port. Observed fields win over declared ones, so a declaration can never spoof a real origin.

expose
} from 'osra'
import {
class Point
Point
,
const point: {
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}
point
} from './point'
const
const withPoint: (defaults: DefaultRevivableModules) => readonly [{
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}, typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), ... 24 more ..., {
...;
}]
withPoint
= (
defaults: readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]
defaults
:
type DefaultRevivableModules = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]
DefaultRevivableModules
) => [
const point: {
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}
point
, ...
defaults: readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]
defaults
] as
type const = readonly [{
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}, typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), ... 24 more ..., {
...;
}]
const
const
const payload: {
scale: (p: Point) => Point;
}
payload
= {
scale: (p: Point) => Point
scale
: (
p: Point
p
:
class Point
Point
) => new
new Point(x: number, y: number): Point
Point
(
p: Point
p
.
Point.x: number
x
* 2,
p: Point
p
.
Point.y: number
y
* 2) }
expose<unknown, readonly [{
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}, typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), ... 24 more ..., {
...;
}], Worker, {
...;
}, unknown>(value: {
...;
}, options: Omit<...> & {
...;
}): Exposed<...>

Expose a value to whoever connects, and get back what they exposed.

Wrap value in context to build it once per connection, which is what lets one server answer each realm differently (scoped resolvers per app) instead of sharing one object across all of them. A bare function stays a plain exposed endpoint, so the wrapper is what disambiguates the two.

The result is both awaitable and async-iterable: awaiting gives the first peer, iterating gives every peer as it connects. Both hand back the same shape.

const remote = await expose(resolvers, { transport }) // the first peer's value
for await (const remote of expose(resolvers, { transport })) { } // every peer's value

connection decides what that shape is. Omit it and it is the peer's value, which is what expose has always resolved to. Return whatever a connection should mean instead:

const { value, context } = await expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})
for await (const peer of expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})) {
if (!allowed(peer.context.origin)) peer.context.abort?.()
}

A peer's identity is whatever the transport can observe merged over whatever the caller declared in context. Only a window message carries a browser-set origin and source; a MessagePort message carries neither, so a port-based server declares what it learned when it received the port. Observed fields win over declared ones, so a declaration can never spoof a real origin.

expose
(
const payload: {
scale: (p: Point) => Point;
}
payload
, {
transport: Transport & Worker
transport
,
revivableModules?: ((defaults: DefaultRevivableModules) => readonly [{
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}, typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), ... 24 more ..., {
...;
}]) | undefined

Configure the revivable module list. Receives the defaults and returns the final ordered list - add modules, drop defaults, reorder, or override per-type as needed.

revivableModules
:
const withPoint: (defaults: DefaultRevivableModules) => readonly [{
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}, typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), ... 24 more ..., {
...;
}]
withPoint
})
const
const remote: {
scale: (p: Point) => Promise<{
x: number;
y: number;
distance: () => Promise<number>;
}>;
}
remote
= await
expose<{
scale: (p: Point) => Point;
}, readonly [{
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}, typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), ... 24 more ..., {
...;
}], Transport, Capable<...>, {
...;
}>(value: Capable<...>, options: Omit<...> & {
...;
}): Exposed<...>

Expose a value to whoever connects, and get back what they exposed.

Wrap value in context to build it once per connection, which is what lets one server answer each realm differently (scoped resolvers per app) instead of sharing one object across all of them. A bare function stays a plain exposed endpoint, so the wrapper is what disambiguates the two.

The result is both awaitable and async-iterable: awaiting gives the first peer, iterating gives every peer as it connects. Both hand back the same shape.

const remote = await expose(resolvers, { transport }) // the first peer's value
for await (const remote of expose(resolvers, { transport })) { } // every peer's value

connection decides what that shape is. Omit it and it is the peer's value, which is what expose has always resolved to. Return whatever a connection should mean instead:

const { value, context } = await expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})
for await (const peer of expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})) {
if (!allowed(peer.context.origin)) peer.context.abort?.()
}

A peer's identity is whatever the transport can observe merged over whatever the caller declared in context. Only a window message carries a browser-set origin and source; a MessagePort message carries neither, so a port-based server declares what it learned when it received the port. Observed fields win over declared ones, so a declaration can never spoof a real origin.

expose
<typeof
const payload: {
scale: (p: Point) => Point;
}
payload
,
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any

Obtain the return type of a function type

ReturnType
<typeof
const withPoint: (defaults: DefaultRevivableModules) => readonly [{
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}, typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), ... 24 more ..., {
...;
}]
withPoint
>>(
{},
{
transport: Transport
transport
,
revivableModules?: ((defaults: DefaultRevivableModules) => readonly [{
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}, typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), ... 24 more ..., {
...;
}]) | undefined

Configure the revivable module list. Receives the defaults and returns the final ordered list - add modules, drop defaults, reorder, or override per-type as needed.

revivableModules
:
const withPoint: (defaults: DefaultRevivableModules) => readonly [{
readonly type: "point";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Point;
readonly box: (value: Point, _context: RevivableContext) => {
type: "point";
x: number;
y: number;
__OSRA_BOX__: "revivable";
};
readonly revive: (value: {
x: number;
y: number;
}, _context: RevivableContext) => Point;
}, typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), ... 24 more ..., {
...;
}]
withPoint
}
)
const
const doubled: {
x: number;
y: number;
distance: () => Promise<number>;
}
doubled
= await
const remote: {
scale: (p: Point) => Promise<{
x: number;
y: number;
distance: () => Promise<number>;
}>;
}
remote
.
scale: (p: Point) => Promise<{
x: number;
y: number;
distance: () => Promise<number>;
}>
scale
(new
new Point(x: number, y: number): Point
Point
(3, 4))
const doubled: {
x: number;
y: number;
distance: () => Promise<number>;
}
doubled
.
function distance(): Promise<number>
distance
() // 10, a real Point with its methods

Both sides need the same list.
The peer needs your revive to rebuild the value, and the same type string to find the right module.
A side that does not know the type hands your code the raw box, a plain object carrying the wire fields, instead of a Point.

On the side that names the peer’s type, pass the module list’s type as the second type argument too, ReturnType<typeof withPoint> above.
That second type argument is what lets the compile time Capable check know about your type.
TypeScript has no partial inference, so naming typeof payload alone resets the module list back to the defaults, and the error lands on revivableModules: withPoint, since the option then expects a function returning the default list.
The bare expose(payload, ...) call needs no type arguments at all, everything is inferred from the options.

One thing to note is that Remote<T> does not know about your module either, so on the peer’s type your class’s methods show up as async.
At runtime they are the real methods of a real instance, doubled.distance() above returns 10 synchronously.

Note: none of this is tied to structured transports.
A custom module works on JSON transports too, as long as what its box produces is JSON safe or boxed the rest of the way.

Boxing walks the list front to back and the first isType that matches wins.
Reviving looks the module up by its type string instead, so on that side the order does not matter.

This means that putting your module first lets it win over the defaults.
That is what you want when your class extends something a default already handles, an Error subclass for example: your module sees the value before the generic error one does, so your extra fields survive.

Dropping or replacing a default works the same way, since you receive the whole list and return whatever you like:

const
const modules: (defaults: DefaultRevivableModules) => readonly [RevivableModule, ...(typeof import("osra/build/revivables/transfer") | typeof import("osra/build/revivables/identity") | typeof import("osra/build/revivables/array-buffer") | typeof import("osra/build/revivables/headers") | typeof import("osra/build/revivables/error") | typeof import("osra/build/revivables/typed-array") | typeof import("osra/build/revivables/promise") | typeof import("osra/build/revivables/function") | typeof import("osra/build/revivables/message-port") | typeof import("osra/build/revivables/readable-stream") | ... 16 more ... | {
...;
})[]]
modules
= (
defaults: readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]
defaults
:
type DefaultRevivableModules = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]
DefaultRevivableModules
) =>
[
const myDate: RevivableModule
myDate
, ...
defaults: readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]
defaults
.
ReadonlyArray<typeof import("osra/build/revivables/transfer") | typeof import("osra/build/revivables/identity") | typeof import("osra/build/revivables/array-buffer") | typeof import("osra/build/revivables/date") | ... 23 more ... | { ...; }>.filter<typeof import("osra/build/revivables/transfer") | typeof import("osra/build/revivables/identity") | typeof import("osra/build/revivables/array-buffer") | typeof import("osra/build/revivables/headers") | typeof import("osra/build/revivables/error") | typeof import("osra/build/revivables/typed-array") | typeof import("osra/build/revivables/promise") | typeof import("osra/build/revivables/function") | typeof import("osra/build/revivables/message-port") | typeof import("osra/build/revivables/readable-stream") | typeof import("osra/build/revivables/writable-stream") | typeof import("osra/build/revivables/abort-signal") | ... 14 more ... | {
...;
}>(predicate: (value: typeof import("osra/build/revivables/transfer") | ... 26 more ... | {
...;
}, index: number, array: readonly (typeof import("osra/build/revivables/transfer") | ... 26 more ... | {
...;
})[]) => value is typeof import("osra/build/revivables/transfer") | ... 25 more ... | {
...;
}, thisArg?: any): (typeof import("osra/build/revivables/transfer") | ... 25 more ... | {
...;
})[] (+1 overload)

Returns the elements of an array that meet the condition specified in a callback function.

@parampredicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.

@paramthisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.

filter
(
m: typeof import("osra/build/revivables/transfer") | typeof import("osra/build/revivables/identity") | typeof import("osra/build/revivables/array-buffer") | typeof import("osra/build/revivables/date") | typeof import("osra/build/revivables/headers") | typeof import("osra/build/revivables/error") | typeof import("osra/build/revivables/typed-array") | typeof import("osra/build/revivables/promise") | typeof import("osra/build/revivables/function") | typeof import("osra/build/revivables/message-port") | typeof import("osra/build/revivables/readable-stream") | typeof import("osra/build/revivables/writable-stream") | ... 15 more ... | {
...;
}
m
=>
m: typeof import("osra/build/revivables/transfer") | typeof import("osra/build/revivables/identity") | typeof import("osra/build/revivables/array-buffer") | typeof import("osra/build/revivables/date") | typeof import("osra/build/revivables/headers") | typeof import("osra/build/revivables/error") | typeof import("osra/build/revivables/typed-array") | typeof import("osra/build/revivables/promise") | typeof import("osra/build/revivables/function") | typeof import("osra/build/revivables/message-port") | typeof import("osra/build/revivables/readable-stream") | typeof import("osra/build/revivables/writable-stream") | ... 15 more ... | {
...;
}
m
.
type: "bigint" | "symbol" | "undefined" | "function" | "transfer" | "identity" | "arrayBuffer" | "date" | "headers" | "error" | "typedArray" | "promise" | "messagePort" | "readableStream" | "writableStream" | "abortSignal" | "response" | "request" | "map" | "set" | "event" | "asyncIterator" | "nonFiniteNumber" | "clonable" | "transferable" | "blob" | "eventTarget" | "unclonable"
type
!== 'date')] as
type const = readonly [RevivableModule, ...(typeof import("osra/build/revivables/transfer") | typeof import("osra/build/revivables/identity") | typeof import("osra/build/revivables/array-buffer") | typeof import("osra/build/revivables/headers") | typeof import("osra/build/revivables/error") | typeof import("osra/build/revivables/typed-array") | typeof import("osra/build/revivables/promise") | typeof import("osra/build/revivables/function") | typeof import("osra/build/revivables/message-port") | typeof import("osra/build/revivables/readable-stream") | typeof import("osra/build/revivables/writable-stream") | ... 15 more ... | {
...;
})[]]
const

Be careful when reordering the defaults though, a few of them depend on their position:

  • The async iterator module sits before the fallbacks, otherwise generators would fall through and coerce to {}.
  • clonable and transferable sit before eventTarget, because OffscreenCanvas and friends also extend EventTarget.
  • blob sits after clonable, so a File, which extends Blob, keeps riding the more specific clonable path.
  • eventTarget sits last among the modules matching EventTarget subclasses, since MessagePort and AbortSignal need first pick.
  • unclonable sits at the very end: it is the catch-all that probes values with structuredClone() and turns whatever fails into {}.

When your module claims a value, osra stops walking: your box runs, and its result ships untouched.
This means that any field of your box that itself needs osra’s treatment, a function, a stream, another custom class, has to go through the walker explicitly.

Call recursiveBox on it with the context you were handed, and mirror it with recursiveRevive on the other side.
This is exactly what the built-in Map, Error and CustomEvent modules do for their own fields:

const
const result: {
type: "result";
objectsOnly: boolean;
isType: (value: unknown) => value is Result;
box: (value: Result, context: RevivableContext) => {
type: "result";
value: never;
at: never;
__OSRA_BOX__: "revivable";
};
revive: (value: {
value: unknown;
at: unknown;
}, context: RevivableContext) => Result;
}
result
= {
type: "result"
type
: 'result' as
type const = "result"
const
,
objectsOnly: boolean
objectsOnly
: true,
isType: (value: unknown) => value is Result
isType
: (
value: unknown
value
: unknown):
value: unknown
value
is
class Result
Result
=>
value: unknown
value
instanceof
class Result
Result
,
box: (value: Result, context: RevivableContext) => {
type: "result";
value: never;
at: never;
__OSRA_BOX__: "revivable";
}
box
: (
value: Result
value
:
class Result
Result
,
context: RevivableContext
context
:
type RevivableContext<TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), ... 23 more ..., { ...; }]> = {
transport: Transport;
remoteUuid: Uuid;
sendMessage: (message: MessageFields & Record<string, unknown>) => void;
revivableModules: TModules;
eventTarget: MessageEventTarget<TModules>;
}
RevivableContext
) => ({
...
const BoxBase: {
readonly __OSRA_BOX__: "revivable";
}
BoxBase
,
type: "result"
type
: 'result' as
type const = "result"
const
,
value: never
value
:
recursiveBox<never, readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]>(value: never, context: RevivableContext<...>): never
recursiveBox
(
value: Result
value
.
Result.value: unknown
value
as never,
context: RevivableContext
context
),
at: never
at
:
recursiveBox<never, readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]>(value: never, context: RevivableContext<...>): never
recursiveBox
(
value: Result
value
.
Result.at: Date
at
as never,
context: RevivableContext
context
)
}),
revive: (value: {
value: unknown;
at: unknown;
}, context: RevivableContext) => Result
revive
: (
value: {
value: unknown;
at: unknown;
}
value
: {
value: unknown
value
: unknown,
at: unknown
at
: unknown },
context: RevivableContext
context
:
type RevivableContext<TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), ... 23 more ..., { ...; }]> = {
transport: Transport;
remoteUuid: Uuid;
sendMessage: (message: MessageFields & Record<string, unknown>) => void;
revivableModules: TModules;
eventTarget: MessageEventTarget<TModules>;
}
RevivableContext
) =>
new
constructor Result(value: unknown, at: Date): Result
Result
(
recursiveRevive<never, readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]>(value: never, context: RevivableContext<...>): never
recursiveRevive
(
value: {
value: unknown;
at: unknown;
}
value
.
value: unknown
value
as never,
context: RevivableContext
context
),
recursiveRevive<never, readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]>(value: never, context: RevivableContext<...>): never
recursiveRevive
(
value: {
value: unknown;
at: unknown;
}
value
.
at: unknown
at
as never,
context: RevivableContext
context
) as unknown as
interface Date

Enables basic storage and retrieval of dates and times.

Date
)
}

Fields holding plain JSON data (strings, numbers, booleans, and arrays or plain objects of those) can stay raw, they survive any transport as they are.
On a structured transport other clonables happen to survive raw too, but on a JSON transport a raw Date field would silently turn into a string, so going through recursiveBox is what keeps a module correct on both modes.

One thing to note is that a module claiming a value in place, rather than a wrapper around one, needs boxClaimedValue(value, context, yourType) for its payload instead.
It walks the value through every other module while skipping yours, which recursiveBox on that same value would hand straight back to you. identity() is the built-in example, since it marks a reference rather than wrapping it.

A wrapper like Point is done the moment it arrives.
A live value keeps talking after it was sent, the way a function keeps receiving calls, or a stream keeps receiving chunks.

Both box and revive receive the connection’s context as their second argument, and it has everything a live value needs:

Field
sendMessage Sends a message of your own to the peer. It must carry a type of your choosing and the remoteUuid of the connection.
eventTarget Where this connection’s incoming messages are dispatched, as CustomEvents with the message on event.detail.
remoteUuid The peer this connection belongs to.
transport The normalized transport. Useful as isJsonOnlyTransport(context.transport) when your wire format differs per mode.
revivableModules The resolved module list of this connection.

The pattern every built-in live value follows is that box registers some local state and returns a plain descriptor, updates flow through sendMessage, and a listener on eventTarget picks them up on the other side.

Here it is for a Cell, a value you can subscribe to.
The peer gets a cell of its own that follows the original:

cell.ts
import type {
type Capable<TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), ... 23 more ..., { ...; }], Ctx extends RevivableContext = RevivableContext> = CapableBase<Ctx> | InferRevivables<TModules, Ctx> | {
[key: string]: Capable<TModules, Ctx>;
} | readonly Capable<TModules, Ctx>[] | Map<Capable<TModules, Ctx>, Capable<TModules, Ctx>> | Set<Capable<TModules, Ctx>>
Capable
,
type DefaultRevivableModules = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]
DefaultRevivableModules
,
type RevivableContext<TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), ... 23 more ..., { ...; }]> = {
transport: Transport;
remoteUuid: Uuid;
sendMessage: (message: MessageFields & Record<string, unknown>) => void;
revivableModules: TModules;
eventTarget: MessageEventTarget<TModules>;
}
RevivableContext
,
type RevivableModule<T extends string = string, T2 = any, T3 extends BoxBase<T> = any, T4 extends MessageFields = MessageFields> = {
readonly type: T;
readonly isType: (value: unknown) => value is T2;
readonly objectsOnly?: boolean;
readonly box: ((value: T2, context: RevivableContext<any>) => T3) | ((...args: any[]) => any);
readonly revive: (value: T3, context: RevivableContext<any>) => T2;
readonly init?: (context: RevivableContext<any>) => void;
readonly Messages?: T4;
}
RevivableModule
} from 'osra'
import {
type BoxBase<T extends string = string> = {
readonly __OSRA_BOX__: "revivable";
} & {
type: T;
}
const BoxBase: {
readonly __OSRA_BOX__: "revivable";
}
BoxBase
,
const onTeardown: (scope: WeakKey, fn: () => void) => (() => void)
onTeardown
,
const recursiveBox: <T extends Capable, TModules extends readonly RevivableModule[]>(value: T, context: RevivableContext<TModules>) => DeepReplaceWithBox<T, TModules[number]>
recursiveBox
,
const recursiveRevive: <T extends Capable, TModules extends readonly RevivableModule[]>(value: T, context: RevivableContext<TModules>) => DeepReplaceWithRevive<T, TModules[number]>
recursiveRevive
} from 'osra'
export class
class Cell<T>
Cell
<
function (type parameter) T in Cell<T>
T
> {
#value:
function (type parameter) T in Cell<T>
T
#listeners = new
var Set: SetConstructor
new <(value: T) => void>(iterable?: Iterable<(value: T) => void> | null | undefined) => Set<(value: T) => void> (+1 overload)
Set
<(
value: T
value
:
function (type parameter) T in Cell<T>
T
) => void>()
constructor(
value: T
value
:
function (type parameter) T in Cell<T>
T
) { this.#value =
value: T
value
}
get
Cell<T>.value: T
value
() { return this.#value }
Cell<T>.set(value: T): void
set
(
value: T
value
:
function (type parameter) T in Cell<T>
T
) {
this.#value =
value: T
value
for (const
const listener: (value: T) => void
listener
of this.#listeners)
const listener: (value: T) => void
listener
(
value: T
value
)
}
Cell<T>.subscribe(listener: (value: T) => void): () => void
subscribe
(
listener: (value: T) => void
listener
: (
value: T
value
:
function (type parameter) T in Cell<T>
T
) => void) {
this.#listeners.
Set<(value: T) => void>.add(value: (value: T) => void): Set<(value: T) => void>

Appends a new element with a specified value to the end of the Set.

add
(
listener: (value: T) => void
listener
)
return () => { this.#listeners.
Set<(value: T) => void>.delete(value: (value: T) => void): boolean

Removes a specified value from the Set.

@returnsReturns true if an element in the Set existed and has been removed, or false if the element does not exist.

delete
(
listener: (value: T) => void
listener
) }
}
}
type
type CellSet = {
type: "cell-set";
cellId: string;
value: Capable;
}
CellSet
= {
type: "cell-set"
type
: 'cell-set',
cellId: string
cellId
: string,
value: Capable
value
:
type Capable<TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), ... 23 more ..., { ...; }], Ctx extends RevivableContext = RevivableContext> = CapableBase<Ctx> | InferRevivables<TModules, Ctx> | {
[key: string]: Capable<TModules, Ctx>;
} | readonly Capable<TModules, Ctx>[] | Map<Capable<TModules, Ctx>, Capable<TModules, Ctx>> | Set<Capable<TModules, Ctx>>
Capable
}
const
const isCellSet: (message: {
type: string;
}) => message is CellSet
isCellSet
= (
message: {
type: string;
}
message
: {
type: string
type
: string }):
message: {
type: string;
}
message
is
type CellSet = {
type: "cell-set";
cellId: string;
value: Capable;
}
CellSet
=>
message: {
type: string;
}
message
.
type: string
type
=== 'cell-set'
export const
const cell: {
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ({
__OSRA_BOX__: "revivable";
type: "arrayBuffer";
} & ({
base64Buffer: string;
} | {
arrayBuffer: ArrayBuffer;
})) | {
__OSRA_BOX__: "revivable";
type: "date";
ISOString: string;
} | ... 20 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}
cell
= {
type: string
type
: 'cell' as
type const = "cell"
const
,
objectsOnly?: boolean | undefined

Declare true when isType never claims a primitive (string, number, boolean, bigint, symbol, undefined, null). The walker then skips the module for primitive leaves, which is most of what a large payload is made of. Leaving it off is always safe: the default is to call isType for every value, exactly as before. objectsOnlyFlagsAreHonest in the suite calls every flagged module with a set of primitives and fails if one of them says yes.

objectsOnly
: true,
isType: (value: unknown) => value is any
isType
: (
value: unknown
value
: unknown):
value: unknown
value
is
class Cell<T>
Cell
<any> =>
value: unknown
value
instanceof
class Cell<T>
Cell
,
box: ((...args: any[]) => any) | ((value: any, context: RevivableContext<any>) => any)
box
: (
value: Cell<any>
value
:
class Cell<T>
Cell
<any>,
context: RevivableContext
context
:
type RevivableContext<TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), ... 23 more ..., { ...; }]> = {
transport: Transport;
remoteUuid: Uuid;
sendMessage: (message: MessageFields & Record<string, unknown>) => void;
revivableModules: TModules;
eventTarget: MessageEventTarget<TModules>;
}
RevivableContext
) => {
const
const cellId: `${string}-${string}-${string}-${string}-${string}`
cellId
=
var crypto: Crypto
crypto
.
Crypto.randomUUID(): `${string}-${string}-${string}-${string}-${string}`

The randomUUID() method of the Crypto interface is used to generate a v4 UUID using a cryptographically secure random number generator. Available only in secure contexts.

MDN Reference

randomUUID
()
const
const unsubscribe: () => void
unsubscribe
=
value: Cell<any>
value
.
Cell<any>.subscribe(listener: (value: any) => void): () => void
subscribe
(
next: any
next
=> {
context: RevivableContext
context
.
sendMessage: (message: MessageFields & Record<string, unknown>) => void

Typed as a broad dispatcher so revivables can post their own message variants without triggering contravariant function-parameter mismatches across modules. The shape is enforced structurally via MessageFields.

sendMessage
({
type: string
type
: 'cell-set',
remoteUuid: `${string}-${string}-${string}-${string}-${string}`
remoteUuid
:
context: RevivableContext
context
.
remoteUuid: `${string}-${string}-${string}-${string}-${string}`
remoteUuid
,
cellId: `${string}-${string}-${string}-${string}-${string}`
cellId
,
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ({
__OSRA_BOX__: "revivable";
type: "arrayBuffer";
} & ({
base64Buffer: string;
} | {
arrayBuffer: ArrayBuffer;
})) | {
__OSRA_BOX__: "revivable";
type: "date";
ISOString: string;
} | {
__OSRA_BOX__: "revivable";
type: "headers";
entries: [string, string][];
} | BoxedError | BoxedTypedArray<TypedArray, RevivableContext> | ... 17 more ... | undefined
value
:
recursiveBox<any, readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]>(value: any, context: RevivableContext<...>): number | ... 27 more ... | undefined
recursiveBox
(
next: any
next
,
context: RevivableContext
context
)
})
})
function onTeardown(scope: WeakKey, fn: () => void): (() => void)
onTeardown
(
context: RevivableContext
context
,
const unsubscribe: () => void
unsubscribe
)
return { ...
const BoxBase: {
readonly __OSRA_BOX__: "revivable";
}
BoxBase
,
type: "cell"
type
: 'cell' as
type const = "cell"
const
,
cellId: `${string}-${string}-${string}-${string}-${string}`
cellId
,
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ({
__OSRA_BOX__: "revivable";
type: "arrayBuffer";
} & ({
base64Buffer: string;
} | {
arrayBuffer: ArrayBuffer;
})) | {
__OSRA_BOX__: "revivable";
type: "date";
ISOString: string;
} | {
__OSRA_BOX__: "revivable";
type: "headers";
entries: [string, string][];
} | BoxedError | BoxedTypedArray<TypedArray, RevivableContext> | ... 17 more ... | undefined
value
:
recursiveBox<any, readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]>(value: any, context: RevivableContext<...>): number | ... 27 more ... | undefined
recursiveBox
(
value: Cell<any>
value
.
Cell<any>.value: any
value
,
context: RevivableContext
context
) }
},
revive: (value: any, context: RevivableContext<any>) => any
revive
: (
boxed: {
cellId: string;
value: Capable;
}
boxed
: {
cellId: string
cellId
: string,
value: Capable
value
:
type Capable<TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), ... 23 more ..., { ...; }], Ctx extends RevivableContext = RevivableContext> = CapableBase<Ctx> | InferRevivables<TModules, Ctx> | {
[key: string]: Capable<TModules, Ctx>;
} | readonly Capable<TModules, Ctx>[] | Map<Capable<TModules, Ctx>, Capable<TModules, Ctx>> | Set<Capable<TModules, Ctx>>
Capable
},
context: RevivableContext
context
:
type RevivableContext<TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), ... 23 more ..., { ...; }]> = {
transport: Transport;
remoteUuid: Uuid;
sendMessage: (message: MessageFields & Record<string, unknown>) => void;
revivableModules: TModules;
eventTarget: MessageEventTarget<TModules>;
}
RevivableContext
) => {
const
const revived: Cell<number | Blob | Clonable | Transferable | undefined>
revived
= new
constructor Cell<number | Blob | Clonable | Transferable | undefined>(value: number | Blob | Clonable | Transferable | undefined): Cell<number | Blob | Clonable | Transferable | undefined>
Cell
(
recursiveRevive<Capable, readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]>(value: Capable, context: RevivableContext<...>): number | ... 3 more ... | undefined
recursiveRevive
(
boxed: {
cellId: string;
value: Capable;
}
boxed
.
value: Capable
value
,
context: RevivableContext
context
))
context: RevivableContext
context
.
eventTarget: MessageEventTarget<readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]>
eventTarget
.
TypedEventTarget<MessageEventMap<readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), ... 23 more ..., { ...; }]>>.addEventListener<"message">(type: "message", listener: ((event: CustomEvent<Message<readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), ... 16 more ..., {
...;
}]>>) => void) | null, options?: boolean | AddEventListenerOptions): void (+1 overload)

The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.

MDN Reference

addEventListener
('message', (
event: CustomEvent<{
type: string;
}>
event
:
interface CustomEvent<T = any>

The CustomEvent interface can be used to attach custom data to an event generated by an application.

MDN Reference

CustomEvent
<{
type: string
type
: string }>) => {
if (!
const isCellSet: (message: {
type: string;
}) => message is CellSet
isCellSet
(
event: CustomEvent<{
type: string;
}>
event
.
CustomEvent<{ type: string; }>.detail: {
type: string;
}

The read-only detail property of the CustomEvent interface returns any data passed when initializing the event.

MDN Reference

detail
) ||
event: CustomEvent<{
type: string;
}>
event
.
CustomEvent<{ type: string; }>.detail: CellSet

The read-only detail property of the CustomEvent interface returns any data passed when initializing the event.

MDN Reference

detail
.
cellId: string
cellId
!==
boxed: {
cellId: string;
value: Capable;
}
boxed
.
cellId: string
cellId
) return
const revived: Cell<number | Blob | Clonable | Transferable | undefined>
revived
.
Cell<number | Blob | Clonable | Transferable | undefined>.set(value: number | Blob | Clonable | Transferable | undefined): void
set
(
recursiveRevive<Capable, readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]>(value: Capable, context: RevivableContext<...>): number | ... 3 more ... | undefined
recursiveRevive
(
event: CustomEvent<{
type: string;
}>
event
.
CustomEvent<{ type: string; }>.detail: CellSet

The read-only detail property of the CustomEvent interface returns any data passed when initializing the event.

MDN Reference

detail
.
value: Capable
value
,
context: RevivableContext
context
))
})
return
const revived: Cell<number | Blob | Clonable | Transferable | undefined>
revived
}
} as
type const = {
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ({
__OSRA_BOX__: "revivable";
type: "arrayBuffer";
} & ({
base64Buffer: string;
} | {
arrayBuffer: ArrayBuffer;
})) | {
__OSRA_BOX__: "revivable";
type: "date";
ISOString: string;
} | ... 20 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}
const
satisfies
type RevivableModule<T extends string = string, T2 = any, T3 extends BoxBase<T> = any, T4 extends MessageFields = MessageFields> = {
readonly type: T;
readonly isType: (value: unknown) => value is T2;
readonly objectsOnly?: boolean;
readonly box: ((value: T2, context: RevivableContext<any>) => T3) | ((...args: any[]) => any);
readonly revive: (value: T3, context: RevivableContext<any>) => T2;
readonly init?: (context: RevivableContext<any>) => void;
readonly Messages?: T4;
}
RevivableModule
export const
const withCell: (defaults: DefaultRevivableModules) => readonly [{
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ({
__OSRA_BOX__: "revivable";
type: "arrayBuffer";
} & ({
base64Buffer: string;
} | {
arrayBuffer: ArrayBuffer;
})) | ... 21 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}, ... 27 more ..., {
...;
}]
withCell
= (
defaults: readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]
defaults
:
type DefaultRevivableModules = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]
DefaultRevivableModules
) => [
const cell: {
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ({
__OSRA_BOX__: "revivable";
type: "arrayBuffer";
} & ({
base64Buffer: string;
} | {
arrayBuffer: ArrayBuffer;
})) | {
__OSRA_BOX__: "revivable";
type: "date";
ISOString: string;
} | ... 20 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}
cell
, ...
defaults: readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), typeof import("osra/build/revivables/writable-stream"), ... 15 more ..., {
...;
}]
defaults
] as
type const = readonly [{
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ({
__OSRA_BOX__: "revivable";
type: "arrayBuffer";
} & ({
base64Buffer: string;
} | {
arrayBuffer: ArrayBuffer;
})) | {
__OSRA_BOX__: "revivable";
type: "date";
ISOString: string;
} | ... 20 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}, ... 27 more ..., {
...;
}]
const

A few things to note about it:

  • box gives each cell an id and puts it in the box, so revive can tell which messages are for it. Every message on the connection reaches every listener, so filtering by type and by that id is what keeps modules from hearing each other’s traffic.
  • sendMessage wraps your message in osra’s envelope and routes it to this connection’s peer, which is why it needs context.remoteUuid.
  • The listener types its event as CustomEvent<{ type: string }>, since the messages of every module flow through the same target, and narrows down to its own message with a type guard.
  • onTeardown(context, fn) runs fn when the connection closes, so the subscription does not outlive it.

Using it is the same as before, both sides register the module and the reading side names it in the second type argument:

worker.ts
import {
const expose: <T = unknown, const TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), ... 16 more ..., {
...;
}], const TTransport extends Transport = Transport, const TValue = Capable<...>, TResult = Remote<...>>(value: TValue extends Contextual<infer U> ? Contextual<CapableCheck<...>> : CapableCheck<...>, options: Omit<StartConnectionsOptions<TModules>, "connection"> & {
transport: TTransport;
connection?: (connected: Connected<Remote<T>>) => TResult;
}) => Exposed<TResult>

Expose a value to whoever connects, and get back what they exposed.

Wrap value in context to build it once per connection, which is what lets one server answer each realm differently (scoped resolvers per app) instead of sharing one object across all of them. A bare function stays a plain exposed endpoint, so the wrapper is what disambiguates the two.

The result is both awaitable and async-iterable: awaiting gives the first peer, iterating gives every peer as it connects. Both hand back the same shape.

const remote = await expose(resolvers, { transport }) // the first peer's value
for await (const remote of expose(resolvers, { transport })) { } // every peer's value

connection decides what that shape is. Omit it and it is the peer's value, which is what expose has always resolved to. Return whatever a connection should mean instead:

const { value, context } = await expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})
for await (const peer of expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})) {
if (!allowed(peer.context.origin)) peer.context.abort?.()
}

A peer's identity is whatever the transport can observe merged over whatever the caller declared in context. Only a window message carries a browser-set origin and source; a MessagePort message carries neither, so a port-based server declares what it learned when it received the port. Observed fields win over declared ones, so a declaration can never spoof a real origin.

expose
} from 'osra'
import {
class Cell<T>
Cell
,
const withCell: (defaults: DefaultRevivableModules) => readonly [{
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ({
__OSRA_BOX__: "revivable";
type: "arrayBuffer";
} & ({
...;
} | {
...;
})) | ... 21 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}, ... 27 more ..., {
...;
}]
withCell
} from './cell'
const
const temperature: Cell<number>
temperature
= new
new Cell<number>(value: number): Cell<number>
Cell
(20)
const
const payload: {
temperature: Cell<number>;
bump: (by: number) => void;
}
payload
= {
temperature: Cell<number>
temperature
,
bump: (by: number) => void
bump
: (
by: number
by
: number) =>
const temperature: Cell<number>
temperature
.
Cell<number>.set(value: number): void
set
(
const temperature: Cell<number>
temperature
.
Cell<number>.value: number
value
+
by: number
by
)
}
export type
type Payload = {
temperature: Cell<number>;
bump: (by: number) => void;
}
Payload
= typeof
const payload: {
temperature: Cell<number>;
bump: (by: number) => void;
}
payload
expose<unknown, readonly [{
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ({
__OSRA_BOX__: "revivable";
type: "arrayBuffer";
} & ({
base64Buffer: string;
} | {
arrayBuffer: ArrayBuffer;
})) | ... 21 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}, ... 27 more ..., {
...;
}], typeof globalThis, {
...;
}, unknown>(value: {
...;
}, options: Omit<...> & {
...;
}): Exposed<...>

Expose a value to whoever connects, and get back what they exposed.

Wrap value in context to build it once per connection, which is what lets one server answer each realm differently (scoped resolvers per app) instead of sharing one object across all of them. A bare function stays a plain exposed endpoint, so the wrapper is what disambiguates the two.

The result is both awaitable and async-iterable: awaiting gives the first peer, iterating gives every peer as it connects. Both hand back the same shape.

const remote = await expose(resolvers, { transport }) // the first peer's value
for await (const remote of expose(resolvers, { transport })) { } // every peer's value

connection decides what that shape is. Omit it and it is the peer's value, which is what expose has always resolved to. Return whatever a connection should mean instead:

const { value, context } = await expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})
for await (const peer of expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})) {
if (!allowed(peer.context.origin)) peer.context.abort?.()
}

A peer's identity is whatever the transport can observe merged over whatever the caller declared in context. Only a window message carries a browser-set origin and source; a MessagePort message carries neither, so a port-based server declares what it learned when it received the port. Observed fields win over declared ones, so a declaration can never spoof a real origin.

expose
(
const payload: {
temperature: Cell<number>;
bump: (by: number) => void;
}
payload
, {
transport: Transport & typeof globalThis
transport
:
module globalThis
globalThis
,
revivableModules?: ((defaults: DefaultRevivableModules) => readonly [{
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ({
__OSRA_BOX__: "revivable";
type: "arrayBuffer";
} & ({
...;
} | {
...;
})) | ... 21 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}, ... 27 more ..., {
...;
}]) | undefined

Configure the revivable module list. Receives the defaults and returns the final ordered list - add modules, drop defaults, reorder, or override per-type as needed.

revivableModules
:
const withCell: (defaults: DefaultRevivableModules) => readonly [{
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ({
__OSRA_BOX__: "revivable";
type: "arrayBuffer";
} & ({
...;
} | {
...;
})) | ... 21 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}, ... 27 more ..., {
...;
}]
withCell
})
main.ts
import type {
type Payload = {
temperature: Cell<number>;
bump: (by: number) => void;
}
Payload
} from './worker'
import {
const expose: <T = unknown, const TModules extends readonly RevivableModule[] = readonly [typeof import("osra/build/revivables/transfer"), typeof import("osra/build/revivables/identity"), typeof import("osra/build/revivables/array-buffer"), typeof import("osra/build/revivables/date"), typeof import("osra/build/revivables/headers"), typeof import("osra/build/revivables/error"), typeof import("osra/build/revivables/typed-array"), typeof import("osra/build/revivables/promise"), typeof import("osra/build/revivables/function"), typeof import("osra/build/revivables/message-port"), typeof import("osra/build/revivables/readable-stream"), ... 16 more ..., {
...;
}], const TTransport extends Transport = Transport, const TValue = Capable<...>, TResult = Remote<...>>(value: TValue extends Contextual<infer U> ? Contextual<CapableCheck<...>> : CapableCheck<...>, options: Omit<StartConnectionsOptions<TModules>, "connection"> & {
transport: TTransport;
connection?: (connected: Connected<Remote<T>>) => TResult;
}) => Exposed<TResult>

Expose a value to whoever connects, and get back what they exposed.

Wrap value in context to build it once per connection, which is what lets one server answer each realm differently (scoped resolvers per app) instead of sharing one object across all of them. A bare function stays a plain exposed endpoint, so the wrapper is what disambiguates the two.

The result is both awaitable and async-iterable: awaiting gives the first peer, iterating gives every peer as it connects. Both hand back the same shape.

const remote = await expose(resolvers, { transport }) // the first peer's value
for await (const remote of expose(resolvers, { transport })) { } // every peer's value

connection decides what that shape is. Omit it and it is the peer's value, which is what expose has always resolved to. Return whatever a connection should mean instead:

const { value, context } = await expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})
for await (const peer of expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})) {
if (!allowed(peer.context.origin)) peer.context.abort?.()
}

A peer's identity is whatever the transport can observe merged over whatever the caller declared in context. Only a window message carries a browser-set origin and source; a MessagePort message carries neither, so a port-based server declares what it learned when it received the port. Observed fields win over declared ones, so a declaration can never spoof a real origin.

expose
} from 'osra'
import {
const withCell: (defaults: DefaultRevivableModules) => readonly [{
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ... 22 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}, ... 27 more ..., {
...;
}]
withCell
} from './cell'
const {
const temperature: {
readonly value: number;
set: (value: number) => Promise<void>;
subscribe: (listener: (value: number) => void) => Promise<() => Promise<void>>;
}
temperature
,
const bump: (by: number) => Promise<void>
bump
} = await
expose<{
temperature: Cell<number>;
bump: (by: number) => void;
}, readonly [{
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ({
__OSRA_BOX__: "revivable";
type: "arrayBuffer";
} & ({
...;
} | {
...;
})) | ... 21 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}, ... 27 more ..., {
...;
}], Transport, Capable<...>, {
...;
}>(value: Capable<...>, options: Omit<...> & {
...;
}): Exposed<...>

Expose a value to whoever connects, and get back what they exposed.

Wrap value in context to build it once per connection, which is what lets one server answer each realm differently (scoped resolvers per app) instead of sharing one object across all of them. A bare function stays a plain exposed endpoint, so the wrapper is what disambiguates the two.

The result is both awaitable and async-iterable: awaiting gives the first peer, iterating gives every peer as it connects. Both hand back the same shape.

const remote = await expose(resolvers, { transport }) // the first peer's value
for await (const remote of expose(resolvers, { transport })) { } // every peer's value

connection decides what that shape is. Omit it and it is the peer's value, which is what expose has always resolved to. Return whatever a connection should mean instead:

const { value, context } = await expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})
for await (const peer of expose(resolvers, {
transport,
connection: ({ value, context }) => ({ value, context }),
})) {
if (!allowed(peer.context.origin)) peer.context.abort?.()
}

A peer's identity is whatever the transport can observe merged over whatever the caller declared in context. Only a window message carries a browser-set origin and source; a MessagePort message carries neither, so a port-based server declares what it learned when it received the port. Observed fields win over declared ones, so a declaration can never spoof a real origin.

expose
<
type Payload = {
temperature: Cell<number>;
bump: (by: number) => void;
}
Payload
,
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any

Obtain the return type of a function type

ReturnType
<typeof
const withCell: (defaults: DefaultRevivableModules) => readonly [{
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ... 22 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}, ... 27 more ..., {
...;
}]
withCell
>>(
{},
{
transport: Transport
transport
:
const worker: Worker
worker
,
revivableModules?: ((defaults: DefaultRevivableModules) => readonly [{
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ... 22 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}, ... 27 more ..., {
...;
}]) | undefined

Configure the revivable module list. Receives the defaults and returns the final ordered list - add modules, drop defaults, reorder, or override per-type as needed.

revivableModules
:
const withCell: (defaults: DefaultRevivableModules) => readonly [{
readonly type: "cell";
readonly objectsOnly: true;
readonly isType: (value: unknown) => value is Cell<any>;
readonly box: (value: Cell<any>, context: RevivableContext) => {
type: "cell";
cellId: `${string}-${string}-${string}-${string}-${string}`;
value: number | Blob | Clonable | Transferable | BoxedTransfer<Capable> | BoxedIdentity<Capable> | ... 22 more ... | undefined;
__OSRA_BOX__: "revivable";
};
readonly revive: (boxed: {
cellId: string;
value: Capable;
}, context: RevivableContext) => Cell<...>;
}, ... 27 more ..., {
...;
}]
withCell
}
)
const temperature: {
readonly value: number;
set: (value: number) => Promise<void>;
subscribe: (listener: (value: number) => void) => Promise<() => Promise<void>>;
}
temperature
.
value: number
value
// 20
const temperature: {
readonly value: number;
set: (value: number) => Promise<void>;
subscribe: (listener: (value: number) => void) => Promise<() => Promise<void>>;
}
temperature
.
function subscribe(listener: (value: number) => void): Promise<() => Promise<void>>
subscribe
(
value: number
value
=>
var console: Console
console
.
Console.log(...data: any[]): void

The console.log() static method outputs a message to the console.

MDN Reference

log
(
value: number
value
))
await
const bump: (by: number) => Promise<void>
bump
(5) // logs 25, and temperature.value is now 25

Two optional module fields exist to support this pattern:

  • init(context) runs once per connection when it starts, before the first value is boxed, in the order of the module list. It is the place to set up per connection state and attach a single eventTarget listener, instead of one per revived value like above.
  • Messages declares, at the type level, the message variants your module sends, so that Message<YourModules> includes them. Every custom message must carry a literal type string and the remoteUuid of the connection.

If you want to go further, the built-in modules in src/revivables/ are the reference implementations, with message-port.ts being the canonical example of the full pattern.