-
Notifications
You must be signed in to change notification settings - Fork 212
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement CapTP forwarding over a plugin device
- Loading branch information
1 parent
6ba171f
commit b4a1be8
Showing
7 changed files
with
179 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* global harden */ | ||
|
||
import { makeCapTP } from '@agoric/captp'; | ||
|
||
export function buildRootDeviceNode(tools) { | ||
const { SO, getDeviceState, setDeviceState, endowments } = tools; | ||
const restart = getDeviceState(); | ||
|
||
let registeredReceiver; | ||
|
||
const connectedMods = []; | ||
const senders = []; | ||
const connectedState = restart ? [...restart.connectedState] : []; | ||
|
||
function saveState() { | ||
setDeviceState( | ||
harden({ | ||
connectedMods, | ||
connectedState: [...connectedState], | ||
}), | ||
); | ||
} | ||
|
||
/** | ||
* Load a module and connect to it. | ||
* @param {string} mod module with an exported `bootPlugin(state = undefined)` | ||
* @param {(obj: Record<string, any>) => void} receive a message from the module | ||
* @returns {(obj: Record<string, any>) => void} send a message to the module | ||
*/ | ||
function connect(mod) { | ||
try { | ||
const modNS = endowments.require(mod); | ||
const index = connectedMods.length; | ||
connectedMods.push(mod); | ||
const receiver = obj => { | ||
console.info('receiver', index, obj); | ||
switch (obj.type) { | ||
case 'PLUGIN_SAVE_STATE': | ||
connectedState[index] = obj.data; | ||
saveState(); | ||
break; | ||
default: | ||
SO(registeredReceiver).receive(index, obj); | ||
} | ||
}; | ||
// Create a bootstrap reference from the module. | ||
const bootstrap = modNS.bootPlugin(connectedState[index]); | ||
|
||
// Establish a CapTP connection. | ||
const { dispatch } = makeCapTP(mod, receiver, bootstrap); | ||
|
||
// Save the dispatch function for later. | ||
senders[index] = dispatch; | ||
return index; | ||
} catch (e) { | ||
console.error(`Cannot connect to ${mod}:`, e); | ||
return `${(e && e.stack) || e}`; | ||
} | ||
} | ||
|
||
function send(index, obj) { | ||
const sender = senders[index]; | ||
console.error('send', obj); | ||
sender(obj); | ||
} | ||
|
||
// Connect to all existing modules. | ||
const preload = restart ? restart.connectedMods : []; | ||
preload.forEach(mod => { | ||
try { | ||
connect(mod); | ||
} catch (e) { | ||
console.error(`Cannot connect to ${mod}:`, e); | ||
} | ||
}); | ||
|
||
return harden({ | ||
connect, | ||
send, | ||
registerReceiver(receiver) { | ||
registeredReceiver = receiver; | ||
}, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export function buildPlugin(pluginRequire) { | ||
const srcPath = require.resolve('./plugin-src'); | ||
|
||
// srcPath and endowments are provided to buildRootDeviceNode() for use | ||
// during configuration. | ||
return { | ||
srcPath, | ||
endowments: { require: pluginRequire }, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/* global harden */ | ||
// @ts-check | ||
|
||
import makeStore from '@agoric/store'; | ||
import { makeCapTP } from '@agoric/captp'; | ||
|
||
/** | ||
* @template T | ||
* @typedef {T} Device | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} PluginManager | ||
* @property {(mod: string) => ERef<any>} load | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} Receiver | ||
* @property {(index: number, obj: Record<string, any>) => void} receive | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} PluginDevice | ||
* @property {(mod: string) => number} connect | ||
* @property {(receiver: Receiver) => void} registerReceiver | ||
* @property {(index: number, obj: Record<string, any>) => void} send | ||
*/ | ||
|
||
/** | ||
* Create a handler that manages a promise interface to external modules. | ||
* | ||
* @param {import('@agoric/eventual-send').EProxy} E The eventual sender | ||
* @param {<T>(target: Device<T>) => T} D The device sender | ||
* @param {Device<PluginDevice>} pluginDevice The bridge to manage | ||
* @returns {PluginManager} admin facet for this handler | ||
*/ | ||
export function makePluginManager(E, D, pluginDevice) { | ||
/** | ||
* @type {import('@agoric/store').Store<number, (obj: Record<string,any>) => void>} | ||
*/ | ||
const modReceivers = makeStore('moduleIndex'); | ||
|
||
// Dispatch object to the right index. | ||
D(pluginDevice).registerReceiver( | ||
harden({ | ||
receive(index, obj) { | ||
console.info('receive', index, obj); | ||
modReceivers.get(index)(obj); | ||
}, | ||
}), | ||
); | ||
|
||
return harden({ | ||
load(mod) { | ||
// Start CapTP on the plugin module's side. | ||
const index = D(pluginDevice).connect(mod); | ||
if (typeof index === 'string') { | ||
throw Error(index); | ||
} | ||
// Create a CapTP channel. | ||
const { getBootstrap, dispatch } = makeCapTP(mod, obj => | ||
D(pluginDevice).send(index, obj), | ||
); | ||
// Register our dispatcher for this connect index. | ||
modReceivers.init(index, dispatch); | ||
|
||
// Give up our bootstrap object for the caller to use. | ||
return getBootstrap(); | ||
}, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters