osra 0.3.2 → 0.4.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.
@@ -1,20 +1,17 @@
1
1
  import type { Capable } from '../types';
2
2
  import type { RevivableContext, BoxBase as BoxBaseType } from './utils';
3
- import type { UnderlyingType } from '../utils/type';
4
3
  import type { BoxedMessagePort } from './message-port';
5
4
  export declare const type: 'abortSignal';
6
5
  type AbortMessage = {
7
6
  type: 'abort';
8
7
  reason?: Capable;
9
8
  };
10
- export type BoxedAbortSignal<T extends AbortSignal = AbortSignal> = BoxBaseType<typeof type> & {
9
+ export type BoxedAbortSignal = BoxBaseType<typeof type> & {
11
10
  aborted: boolean;
12
11
  reason?: Capable;
13
12
  port: BoxedMessagePort<AbortMessage>;
14
- } & {
15
- [UnderlyingType]: T;
16
13
  };
17
14
  export declare const isType: (value: unknown) => value is AbortSignal;
18
- export declare const box: <T extends AbortSignal, T2 extends RevivableContext>(value: T, context: T2) => BoxedAbortSignal<T>;
15
+ export declare const box: <T extends AbortSignal, T2 extends RevivableContext>(value: T, context: T2) => BoxedAbortSignal;
19
16
  export declare const revive: <T extends BoxedAbortSignal, T2 extends RevivableContext>(value: T, context: T2) => AbortSignal;
20
17
  export {};
@@ -1,12 +1,8 @@
1
- import type { RevivableContext, UnderlyingType, BoxedBuffer } from './utils';
2
- import { BoxBase } from './utils';
1
+ import type { RevivableContext } from './utils';
3
2
  export declare const type: 'arrayBuffer';
4
- type BoxedArrayBuffer<T extends ArrayBuffer, T2 extends RevivableContext> = typeof BoxBase & {
5
- type: typeof type;
6
- } & BoxedBuffer<T2> & {
7
- [UnderlyingType]: T;
8
- };
9
3
  export declare const isType: (value: unknown) => value is ArrayBuffer;
10
- export declare const box: <T extends ArrayBuffer, T2 extends RevivableContext>(value: T, context: T2) => BoxedArrayBuffer<T, T2>;
11
- export declare const revive: <T extends BoxedArrayBuffer<ArrayBuffer, RevivableContext>>(value: T, _context: RevivableContext) => T[UnderlyingType];
12
- export {};
4
+ export declare const box: <T extends ArrayBuffer, T2 extends RevivableContext>(value: T, context: T2) => {
5
+ __OSRA_BOX__: 'revivable';
6
+ type: "arrayBuffer";
7
+ } & import("./utils").BoxedBuffer<T2>;
8
+ export declare const revive: <T extends ReturnType<typeof box>>(value: T, _context: RevivableContext) => ArrayBuffer;
@@ -1,14 +1,9 @@
1
- import type { RevivableContext, UnderlyingType } from './utils';
2
- import { BoxBase } from './utils';
1
+ import type { RevivableContext } from './utils';
3
2
  export declare const type: 'bigint';
4
- type BoxedBigInt<T extends bigint> = typeof BoxBase & {
5
- type: typeof type;
6
- } & {
3
+ export declare const isType: (value: unknown) => value is bigint;
4
+ export declare const box: <T extends bigint, T2 extends RevivableContext>(value: T, _context: T2) => {
5
+ __OSRA_BOX__: 'revivable';
6
+ type: "bigint";
7
7
  value: string;
8
- } & {
9
- [UnderlyingType]: T;
10
8
  };
11
- export declare const isType: (value: unknown) => value is bigint;
12
- export declare const box: <T extends bigint, T2 extends RevivableContext>(value: T, _context: T2) => BoxedBigInt<T>;
13
- export declare const revive: <T extends BoxedBigInt<bigint>>(value: T, _context: RevivableContext) => T[UnderlyingType];
14
- export {};
9
+ export declare const revive: <T extends ReturnType<typeof box>>(value: T, _context: RevivableContext) => bigint;
@@ -0,0 +1,17 @@
1
+ import type { RevivableContext, BoxBase as BoxBaseType } from './utils';
2
+ import type { UnderlyingType } from '../utils/type';
3
+ import type { BoxedPromise } from './promise';
4
+ export declare const type: 'blob';
5
+ export type BoxedBlob<T extends Blob = Blob> = BoxBaseType<typeof type> & {
6
+ mimeType: string;
7
+ } & {
8
+ buffer: BoxedPromise<ArrayBuffer>;
9
+ } & {
10
+ fileName?: string;
11
+ lastModified?: number;
12
+ } & {
13
+ [UnderlyingType]: Promise<T>;
14
+ };
15
+ export declare const isType: (value: unknown) => value is Blob;
16
+ export declare const box: <T extends Blob, T2 extends RevivableContext>(value: T, context: T2) => BoxedBlob<T>;
17
+ export declare const revive: <T extends BoxedBlob, T2 extends RevivableContext>(value: T, context: T2) => T[UnderlyingType];
@@ -1,10 +1,12 @@
1
- import type { RevivableContext } from './utils';
1
+ import type { Capable } from '../types';
2
+ import type { RevivableContext, BoxBase as BoxBaseType } from './utils';
2
3
  export declare const type: 'error';
3
- export declare const isType: (value: unknown) => value is Error;
4
- export declare const box: <T extends Error, T2 extends RevivableContext>(value: T, _context: T2) => {
5
- __OSRA_BOX__: 'revivable';
6
- type: "error";
4
+ export type BoxedError = BoxBaseType<typeof type> & {
5
+ name: string;
7
6
  message: string;
8
7
  stack: string;
8
+ cause?: Capable;
9
9
  };
10
- export declare const revive: <T extends ReturnType<typeof box>, T2 extends RevivableContext>(value: T, _context: T2) => Error;
10
+ export declare const isType: (value: unknown) => value is Error;
11
+ export declare const box: <T extends Error, T2 extends RevivableContext>(value: T, context: T2) => BoxedError;
12
+ export declare const revive: <T extends BoxedError, T2 extends RevivableContext>(value: T, context: T2) => Error;
@@ -1,29 +1,18 @@
1
- import type { Capable } from '../types';
2
- import type { RevivableContext, BoxBase as BoxBaseType, UnderlyingType } from './utils';
3
- import type { BoxedMessagePort } from './message-port';
1
+ import { type RevivableContext } from './utils';
4
2
  export declare const type: 'eventTarget';
5
- type EventTargetMessage = {
6
- kind: 'subscribe';
7
- eventType: string;
8
- } | {
9
- kind: 'unsubscribe';
10
- eventType: string;
11
- } | {
12
- kind: 'close';
13
- } | {
14
- kind: 'event';
15
- eventType: string;
16
- bubbles: boolean;
17
- cancelable: boolean;
18
- composed: boolean;
19
- detail?: Capable;
20
- };
21
- export type BoxedEventTarget<T extends EventTarget = EventTarget> = BoxBaseType<typeof type> & {
22
- port: BoxedMessagePort<EventTargetMessage>;
23
- } & {
24
- [UnderlyingType]: T;
3
+ type ListenerOpts = boolean | {
4
+ capture?: boolean;
5
+ once?: boolean;
6
+ passive?: boolean;
7
+ signal?: AbortSignal;
25
8
  };
26
9
  export declare const isType: (value: unknown) => value is EventTarget;
27
- export declare const box: <T extends EventTarget, T2 extends RevivableContext>(value: T, context: T2) => BoxedEventTarget<T>;
28
- export declare const revive: <T extends BoxedEventTarget, T2 extends RevivableContext>(value: T, context: T2) => T[UnderlyingType];
10
+ export declare const box: <T extends EventTarget, T2 extends RevivableContext>(value: T, context: T2) => {
11
+ __OSRA_BOX__: 'revivable';
12
+ type: "eventTarget";
13
+ addListener: import("./function").BoxedFunction<(type: string, listener: EventListener, options?: ListenerOpts) => void>;
14
+ removeListener: import("./function").BoxedFunction<(type: string, listener: EventListener, options?: ListenerOpts) => void>;
15
+ };
16
+ export type BoxedEventTarget = ReturnType<typeof box>;
17
+ export declare const revive: <T extends ReturnType<typeof box>, T2 extends RevivableContext>(value: T, context: T2) => EventTarget;
29
18
  export {};
@@ -0,0 +1,15 @@
1
+ import type { Capable } from '../types';
2
+ import type { RevivableContext, BoxBase as BoxBaseType } from './utils';
3
+ export declare const type: 'event';
4
+ /** Boxes Event/CustomEvent only. Subclass-specific fields (MessageEvent.data,
5
+ * ErrorEvent.error, ProgressEvent.loaded, etc.) are dropped on the wire. */
6
+ export type BoxedEvent = BoxBaseType<typeof type> & {
7
+ eventType: string;
8
+ bubbles: boolean;
9
+ cancelable: boolean;
10
+ composed: boolean;
11
+ detail?: Capable;
12
+ };
13
+ export declare const isType: (value: unknown) => value is Event;
14
+ export declare const box: <T extends Event, T2 extends RevivableContext>(value: T, context: T2) => BoxedEvent;
15
+ export declare const revive: <T extends BoxedEvent, T2 extends RevivableContext>(value: T, context: T2) => Event;
@@ -0,0 +1,136 @@
1
+ import type { BoxBase as BoxBaseType, RevivableContext } from './utils';
2
+ declare const TYPED_CLONABLE_CTORS: readonly [{
3
+ new (fileBits: BlobPart[], fileName: string, options?: FilePropertyBag): File;
4
+ prototype: File;
5
+ }, {
6
+ new (): FileList;
7
+ prototype: FileList;
8
+ }, RegExpConstructor, DataViewConstructor, {
9
+ new (sw: number, sh: number, settings?: ImageDataSettings): ImageData;
10
+ new (data: ImageDataArray, sw: number, sh?: number, settings?: ImageDataSettings): ImageData;
11
+ prototype: ImageData;
12
+ }, {
13
+ new (form?: HTMLFormElement, submitter?: HTMLElement | null): FormData;
14
+ prototype: FormData;
15
+ }, {
16
+ new (message?: string, name?: string): DOMException;
17
+ prototype: DOMException;
18
+ readonly INDEX_SIZE_ERR: 1;
19
+ readonly DOMSTRING_SIZE_ERR: 2;
20
+ readonly HIERARCHY_REQUEST_ERR: 3;
21
+ readonly WRONG_DOCUMENT_ERR: 4;
22
+ readonly INVALID_CHARACTER_ERR: 5;
23
+ readonly NO_DATA_ALLOWED_ERR: 6;
24
+ readonly NO_MODIFICATION_ALLOWED_ERR: 7;
25
+ readonly NOT_FOUND_ERR: 8;
26
+ readonly NOT_SUPPORTED_ERR: 9;
27
+ readonly INUSE_ATTRIBUTE_ERR: 10;
28
+ readonly INVALID_STATE_ERR: 11;
29
+ readonly SYNTAX_ERR: 12;
30
+ readonly INVALID_MODIFICATION_ERR: 13;
31
+ readonly NAMESPACE_ERR: 14;
32
+ readonly INVALID_ACCESS_ERR: 15;
33
+ readonly VALIDATION_ERR: 16;
34
+ readonly TYPE_MISMATCH_ERR: 17;
35
+ readonly SECURITY_ERR: 18;
36
+ readonly NETWORK_ERR: 19;
37
+ readonly ABORT_ERR: 20;
38
+ readonly URL_MISMATCH_ERR: 21;
39
+ readonly QUOTA_EXCEEDED_ERR: 22;
40
+ readonly TIMEOUT_ERR: 23;
41
+ readonly INVALID_NODE_TYPE_ERR: 24;
42
+ readonly DATA_CLONE_ERR: 25;
43
+ }, {
44
+ new (init?: string | number[]): DOMMatrix;
45
+ prototype: DOMMatrix;
46
+ fromFloat32Array(array32: Float32Array<ArrayBuffer>): DOMMatrix;
47
+ fromFloat64Array(array64: Float64Array<ArrayBuffer>): DOMMatrix;
48
+ fromMatrix(other?: DOMMatrixInit): DOMMatrix;
49
+ }, {
50
+ new (init?: string | number[]): DOMMatrixReadOnly;
51
+ prototype: DOMMatrixReadOnly;
52
+ fromFloat32Array(array32: Float32Array<ArrayBuffer>): DOMMatrixReadOnly;
53
+ fromFloat64Array(array64: Float64Array<ArrayBuffer>): DOMMatrixReadOnly;
54
+ fromMatrix(other?: DOMMatrixInit): DOMMatrixReadOnly;
55
+ }, {
56
+ new (x?: number, y?: number, z?: number, w?: number): DOMPoint;
57
+ prototype: DOMPoint;
58
+ fromPoint(other?: DOMPointInit): DOMPoint;
59
+ }, {
60
+ new (x?: number, y?: number, z?: number, w?: number): DOMPointReadOnly;
61
+ prototype: DOMPointReadOnly;
62
+ fromPoint(other?: DOMPointInit): DOMPointReadOnly;
63
+ }, {
64
+ new (p1?: DOMPointInit, p2?: DOMPointInit, p3?: DOMPointInit, p4?: DOMPointInit): DOMQuad;
65
+ prototype: DOMQuad;
66
+ fromQuad(other?: DOMQuadInit): DOMQuad;
67
+ fromRect(other?: DOMRectInit): DOMQuad;
68
+ }, {
69
+ new (x?: number, y?: number, width?: number, height?: number): DOMRect;
70
+ prototype: DOMRect;
71
+ fromRect(other?: DOMRectInit): DOMRect;
72
+ }, {
73
+ new (x?: number, y?: number, width?: number, height?: number): DOMRectReadOnly;
74
+ prototype: DOMRectReadOnly;
75
+ fromRect(other?: DOMRectInit): DOMRectReadOnly;
76
+ }, {
77
+ new (): CryptoKey;
78
+ prototype: CryptoKey;
79
+ }, {
80
+ new (): FileSystemHandle;
81
+ prototype: FileSystemHandle;
82
+ }, {
83
+ new (): FileSystemFileHandle;
84
+ prototype: FileSystemFileHandle;
85
+ }, {
86
+ new (): FileSystemDirectoryHandle;
87
+ prototype: FileSystemDirectoryHandle;
88
+ }, {
89
+ new (): RTCCertificate;
90
+ prototype: RTCCertificate;
91
+ }];
92
+ export type Clonable = InstanceType<(typeof TYPED_CLONABLE_CTORS)[number]>;
93
+ export type BoxedClonable = BoxBaseType<'clonable'>;
94
+ export declare const clonable: {
95
+ readonly type: 'clonable';
96
+ readonly capableOnly: true;
97
+ readonly isType: (value: unknown) => value is Clonable;
98
+ readonly box: (value: Clonable, _context: RevivableContext<any>) => Clonable;
99
+ readonly revive: (value: BoxedClonable, _context: RevivableContext<any>) => Clonable;
100
+ };
101
+ declare const TYPED_TRANSFERABLE_CTORS: readonly [{
102
+ new (): ImageBitmap;
103
+ prototype: ImageBitmap;
104
+ }, {
105
+ new (width: number, height: number): OffscreenCanvas;
106
+ prototype: OffscreenCanvas;
107
+ }, {
108
+ new <W = any>(underlyingSink?: UnderlyingSink<W>, strategy?: QueuingStrategy<W>): WritableStream<W>;
109
+ prototype: WritableStream;
110
+ }, {
111
+ new <I = any, O = any>(transformer?: Transformer<I, O>, writableStrategy?: QueuingStrategy<I>, readableStrategy?: QueuingStrategy<O>): TransformStream<I, O>;
112
+ prototype: TransformStream;
113
+ }, {
114
+ new (): MediaStreamTrack;
115
+ prototype: MediaStreamTrack;
116
+ }, {
117
+ new (): RTCDataChannel;
118
+ prototype: RTCDataChannel;
119
+ }];
120
+ export type Transferable = InstanceType<(typeof TYPED_TRANSFERABLE_CTORS)[number]>;
121
+ export type BoxedTransferable = BoxBaseType<'transferable'>;
122
+ export declare const transferable: {
123
+ readonly type: 'transferable';
124
+ readonly capableOnly: true;
125
+ readonly isType: (value: unknown) => value is Transferable;
126
+ readonly box: (value: Transferable, _context: RevivableContext<any>) => Transferable;
127
+ readonly revive: (value: BoxedTransferable, _context: RevivableContext<any>) => Transferable;
128
+ };
129
+ export type BoxedUnclonable = BoxBaseType<'unclonable'>;
130
+ export declare const unclonable: {
131
+ readonly type: 'unclonable';
132
+ readonly isType: (value: unknown) => value is never;
133
+ readonly box: (_value: never, _context: RevivableContext<any>) => BoxedUnclonable;
134
+ readonly revive: (_value: BoxedUnclonable, _context: RevivableContext<any>) => Record<string, never>;
135
+ };
136
+ export {};
@@ -1,24 +1,11 @@
1
1
  import type { Capable } from '../types';
2
2
  import type { UnderlyingType, RevivableContext, BoxBase as BoxBaseType } from './utils';
3
- import type { AnyPort } from './message-port';
3
+ import { type EventPort } from '../utils/event-channel';
4
4
  import { BoxedMessagePort } from './message-port';
5
5
  export declare const type: 'function';
6
- type ResultMessage = {
7
- __osra_ok__: true;
8
- value: Capable;
9
- } | {
10
- __osra_err__: true;
11
- error: string;
12
- };
13
- type CallMessage = CallContext | {
14
- __osra_close__: true;
15
- };
16
- /** Call-site payload as received by the callee, after box-side revival: the
17
- * return port is now a live AnyPort<ResultMessage> (ProtocolPort on clone,
18
- * EventPort on JSON); args are revived. */
19
- export type CallContext = [AnyPort<ResultMessage>, Capable[]];
6
+ type CallContext = [EventPort<Capable>, Capable[]];
20
7
  export type BoxedFunction<T extends (...args: any[]) => any = (...args: any[]) => any> = BoxBaseType<typeof type> & {
21
- port: BoxedMessagePort<CallMessage>;
8
+ port: BoxedMessagePort<CallContext>;
22
9
  } & {
23
10
  [UnderlyingType]: (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>>;
24
11
  };
@@ -5,7 +5,6 @@ export declare const type: 'identity';
5
5
  export type Messages = {
6
6
  type: 'identity-dispose';
7
7
  remoteUuid: Uuid;
8
- /** id of the identity-wrapped value that was collected */
9
8
  id: string;
10
9
  };
11
10
  export declare const Messages: Messages;
@@ -19,21 +18,16 @@ export type BoxedIdentity<T extends Capable = Capable> = BoxBaseType<typeof type
19
18
  inner?: Capable;
20
19
  [UnderlyingType]: T;
21
20
  };
22
- /**
23
- * Wrap a value so that osra preserves its reference identity across the
24
- * RPC boundary, per connection. Calling identity(X) twice on the same
25
- * value produces the same wrapper, and both wrapper sends resolve to the
26
- * same revived reference on the remote side.
27
- *
28
- * - Primitives pass through unchanged (there is no identity to preserve).
29
- * - Already-wrapped values pass through unchanged (idempotent).
30
- *
31
- * NOTE: This lies at the type level — the runtime value for object/function
32
- * inputs is an IdentityWrapper<T>, typed as T so the user's surrounding
33
- * code stays unchanged. The box-site unwraps it.
34
- */
21
+ /** Wrap a value so osra preserves reference identity across the RPC
22
+ * boundary. Idempotent; primitives pass through unchanged. Lies at the
23
+ * type level runtime value is an IdentityWrapper<T> typed as T. */
35
24
  export declare const identity: <T>(value: T) => T;
36
25
  export declare const isType: (value: unknown) => value is IdentityWrapper;
37
26
  export declare const box: <T extends Capable, TContext extends RevivableContext>(wrapper: IdentityWrapper<T>, context: TContext) => BoxedIdentity<T>;
27
+ /** Identity-box a referenceable value with a caller-supplied inner box,
28
+ * bypassing the recursive-box step. Used by revivables (symbol with
29
+ * description=undefined) where recursing back through their own box
30
+ * would loop into this module again. */
31
+ export declare const boxByReference: <T extends WeakKey, TContext extends RevivableContext>(value: T, innerBox: Capable, context: TContext) => BoxedIdentity;
38
32
  export declare const revive: <T extends BoxedIdentity, TContext extends RevivableContext>(value: T, context: TContext) => T[UnderlyingType];
39
33
  export {};
@@ -18,7 +18,10 @@ import * as transfer from './transfer';
18
18
  import * as map from './map';
19
19
  import * as set from './set';
20
20
  import * as bigInt from './bigint';
21
+ import * as event from './event';
21
22
  import * as eventTarget from './event-target';
23
+ import * as blob from './blob';
24
+ import * as symbol from './symbol';
22
25
  export { identity } from './identity';
23
26
  export { transfer } from './transfer';
24
27
  export * from './utils';
@@ -30,7 +33,24 @@ export type RevivableModule<T extends string = string, T2 = any, T3 extends BoxB
30
33
  readonly init?: (context: RevivableContext<any>) => void;
31
34
  readonly Messages?: T4;
32
35
  };
33
- export declare const defaultRevivableModules: readonly [typeof transfer, typeof identity, typeof arrayBuffer, typeof date, typeof headers, typeof error, typeof typedArray, typeof promise, typeof func, typeof messagePort, typeof readableStream, typeof abortSignal, typeof response, typeof request, typeof map, typeof set, typeof bigInt, typeof eventTarget];
36
+ export declare const defaultRevivableModules: readonly [typeof transfer, typeof identity, typeof arrayBuffer, typeof date, typeof headers, typeof error, typeof typedArray, typeof blob, typeof promise, typeof func, typeof messagePort, typeof readableStream, typeof abortSignal, typeof response, typeof request, typeof map, typeof set, typeof bigInt, typeof symbol, typeof event, typeof eventTarget, {
37
+ readonly type: 'clonable';
38
+ readonly capableOnly: true;
39
+ readonly isType: (value: unknown) => value is import("./fallbacks").Clonable;
40
+ readonly box: (value: import("./fallbacks").Clonable, _context: RevivableContext<any>) => import("./fallbacks").Clonable;
41
+ readonly revive: (value: import("./fallbacks").BoxedClonable, _context: RevivableContext<any>) => import("./fallbacks").Clonable;
42
+ }, {
43
+ readonly type: 'transferable';
44
+ readonly capableOnly: true;
45
+ readonly isType: (value: unknown) => value is import("./fallbacks").Transferable;
46
+ readonly box: (value: import("./fallbacks").Transferable, _context: RevivableContext<any>) => import("./fallbacks").Transferable;
47
+ readonly revive: (value: import("./fallbacks").BoxedTransferable, _context: RevivableContext<any>) => import("./fallbacks").Transferable;
48
+ }, {
49
+ readonly type: 'unclonable';
50
+ readonly isType: (value: unknown) => value is never;
51
+ readonly box: (_value: never, _context: RevivableContext<any>) => import("./fallbacks").BoxedUnclonable;
52
+ readonly revive: (_value: import("./fallbacks").BoxedUnclonable, _context: RevivableContext<any>) => Record<string, never>;
53
+ }];
34
54
  export type DefaultRevivableModules = typeof defaultRevivableModules;
35
55
  export type DefaultRevivableModule = DefaultRevivableModules[number];
36
56
  export declare const box: <T extends Capable, TModules extends readonly RevivableModule[]>(value: T, context: RevivableContext<TModules>) => ReplaceWithBox<T, TModules[number]>;
@@ -9,46 +9,21 @@ export type Messages = {
9
9
  type: 'message';
10
10
  remoteUuid: Uuid;
11
11
  data: Capable;
12
- /** uuid of the messagePort that the message was sent through */
13
12
  portId: Uuid;
14
13
  } | {
15
14
  type: 'message-port-close';
16
15
  remoteUuid: Uuid;
17
- /** uuid of the messagePort that closed */
18
16
  portId: Uuid;
19
17
  };
20
18
  export declare const Messages: Messages;
21
- /** Any port-shape the message-port revivable is happy to accept. Real
22
- * MessagePorts (from `new MessageChannel()`) and synthetic EventPorts
23
- * (from `new EventChannel()`) both flow through here. Messages can be any
24
- * Capable value — message-port boxes/revives as they cross the transport,
25
- * and the in-realm side uses pass-by-reference via EventChannel. */
26
19
  export type AnyPort<T = Capable> = TypedMessagePort<T> | EventPort<T>;
27
- export type BoxedMessagePort<T = Capable> = BoxBaseType<typeof type> & (
28
- /** The origin was a synthetic EventPort — revive must reproduce an
29
- * EventPort on the other side so live (non-clonable) Promises/Functions
30
- * etc. can flow through by reference. */
31
- {
20
+ export type BoxedMessagePort<T = Capable> = BoxBaseType<typeof type> & ({
32
21
  portId: Uuid;
33
22
  synthetic: true;
34
- }
35
- /** The origin was a real MessagePort but the transport can't carry
36
- * ports (JSON-only) — revive produces a real MessagePort proxy so the
37
- * receiver sees it as if it had been transferred. Payloads must be
38
- * structured-clonable; live (non-clonable) values nested in a
39
- * user-level MessagePort aren't supported in this mode. */
40
- | {
23
+ } | {
41
24
  portId: Uuid;
42
25
  synthetic: false;
43
- }
44
- /** The origin was a real MessagePort and the transport supports
45
- * structured clone — the port is transferred on the wire. When
46
- * `autoBox` is true, the revive side wraps it in a `ProtocolPort`
47
- * that auto-boxes outgoing / auto-revives incoming payloads so
48
- * live values (Promises/Functions) flow through unchanged. When
49
- * `autoBox` is absent/false, the receiver gets the raw MessagePort
50
- * with structured-clone semantics (used for user-owned ports). */
51
- | {
26
+ } | {
52
27
  port: AnyPort<T>;
53
28
  autoBox?: boolean;
54
29
  }) & {
@@ -66,16 +41,9 @@ export declare const box: <T, T2 extends RevivableContext = RevivableContext>(va
66
41
  autoBox?: boolean;
67
42
  }) => BoxedMessagePort<T>;
68
43
  export declare const revive: <T extends Capable, T2 extends RevivableContext>(value: BoxedMessagePort<T>, context: T2) => TypedMessagePort<T>;
69
- /**
70
- * Factory for revivable-internal channels. Returns a local port used by the
71
- * revivable and a pre-boxed remote port ready to embed in the revivable's
72
- * Boxed* structure. The local port auto-boxes/revives on clone transports
73
- * (via ProtocolPort over a MessageChannel) and passes by reference on JSON
74
- * transports (via EventChannel → portId routing).
75
- *
76
- * Revivables can post live values (Promises/Functions/…) on `localPort`
77
- * without caring about the transport mode.
78
- */
44
+ /** Factory for revivable-internal channels. Returns a local port that
45
+ * auto-boxes live values regardless of transport, plus a pre-boxed remote
46
+ * port the revivable embeds in its Boxed* structure. */
79
47
  export declare const createRevivableChannel: <T extends Capable>(context: RevivableContext) => {
80
48
  localPort: AnyPort<T>;
81
49
  boxedRemote: BoxedMessagePort<T>;
@@ -0,0 +1,11 @@
1
+ import type { RevivableContext } from './utils';
2
+ export declare const type: 'symbol';
3
+ export declare const isType: (value: unknown) => value is symbol;
4
+ export declare const box: <T extends symbol, T2 extends RevivableContext>(value: T, context: T2) => {
5
+ __OSRA_BOX__: 'revivable';
6
+ type: "symbol";
7
+ description: string | undefined;
8
+ } | import("./identity").BoxedIdentity<import("..").Capable>;
9
+ export declare const revive: <T extends {
10
+ description: string | undefined;
11
+ }, T2 extends RevivableContext>(value: T, _context: T2) => symbol;
@@ -11,25 +11,10 @@ export type BoxedTransfer<T extends Capable = Capable> = BoxBaseType<typeof type
11
11
  degraded: boolean;
12
12
  [UnderlyingType]: T;
13
13
  };
14
- /**
15
- * Opt into transfer semantics for a transferable value. Without this wrapper
16
- * osra sends transferables as structured clones (copies) the sender-side
17
- * reference stays usable after the RPC. Wrapping hands the underlying storage
18
- * off to the receiver and neuters the sender-side reference, matching what
19
- * you'd get by listing it in the transfer list of `postMessage(msg, [buf])`.
20
- *
21
- * - Primitives, null, undefined, plain objects, Promises, Dates, etc. are
22
- * returned unchanged.
23
- * - Typed array views (`Uint8Array`, `DataView`, …) are accepted as a
24
- * convenience — their underlying `.buffer` is what actually gets moved.
25
- * - `transfer(transfer(x))` returns the same wrapper as `transfer(x)`.
26
- * - If the current platform cannot transfer the given type, the wrapper
27
- * silently degrades to a copy — nothing throws.
28
- *
29
- * NOTE: This lies at the type level — the runtime value for transferable
30
- * inputs is a TransferWrapper<T>, typed as T so the user's surrounding
31
- * code stays unchanged. The box-site unwraps it.
32
- */
14
+ /** Opt into transfer (move) semantics for a transferable value. Idempotent;
15
+ * non-transferable inputs pass through unchanged. Silently degrades to a
16
+ * copy when the platform/transport can't transfer the given type. Lies at
17
+ * the type level runtime value is a TransferWrapper<T> typed as T. */
33
18
  export declare const transfer: <T>(value: T) => T;
34
19
  export declare const isType: (value: unknown) => value is TransferWrapper;
35
20
  export declare const box: <T extends Capable, TContext extends RevivableContext>(wrapper: TransferWrapper<T>, context: TContext) => BoxedTransfer<T>;
@@ -20,7 +20,14 @@ export type RevivableContext<TModules extends readonly RevivableModule[] = Defau
20
20
  revivableModules: TModules;
21
21
  eventTarget: MessageEventTarget<TModules>;
22
22
  };
23
- export type ExtractType<T> = T extends {
23
+ /** Extract the type a module's `isType` narrows to. Modules marked
24
+ * `capableOnly: true` (clonable, transferable) contribute `never` on JSON
25
+ * transports so users can't type values JSON would silently drop. */
26
+ export type ExtractType<T, Ctx extends RevivableContext = RevivableContext> = T extends {
27
+ capableOnly: true;
28
+ } ? IsJsonOnlyTransport<Ctx['transport']> extends true ? never : T extends {
29
+ isType: (value: unknown) => value is infer S;
30
+ } ? S : never : T extends {
24
31
  isType: (value: unknown) => value is infer S;
25
32
  } ? S : never;
26
33
  export type ExtractBox<T> = T extends {
@@ -32,16 +39,11 @@ export type ExtractMessages<T> = T extends {
32
39
  type: string;
33
40
  } ? string extends B['type'] ? never : B : never : never;
34
41
  export type InferMessages<TModules extends readonly unknown[]> = ExtractMessages<TModules[number]>;
35
- export type InferRevivables<TModules extends readonly unknown[]> = ExtractType<TModules[number]>;
42
+ export type InferRevivables<TModules extends readonly unknown[], Ctx extends RevivableContext = RevivableContext> = ExtractType<TModules[number], Ctx>;
36
43
  export type InferRevivableBox<TModules extends readonly unknown[]> = ExtractBox<TModules[number]>;
37
44
  export declare const isRevivableBox: (value: unknown) => value is BoxBase;
38
- /** Stable string form for an unknown rejection value. Errors keep their stack;
39
- * everything else gets coerced via `String()`. Used wherever a Promise/Function
40
- * rejection has to cross the wire as a serialisable string. */
41
45
  export declare const serializeError: (error: unknown) => string;
42
- /** Wire shape for an ArrayBuffer carried by a JSON or clone transport. JSON
43
- * paths emit a base64 string (so the buffer survives JSON.stringify); clone
44
- * paths pass the buffer through structured-clone unchanged. */
46
+ /** Wire shape for an ArrayBuffer: base64 on JSON, raw on clone. */
45
47
  export type BoxedBuffer<TCtx extends RevivableContext = RevivableContext> = IsJsonOnlyTransport<TCtx['transport']> extends true ? {
46
48
  base64Buffer: string;
47
49
  } : IsJsonOnlyTransport<TCtx['transport']> extends false ? {
package/build/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { ConnectionMessage } from './connections';
2
2
  import type { TypedEventTarget } from './utils';
3
- import type { DefaultRevivableModules, RevivableModule, InferMessages, InferRevivables } from './revivables';
3
+ import type { IsJsonOnlyTransport } from './utils/type-guards';
4
+ import type { DefaultRevivableModules, RevivableModule, InferMessages, InferRevivables, RevivableContext } from './revivables';
4
5
  export declare const OSRA_KEY: '__OSRA_KEY__';
5
6
  export declare const OSRA_DEFAULT_KEY: '__OSRA_DEFAULT_KEY__';
6
7
  export declare const OSRA_BOX: '__OSRA_BOX__';
@@ -16,9 +17,14 @@ export type Structurable = Jsonable
16
17
  export type StructurableTransferable = Structurable | Transferable | {
17
18
  [key: string]: StructurableTransferable;
18
19
  } | Array<StructurableTransferable> | Map<StructurableTransferable, StructurableTransferable> | Set<StructurableTransferable>;
19
- export type Capable<TModules extends readonly RevivableModule[] = DefaultRevivableModules> = StructurableTransferable | InferRevivables<TModules> | {
20
- [key: string]: Capable<TModules>;
21
- } | Array<Capable<TModules>> | Map<Capable<TModules>, Capable<TModules>> | Set<Capable<TModules>>;
20
+ /** "Free" types in `Capable` narrows to `Jsonable` on JSON transports so
21
+ * user code can't type a `Date`/`Blob`/etc. that JSON would silently coerce.
22
+ * Modules that DO support JSON (date, map, set, bigint, …) put their type
23
+ * back via `InferRevivables`. */
24
+ type CapableBase<Ctx extends RevivableContext> = IsJsonOnlyTransport<Ctx['transport']> extends true ? Jsonable | undefined | void : StructurableTransferable;
25
+ export type Capable<TModules extends readonly RevivableModule[] = DefaultRevivableModules, Ctx extends RevivableContext = RevivableContext> = CapableBase<Ctx> | InferRevivables<TModules, Ctx> | {
26
+ [key: string]: Capable<TModules, Ctx>;
27
+ } | Array<Capable<TModules, Ctx>> | Map<Capable<TModules, Ctx>, Capable<TModules, Ctx>> | Set<Capable<TModules, Ctx>>;
22
28
  export type MessageFields = {
23
29
  type: string;
24
30
  remoteUuid: Uuid;
@@ -31,7 +37,6 @@ export type MessageBase = {
31
37
  };
32
38
  export type ProtocolMessage = {
33
39
  type: 'announce';
34
- /** Only set when acknowledging a remote announcement */
35
40
  remoteUuid?: Uuid;
36
41
  } | {
37
42
  type: 'close';
@@ -43,3 +48,4 @@ export type MessageEventMap<TModules extends readonly RevivableModule[] = Defaul
43
48
  message: CustomEvent<Message<TModules>>;
44
49
  };
45
50
  export type MessageEventTarget<TModules extends readonly RevivableModule[] = DefaultRevivableModules> = TypedEventTarget<MessageEventMap<TModules>>;
51
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Run `cleanup` after `target` is garbage-collected. Returns a handle to
3
+ * cancel the tracking before that happens.
4
+ *
5
+ * Backed by a single shared FinalizationRegistry — every revivable that
6
+ * needs FR semantics goes through this so the boilerplate (token,
7
+ * unregister, cycle-safety contract) lives in one place.
8
+ *
9
+ * Contract: `cleanup` MUST NOT (transitively) reference `target`. The
10
+ * registry strong-holds the cleanup callback, the cleanup would then
11
+ * strong-hold target, and the engine would never see target as
12
+ * collectable. Use a `WeakRef` if cleanup needs something that points
13
+ * back at target.
14
+ *
15
+ * Errors thrown from cleanup are swallowed: the callback fires from the
16
+ * FR thread, where there's no caller to surface them to.
17
+ */
18
+ export type GcUnregister = () => void;
19
+ export declare const trackGc: (target: WeakKey, cleanup: () => void) => GcUnregister;
@@ -9,3 +9,4 @@ export * from './typed-message-channel';
9
9
  export * from './event-channel';
10
10
  export * from './type';
11
11
  export * from './capable-check';
12
+ export * from './gc-tracker';