extensionrunner v0.0.1
extensionrunner
This package is currently under development
Clone or fork the GitHub repo for now: extensionrunner
extensionrunner uses CDNs (jsdelivr, githack, unpkg), to power external extensions safely in your App via Web Workers and IFrames directly from GitHub or npm.
Features
- Fetch files assets from extensions
- Import modules from extensions
- Display components from extensions
- Shared state between modules and components
Usage
Provider
https://example.com
index.js
import Provider from "extensionrunner";
const provider = new Provider();
const extension = await provider.loadExtension({
type: "github", // or "npm"
name: ":owner/:repo", // or npm package name
version ":git_commit_sha", // or npm package version
})
const providerApi = {
alert: (text: string) => alert("module/component:", text),
sum: (a: number, b: number) => a+b
}
// -- Launch module
const module = await extension.launchModule("modules/module.js", providerApi, {
// Deactivated by default
allowPopulateState: (state, oldState) => true, // or return new state
});
document.getElementById("echo_btn").onclick=()=>{
const echo = module.execute("echo", "Hello from provider");
console.log("Echo from module:", echo)
}
document.getElementById("print_btn").onclick=()=>{
module.execute("print", "Hello", "from", "provider")
}
// -- Launch component
// `launchComponent` returns the same type as `launchModule`
const module = await extension.launchComponent("components/component.html", providerApi, {
allowPopulateState: () => true,
});
setInterval(async ()=>{
const newCounter = await module.execute("increment");
console.log("Module confirmed new counter:", newCounter)
}, 1000)
document.getElementById("change_increment_by_btn").onclick= () => {
// push state from provider
module.pushState({ incrementBy: 2 })
}
Extension
\<github_or_npm>/modules/module.js
import Adapter from "extensionrunner/adapter";
const adapterApi = {
echo: text => {
return text;
},
print: (...text) => {
console.log(...text);
},
};
new Adapter({
provider: "https://example.com",
out: adapterApi,
}).start(async adapter => {
adapter.execute("alert", "module running...");
console.log("The sum of 40 and 2 is", await adapter.execute("sum", 2, 40));
});
\<github_or_npm>/components/component.html
<div>
<!-- or external script -->
<script type="module">
import Adapter from "extensionrunner/adapter";
let counter = 0;
new Adapter({
provider: "https://example.com",
out: {
increment: function () {
return (counter += this.state.incrementBy || 1);
},
reset: () => {
counter = 0;
},
},
}).start(adapter => {
// Listen to operations (This could also done directly in increment)
adapter.addEventListener("op:increment", e => {
document.getElementById("counter").innerHtml = e.payload.result + "";
});
// Listen to other events
adapter.addEventListener("state_push", e => {
document.getElementById("increment").innerHtml = "Incrementing by " + e.payload.incrementBy;
});
document.getElementById("change_increment").onclick = () => {
// Push state from a component/module
adapter.pushState(
{ incrementBy: 5 },
{
// Share state with other components (components with the same path).
// Defaults to true
populate: true,
}
);
};
});
</script>
<h1>Counter</h1>
<p id="counter"></p>
<p>Incrementing by <span id="increment">1</span></p>
<button id="reset">Reset</button>
</div>
TypeScript
interface ModuleInterface {
print: (text: string) => string
multiply: (a: number, b: number) => number
}
interface ProviderInterface {
log: (...text: string[]) => void
alert: () => void
sum: (a: number, b: number) => number
}
interface ModuleState {
user: string;
}
// Provider
provider.launchModule<ProviderInterface, ModuleInterface, ModleState>()
// Adapter
new Adapter<ProviderInterface, ModuleInterface, ModleState>(...).start(...)
Fetch Files
const response = await provider.loadFile("path/to/file");
const response = await adapter.loadFile("path/to/file");
pushState
// -- Provider
// Extension level
await extension.pushState(
{ activeTab: "files" },
{
// default: All modules/components
filter: {
check: module => module.meta.path.startsWith("toolbar/"),
},
merge: true, // default: true
}
);
// Module Level
await module.pushState({
activeTab: "account",
});
// -- Adapter
await adapter.pushState(
{
activeTab: "settings",
},
{
populate: true, // default: true
}
);
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago