osra 0.2.13 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +319 -304
- package/build/connections/bidirectional.d.ts +53 -0
- package/build/connections/index.d.ts +20 -0
- package/build/connections/utils.d.ts +31 -0
- package/build/index.d.ts +16 -630
- package/build/index.js +918 -634
- package/build/index.js.map +1 -1
- package/build/revivables/abort-signal.d.ts +20 -0
- package/build/revivables/array-buffer.d.ts +12 -0
- package/build/revivables/bigint.d.ts +14 -0
- package/build/revivables/date.d.ts +9 -0
- package/build/revivables/error.d.ts +10 -0
- package/build/revivables/event-target.d.ts +29 -0
- package/build/revivables/function.d.ts +29 -0
- package/build/revivables/headers.d.ts +9 -0
- package/build/revivables/identity.d.ts +39 -0
- package/build/revivables/index.d.ts +39 -0
- package/build/revivables/map.d.ts +11 -0
- package/build/revivables/message-port.d.ts +83 -0
- package/build/revivables/promise.d.ts +34 -0
- package/build/revivables/readable-stream.d.ts +18 -0
- package/build/revivables/request.d.ts +23 -0
- package/build/revivables/response.d.ts +18 -0
- package/build/revivables/set.d.ts +11 -0
- package/build/revivables/transfer.d.ts +37 -0
- package/build/revivables/typed-array.d.ts +15 -0
- package/build/revivables/utils.d.ts +59 -0
- package/build/types.d.ts +45 -0
- package/build/utils/capable-check.d.ts +44 -0
- package/build/utils/event-channel.d.ts +27 -0
- package/build/utils/index.d.ts +11 -0
- package/build/utils/replace.d.ts +25 -0
- package/build/utils/transferable.d.ts +20 -0
- package/build/utils/transport.d.ts +56 -0
- package/build/utils/type-guards.d.ts +58 -0
- package/build/utils/type.d.ts +2 -0
- package/build/utils/typed-event-target.d.ts +16 -0
- package/build/utils/typed-message-channel.d.ts +19 -0
- package/package.json +60 -67
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Capable } from '../types';
|
|
2
|
+
import type { RevivableContext, BoxBase as BoxBaseType } from './utils';
|
|
3
|
+
import type { UnderlyingType } from '../utils/type';
|
|
4
|
+
import type { BoxedMessagePort } from './message-port';
|
|
5
|
+
export declare const type: 'abortSignal';
|
|
6
|
+
type AbortMessage = {
|
|
7
|
+
type: 'abort';
|
|
8
|
+
reason?: Capable;
|
|
9
|
+
};
|
|
10
|
+
export type BoxedAbortSignal<T extends AbortSignal = AbortSignal> = BoxBaseType<typeof type> & {
|
|
11
|
+
aborted: boolean;
|
|
12
|
+
reason?: Capable;
|
|
13
|
+
port: BoxedMessagePort<AbortMessage>;
|
|
14
|
+
} & {
|
|
15
|
+
[UnderlyingType]: T;
|
|
16
|
+
};
|
|
17
|
+
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>;
|
|
19
|
+
export declare const revive: <T extends BoxedAbortSignal, T2 extends RevivableContext>(value: T, context: T2) => AbortSignal;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RevivableContext, UnderlyingType, BoxedBuffer } from './utils';
|
|
2
|
+
import { BoxBase } from './utils';
|
|
3
|
+
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
|
+
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 {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RevivableContext, UnderlyingType } from './utils';
|
|
2
|
+
import { BoxBase } from './utils';
|
|
3
|
+
export declare const type: 'bigint';
|
|
4
|
+
type BoxedBigInt<T extends bigint> = typeof BoxBase & {
|
|
5
|
+
type: typeof type;
|
|
6
|
+
} & {
|
|
7
|
+
value: string;
|
|
8
|
+
} & {
|
|
9
|
+
[UnderlyingType]: T;
|
|
10
|
+
};
|
|
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 {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RevivableContext } from './utils';
|
|
2
|
+
export declare const type: 'date';
|
|
3
|
+
export declare const isType: (value: unknown) => value is Date;
|
|
4
|
+
export declare const box: <T extends Date, T2 extends RevivableContext>(value: T, _context: T2) => {
|
|
5
|
+
__OSRA_BOX__: 'revivable';
|
|
6
|
+
type: "date";
|
|
7
|
+
ISOString: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const revive: <T extends ReturnType<typeof box>, T2 extends RevivableContext>(value: T, _context: T2) => Date;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RevivableContext } from './utils';
|
|
2
|
+
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";
|
|
7
|
+
message: string;
|
|
8
|
+
stack: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const revive: <T extends ReturnType<typeof box>, T2 extends RevivableContext>(value: T, _context: T2) => Error;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Capable } from '../types';
|
|
2
|
+
import type { RevivableContext, BoxBase as BoxBaseType, UnderlyingType } from './utils';
|
|
3
|
+
import type { BoxedMessagePort } from './message-port';
|
|
4
|
+
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;
|
|
25
|
+
};
|
|
26
|
+
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];
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Capable } from '../types';
|
|
2
|
+
import type { UnderlyingType, RevivableContext, BoxBase as BoxBaseType } from './utils';
|
|
3
|
+
import type { AnyPort } from './message-port';
|
|
4
|
+
import { BoxedMessagePort } from './message-port';
|
|
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[]];
|
|
20
|
+
export type BoxedFunction<T extends (...args: any[]) => any = (...args: any[]) => any> = BoxBaseType<typeof type> & {
|
|
21
|
+
port: BoxedMessagePort<CallMessage>;
|
|
22
|
+
} & {
|
|
23
|
+
[UnderlyingType]: (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>>;
|
|
24
|
+
};
|
|
25
|
+
type CapableFunction<T> = T extends (...args: infer P) => infer R ? P extends Capable[] ? R extends Capable ? T : never : never : never;
|
|
26
|
+
export declare const isType: (value: unknown) => value is (...args: any[]) => any;
|
|
27
|
+
export declare const box: <T extends (...args: any[]) => any, T2 extends RevivableContext>(value: T & CapableFunction<T>, context: T2) => BoxedFunction<T>;
|
|
28
|
+
export declare const revive: <T extends BoxedFunction, T2 extends RevivableContext>(value: T, context: T2) => T[UnderlyingType];
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RevivableContext } from './utils';
|
|
2
|
+
export declare const type: 'headers';
|
|
3
|
+
export declare const isType: (value: unknown) => value is Headers;
|
|
4
|
+
export declare const box: <T extends Headers, T2 extends RevivableContext>(value: T, _context: T2) => {
|
|
5
|
+
__OSRA_BOX__: 'revivable';
|
|
6
|
+
type: "headers";
|
|
7
|
+
entries: [string, string][];
|
|
8
|
+
};
|
|
9
|
+
export declare const revive: <T extends ReturnType<typeof box>, T2 extends RevivableContext>(value: T, _context: T2) => Headers;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Capable, Uuid } from '../types';
|
|
2
|
+
import type { RevivableContext, BoxBase as BoxBaseType } from './utils';
|
|
3
|
+
import type { UnderlyingType } from '../utils/type';
|
|
4
|
+
export declare const type: 'identity';
|
|
5
|
+
export type Messages = {
|
|
6
|
+
type: 'identity-dispose';
|
|
7
|
+
remoteUuid: Uuid;
|
|
8
|
+
/** id of the identity-wrapped value that was collected */
|
|
9
|
+
id: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const Messages: Messages;
|
|
12
|
+
declare const IDENTITY_MARKER: unique symbol;
|
|
13
|
+
type IdentityWrapper<T = unknown> = {
|
|
14
|
+
readonly [IDENTITY_MARKER]: true;
|
|
15
|
+
readonly value: T;
|
|
16
|
+
};
|
|
17
|
+
export type BoxedIdentity<T extends Capable = Capable> = BoxBaseType<typeof type> & {
|
|
18
|
+
id: string;
|
|
19
|
+
inner?: Capable;
|
|
20
|
+
[UnderlyingType]: T;
|
|
21
|
+
};
|
|
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
|
+
*/
|
|
35
|
+
export declare const identity: <T>(value: T) => T;
|
|
36
|
+
export declare const isType: (value: unknown) => value is IdentityWrapper;
|
|
37
|
+
export declare const box: <T extends Capable, TContext extends RevivableContext>(wrapper: IdentityWrapper<T>, context: TContext) => BoxedIdentity<T>;
|
|
38
|
+
export declare const revive: <T extends BoxedIdentity, TContext extends RevivableContext>(value: T, context: TContext) => T[UnderlyingType];
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { BoxBase, RevivableContext } from './utils';
|
|
2
|
+
import type { DeepReplaceWithBox, DeepReplaceWithRevive, ReplaceWithBox, ReplaceWithRevive } from '../utils/replace';
|
|
3
|
+
import type { MessageFields, Capable } from '../types';
|
|
4
|
+
import * as arrayBuffer from './array-buffer';
|
|
5
|
+
import * as date from './date';
|
|
6
|
+
import * as headers from './headers';
|
|
7
|
+
import * as error from './error';
|
|
8
|
+
import * as typedArray from './typed-array';
|
|
9
|
+
import * as promise from './promise';
|
|
10
|
+
import * as func from './function';
|
|
11
|
+
import * as messagePort from './message-port';
|
|
12
|
+
import * as readableStream from './readable-stream';
|
|
13
|
+
import * as abortSignal from './abort-signal';
|
|
14
|
+
import * as response from './response';
|
|
15
|
+
import * as request from './request';
|
|
16
|
+
import * as identity from './identity';
|
|
17
|
+
import * as transfer from './transfer';
|
|
18
|
+
import * as map from './map';
|
|
19
|
+
import * as set from './set';
|
|
20
|
+
import * as bigInt from './bigint';
|
|
21
|
+
import * as eventTarget from './event-target';
|
|
22
|
+
export { identity } from './identity';
|
|
23
|
+
export { transfer } from './transfer';
|
|
24
|
+
export * from './utils';
|
|
25
|
+
export type RevivableModule<T extends string = string, T2 = any, T3 extends BoxBase<T> = any, T4 extends MessageFields = MessageFields> = {
|
|
26
|
+
readonly type: T;
|
|
27
|
+
readonly isType: (value: unknown) => value is T2;
|
|
28
|
+
readonly box: ((value: T2, context: RevivableContext<any>) => T3) | ((...args: any[]) => any);
|
|
29
|
+
readonly revive: (value: T3, context: RevivableContext<any>) => T2;
|
|
30
|
+
readonly init?: (context: RevivableContext<any>) => void;
|
|
31
|
+
readonly Messages?: T4;
|
|
32
|
+
};
|
|
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];
|
|
34
|
+
export type DefaultRevivableModules = typeof defaultRevivableModules;
|
|
35
|
+
export type DefaultRevivableModule = DefaultRevivableModules[number];
|
|
36
|
+
export declare const box: <T extends Capable, TModules extends readonly RevivableModule[]>(value: T, context: RevivableContext<TModules>) => ReplaceWithBox<T, TModules[number]>;
|
|
37
|
+
export declare const recursiveBox: <T extends Capable, TModules extends readonly RevivableModule[]>(value: T, context: RevivableContext<TModules>) => DeepReplaceWithBox<T, TModules[number]>;
|
|
38
|
+
export declare const revive: <T extends ReturnType<typeof box>, TModules extends readonly RevivableModule[]>(value: T, context: RevivableContext<TModules>) => ReplaceWithRevive<T, TModules[number]>;
|
|
39
|
+
export declare const recursiveRevive: <T extends Capable, TModules extends readonly RevivableModule[]>(value: T, context: RevivableContext<TModules>) => DeepReplaceWithRevive<T, TModules[number]>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Capable } from '../types';
|
|
2
|
+
import type { RevivableContext, UnderlyingType, BoxBase as BoxBaseType } from './utils';
|
|
3
|
+
export declare const type: 'map';
|
|
4
|
+
export type BoxedMap<T extends Map<Capable, Capable> = Map<Capable, Capable>> = BoxBaseType<typeof type> & {
|
|
5
|
+
entries: Array<[Capable, Capable]>;
|
|
6
|
+
} & {
|
|
7
|
+
[UnderlyingType]: T;
|
|
8
|
+
};
|
|
9
|
+
export declare const isType: (value: unknown) => value is Map<unknown, unknown>;
|
|
10
|
+
export declare const box: <T extends Map<Capable, Capable>, T2 extends RevivableContext>(value: T, context: T2) => BoxedMap<T>;
|
|
11
|
+
export declare const revive: <T extends BoxedMap, T2 extends RevivableContext>(value: T, context: T2) => T[UnderlyingType];
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Capable, StructurableTransferable, Uuid } from '../types';
|
|
2
|
+
import type { TypedMessagePort } from '../utils/typed-message-channel';
|
|
3
|
+
import type { RevivableContext, BoxBase as BoxBaseType } from './utils';
|
|
4
|
+
import type { UnderlyingType } from '../utils/type';
|
|
5
|
+
import type { BadFieldValue, BadFieldPath, BadFieldParent, ErrorMessage, BadValue, Path, ParentObject } from '../utils/capable-check';
|
|
6
|
+
import { EventPort } from '../utils/event-channel';
|
|
7
|
+
export declare const type: 'messagePort';
|
|
8
|
+
export type Messages = {
|
|
9
|
+
type: 'message';
|
|
10
|
+
remoteUuid: Uuid;
|
|
11
|
+
data: Capable;
|
|
12
|
+
/** uuid of the messagePort that the message was sent through */
|
|
13
|
+
portId: Uuid;
|
|
14
|
+
} | {
|
|
15
|
+
type: 'message-port-close';
|
|
16
|
+
remoteUuid: Uuid;
|
|
17
|
+
/** uuid of the messagePort that closed */
|
|
18
|
+
portId: Uuid;
|
|
19
|
+
};
|
|
20
|
+
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
|
+
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
|
+
{
|
|
32
|
+
portId: Uuid;
|
|
33
|
+
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
|
+
| {
|
|
41
|
+
portId: Uuid;
|
|
42
|
+
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
|
+
| {
|
|
52
|
+
port: AnyPort<T>;
|
|
53
|
+
autoBox?: boolean;
|
|
54
|
+
}) & {
|
|
55
|
+
[UnderlyingType]: TypedMessagePort<T>;
|
|
56
|
+
};
|
|
57
|
+
type StructurableTransferablePort<T> = [T] extends [Capable] ? AnyPort<T> : AnyPort<T> & {
|
|
58
|
+
[ErrorMessage]: 'Message type must extend Capable';
|
|
59
|
+
[BadValue]: BadFieldValue<T, Capable>;
|
|
60
|
+
[Path]: BadFieldPath<T, Capable>;
|
|
61
|
+
[ParentObject]: BadFieldParent<T, Capable>;
|
|
62
|
+
};
|
|
63
|
+
export declare const init: (context: RevivableContext) => void;
|
|
64
|
+
export declare const isType: (value: unknown) => value is MessagePort | EventPort<StructurableTransferable>;
|
|
65
|
+
export declare const box: <T, T2 extends RevivableContext = RevivableContext>(value: StructurableTransferablePort<T>, context: T2, options?: {
|
|
66
|
+
autoBox?: boolean;
|
|
67
|
+
}) => BoxedMessagePort<T>;
|
|
68
|
+
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
|
+
*/
|
|
79
|
+
export declare const createRevivableChannel: <T extends Capable>(context: RevivableContext) => {
|
|
80
|
+
localPort: AnyPort<T>;
|
|
81
|
+
boxedRemote: BoxedMessagePort<T>;
|
|
82
|
+
};
|
|
83
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Capable } from '../types';
|
|
2
|
+
import type { RevivableContext, BoxBase as BoxBaseType } from './utils';
|
|
3
|
+
import type { UnderlyingType } from '.';
|
|
4
|
+
import type { BadFieldValue, BadFieldPath, BadFieldParent, ErrorMessage, BadValue, Path, ParentObject } from '../utils/capable-check';
|
|
5
|
+
import { BoxedMessagePort } from './message-port';
|
|
6
|
+
export declare const type: 'promise';
|
|
7
|
+
export type Context = {
|
|
8
|
+
type: 'resolve';
|
|
9
|
+
data: Capable;
|
|
10
|
+
} | {
|
|
11
|
+
type: 'reject';
|
|
12
|
+
error: string;
|
|
13
|
+
};
|
|
14
|
+
type CapablePromise<T> = T extends Promise<infer U> ? U extends Capable ? T : T & {
|
|
15
|
+
[ErrorMessage]: 'Value type must extend a Promise that resolves to a Capable';
|
|
16
|
+
[BadValue]: BadFieldValue<U, Capable>;
|
|
17
|
+
[Path]: BadFieldPath<U, Capable>;
|
|
18
|
+
[ParentObject]: BadFieldParent<U, Capable>;
|
|
19
|
+
} : T & {
|
|
20
|
+
[ErrorMessage]: 'Value type must extend a Promise that resolves to a Capable';
|
|
21
|
+
[BadValue]: T;
|
|
22
|
+
[Path]: '';
|
|
23
|
+
[ParentObject]: T;
|
|
24
|
+
};
|
|
25
|
+
type ExtractCapable<T> = T extends Promise<infer U> ? U extends Capable ? U : never : never;
|
|
26
|
+
export type BoxedPromise<T extends Capable = Capable> = BoxBaseType<typeof type> & {
|
|
27
|
+
port: BoxedMessagePort<Context>;
|
|
28
|
+
} & {
|
|
29
|
+
[UnderlyingType]: T;
|
|
30
|
+
};
|
|
31
|
+
export declare const isType: (value: unknown) => value is Promise<any>;
|
|
32
|
+
export declare const box: <T, T2 extends RevivableContext>(value: CapablePromise<T>, context: T2) => BoxedPromise<ExtractCapable<T>>;
|
|
33
|
+
export declare const revive: <T extends BoxedPromise, T2 extends RevivableContext>(value: T, context: T2) => Promise<T[typeof UnderlyingType]>;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RevivableContext, BoxBase as BoxBaseType } from './utils';
|
|
2
|
+
import type { UnderlyingType } from '.';
|
|
3
|
+
import { BoxedMessagePort } from './message-port';
|
|
4
|
+
export declare const type: 'readableStream';
|
|
5
|
+
export type PullContext = {
|
|
6
|
+
type: 'pull' | 'cancel';
|
|
7
|
+
};
|
|
8
|
+
type ChunkMessage<T = unknown> = Promise<ReadableStreamReadResult<T>>;
|
|
9
|
+
type Msg = PullContext | ChunkMessage;
|
|
10
|
+
export type BoxedReadableStream<T extends ReadableStream = ReadableStream> = BoxBaseType<typeof type> & {
|
|
11
|
+
port: BoxedMessagePort<Msg>;
|
|
12
|
+
} & {
|
|
13
|
+
[UnderlyingType]: T;
|
|
14
|
+
};
|
|
15
|
+
export declare const isType: (value: unknown) => value is ReadableStream;
|
|
16
|
+
export declare const box: <T extends ReadableStream, T2 extends RevivableContext>(value: T, context: T2) => BoxedReadableStream<T>;
|
|
17
|
+
export declare const revive: <T extends BoxedReadableStream, T2 extends RevivableContext>(value: T, context: T2) => T[UnderlyingType];
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { RevivableContext } from './utils';
|
|
2
|
+
export declare const type: 'request';
|
|
3
|
+
export declare const isType: (value: unknown) => value is Request;
|
|
4
|
+
export declare const box: <T extends Request, T2 extends RevivableContext>(value: T, context: T2) => {
|
|
5
|
+
__OSRA_BOX__: 'revivable';
|
|
6
|
+
type: "request";
|
|
7
|
+
method: string;
|
|
8
|
+
url: string;
|
|
9
|
+
headers: {
|
|
10
|
+
__OSRA_BOX__: 'revivable';
|
|
11
|
+
type: "headers";
|
|
12
|
+
entries: [string, string][];
|
|
13
|
+
};
|
|
14
|
+
body: import("./readable-stream").BoxedReadableStream<ReadableStream<Uint8Array<ArrayBuffer>>> | null;
|
|
15
|
+
credentials: RequestCredentials;
|
|
16
|
+
cache: RequestCache;
|
|
17
|
+
redirect: RequestRedirect;
|
|
18
|
+
referrer: string;
|
|
19
|
+
referrerPolicy: ReferrerPolicy;
|
|
20
|
+
integrity: string;
|
|
21
|
+
keepalive: boolean;
|
|
22
|
+
};
|
|
23
|
+
export declare const revive: <T extends ReturnType<typeof box>, T2 extends RevivableContext>(value: T, context: T2) => Request;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RevivableContext } from './utils';
|
|
2
|
+
export declare const type: 'response';
|
|
3
|
+
export declare const isType: (value: unknown) => value is Response;
|
|
4
|
+
export declare const box: <T extends Response, T2 extends RevivableContext>(value: T, context: T2) => {
|
|
5
|
+
__OSRA_BOX__: 'revivable';
|
|
6
|
+
type: "response";
|
|
7
|
+
status: number;
|
|
8
|
+
statusText: string;
|
|
9
|
+
headers: {
|
|
10
|
+
__OSRA_BOX__: 'revivable';
|
|
11
|
+
type: "headers";
|
|
12
|
+
entries: [string, string][];
|
|
13
|
+
};
|
|
14
|
+
body: import("./readable-stream").BoxedReadableStream<ReadableStream<Uint8Array<ArrayBuffer>>> | null;
|
|
15
|
+
url: string;
|
|
16
|
+
redirected: boolean;
|
|
17
|
+
};
|
|
18
|
+
export declare const revive: <T extends ReturnType<typeof box>, T2 extends RevivableContext>(value: T, context: T2) => Response;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Capable } from '../types';
|
|
2
|
+
import type { RevivableContext, UnderlyingType, BoxBase as BoxBaseType } from './utils';
|
|
3
|
+
export declare const type: 'set';
|
|
4
|
+
export type BoxedSet<T extends Set<Capable> = Set<Capable>> = BoxBaseType<typeof type> & {
|
|
5
|
+
values: Array<Capable>;
|
|
6
|
+
} & {
|
|
7
|
+
[UnderlyingType]: T;
|
|
8
|
+
};
|
|
9
|
+
export declare const isType: (value: unknown) => value is Set<unknown>;
|
|
10
|
+
export declare const box: <T extends Set<Capable>, T2 extends RevivableContext>(value: T, context: T2) => BoxedSet<T>;
|
|
11
|
+
export declare const revive: <T extends BoxedSet, T2 extends RevivableContext>(value: T, context: T2) => T[UnderlyingType];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Capable } from '../types';
|
|
2
|
+
import type { BoxBase as BoxBaseType, RevivableContext, UnderlyingType } from './utils';
|
|
3
|
+
export declare const type: 'transfer';
|
|
4
|
+
declare const TRANSFER_MARKER: unique symbol;
|
|
5
|
+
type TransferWrapper<T = unknown> = {
|
|
6
|
+
readonly [TRANSFER_MARKER]: true;
|
|
7
|
+
readonly value: T;
|
|
8
|
+
};
|
|
9
|
+
export type BoxedTransfer<T extends Capable = Capable> = BoxBaseType<typeof type> & {
|
|
10
|
+
inner: Capable;
|
|
11
|
+
degraded: boolean;
|
|
12
|
+
[UnderlyingType]: T;
|
|
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
|
+
*/
|
|
33
|
+
export declare const transfer: <T>(value: T) => T;
|
|
34
|
+
export declare const isType: (value: unknown) => value is TransferWrapper;
|
|
35
|
+
export declare const box: <T extends Capable, TContext extends RevivableContext>(wrapper: TransferWrapper<T>, context: TContext) => BoxedTransfer<T>;
|
|
36
|
+
export declare const revive: <T extends BoxedTransfer, TContext extends RevivableContext>(value: T, context: TContext) => T[UnderlyingType];
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RevivableContext, UnderlyingType, BoxedBuffer } from './utils';
|
|
2
|
+
import type { TypedArray, TypedArrayType } from '../utils/type-guards';
|
|
3
|
+
import { BoxBase } from './utils';
|
|
4
|
+
export declare const type: 'typedArray';
|
|
5
|
+
type BoxedTypedArray<T extends TypedArray, T2 extends RevivableContext> = typeof BoxBase & {
|
|
6
|
+
type: typeof type;
|
|
7
|
+
} & {
|
|
8
|
+
typedArrayType: TypedArrayType;
|
|
9
|
+
} & BoxedBuffer<T2> & {
|
|
10
|
+
[UnderlyingType]: T;
|
|
11
|
+
};
|
|
12
|
+
export declare const isType: (value: unknown) => value is TypedArray;
|
|
13
|
+
export declare const box: <T extends TypedArray, T2 extends RevivableContext>(value: T, context: T2) => BoxedTypedArray<T, T2>;
|
|
14
|
+
export declare const revive: <T extends BoxedTypedArray<TypedArray, RevivableContext>>(value: T, _context: RevivableContext) => T[UnderlyingType];
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { DefaultRevivableModules, RevivableModule } from '.';
|
|
2
|
+
import type { MessageEventTarget, MessageFields, Uuid } from '../types';
|
|
3
|
+
import type { Transport } from '../utils/transport';
|
|
4
|
+
import type { IsJsonOnlyTransport } from '../utils/type-guards';
|
|
5
|
+
export type { UnderlyingType } from '../utils/type';
|
|
6
|
+
export declare const BoxBase: {
|
|
7
|
+
readonly __OSRA_BOX__: 'revivable';
|
|
8
|
+
};
|
|
9
|
+
export type BoxBase<T extends string = string> = typeof BoxBase & {
|
|
10
|
+
type: T;
|
|
11
|
+
};
|
|
12
|
+
export type RevivableContext<TModules extends readonly RevivableModule[] = DefaultRevivableModules> = {
|
|
13
|
+
transport: Transport;
|
|
14
|
+
remoteUuid: Uuid;
|
|
15
|
+
unregisterSignal?: AbortSignal;
|
|
16
|
+
/** Typed as a broad dispatcher so revivables can post their own message
|
|
17
|
+
* variants without triggering contravariant function-parameter mismatches
|
|
18
|
+
* across modules. The shape is enforced structurally via `MessageFields`. */
|
|
19
|
+
sendMessage: (message: MessageFields & Record<string, unknown>) => void;
|
|
20
|
+
revivableModules: TModules;
|
|
21
|
+
eventTarget: MessageEventTarget<TModules>;
|
|
22
|
+
};
|
|
23
|
+
export type ExtractType<T> = T extends {
|
|
24
|
+
isType: (value: unknown) => value is infer S;
|
|
25
|
+
} ? S : never;
|
|
26
|
+
export type ExtractBox<T> = T extends {
|
|
27
|
+
box: (...args: any[]) => infer B;
|
|
28
|
+
} ? B : never;
|
|
29
|
+
export type ExtractMessages<T> = T extends {
|
|
30
|
+
Messages?: infer B;
|
|
31
|
+
} ? B extends {
|
|
32
|
+
type: string;
|
|
33
|
+
} ? string extends B['type'] ? never : B : never : never;
|
|
34
|
+
export type InferMessages<TModules extends readonly unknown[]> = ExtractMessages<TModules[number]>;
|
|
35
|
+
export type InferRevivables<TModules extends readonly unknown[]> = ExtractType<TModules[number]>;
|
|
36
|
+
export type InferRevivableBox<TModules extends readonly unknown[]> = ExtractBox<TModules[number]>;
|
|
37
|
+
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
|
+
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. */
|
|
45
|
+
export type BoxedBuffer<TCtx extends RevivableContext = RevivableContext> = IsJsonOnlyTransport<TCtx['transport']> extends true ? {
|
|
46
|
+
base64Buffer: string;
|
|
47
|
+
} : IsJsonOnlyTransport<TCtx['transport']> extends false ? {
|
|
48
|
+
arrayBuffer: ArrayBuffer;
|
|
49
|
+
} : {
|
|
50
|
+
base64Buffer: string;
|
|
51
|
+
} | {
|
|
52
|
+
arrayBuffer: ArrayBuffer;
|
|
53
|
+
};
|
|
54
|
+
export declare const boxBuffer: <TCtx extends RevivableContext>(buffer: ArrayBuffer, context: TCtx) => BoxedBuffer<TCtx>;
|
|
55
|
+
export declare const reviveBuffer: (boxed: {
|
|
56
|
+
arrayBuffer: ArrayBuffer;
|
|
57
|
+
} | {
|
|
58
|
+
base64Buffer: string;
|
|
59
|
+
}) => ArrayBuffer;
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ConnectionMessage } from './connections';
|
|
2
|
+
import type { TypedEventTarget } from './utils';
|
|
3
|
+
import type { DefaultRevivableModules, RevivableModule, InferMessages, InferRevivables } from './revivables';
|
|
4
|
+
export declare const OSRA_KEY: '__OSRA_KEY__';
|
|
5
|
+
export declare const OSRA_DEFAULT_KEY: '__OSRA_DEFAULT_KEY__';
|
|
6
|
+
export declare const OSRA_BOX: '__OSRA_BOX__';
|
|
7
|
+
export type Uuid = `${string}-${string}-${string}-${string}-${string}`;
|
|
8
|
+
export type Jsonable = boolean | null | number | string | {
|
|
9
|
+
[key: string]: Jsonable;
|
|
10
|
+
} | Array<Jsonable>;
|
|
11
|
+
export type Structurable = Jsonable
|
|
12
|
+
/** not really structureable but here for convenience */
|
|
13
|
+
| void | undefined | bigint | Date | RegExp | Blob | File | FileList | ArrayBuffer | ArrayBufferView | ImageBitmap | ImageData | {
|
|
14
|
+
[key: string]: Structurable;
|
|
15
|
+
} | Array<Structurable> | Map<Structurable, Structurable> | Set<Structurable>;
|
|
16
|
+
export type StructurableTransferable = Structurable | Transferable | {
|
|
17
|
+
[key: string]: StructurableTransferable;
|
|
18
|
+
} | 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>>;
|
|
22
|
+
export type MessageFields = {
|
|
23
|
+
type: string;
|
|
24
|
+
remoteUuid: Uuid;
|
|
25
|
+
};
|
|
26
|
+
export type MessageBase = {
|
|
27
|
+
[OSRA_KEY]: string;
|
|
28
|
+
/** UUID of the client that sent the message */
|
|
29
|
+
uuid: Uuid;
|
|
30
|
+
name?: string;
|
|
31
|
+
};
|
|
32
|
+
export type ProtocolMessage = {
|
|
33
|
+
type: 'announce';
|
|
34
|
+
/** Only set when acknowledging a remote announcement */
|
|
35
|
+
remoteUuid?: Uuid;
|
|
36
|
+
} | {
|
|
37
|
+
type: 'close';
|
|
38
|
+
remoteUuid: Uuid;
|
|
39
|
+
};
|
|
40
|
+
export type MessageVariant<TModules extends readonly RevivableModule[] = DefaultRevivableModules> = ProtocolMessage | ConnectionMessage<TModules> | InferMessages<TModules>;
|
|
41
|
+
export type Message<TModules extends readonly RevivableModule[] = DefaultRevivableModules> = MessageBase & MessageVariant<TModules>;
|
|
42
|
+
export type MessageEventMap<TModules extends readonly RevivableModule[] = DefaultRevivableModules> = {
|
|
43
|
+
message: CustomEvent<Message<TModules>>;
|
|
44
|
+
};
|
|
45
|
+
export type MessageEventTarget<TModules extends readonly RevivableModule[] = DefaultRevivableModules> = TypedEventTarget<MessageEventMap<TModules>>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare const ErrorMessage: unique symbol;
|
|
2
|
+
export declare const BadValue: unique symbol;
|
|
3
|
+
export declare const Path: unique symbol;
|
|
4
|
+
export declare const ParentObject: unique symbol;
|
|
5
|
+
type IsPlainObject<T> = T extends object ? T extends readonly unknown[] ? false : T extends (...args: any[]) => any ? false : T extends Date | RegExp | Blob | File | FileList | ArrayBuffer | ArrayBufferView | ImageBitmap | ImageData | Map<any, any> | Set<any> | Promise<any> ? false : true : false;
|
|
6
|
+
type FindBadField<T, TConstraint, TPath extends string = '', TParent = T> = T extends TConstraint ? never : T extends readonly unknown[] ? {
|
|
7
|
+
[K in Extract<keyof T, `${number}`>]: T[K] extends TConstraint ? never : FindBadField<T[K], TConstraint, `${TPath}[${K}]`, T>;
|
|
8
|
+
}[Extract<keyof T, `${number}`>] extends infer R ? [R] extends [never] ? {
|
|
9
|
+
value: T;
|
|
10
|
+
path: TPath;
|
|
11
|
+
parent: TParent;
|
|
12
|
+
} : R : never : IsPlainObject<T> extends true ? {
|
|
13
|
+
[K in Extract<keyof T, string>]: T[K] extends TConstraint ? never : FindBadField<T[K], TConstraint, TPath extends '' ? K : `${TPath}.${K}`, T>;
|
|
14
|
+
}[Extract<keyof T, string>] extends infer R ? [R] extends [never] ? {
|
|
15
|
+
value: T;
|
|
16
|
+
path: TPath;
|
|
17
|
+
parent: TParent;
|
|
18
|
+
} : R : never : {
|
|
19
|
+
value: T;
|
|
20
|
+
path: TPath;
|
|
21
|
+
parent: TParent;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* The first non-conforming value found by deep traversal of `T` against
|
|
25
|
+
* `TConstraint`. Falls back to `T` if no traversal is applicable.
|
|
26
|
+
*/
|
|
27
|
+
export type BadFieldValue<T, TConstraint> = FindBadField<T, TConstraint> extends {
|
|
28
|
+
value: infer V;
|
|
29
|
+
} ? V : T;
|
|
30
|
+
/**
|
|
31
|
+
* Dotted/bracketed path to the first non-conforming value (e.g. `"a.b[2]"`).
|
|
32
|
+
* Empty string if the root itself fails the check.
|
|
33
|
+
*/
|
|
34
|
+
export type BadFieldPath<T, TConstraint> = FindBadField<T, TConstraint> extends {
|
|
35
|
+
path: infer P extends string;
|
|
36
|
+
} ? P : '';
|
|
37
|
+
/**
|
|
38
|
+
* The immediate parent object/array containing the first non-conforming
|
|
39
|
+
* value. Equals `T` when the root itself fails.
|
|
40
|
+
*/
|
|
41
|
+
export type BadFieldParent<T, TConstraint> = FindBadField<T, TConstraint> extends {
|
|
42
|
+
parent: infer P;
|
|
43
|
+
} ? P : T;
|
|
44
|
+
export {};
|