mirror of
https://github.com/Nystik-gh/ignis.git
synced 2026-06-17 04:35:53 +00:00
prevent conflic between obsidian sync and headless sync.
This commit is contained in:
@@ -7,6 +7,7 @@ import { createFsWatch } from "./watch.js";
|
||||
import { createWatcherClient } from "./watcher-client.js";
|
||||
import { createFdOps } from "./fd.js";
|
||||
import { constants } from "./constants.js";
|
||||
import { registerReadTransform, removeReadTransform } from "./read-transforms.js";
|
||||
|
||||
const metadataCache = new MetadataCache();
|
||||
const contentCache = new ContentCache();
|
||||
@@ -43,6 +44,8 @@ export const fsShim = {
|
||||
_metadataCache: metadataCache,
|
||||
_contentCache: contentCache,
|
||||
_watcherClient: watcherClient,
|
||||
_registerReadTransform: registerReadTransform,
|
||||
_removeReadTransform: removeReadTransform,
|
||||
|
||||
async _init(basePath) {
|
||||
const tree = await transport.fetchTree(basePath);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { markLocalOp } from "./echo-guard.js";
|
||||
import { isInputCachePath, inputCacheGet } from "./input-cache.js";
|
||||
import { applyReadTransform } from "./read-transforms.js";
|
||||
|
||||
export function createFsPromises(metadataCache, contentCache, transport) {
|
||||
return {
|
||||
@@ -46,60 +47,52 @@ export function createFsPromises(metadataCache, contentCache, transport) {
|
||||
|
||||
const wantText = encoding === "utf8" || encoding === "utf-8";
|
||||
|
||||
let result = null;
|
||||
|
||||
// Check input cache for files picked via browser file dialogs.
|
||||
if (isInputCachePath(path)) {
|
||||
const inputData = inputCacheGet(path);
|
||||
|
||||
if (inputData !== null) {
|
||||
if (wantText) {
|
||||
return typeof inputData === "string"
|
||||
? inputData
|
||||
: new TextDecoder().decode(inputData);
|
||||
}
|
||||
|
||||
if (typeof inputData === "string") {
|
||||
return new TextEncoder().encode(inputData);
|
||||
}
|
||||
|
||||
return inputData;
|
||||
}
|
||||
result = inputCacheGet(path);
|
||||
}
|
||||
|
||||
const meta = metadataCache.get(path);
|
||||
if (meta && meta.type === "directory") {
|
||||
const e = new Error("EISDIR: illegal operation on a directory, read");
|
||||
e.code = "EISDIR";
|
||||
throw e;
|
||||
}
|
||||
if (result === null) {
|
||||
const meta = metadataCache.get(path);
|
||||
|
||||
if (!meta && path) {
|
||||
const e = new Error(
|
||||
`ENOENT: no such file or directory, open '${path}'`,
|
||||
);
|
||||
e.code = "ENOENT";
|
||||
throw e;
|
||||
}
|
||||
|
||||
const cached = contentCache.get(path);
|
||||
|
||||
if (cached !== null) {
|
||||
if (wantText) {
|
||||
return typeof cached === "string"
|
||||
? cached
|
||||
: new TextDecoder().decode(cached);
|
||||
if (meta && meta.type === "directory") {
|
||||
const e = new Error("EISDIR: illegal operation on a directory, read");
|
||||
e.code = "EISDIR";
|
||||
throw e;
|
||||
}
|
||||
|
||||
// binary. ensure we return a proper Uint8Array with .buffer
|
||||
if (typeof cached === "string") {
|
||||
return new TextEncoder().encode(cached);
|
||||
if (!meta && path) {
|
||||
const e = new Error(
|
||||
`ENOENT: no such file or directory, open '${path}'`,
|
||||
);
|
||||
e.code = "ENOENT";
|
||||
throw e;
|
||||
}
|
||||
|
||||
return cached;
|
||||
result = contentCache.get(path);
|
||||
}
|
||||
|
||||
const data = await transport.readFile(path, encoding);
|
||||
contentCache.set(path, data);
|
||||
return data;
|
||||
if (result === null) {
|
||||
result = await transport.readFile(path, encoding);
|
||||
contentCache.set(path, result);
|
||||
}
|
||||
|
||||
// Apply registered read transforms (e.g., patching synced config files).
|
||||
result = applyReadTransform(path, result);
|
||||
|
||||
if (wantText) {
|
||||
return typeof result === "string"
|
||||
? result
|
||||
: new TextDecoder().decode(result);
|
||||
}
|
||||
|
||||
if (typeof result === "string") {
|
||||
return new TextEncoder().encode(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
async writeFile(path, data, encoding) {
|
||||
|
||||
49
src/shims/fs/read-transforms.js
Normal file
49
src/shims/fs/read-transforms.js
Normal file
@@ -0,0 +1,49 @@
|
||||
// Post-read transforms for specific file paths.
|
||||
// Allows patching file content after reading but before returning to the caller.
|
||||
// Used to prevent synced config files from activating conflicting features.
|
||||
|
||||
const transforms = new Map();
|
||||
|
||||
function normalize(p) {
|
||||
return (p || "")
|
||||
.replace(/\\/g, "/")
|
||||
.replace(/^\/+/, "")
|
||||
.replace(/\/+$/, "");
|
||||
}
|
||||
|
||||
export function registerReadTransform(path, fn) {
|
||||
transforms.set(normalize(path), fn);
|
||||
}
|
||||
|
||||
export function removeReadTransform(path) {
|
||||
transforms.delete(normalize(path));
|
||||
}
|
||||
|
||||
export function applyReadTransform(path, data) {
|
||||
const norm = normalize(path);
|
||||
const fn = transforms.get(norm);
|
||||
|
||||
if (!fn) {
|
||||
return data;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = fn(data);
|
||||
|
||||
if (result !== data) {
|
||||
const before = typeof data === "string" ? data : new TextDecoder().decode(data);
|
||||
const after = typeof result === "string" ? result : new TextDecoder().decode(result);
|
||||
console.log(`[shim:fs] Read transform applied: ${norm}`);
|
||||
console.log(`[shim:fs] before:`, before);
|
||||
console.log(`[shim:fs] after:`, after);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export function hasReadTransform(path) {
|
||||
return transforms.has(normalize(path));
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { markLocalOp } from "./echo-guard.js";
|
||||
import { isInputCachePath, inputCacheGet } from "./input-cache.js";
|
||||
import { applyReadTransform } from "./read-transforms.js";
|
||||
|
||||
export function createFsSync(metadataCache, contentCache, transport) {
|
||||
return {
|
||||
@@ -58,6 +59,8 @@ export function createFsSync(metadataCache, contentCache, transport) {
|
||||
encoding = encoding?.encoding;
|
||||
}
|
||||
|
||||
const wantText = encoding === "utf8" || encoding === "utf-8";
|
||||
|
||||
const meta = metadataCache.get(path);
|
||||
if (meta && meta.type === "directory") {
|
||||
const e = new Error("EISDIR: illegal operation on a directory, read");
|
||||
@@ -65,39 +68,37 @@ export function createFsSync(metadataCache, contentCache, transport) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
let result = null;
|
||||
|
||||
// Check input cache for files picked via browser file dialogs.
|
||||
// These never hit the server; they exist only in browser memory.
|
||||
if (isInputCachePath(path)) {
|
||||
const inputData = inputCacheGet(path);
|
||||
|
||||
if (inputData !== null) {
|
||||
if (encoding === "utf8" || encoding === "utf-8") {
|
||||
return typeof inputData === "string"
|
||||
? inputData
|
||||
: new TextDecoder().decode(inputData);
|
||||
}
|
||||
|
||||
return inputData;
|
||||
result = inputData;
|
||||
}
|
||||
}
|
||||
|
||||
const cached = contentCache.get(path);
|
||||
if (cached !== null) {
|
||||
if (encoding === "utf8" || encoding === "utf-8") {
|
||||
return typeof cached === "string"
|
||||
? cached
|
||||
: new TextDecoder().decode(cached);
|
||||
}
|
||||
|
||||
return cached;
|
||||
if (result === null) {
|
||||
result = contentCache.get(path);
|
||||
}
|
||||
|
||||
console.warn("[shim:fs] readFileSync cache miss, using sync XHR:", path);
|
||||
if (result === null) {
|
||||
console.warn("[shim:fs] readFileSync cache miss, using sync XHR:", path);
|
||||
result = transport.readFileSync(path, encoding);
|
||||
contentCache.set(path, result);
|
||||
}
|
||||
|
||||
const data = transport.readFileSync(path, encoding);
|
||||
contentCache.set(path, data);
|
||||
// Apply registered read transforms (e.g., patching synced config files).
|
||||
result = applyReadTransform(path, result);
|
||||
|
||||
return data;
|
||||
if (wantText) {
|
||||
return typeof result === "string"
|
||||
? result
|
||||
: new TextDecoder().decode(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
writeFileSync(path, data, encoding) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { fsShim } from "./fs/index.js";
|
||||
import { installRequestUrlShim } from "./request-url.js";
|
||||
import { vaultService } from "../services/vault-service.js";
|
||||
import { showPluginInstallDialog } from "../ui/bootstrap.js";
|
||||
import { registerReadTransform } from "./fs/read-transforms.js";
|
||||
|
||||
function resolveVaultId() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
@@ -107,11 +108,71 @@ function initPluginPrompt() {
|
||||
});
|
||||
}
|
||||
|
||||
// if headless sync is active, we transform reads of core-plugins.json to hide the sync setting from Obsidian.
|
||||
// this prevents headless sync from being disabled as a result of a different device syncing "Active core plugins list".
|
||||
// i.e ensure Ignis always has sync: false if headless sync is active.
|
||||
// This may be somewhat overengineered. Could revisit later.
|
||||
function initCoreSyncGuard() {
|
||||
const vaultId = window.__currentVaultId;
|
||||
|
||||
if (!vaultId) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open("GET", "/api/plugins", false);
|
||||
xhr.send();
|
||||
|
||||
if (xhr.status !== 200) {
|
||||
return;
|
||||
}
|
||||
|
||||
const plugins = JSON.parse(xhr.responseText);
|
||||
const headlessSync = plugins.find(
|
||||
(p) => p.id === "headless-sync" && p.bundledPluginId,
|
||||
);
|
||||
|
||||
if (!headlessSync || !headlessSync.enabledVaults.includes(vaultId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
"[ignis] Headless sync active for this vault, patching core-plugins.json reads",
|
||||
);
|
||||
window.__ignisHeadlessSyncActive = true;
|
||||
|
||||
registerReadTransform(".obsidian/core-plugins.json", (data) => {
|
||||
if (!window.__ignisHeadlessSyncActive) {
|
||||
return data;
|
||||
}
|
||||
|
||||
let text =
|
||||
typeof data === "string" ? data : new TextDecoder().decode(data);
|
||||
|
||||
try {
|
||||
const config = JSON.parse(text);
|
||||
|
||||
if (config.sync === true) {
|
||||
config.sync = false;
|
||||
return JSON.stringify(config);
|
||||
}
|
||||
} catch {}
|
||||
|
||||
return data;
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn("[ignis] Failed to init core sync guard:", e);
|
||||
}
|
||||
}
|
||||
|
||||
export function initialize() {
|
||||
resolveVaultId();
|
||||
initVaultConfig();
|
||||
initVaultList();
|
||||
initMetadataCache();
|
||||
initCoreSyncGuard();
|
||||
installRequestUrlShim();
|
||||
initPluginPrompt();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user