Files
ignis/src/shims/loader.js

295 lines
7.6 KiB
JavaScript
Raw Normal View History

2026-03-07 12:23:08 +01:00
import { electronShim } from "./electron/index.js";
import { remoteShim } from "./electron/remote/index.js";
import { fsShim } from "./fs/index.js";
import { pathShim } from "./path.js";
import { urlShim } from "./url.js";
import { cryptoShim } from "./crypto/index.js";
import { processShim } from "./process.js";
import { installRequestUrlShim } from "./request-url.js";
2026-03-13 20:43:38 +01:00
import {
registerPopupWindow,
unregisterPopupWindow,
} from "./electron/remote/window.js";
import * as childProcessShim from "./node/child_process.js";
import * as eventsShim from "./node/events.js";
import * as osShim from "./node/os.js";
import * as netShim from "./node/net.js";
import * as httpShim from "./node/http.js";
import { vaultService } from "../services/vault-service.js";
import { showPluginInstallDialog, showVaultManager } from "../ui/bootstrap.js";
2026-03-07 12:23:08 +01:00
const DEBUG = true;
const _accessLog = new Map(); // "module.property" -> count
function wrapWithProxy(obj, name) {
2026-03-17 12:38:30 +01:00
if (!DEBUG || !obj || typeof obj !== "object") {
return obj;
}
2026-03-07 12:23:08 +01:00
return new Proxy(obj, {
get(target, prop) {
if (
typeof prop === "string" &&
prop !== "then" &&
prop !== "toJSON" &&
!prop.startsWith("_")
) {
const key = `${name}.${prop}`;
_accessLog.set(key, (_accessLog.get(key) || 0) + 1);
2026-03-17 12:38:30 +01:00
2026-03-07 12:23:08 +01:00
if (!(prop in target)) {
2026-03-11 22:08:30 +01:00
console.warn(`[shim:MISS] ${key} - property not found on shim`);
2026-03-07 12:23:08 +01:00
}
}
2026-03-17 12:38:30 +01:00
2026-03-07 12:23:08 +01:00
return target[prop];
},
});
}
window.__shimLog = function () {
const sorted = [..._accessLog.entries()].sort((a, b) => b[1] - a[1]);
console.table(sorted.map(([k, v]) => ({ api: k, calls: v })));
};
2026-03-17 12:38:30 +01:00
2026-03-07 12:23:08 +01:00
window.__shimMisses = function () {
const sorted = [..._accessLog.entries()]
.filter(([k]) => {
const [mod, prop] = k.split(".");
const shim = rawRegistry[mod];
return shim && !(prop in shim);
})
.sort((a, b) => b[1] - a[1]);
2026-03-17 12:38:30 +01:00
2026-03-07 12:23:08 +01:00
console.table(sorted.map(([k, v]) => ({ api: k, calls: v })));
};
const rawRegistry = {
electron: electronShim,
"@electron/remote": remoteShim,
"original-fs": fsShim,
fs: fsShim,
path: pathShim,
url: urlShim,
crypto: cryptoShim,
child_process: childProcessShim,
events: eventsShim,
os: osShim,
net: netShim,
http: httpShim,
https: httpShim,
2026-03-07 12:23:08 +01:00
};
const shimRegistry = {};
for (const [name, shim] of Object.entries(rawRegistry)) {
shimRegistry[name] = wrapWithProxy(shim, name);
}
const throwOnRequire = new Set(["btime", "get-fonts", "vibrancy-win"]);
window.require = function (moduleName) {
2026-03-23 13:04:35 +01:00
// Strip node: prefix if present
const normalizedName = moduleName.startsWith("node:")
? moduleName.slice(5)
: moduleName;
if (throwOnRequire.has(normalizedName)) {
2026-03-07 12:23:08 +01:00
throw new Error(`Cannot find module '${moduleName}'`);
}
2026-03-17 12:38:30 +01:00
2026-03-23 13:04:35 +01:00
if (shimRegistry[normalizedName]) {
return shimRegistry[normalizedName];
2026-03-07 12:23:08 +01:00
}
2026-03-17 12:38:30 +01:00
2026-03-12 22:49:51 +01:00
console.warn("[ignis] Unshimmed require:", moduleName);
2026-03-07 12:23:08 +01:00
return wrapWithProxy({}, `UNKNOWN(${moduleName})`);
};
window.process = processShim;
if (typeof window.Buffer === "undefined") {
window.Buffer = {
from: function (data, encoding) {
if (typeof data === "string") {
return new TextEncoder().encode(data);
}
2026-03-17 12:38:30 +01:00
2026-03-07 12:23:08 +01:00
if (data instanceof ArrayBuffer) {
return new Uint8Array(data);
}
2026-03-17 12:38:30 +01:00
2026-03-07 12:23:08 +01:00
return new Uint8Array(data);
},
concat: function (arrays) {
const total = arrays.reduce((sum, a) => sum + a.length, 0);
const result = new Uint8Array(total);
let offset = 0;
2026-03-17 12:38:30 +01:00
2026-03-07 12:23:08 +01:00
for (const arr of arrays) {
result.set(arr, offset);
offset += arr.length;
}
2026-03-17 12:38:30 +01:00
2026-03-07 12:23:08 +01:00
return result;
},
isBuffer: function (obj) {
return obj instanceof Uint8Array;
},
};
}
window.close = function () {
2026-03-12 22:49:51 +01:00
console.log("[ignis] window.close() blocked");
if (!window.__vaultConfig) {
showVaultManager();
}
2026-03-07 12:23:08 +01:00
};
2026-03-13 20:43:38 +01:00
window.__popupIframe = null;
const _originalOpen = window.open;
window.open = function (url, target, features) {
if (url === "about:blank" || (features && features.includes("popup"))) {
console.log("[ignis] intercepted popup:", url, features);
2026-03-17 12:38:30 +01:00
2026-03-13 20:43:38 +01:00
registerPopupWindow();
2026-03-17 12:38:30 +01:00
2026-03-13 20:43:38 +01:00
const iframe = document.createElement("iframe");
iframe.style.cssText =
"position:fixed;left:-9999px;width:0;height:0;border:none;";
2026-03-17 12:38:30 +01:00
2026-03-13 20:43:38 +01:00
document.body.appendChild(iframe);
window.__popupIframe = iframe;
2026-03-17 12:38:30 +01:00
2026-03-13 20:43:38 +01:00
const iframeWin = iframe.contentWindow;
2026-03-17 12:38:30 +01:00
2026-03-13 20:43:38 +01:00
iframeWin.require = window.require;
iframeWin.module = window.module;
iframeWin.Buffer = window.Buffer;
iframeWin.process = window.process;
iframeWin.global = iframeWin;
iframeWin.globalEnhance = window.globalEnhance;
2026-03-17 12:38:30 +01:00
2026-03-13 20:43:38 +01:00
iframeWin.close = function () {
unregisterPopupWindow();
iframe.remove();
window.__popupIframe = null;
};
2026-03-17 12:38:30 +01:00
2026-03-13 20:43:38 +01:00
return iframeWin;
}
return _originalOpen.call(window, url, target, features);
};
2026-03-17 12:38:30 +01:00
// hacky fix to prevent browser from showing context menu while allowing obsidian context menu
2026-03-10 20:49:10 +01:00
window.addEventListener(
"contextmenu",
(e) => {
e.preventDefault();
Object.defineProperty(e, "defaultPrevented", { get: () => false });
},
true,
);
2026-03-10 22:31:01 +01:00
const _urlParams = new URLSearchParams(window.location.search);
window.__currentVaultId =
_urlParams.get("vault") || localStorage.getItem("last-vault") || "";
2026-03-10 22:31:01 +01:00
(function initVaultConfig() {
try {
const vaultParam = window.__currentVaultId
? "?vault=" + encodeURIComponent(window.__currentVaultId)
: "";
2026-03-17 12:38:30 +01:00
2026-03-10 22:31:01 +01:00
const xhr = new XMLHttpRequest();
2026-03-17 12:38:30 +01:00
2026-03-10 22:31:01 +01:00
xhr.open("GET", "/api/vault/info" + vaultParam, false);
xhr.send();
2026-03-17 12:38:30 +01:00
2026-03-10 22:31:01 +01:00
if (xhr.status === 200) {
const info = JSON.parse(xhr.responseText);
2026-03-17 12:38:30 +01:00
2026-03-10 22:31:01 +01:00
window.__currentVaultId = info.id;
localStorage.setItem("last-vault", info.id);
2026-03-12 22:46:53 +01:00
window.__obsidianVersion = info.version || "0.0.0";
2026-03-17 12:38:30 +01:00
2026-03-10 22:31:01 +01:00
window.__vaultConfig = {
id: info.id,
path: "/",
};
2026-03-17 12:38:30 +01:00
window.__ignisPlugin = info.ignisPlugin || null;
2026-03-12 22:49:51 +01:00
console.log("[ignis] Vault:", window.__vaultConfig);
console.log("[ignis] Obsidian version:", window.__obsidianVersion);
2026-03-12 21:34:39 +01:00
} else {
2026-03-12 22:49:51 +01:00
console.warn("[ignis] No vault found, will show manager");
2026-03-10 22:31:01 +01:00
}
} catch (e) {
2026-03-12 22:49:51 +01:00
console.error("[ignis] Failed to fetch vault config:", e);
2026-03-10 22:31:01 +01:00
}
})();
(function initVaultList() {
try {
vaultService.listVaultsSync();
2026-03-10 22:31:01 +01:00
} catch (e) {
window.__vaultList = [];
}
})();
2026-03-07 12:23:08 +01:00
(function initMetadataCache() {
try {
2026-03-10 22:31:01 +01:00
const vaultParam = window.__currentVaultId
? "?vault=" + encodeURIComponent(window.__currentVaultId)
: "";
2026-03-17 12:38:30 +01:00
2026-03-07 12:23:08 +01:00
const xhr = new XMLHttpRequest();
2026-03-17 12:38:30 +01:00
2026-03-10 22:31:01 +01:00
xhr.open("GET", "/api/fs/tree" + vaultParam, false);
2026-03-07 12:23:08 +01:00
xhr.send();
2026-03-17 12:38:30 +01:00
2026-03-07 12:23:08 +01:00
if (xhr.status === 200) {
const tree = JSON.parse(xhr.responseText);
2026-03-17 12:38:30 +01:00
2026-03-07 12:23:08 +01:00
fsShim._metadataCache.populate(tree);
fsShim._metadataCache.set("", { type: "directory" });
fsShim._metadataCache.set("/", { type: "directory" });
2026-03-17 12:38:30 +01:00
2026-03-07 12:23:08 +01:00
console.log(
2026-03-12 22:49:51 +01:00
"[ignis] Metadata cache populated:",
2026-03-07 12:23:08 +01:00
fsShim._metadataCache.size,
"entries",
);
} else {
2026-03-12 22:49:51 +01:00
console.error("[ignis] Failed to fetch metadata tree:", xhr.status);
2026-03-07 12:23:08 +01:00
}
} catch (e) {
2026-03-12 22:49:51 +01:00
console.error("[ignis] Failed to init metadata cache:", e);
2026-03-07 12:23:08 +01:00
}
})();
installRequestUrlShim();
// Check if plugin install prompt is needed (once per session, after workspace loads)
if (
window.__ignisPlugin &&
!window.__ignisPlugin.installed &&
!window.__ignisPlugin.prompted
) {
const vaultId = window.__currentVaultId;
const observer = new MutationObserver(() => {
if (document.querySelector(".workspace")) {
observer.disconnect();
showPluginInstallDialog(vaultId);
}
});
observer.observe(document.documentElement, {
childList: true,
subtree: true,
});
}
2026-03-12 22:49:51 +01:00
console.log("[ignis] Shim loader initialized");