Compare commits

...

3 Commits

4 changed files with 115 additions and 58 deletions

View File

@ -1,3 +1,5 @@
# [0.1.0-develop.21](https://git.lumeweb.com/LumeWeb/libkernel/compare/v0.1.0-develop.20...v0.1.0-develop.21) (2023-07-20)
# [0.1.0-develop.20](https://git.lumeweb.com/LumeWeb/libkernel/compare/v0.1.0-develop.19...v0.1.0-develop.20) (2023-07-18) # [0.1.0-develop.20](https://git.lumeweb.com/LumeWeb/libkernel/compare/v0.1.0-develop.19...v0.1.0-develop.20) (2023-07-18)
# [0.1.0-develop.19](https://git.lumeweb.com/LumeWeb/libkernel/compare/v0.1.0-develop.18...v0.1.0-develop.19) (2023-07-18) # [0.1.0-develop.19](https://git.lumeweb.com/LumeWeb/libkernel/compare/v0.1.0-develop.18...v0.1.0-develop.19) (2023-07-18)

4
npm-shrinkwrap.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "@lumeweb/libkernel", "name": "@lumeweb/libkernel",
"version": "0.1.0-develop.20", "version": "0.1.0-develop.21",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@lumeweb/libkernel", "name": "@lumeweb/libkernel",
"version": "0.1.0-develop.20", "version": "0.1.0-develop.21",
"dependencies": { "dependencies": {
"@lumeweb/libweb": "0.2.0-develop.26", "@lumeweb/libweb": "0.2.0-develop.26",
"emittery": "^1.0.1" "emittery": "^1.0.1"

View File

@ -1,6 +1,6 @@
{ {
"name": "@lumeweb/libkernel", "name": "@lumeweb/libkernel",
"version": "0.1.0-develop.20", "version": "0.1.0-develop.21",
"main": "lib/index.js", "main": "lib/index.js",
"type": "module", "type": "module",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",

View File

@ -24,6 +24,19 @@ interface queryMap {
}; };
} }
declare global {
interface Window {
browser: any;
}
}
const IS_EXTENSION =
window.browser?.runtime?.id &&
window.location.pathname.includes("_generated_background_page.html");
const EXTENSION_KERNEL_ORIGIN = "http://kernel.lume";
const EXTENSION_HOSTED_ORIGIN = "https://kernel.lumeweb.com";
// Create the queryMap. // Create the queryMap.
const queries: queryMap = {}; const queries: queryMap = {};
@ -70,20 +83,29 @@ function handleMessage(event: MessageEvent) {
// Ignore all messages that aren't from approved kernel sources. The two // Ignore all messages that aren't from approved kernel sources. The two
// approved sources are skt.us and the browser extension bridge (which has // approved sources are skt.us and the browser extension bridge (which has
// an event.source equal to 'window') // an event.source equal to 'window')
if ( const FROM_KERNEL =
event.source !== window && event.source !== window &&
event.origin !== "https://kernel.lumeweb.com" event.origin === EXTENSION_KERNEL_ORIGIN &&
) { IS_EXTENSION;
const FROM_HOSTED_KERNEL =
event.source !== window && event.origin === EXTENSION_HOSTED_ORIGIN;
if (!FROM_KERNEL && !FROM_HOSTED_KERNEL) {
return; return;
} }
if (IS_EXTENSION && !event.data?.data) {
event.data.data = Object.assign({}, event.data);
}
// Ignore any messages that don't have a method and data field. // Ignore any messages that don't have a method and data field.
if (!("method" in event.data) || !("data" in event.data)) { if (!("method" in event.data) || !("data" in event.data)) {
return; return;
} }
// Handle logging messages. // Handle logging messages.
if (event.data.method === "log") { if (event.data.method === "log" && !IS_EXTENSION) {
// We display the logging message if the kernel is a browser // We display the logging message if the kernel is a browser
// extension, so that the kernel's logs appear in the app // extension, so that the kernel's logs appear in the app
// console as well as the extension console. If the kernel is // console as well as the extension console. If the kernel is
@ -105,7 +127,7 @@ function handleMessage(event: MessageEvent) {
if (event.data.method === "kernelAuthStatus") { if (event.data.method === "kernelAuthStatus") {
// If we have received an auth status message, it means the bootloader // If we have received an auth status message, it means the bootloader
// at a minimum is working. // at a minimum is working.
if (initResolved === false) { if (!initResolved) {
initResolved = true; initResolved = true;
// We can't actually establish that init is complete until the // We can't actually establish that init is complete until the
@ -116,19 +138,30 @@ function handleMessage(event: MessageEvent) {
}); });
} }
if (IS_EXTENSION && event.data.data.kernelLoaded === "success") {
const nonce = nextNonce();
queries[nonce] = {
resolve: sourceResolve,
};
const kernelMessage = {
method: "version",
nonce,
data: null,
};
kernelSource.postMessage(kernelMessage, kernelOrigin);
}
// If the auth status message says that login is complete, it means // If the auth status message says that login is complete, it means
// that the user is logged in. // that the user is logged in.
if (loginResolved === false && event.data.data.loginComplete === true) { if (!loginResolved && event.data.data.loginComplete) {
loginResolved = true; loginResolved = true;
loginResolve(); loginResolve();
} }
// If the auth status message says that the kernel loaded, it means // If the auth status message says that the kernel loaded, it means
// that the kernel is ready to receive messages. // that the kernel is ready to receive messages.
if ( if (!kernelLoadedResolved && event.data.data.kernelLoaded !== "not yet") {
kernelLoadedResolved === false &&
event.data.data.kernelLoaded !== "not yet"
) {
kernelLoadedResolved = true; kernelLoadedResolved = true;
if (event.data.data.kernelLoaded === "success") { if (event.data.data.kernelLoaded === "success") {
kernelLoadedResolve(null); kernelLoadedResolve(null);
@ -139,8 +172,8 @@ function handleMessage(event: MessageEvent) {
// If we have received a message indicating that the user has logged // If we have received a message indicating that the user has logged
// out, we need to reload the page and reset the auth process. // out, we need to reload the page and reset the auth process.
if (event.data.data.logoutComplete === true) { if (event.data.data.logoutComplete) {
if (logoutResolved === false) { if (!logoutResolved) {
logoutResolve(); logoutResolve();
} }
window.location.reload(); window.location.reload();
@ -198,14 +231,14 @@ function launchKernelFrame() {
iframe.style.position = "absolute"; iframe.style.position = "absolute";
document.body.appendChild(iframe); document.body.appendChild(iframe);
kernelSource = <Window>iframe.contentWindow; kernelSource = <Window>iframe.contentWindow;
kernelOrigin = "https://kernel.lumeweb.com"; kernelOrigin = EXTENSION_HOSTED_ORIGIN;
kernelAuthLocation = "https://kernel.lumeweb.com/auth.html"; kernelAuthLocation = `${EXTENSION_HOSTED_ORIGIN}/auth.html`;
sourceResolve(); sourceResolve();
// Set a timer to fail the login process if the kernel doesn't load in // Set a timer to fail the login process if the kernel doesn't load in
// time. // time.
setTimeout(() => { setTimeout(() => {
if (initResolved === true) { if (initResolved) {
return; return;
} }
initResolved = true; initResolved = true;
@ -229,53 +262,72 @@ function messageBridge() {
}); });
p.then(([, err]) => { p.then(([, err]) => {
// Check if the timeout already elapsed. // Check if the timeout already elapsed.
if (bridgeInitComplete === true) { if (bridgeInitComplete) {
logErr("received response from bridge, but init already finished"); logErr("received response from bridge, but init already finished");
return; return;
} }
bridgeInitComplete = true; bridgeInitComplete = true;
// Bridge has responded successfully, and there's no error. // Bridge has responded successfully, and there's no error.
kernelSource = window; if (IS_EXTENSION) {
kernelOrigin = window.origin; const iframes = Array.from(
kernelAuthLocation = "http://kernel.lume/auth.html"; document.getElementsByTagName("iframe"),
console.log( ).filter((item) => item.src === EXTENSION_KERNEL_ORIGIN + "/");
"established connection to bridge, using browser extension for kernel", if (!iframes.length) {
); logErr("could not find kernel iframe");
sourceResolve(); return;
}
kernelSource = iframes[0].contentWindow as Window;
kernelOrigin = EXTENSION_KERNEL_ORIGIN;
} else {
kernelSource = window;
kernelOrigin = window.origin;
}
kernelAuthLocation = `${EXTENSION_KERNEL_ORIGIN}/auth.html`;
log("established connection to bridge, using browser extension for kernel");
if (!IS_EXTENSION) {
sourceResolve();
}
}); });
// Add the handler to the queries map. if (!IS_EXTENSION) {
const nonce = nextNonce(); // Add the handler to the queries map.
queries[nonce] = { const nonce = nextNonce();
resolve: bridgeResolve, queries[nonce] = {
}; resolve: bridgeResolve,
};
// Send a message to the bridge of the browser extension to determine // Send a message to the bridge of the browser extension to determine
// whether the bridge exists. // whether the bridge exists.
window.postMessage( window.postMessage(
{ {
nonce, nonce,
method: "kernelBridgeVersion", method: "kernelBridgeVersion",
}, },
window.origin, window.origin,
); );
// Set a timeout, if we do not hear back from the bridge in 500 // Set a timeout, if we do not hear back from the bridge in 500
// milliseconds we assume that the bridge is not available. // milliseconds we assume that the bridge is not available.
setTimeout(() => { setTimeout(() => {
// If we've already received and processed a message from the // If we've already received and processed a message from the
// bridge, there is nothing to do. // bridge, there is nothing to do.
if (bridgeInitComplete === true) { if (bridgeInitComplete) {
return; return;
} }
bridgeInitComplete = true; bridgeInitComplete = true;
if ("localhost" === window.location.hostname) { if ("localhost" === window.location.hostname) {
log("browser extension not found, falling back to lumeweb.com"); log("browser extension not found, falling back to lumeweb.com");
launchKernelFrame(); launchKernelFrame();
} }
}, 500); }, 500);
}
if (IS_EXTENSION) {
bridgeResolve([null, null]);
}
return initPromise; return initPromise;
} }
@ -303,7 +355,7 @@ let sourceResolve: () => void;
let sourcePromise: Promise<void>; // resolves when the source is known and set let sourcePromise: Promise<void>; // resolves when the source is known and set
function init(): Promise<void> { function init(): Promise<void> {
// If init has already been called, just return the init promise. // If init has already been called, just return the init promise.
if (initialized === true) { if (initialized) {
return initPromise; return initPromise;
} }
initialized = true; initialized = true;
@ -493,7 +545,7 @@ function newKernelQuery(
// promise that blocks until the sendUpdate function is ready to receive // promise that blocks until the sendUpdate function is ready to receive
// the kernel nonce. // the kernel nonce.
let sendUpdate: DataFn; let sendUpdate: DataFn;
if (sendUpdates !== true) { if (!sendUpdates) {
sendUpdate = () => {}; sendUpdate = () => {};
readyForKernelNonce(); // We won't get a kernel nonce, no reason to block. readyForKernelNonce(); // We won't get a kernel nonce, no reason to block.
} else { } else {
@ -540,7 +592,7 @@ function newKernelQuery(
nonce, nonce,
data, data,
sendKernelNonce: sendUpdates, sendKernelNonce: sendUpdates,
}; } as any;
const backgroundMessage = { const backgroundMessage = {
method: "newKernelQuery", method: "newKernelQuery",
nonce, nonce,
@ -550,7 +602,10 @@ function newKernelQuery(
// The message structure needs to adjust based on whether we are // The message structure needs to adjust based on whether we are
// talking directly to the kernel or whether we are talking to the // talking directly to the kernel or whether we are talking to the
// background page. // background page.
if (kernelOrigin === "https://kernel.lumeweb.com") { if (kernelOrigin === "https://kernel.lumeweb.com" || IS_EXTENSION) {
if (IS_EXTENSION) {
kernelMessage.domain = window.origin;
}
kernelSource.postMessage(kernelMessage, kernelOrigin); kernelSource.postMessage(kernelMessage, kernelOrigin);
} else { } else {
kernelSource.postMessage(backgroundMessage, kernelOrigin); kernelSource.postMessage(backgroundMessage, kernelOrigin);