Skip to content

Commit 8ea2678

Browse files
shakyShaneShane Osbourne
and
Shane Osbourne
authored
Added messaging library (#331)
* Added messaging library as an example to document * better docs and examples --------- Co-authored-by: Shane Osbourne <[email protected]>
1 parent 96c0d24 commit 8ea2678

File tree

10 files changed

+865
-3
lines changed

10 files changed

+865
-3
lines changed

package-lock.json

Lines changed: 14 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
},
3939
"type": "module",
4040
"workspaces": [
41-
"packages/special-pages"
41+
"packages/special-pages",
42+
"packages/messaging"
4243
],
4344
"dependencies": {
4445
"seedrandom": "^3.0.5",

packages/messaging/index.js

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/**
2+
* @module Messaging
3+
* @category Libraries
4+
* @description
5+
*
6+
* An abstraction for communications between JavaScript and host platforms.
7+
*
8+
* 1) First you construct your platform-specific configuration (eg: {@link WebkitMessagingConfig})
9+
* 2) Then use that to get an instance of the Messaging utility which allows
10+
* you to send and receive data in a unified way
11+
* 3) Each platform implements {@link MessagingTransport} along with its own Configuration
12+
* - For example, to learn what configuration is required for Webkit, see: {@link WebkitMessagingConfig}
13+
* - Or, to learn about how messages are sent and received in Webkit, see {@link WebkitMessagingTransport}
14+
*
15+
* @example Webkit Messaging
16+
*
17+
* ```javascript
18+
* [[include:packages/messaging/lib/examples/webkit.example.js]]```
19+
*
20+
* @example Windows Messaging
21+
*
22+
* ```javascript
23+
* [[include:packages/messaging/lib/examples/windows.example.js]]```
24+
*/
25+
import { WindowsMessagingConfig, WindowsMessagingTransport, WindowsInteropMethods } from './lib/windows.js'
26+
import { WebkitMessagingConfig, WebkitMessagingTransport } from './lib/webkit.js'
27+
28+
/**
29+
* @implements {MessagingTransport}
30+
*/
31+
export class Messaging {
32+
/**
33+
* @param {WebkitMessagingConfig | WindowsMessagingConfig} config
34+
*/
35+
constructor (config) {
36+
this.transport = getTransport(config)
37+
}
38+
39+
/**
40+
* Send a 'fire-and-forget' message.
41+
* @throws {MissingHandler}
42+
*
43+
* @example
44+
*
45+
* ```ts
46+
* const messaging = new Messaging(config)
47+
* messaging.notify("foo", {bar: "baz"})
48+
* ```
49+
* @param {string} name
50+
* @param {Record<string, any>} [data]
51+
*/
52+
notify (name, data = {}) {
53+
this.transport.notify(name, data)
54+
}
55+
56+
/**
57+
* Send a request, and wait for a response
58+
* @throws {MissingHandler}
59+
*
60+
* @example
61+
* ```
62+
* const messaging = new Messaging(config)
63+
* const response = await messaging.request("foo", {bar: "baz"})
64+
* ```
65+
*
66+
* @param {string} name
67+
* @param {Record<string, any>} [data]
68+
* @return {Promise<any>}
69+
*/
70+
request (name, data = {}) {
71+
return this.transport.request(name, data)
72+
}
73+
74+
/**
75+
* @param {string} name
76+
* @param {(value: unknown) => void} callback
77+
* @return {() => void}
78+
*/
79+
subscribe (name, callback) {
80+
return this.transport.subscribe(name, callback)
81+
}
82+
}
83+
84+
/**
85+
* @interface
86+
*/
87+
export class MessagingTransport {
88+
/**
89+
* @param {string} name
90+
* @param {Record<string, any>} [data]
91+
* @returns {void}
92+
*/
93+
// @ts-ignore - ignoring a no-unused ts error, this is only an interface.
94+
notify (name, data = {}) {
95+
throw new Error("must implement 'notify'")
96+
}
97+
98+
/**
99+
* @param {string} name
100+
* @param {Record<string, any>} [data]
101+
* @param {{signal?: AbortSignal}} [options]
102+
* @return {Promise<any>}
103+
*/
104+
// @ts-ignore - ignoring a no-unused ts error, this is only an interface.
105+
request (name, data = {}, options = {}) {
106+
throw new Error('must implement')
107+
}
108+
109+
/**
110+
* @param {string} name
111+
* @param {(value: unknown) => void} callback
112+
* @return {() => void}
113+
*/
114+
// @ts-ignore - ignoring a no-unused ts error, this is only an interface.
115+
subscribe (name, callback) {
116+
throw new Error('must implement')
117+
}
118+
}
119+
120+
/**
121+
* @param {WebkitMessagingConfig | WindowsMessagingConfig} config
122+
* @returns {MessagingTransport}
123+
*/
124+
function getTransport (config) {
125+
if (config instanceof WebkitMessagingConfig) {
126+
return new WebkitMessagingTransport(config)
127+
}
128+
if (config instanceof WindowsMessagingConfig) {
129+
return new WindowsMessagingTransport(config)
130+
}
131+
throw new Error('unreachable')
132+
}
133+
134+
/**
135+
* Thrown when a handler cannot be found
136+
*/
137+
export class MissingHandler extends Error {
138+
/**
139+
* @param {string} message
140+
* @param {string} handlerName
141+
*/
142+
constructor (message, handlerName) {
143+
super(message)
144+
this.handlerName = handlerName
145+
}
146+
}
147+
148+
/**
149+
* Some re-exports for convenience
150+
*/
151+
export {
152+
WebkitMessagingConfig,
153+
WebkitMessagingTransport,
154+
WindowsMessagingConfig,
155+
WindowsMessagingTransport,
156+
WindowsInteropMethods
157+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Messaging, WebkitMessagingConfig } from '../../index.js'
2+
3+
/**
4+
* Webkit messaging involves calling methods on `window.webkit.messageHandlers`.
5+
*
6+
* For catalina support we support encryption which is the bulk of this configuration
7+
*/
8+
const config = new WebkitMessagingConfig({
9+
hasModernWebkitAPI: true,
10+
secret: 'SECRET',
11+
webkitMessageHandlerNames: ['helloWorld', 'sendPixel'],
12+
})
13+
14+
/**
15+
* Send notifications!
16+
*/
17+
const messaging = new Messaging(config)
18+
messaging.notify('sendPixel')
19+
20+
/**
21+
* Or request some data
22+
*/
23+
messaging.request('helloWorld', { foo: 'bar' }).then(console.log).catch(console.error)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { WindowsMessagingConfig } from '../windows.js'
2+
import { Messaging } from '../../index.js'
3+
4+
/**
5+
* These 3 required methods that get assigned by the Native side.
6+
*/
7+
// @ts-ignore
8+
const windowsInteropPostMessage = window.chrome.webview.postMessage
9+
// @ts-ignore
10+
const windowsInteropAddEventListener = window.chrome.webview.addEventListener
11+
// @ts-ignore
12+
const windowsInteropRemoveEventListener = window.chrome.webview.removeEventListener
13+
14+
/**
15+
* With those methods available in the same lexical scope, we can then create
16+
* our WindowsMessagingConfig
17+
*/
18+
const config = new WindowsMessagingConfig({
19+
featureName: 'ExamplePage',
20+
methods: {
21+
postMessage: windowsInteropPostMessage,
22+
addEventListener: windowsInteropAddEventListener,
23+
removeEventListener: windowsInteropRemoveEventListener,
24+
},
25+
})
26+
27+
/**
28+
* And then send notifications!
29+
*/
30+
const messaging = new Messaging(config)
31+
messaging.notify('helloWorld')
32+
33+
/**
34+
* Or request some data
35+
*/
36+
messaging.request('getData', { foo: 'bar' }).then(console.log).catch(console.error)
37+
38+
/**
39+
* Or subscribe for push messages
40+
*/
41+
const unsubscribe = messaging.subscribe('getData', (data) => console.log(data))
42+
43+
// later
44+
unsubscribe()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
interface UnstableWebkit {
2+
messageHandlers: Record<
3+
string,
4+
{
5+
postMessage?: (...args: unknown[]) => void
6+
}
7+
>
8+
}
9+
10+
interface Window {
11+
webkit: UnstableWebkit
12+
}
13+
14+
declare let windowsInteropPostMessage: any
15+
declare let windowsInteropAddEventListener: any
16+
declare let windowsInteropRemoveEventListener: any

0 commit comments

Comments
 (0)