@peerbit/canonical-host 0.0.0-e209d2e
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 +202 -0
- package/dist/src/index.d.ts +45 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +272 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/service-worker.d.ts +16 -0
- package/dist/src/service-worker.d.ts.map +1 -0
- package/dist/src/service-worker.js +69 -0
- package/dist/src/service-worker.js.map +1 -0
- package/dist/src/shared-worker.d.ts +7 -0
- package/dist/src/shared-worker.d.ts.map +1 -0
- package/dist/src/shared-worker.js +27 -0
- package/dist/src/shared-worker.js.map +1 -0
- package/dist/src/window.d.ts +22 -0
- package/dist/src/window.d.ts.map +1 -0
- package/dist/src/window.js +117 -0
- package/dist/src/window.js.map +1 -0
- package/package.json +98 -0
- package/src/index.ts +394 -0
- package/src/service-worker.ts +105 -0
- package/src/shared-worker.ts +41 -0
- package/src/window.ts +167 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CanonicalHost,
|
|
3
|
+
type CanonicalHostOptions,
|
|
4
|
+
type CanonicalModule,
|
|
5
|
+
type CanonicalRuntimeOptions,
|
|
6
|
+
PeerbitCanonicalRuntime,
|
|
7
|
+
} from "./index.js";
|
|
8
|
+
|
|
9
|
+
export const CANONICAL_SERVICE_WORKER_CONNECT_OP =
|
|
10
|
+
"__peerbit_canonical_connect__";
|
|
11
|
+
export const CANONICAL_SERVICE_WORKER_READY_KEY = "__peerbit_canonical_ready__";
|
|
12
|
+
|
|
13
|
+
export type InstallServiceWorkerHostOptions = CanonicalRuntimeOptions & {
|
|
14
|
+
modules?: CanonicalModule[];
|
|
15
|
+
hostOptions?: CanonicalHostOptions;
|
|
16
|
+
host?: CanonicalHost;
|
|
17
|
+
createHost?: () => CanonicalHost | Promise<CanonicalHost>;
|
|
18
|
+
lifecycle?: {
|
|
19
|
+
skipWaiting?: boolean;
|
|
20
|
+
clientsClaim?: boolean;
|
|
21
|
+
};
|
|
22
|
+
onError?: (error: unknown) => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
let hostPromise: Promise<CanonicalHost> | undefined;
|
|
26
|
+
|
|
27
|
+
const getHost = async (options?: InstallServiceWorkerHostOptions) => {
|
|
28
|
+
if (hostPromise) return hostPromise;
|
|
29
|
+
|
|
30
|
+
const provided = options?.host;
|
|
31
|
+
const create = options?.createHost;
|
|
32
|
+
const { modules, hostOptions, ...runtimeOptions } = options ?? {};
|
|
33
|
+
|
|
34
|
+
const host = provided
|
|
35
|
+
? provided
|
|
36
|
+
: create
|
|
37
|
+
? await create()
|
|
38
|
+
: new CanonicalHost(
|
|
39
|
+
new PeerbitCanonicalRuntime(runtimeOptions),
|
|
40
|
+
hostOptions,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (modules?.length) {
|
|
44
|
+
host.registerModules(modules);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
hostPromise = Promise.resolve(host);
|
|
48
|
+
return hostPromise;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const coerceErrorMessage = (error: unknown): string => {
|
|
52
|
+
if (error instanceof Error) return error.message;
|
|
53
|
+
return String((error as any)?.message ?? error);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const installServiceWorkerHost = (
|
|
57
|
+
options?: InstallServiceWorkerHostOptions,
|
|
58
|
+
) => {
|
|
59
|
+
const scope = self as unknown as ServiceWorkerGlobalScope;
|
|
60
|
+
|
|
61
|
+
if (options?.lifecycle?.skipWaiting) {
|
|
62
|
+
scope.addEventListener("install", (event) => {
|
|
63
|
+
event.waitUntil(scope.skipWaiting());
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (options?.lifecycle?.clientsClaim) {
|
|
68
|
+
scope.addEventListener("activate", (event) => {
|
|
69
|
+
event.waitUntil(scope.clients.claim());
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
scope.addEventListener("message", (event: ExtendableMessageEvent) => {
|
|
74
|
+
const data = event.data as any;
|
|
75
|
+
if (
|
|
76
|
+
data?.op !== CANONICAL_SERVICE_WORKER_CONNECT_OP &&
|
|
77
|
+
data?.__peerbit_canonical_connect__ !== true
|
|
78
|
+
) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const port = event.ports?.[0];
|
|
83
|
+
if (!port) return;
|
|
84
|
+
|
|
85
|
+
event.waitUntil(
|
|
86
|
+
getHost(options)
|
|
87
|
+
.then((host) => {
|
|
88
|
+
host.attachControlPort(port);
|
|
89
|
+
try {
|
|
90
|
+
port.postMessage({ [CANONICAL_SERVICE_WORKER_READY_KEY]: true });
|
|
91
|
+
} catch {}
|
|
92
|
+
})
|
|
93
|
+
.catch((error) => {
|
|
94
|
+
options?.onError?.(error);
|
|
95
|
+
const message = coerceErrorMessage(error);
|
|
96
|
+
try {
|
|
97
|
+
port.postMessage({
|
|
98
|
+
[CANONICAL_SERVICE_WORKER_READY_KEY]: false,
|
|
99
|
+
error: message,
|
|
100
|
+
});
|
|
101
|
+
} catch {}
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CanonicalHost,
|
|
3
|
+
type CanonicalHostOptions,
|
|
4
|
+
type CanonicalModule,
|
|
5
|
+
type CanonicalRuntimeOptions,
|
|
6
|
+
PeerbitCanonicalRuntime,
|
|
7
|
+
} from "./index.js";
|
|
8
|
+
|
|
9
|
+
export type InstallSharedWorkerHostOptions = CanonicalRuntimeOptions & {
|
|
10
|
+
modules?: CanonicalModule[];
|
|
11
|
+
hostOptions?: CanonicalHostOptions;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
let hostPromise: Promise<CanonicalHost> | undefined;
|
|
15
|
+
|
|
16
|
+
const getHost = async (options?: InstallSharedWorkerHostOptions) => {
|
|
17
|
+
if (hostPromise) return hostPromise;
|
|
18
|
+
const { modules, hostOptions, ...runtimeOptions } = options ?? {};
|
|
19
|
+
const runtime = new PeerbitCanonicalRuntime(runtimeOptions);
|
|
20
|
+
const host = new CanonicalHost(runtime, hostOptions);
|
|
21
|
+
if (modules?.length) {
|
|
22
|
+
host.registerModules(modules);
|
|
23
|
+
}
|
|
24
|
+
hostPromise = Promise.resolve(host);
|
|
25
|
+
return hostPromise;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const installSharedWorkerHost = (
|
|
29
|
+
options?: InstallSharedWorkerHostOptions,
|
|
30
|
+
) => {
|
|
31
|
+
const scope = self as unknown as SharedWorkerGlobalScope;
|
|
32
|
+
const onConnect = async (e: MessageEvent) => {
|
|
33
|
+
const port: MessagePort = (e as any).ports?.[0];
|
|
34
|
+
if (!port) {
|
|
35
|
+
throw new Error("SharedWorker onconnect event missing MessagePort");
|
|
36
|
+
}
|
|
37
|
+
const host = await getHost(options);
|
|
38
|
+
host.attachControlPort(port);
|
|
39
|
+
};
|
|
40
|
+
scope.addEventListener("connect", onConnect as unknown as EventListener);
|
|
41
|
+
};
|
package/src/window.ts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { createWindowTransport } from "@peerbit/canonical-transport";
|
|
2
|
+
import {
|
|
3
|
+
CanonicalHost,
|
|
4
|
+
type CanonicalHostOptions,
|
|
5
|
+
type CanonicalModule,
|
|
6
|
+
type CanonicalRuntimeOptions,
|
|
7
|
+
PeerbitCanonicalRuntime,
|
|
8
|
+
} from "./index.js";
|
|
9
|
+
|
|
10
|
+
export const CANONICAL_WINDOW_CONNECT_OP =
|
|
11
|
+
"__peerbit_canonical_window_connect__";
|
|
12
|
+
export const CANONICAL_WINDOW_READY_KEY = "__peerbit_canonical_window_ready__";
|
|
13
|
+
|
|
14
|
+
export type InstallWindowHostOptions = CanonicalRuntimeOptions & {
|
|
15
|
+
modules?: CanonicalModule[];
|
|
16
|
+
hostOptions?: CanonicalHostOptions;
|
|
17
|
+
host?: CanonicalHost;
|
|
18
|
+
createHost?: () => CanonicalHost | Promise<CanonicalHost>;
|
|
19
|
+
channel?: string;
|
|
20
|
+
targetOrigin?: string;
|
|
21
|
+
allow?: (event: MessageEvent) => boolean;
|
|
22
|
+
onError?: (error: unknown) => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type WindowHost = {
|
|
26
|
+
connect: (childWindow: Window, options?: { origin?: string }) => void;
|
|
27
|
+
disconnect: (childWindow: Window) => void;
|
|
28
|
+
close: () => void;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
let hostPromise: Promise<CanonicalHost> | undefined;
|
|
32
|
+
|
|
33
|
+
const getHost = async (options?: InstallWindowHostOptions) => {
|
|
34
|
+
if (hostPromise) return hostPromise;
|
|
35
|
+
|
|
36
|
+
const provided = options?.host;
|
|
37
|
+
const create = options?.createHost;
|
|
38
|
+
const { modules, hostOptions, ...runtimeOptions } = options ?? {};
|
|
39
|
+
|
|
40
|
+
const host = provided
|
|
41
|
+
? provided
|
|
42
|
+
: create
|
|
43
|
+
? await create()
|
|
44
|
+
: new CanonicalHost(
|
|
45
|
+
new PeerbitCanonicalRuntime(runtimeOptions),
|
|
46
|
+
hostOptions,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (modules?.length) {
|
|
50
|
+
host.registerModules(modules);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
hostPromise = Promise.resolve(host);
|
|
54
|
+
return hostPromise;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const coerceErrorMessage = (error: unknown): string => {
|
|
58
|
+
if (error instanceof Error) return error.message;
|
|
59
|
+
return String((error as any)?.message ?? error);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const coerceOrigin = (origin: unknown): string | undefined => {
|
|
63
|
+
if (typeof origin !== "string") return undefined;
|
|
64
|
+
if (origin.length === 0 || origin === "null") return undefined;
|
|
65
|
+
return origin;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const installWindowHost = (
|
|
69
|
+
options?: InstallWindowHostOptions,
|
|
70
|
+
): WindowHost => {
|
|
71
|
+
const channel = options?.channel ?? "peerbit-canonical";
|
|
72
|
+
const defaultTargetOrigin = options?.targetOrigin ?? "*";
|
|
73
|
+
const connections = new Map<Window, () => void>();
|
|
74
|
+
|
|
75
|
+
const connect = (
|
|
76
|
+
childWindow: Window,
|
|
77
|
+
connectOptions?: { origin?: string },
|
|
78
|
+
) => {
|
|
79
|
+
if (connections.has(childWindow)) return;
|
|
80
|
+
const targetOrigin = connectOptions?.origin ?? defaultTargetOrigin;
|
|
81
|
+
const transport = createWindowTransport(childWindow, {
|
|
82
|
+
channel,
|
|
83
|
+
source: childWindow,
|
|
84
|
+
targetOrigin,
|
|
85
|
+
});
|
|
86
|
+
getHost(options)
|
|
87
|
+
.then((host) => {
|
|
88
|
+
const detach = host.attachControlTransport(transport);
|
|
89
|
+
connections.set(childWindow, detach);
|
|
90
|
+
})
|
|
91
|
+
.catch((error) => {
|
|
92
|
+
options?.onError?.(error);
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const disconnect = (childWindow: Window) => {
|
|
97
|
+
const detach = connections.get(childWindow);
|
|
98
|
+
if (!detach) return;
|
|
99
|
+
connections.delete(childWindow);
|
|
100
|
+
detach();
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const onMessage = (event: MessageEvent) => {
|
|
104
|
+
if (options?.allow && !options.allow(event)) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const data = event.data as any;
|
|
109
|
+
const isConnect =
|
|
110
|
+
data?.op === CANONICAL_WINDOW_CONNECT_OP ||
|
|
111
|
+
data?.__peerbit_canonical_window_connect__ === true;
|
|
112
|
+
if (!isConnect) return;
|
|
113
|
+
if (data?.channel && String(data.channel) !== channel) return;
|
|
114
|
+
|
|
115
|
+
const source = event.source as Window | null;
|
|
116
|
+
if (!source || typeof (source as any).postMessage !== "function") {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const requestId =
|
|
121
|
+
data?.requestId != null ? String(data.requestId) : undefined;
|
|
122
|
+
const origin = coerceOrigin(event.origin) ?? defaultTargetOrigin;
|
|
123
|
+
|
|
124
|
+
connect(source, { origin });
|
|
125
|
+
|
|
126
|
+
getHost(options)
|
|
127
|
+
.then(() => {
|
|
128
|
+
try {
|
|
129
|
+
source.postMessage(
|
|
130
|
+
{
|
|
131
|
+
[CANONICAL_WINDOW_READY_KEY]: true,
|
|
132
|
+
channel,
|
|
133
|
+
requestId,
|
|
134
|
+
},
|
|
135
|
+
origin === "*" ? "*" : origin,
|
|
136
|
+
);
|
|
137
|
+
} catch {}
|
|
138
|
+
})
|
|
139
|
+
.catch((error) => {
|
|
140
|
+
options?.onError?.(error);
|
|
141
|
+
const message = coerceErrorMessage(error);
|
|
142
|
+
try {
|
|
143
|
+
source.postMessage(
|
|
144
|
+
{
|
|
145
|
+
[CANONICAL_WINDOW_READY_KEY]: false,
|
|
146
|
+
channel,
|
|
147
|
+
requestId,
|
|
148
|
+
error: message,
|
|
149
|
+
},
|
|
150
|
+
origin === "*" ? "*" : origin,
|
|
151
|
+
);
|
|
152
|
+
} catch {}
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
globalThis.addEventListener("message", onMessage);
|
|
157
|
+
|
|
158
|
+
const close = () => {
|
|
159
|
+
globalThis.removeEventListener("message", onMessage);
|
|
160
|
+
for (const detach of connections.values()) {
|
|
161
|
+
detach();
|
|
162
|
+
}
|
|
163
|
+
connections.clear();
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
return { connect, disconnect, close };
|
|
167
|
+
};
|