for automatic review

This commit is contained in:
vorotamoroz
2026-06-17 05:51:01 +01:00
parent 497fd04081
commit 2d5cdccf7d
24 changed files with 123 additions and 85 deletions
+3 -2
View File
@@ -2,14 +2,15 @@ import type { LiveSyncBaseCore } from "@/LiveSyncBaseCore";
import { P2P_DEFAULT_SETTINGS } from "@lib/common/types"; import { P2P_DEFAULT_SETTINGS } from "@lib/common/types";
import type { ServiceContext } from "@lib/services/base/ServiceBase"; import type { ServiceContext } from "@lib/services/base/ServiceBase";
import { LiveSyncTrysteroReplicator } from "@lib/replication/trystero/LiveSyncTrysteroReplicator"; import { LiveSyncTrysteroReplicator } from "@lib/replication/trystero/LiveSyncTrysteroReplicator";
import { addP2PEventHandlers } from "@lib/replication/trystero/addP2PEventHandlers"; import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
type CLIP2PPeer = { type CLIP2PPeer = {
peerId: string; peerId: string;
name: string; name: string;
}; };
function delay(ms: number): Promise<void> { function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => compatGlobal.setTimeout(resolve, ms));
} }
export function parseTimeoutSeconds(value: string, commandName: string): number { export function parseTimeoutSeconds(value: string, commandName: string): number {
+4 -3
View File
@@ -18,6 +18,7 @@ import { promptForPassphrase, readStdinAsUtf8, toArrayBuffer, toDatabaseRelative
import { collectPeers, openP2PHost, parseTimeoutSeconds, syncWithPeer } from "./p2p"; import { collectPeers, openP2PHost, parseTimeoutSeconds, syncWithPeer } from "./p2p";
import { performFullScan } from "@lib/serviceFeatures/offlineScanner"; import { performFullScan } from "@lib/serviceFeatures/offlineScanner";
import { UnresolvedErrorManager } from "@lib/services/base/UnresolvedErrorManager"; import { UnresolvedErrorManager } from "@lib/services/base/UnresolvedErrorManager";
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
function redactConnectionString(uri: string): string { function redactConnectionString(uri: string): string {
return uri.replace(/\/\/([^@/]+)@/u, "//***@"); return uri.replace(/\/\/([^@/]+)@/u, "//***@");
@@ -150,11 +151,11 @@ export async function runCommand(options: CLIOptions, context: CLICommandContext
); );
} }
} }
pollTimer = setTimeout(poll, currentIntervalMs); pollTimer = compatGlobal.setTimeout(poll, currentIntervalMs);
}; };
let pollTimer: ReturnType<typeof setTimeout> = setTimeout(poll, currentIntervalMs); let pollTimer = compatGlobal.setTimeout(poll, currentIntervalMs);
core.services.appLifecycle.onUnload.addHandler(async () => { core.services.appLifecycle.onUnload.addHandler(async () => {
clearTimeout(pollTimer); compatGlobal.clearTimeout(pollTimer);
return true; return true;
}); });
} else { } else {
+2 -1
View File
@@ -3,6 +3,7 @@ import { applyRemoteSyncSettings, initSettingsFile } from "./helpers/settings.ts
import { assertFilesEqual, runCliOrFail } from "./helpers/cli.ts"; import { assertFilesEqual, runCliOrFail } from "./helpers/cli.ts";
import { startCouchdb, stopCouchdb } from "./helpers/docker.ts"; import { startCouchdb, stopCouchdb } from "./helpers/docker.ts";
import { createDeterministicDataset, type DatasetEntry } from "./helpers/dataset.ts"; import { createDeterministicDataset, type DatasetEntry } from "./helpers/dataset.ts";
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
type BenchmarkConfig = { type BenchmarkConfig = {
couchdbBackendUri: string; couchdbBackendUri: string;
@@ -137,7 +138,7 @@ function startCouchdbProxy(options: { backendUri: string; proxyUri: string; requ
}, },
}, },
async (request) => { async (request) => {
await new Promise((resolve) => setTimeout(resolve, halfDelayMs)); await new Promise((resolve) => compatGlobal.setTimeout(resolve, halfDelayMs));
const targetUrl = new URL(request.url); const targetUrl = new URL(request.url);
targetUrl.protocol = backend.protocol; targetUrl.protocol = backend.protocol;
@@ -1,5 +1,6 @@
import { CLI_DIR, TEE_ENABLED, formatTeeCommand, createLineTeeWriter } from "./cli.ts"; import { CLI_DIR, TEE_ENABLED, formatTeeCommand, createLineTeeWriter } from "./cli.ts";
import { join } from "@std/path"; import { join } from "@std/path";
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
const CLI_DIST = join(CLI_DIR, "dist", "index.cjs"); const CLI_DIST = join(CLI_DIR, "dist", "index.cjs");
const VERBOSE_ENABLED = Deno.env.get("LIVESYNC_CLI_VERBOSE") === "1"; const VERBOSE_ENABLED = Deno.env.get("LIVESYNC_CLI_VERBOSE") === "1";
@@ -77,7 +78,9 @@ export class BackgroundCliProcess {
if (this.combined.includes(needle)) return; if (this.combined.includes(needle)) return;
const status = await Promise.race([ const status = await Promise.race([
this.child.status.then((s) => ({ type: "status" as const, status: s })), this.child.status.then((s) => ({ type: "status" as const, status: s })),
new Promise<{ type: "tick" }>((resolve) => setTimeout(() => resolve({ type: "tick" }), 100)), new Promise<{ type: "tick" }>((resolve) =>
compatGlobal.setTimeout(() => resolve({ type: "tick" }), 100)
),
]); ]);
if (status.type === "status") { if (status.type === "status") {
throw new Error( throw new Error(
+2 -1
View File
@@ -1,4 +1,5 @@
import { join } from "@std/path"; import { join } from "@std/path";
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Path resolution // Path resolution
@@ -25,7 +26,7 @@ const VERBOSE_ENABLED = Deno.env.get("LIVESYNC_CLI_VERBOSE") === "1";
const DEBUG_ENABLED = Deno.env.get("LIVESYNC_CLI_DEBUG") === "1"; const DEBUG_ENABLED = Deno.env.get("LIVESYNC_CLI_DEBUG") === "1";
function sleep(ms: number): Promise<void> { function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => compatGlobal.setTimeout(resolve, ms));
} }
function concatChunks(chunks: Uint8Array[]): Uint8Array { function concatChunks(chunks: Uint8Array[]): Uint8Array {
+3 -1
View File
@@ -1,3 +1,5 @@
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
/** /**
* Docker service management for tests. * Docker service management for tests.
* *
@@ -256,7 +258,7 @@ function untrackContainer(container: string): void {
} }
function sleep(ms: number): Promise<void> { function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => compatGlobal.setTimeout(resolve, ms));
} }
async function waitForCouchdbStable(hostname: string, user: string, password: string): Promise<void> { async function waitForCouchdbStable(hostname: string, user: string, password: string): Promise<void> {
+5 -3
View File
@@ -1,3 +1,5 @@
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
type WaitForPortOptions = { type WaitForPortOptions = {
timeoutMs?: number; timeoutMs?: number;
intervalMs?: number; intervalMs?: number;
@@ -5,7 +7,7 @@ type WaitForPortOptions = {
}; };
function sleep(ms: number): Promise<void> { function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => compatGlobal.setTimeout(resolve, ms));
} }
async function connectWithTimeout(hostname: string, port: number, timeoutMs: number): Promise<void> { async function connectWithTimeout(hostname: string, port: number, timeoutMs: number): Promise<void> {
@@ -13,13 +15,13 @@ async function connectWithTimeout(hostname: string, port: number, timeoutMs: num
try { try {
const connPromise = Deno.connect({ hostname, port }); const connPromise = Deno.connect({ hostname, port });
const timeoutPromise = new Promise<never>((_, reject) => { const timeoutPromise = new Promise<never>((_, reject) => {
timer = setTimeout(() => reject(new Error(`connect timeout after ${timeoutMs}ms`)), timeoutMs); timer = compatGlobal.setTimeout(() => reject(new Error(`connect timeout after ${timeoutMs}ms`)), timeoutMs);
}); });
const conn = await Promise.race([connPromise, timeoutPromise]); const conn = await Promise.race([connPromise, timeoutPromise]);
conn.close(); conn.close();
} finally { } finally {
if (timer !== undefined) { if (timer !== undefined) {
clearTimeout(timer); compatGlobal.clearTimeout(timer);
} }
} }
} }
+2 -1
View File
@@ -1,6 +1,7 @@
import { runCli } from "./cli.ts"; import { runCli } from "./cli.ts";
import { isLocalP2pRelay, startP2pRelay, stopP2pRelay, startCoturn, stopCoturn } from "./docker.ts"; import { isLocalP2pRelay, startP2pRelay, stopP2pRelay, startCoturn, stopCoturn } from "./docker.ts";
import { waitForPort } from "./net.ts"; import { waitForPort } from "./net.ts";
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
export type PeerEntry = { export type PeerEntry = {
id: string; id: string;
@@ -8,7 +9,7 @@ export type PeerEntry = {
}; };
function sleep(ms: number): Promise<void> { function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => compatGlobal.setTimeout(resolve, ms));
} }
function parseRelayEndpoint(relay: string): { hostname: string; port: number } { function parseRelayEndpoint(relay: string): { hostname: string; port: number } {
@@ -28,6 +28,7 @@ import { TempDir } from "./helpers/temp.ts";
import { runCliOrFail, jsonFieldIsNa } from "./helpers/cli.ts"; import { runCliOrFail, jsonFieldIsNa } from "./helpers/cli.ts";
import { applyCouchdbSettings, initSettingsFile } from "./helpers/settings.ts"; import { applyCouchdbSettings, initSettingsFile } from "./helpers/settings.ts";
import { startCouchdb, stopCouchdb } from "./helpers/docker.ts"; import { startCouchdb, stopCouchdb } from "./helpers/docker.ts";
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Load configuration // Load configuration
@@ -109,7 +110,7 @@ async function runSuite(
config: { uri: string; user: string; password: string }, config: { uri: string; user: string; password: string },
dbname: string dbname: string
): Promise<void> { ): Promise<void> {
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const sleep = (ms: number) => new Promise((resolve) => compatGlobal.setTimeout(resolve, ms));
const runWithRetry = async <T>(label: string, fn: () => Promise<T>, retries = SYNC_RETRY): Promise<T> => { const runWithRetry = async <T>(label: string, fn: () => Promise<T>, retries = SYNC_RETRY): Promise<T> => {
let lastErr: unknown; let lastErr: unknown;
for (let i = 0; i <= retries; i++) { for (let i = 0; i <= retries; i++) {
+11 -10
View File
@@ -1,11 +1,12 @@
import { LiveSyncWebApp } from "./main"; import { LiveSyncWebApp } from "./main";
import { VaultHistoryStore, type VaultHistoryItem } from "./vaultSelector"; import { VaultHistoryStore, type VaultHistoryItem } from "./vaultSelector";
import { compatGlobal, _activeDocument } from "@lib/common/coreEnvFunctions.ts";
const historyStore = new VaultHistoryStore(); const historyStore = new VaultHistoryStore();
let app: LiveSyncWebApp | null = null; let app: LiveSyncWebApp | null = null;
function getRequiredElement<T extends HTMLElement>(id: string): T { function getRequiredElement<T extends HTMLElement>(id: string): T {
const element = document.getElementById(id); const element = _activeDocument.getElementById(id);
if (!element) { if (!element) {
throw new Error(`Missing element: #${id}`); throw new Error(`Missing element: #${id}`);
} }
@@ -22,7 +23,7 @@ function setBusyState(isBusy: boolean): void {
const pickNewBtn = getRequiredElement<HTMLButtonElement>("pick-new-vault"); const pickNewBtn = getRequiredElement<HTMLButtonElement>("pick-new-vault");
pickNewBtn.disabled = isBusy; pickNewBtn.disabled = isBusy;
const historyButtons = document.querySelectorAll<HTMLButtonElement>(".vault-item button"); const historyButtons = _activeDocument.querySelectorAll<HTMLButtonElement>(".vault-item button");
historyButtons.forEach((button) => { historyButtons.forEach((button) => {
button.disabled = isBusy; button.disabled = isBusy;
}); });
@@ -45,24 +46,24 @@ async function renderHistoryList(): Promise<VaultHistoryItem[]> {
emptyEl.classList.toggle("is-hidden", items.length > 0); emptyEl.classList.toggle("is-hidden", items.length > 0);
for (const item of items) { for (const item of items) {
const row = document.createElement("div"); const row = _activeDocument.createElement("div");
row.className = "vault-item"; row.className = "vault-item";
const info = document.createElement("div"); const info = _activeDocument.createElement("div");
info.className = "vault-item-info"; info.className = "vault-item-info";
const name = document.createElement("div"); const name = _activeDocument.createElement("div");
name.className = "vault-item-name"; name.className = "vault-item-name";
name.textContent = item.name; name.textContent = item.name;
const meta = document.createElement("div"); const meta = _activeDocument.createElement("div");
meta.className = "vault-item-meta"; meta.className = "vault-item-meta";
const label = item.id === lastUsedId ? "Last used" : "Used"; const label = item.id === lastUsedId ? "Last used" : "Used";
meta.textContent = `${label}: ${formatLastUsed(item.lastUsedAt)}`; meta.textContent = `${label}: ${formatLastUsed(item.lastUsedAt)}`;
info.append(name, meta); info.append(name, meta);
const useButton = document.createElement("button"); const useButton = _activeDocument.createElement("button");
useButton.type = "button"; useButton.type = "button";
useButton.textContent = "Use this vault"; useButton.textContent = "Use this vault";
useButton.addEventListener("click", () => { useButton.addEventListener("click", () => {
@@ -120,7 +121,7 @@ async function initializeVaultSelector(): Promise<void> {
await renderHistoryList(); await renderHistoryList();
} }
window.addEventListener("load", async () => { compatGlobal.addEventListener("load", async () => {
try { try {
await initializeVaultSelector(); await initializeVaultSelector();
} catch (error) { } catch (error) {
@@ -129,11 +130,11 @@ window.addEventListener("load", async () => {
} }
}); });
window.addEventListener("beforeunload", () => { compatGlobal.addEventListener("beforeunload", () => {
void app?.shutdown(); void app?.shutdown();
}); });
(window as any).livesyncApp = { (compatGlobal as any).livesyncApp = {
getApp: () => app, getApp: () => app,
historyStore, historyStore,
}; };
+6 -5
View File
@@ -19,6 +19,7 @@ import { SetupManager } from "@/modules/features/SetupManager";
import { useSetupManagerHandlersFeature } from "@/serviceFeatures/setupObsidian/setupManagerHandlers"; import { useSetupManagerHandlersFeature } from "@/serviceFeatures/setupObsidian/setupManagerHandlers";
import { useP2PReplicatorCommands } from "@lib/replication/trystero/useP2PReplicatorCommands"; import { useP2PReplicatorCommands } from "@lib/replication/trystero/useP2PReplicatorCommands";
import { useP2PReplicatorFeature } from "@lib/replication/trystero/useP2PReplicatorFeature"; import { useP2PReplicatorFeature } from "@lib/replication/trystero/useP2PReplicatorFeature";
import { compatGlobal, _activeDocument } from "@lib/common/coreEnvFunctions.ts";
const SETTINGS_DIR = ".livesync"; const SETTINGS_DIR = ".livesync";
const SETTINGS_FILE = "settings.json"; const SETTINGS_FILE = "settings.json";
@@ -102,8 +103,8 @@ class LiveSyncWebApp {
console.log("[AppLifecycle] Restart requested"); console.log("[AppLifecycle] Restart requested");
await this.shutdown(); await this.shutdown();
await this.initialize(); await this.initialize();
setTimeout(() => { compatGlobal.setTimeout(() => {
window.location.reload(); compatGlobal.location.reload();
}, 1000); }, 1000);
}); });
@@ -235,7 +236,7 @@ class LiveSyncWebApp {
} }
private showError(message: string) { private showError(message: string) {
const statusEl = document.getElementById("status"); const statusEl = _activeDocument.getElementById("status");
if (statusEl) { if (statusEl) {
statusEl.className = "error"; statusEl.className = "error";
statusEl.textContent = `Error: ${message}`; statusEl.textContent = `Error: ${message}`;
@@ -243,7 +244,7 @@ class LiveSyncWebApp {
} }
private showWarning(message: string) { private showWarning(message: string) {
const statusEl = document.getElementById("status"); const statusEl = _activeDocument.getElementById("status");
if (statusEl) { if (statusEl) {
statusEl.className = "warning"; statusEl.className = "warning";
statusEl.textContent = `Warning: ${message}`; statusEl.textContent = `Warning: ${message}`;
@@ -251,7 +252,7 @@ class LiveSyncWebApp {
} }
private showSuccess(message: string) { private showSuccess(message: string) {
const statusEl = document.getElementById("status"); const statusEl = _activeDocument.getElementById("status");
if (statusEl) { if (statusEl) {
statusEl.className = "success"; statusEl.className = "success";
statusEl.textContent = message; statusEl.textContent = message;
@@ -11,6 +11,7 @@ import type {
} from "@lib/managers/adapters"; } from "@lib/managers/adapters";
import type { FileEventItemSentinel } from "@lib/managers/StorageEventManager"; import type { FileEventItemSentinel } from "@lib/managers/StorageEventManager";
import type { FSAPIFile, FSAPIFolder } from "@/apps/webapp/adapters/FSAPITypes"; import type { FSAPIFile, FSAPIFolder } from "@/apps/webapp/adapters/FSAPITypes";
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
/** /**
* FileSystem API-specific type guard adapter * FileSystem API-specific type guard adapter
@@ -149,14 +150,14 @@ class FSAPIWatchAdapter implements IStorageEventWatchAdapter {
async beginWatch(handlers: IStorageEventWatchHandlers): Promise<void> { async beginWatch(handlers: IStorageEventWatchHandlers): Promise<void> {
// Use FileSystemObserver if available (Chrome 124+) // Use FileSystemObserver if available (Chrome 124+)
if (typeof (window as any).FileSystemObserver === "undefined") { if (typeof (compatGlobal as any).FileSystemObserver === "undefined") {
console.log("[FSAPIWatchAdapter] FileSystemObserver not available, file watching disabled"); console.log("[FSAPIWatchAdapter] FileSystemObserver not available, file watching disabled");
console.log("[FSAPIWatchAdapter] Consider using Chrome 124+ for real-time file watching"); console.log("[FSAPIWatchAdapter] Consider using Chrome 124+ for real-time file watching");
return Promise.resolve(); return Promise.resolve();
} }
try { try {
const FileSystemObserver = (window as any).FileSystemObserver; const FileSystemObserver = (compatGlobal as any).FileSystemObserver;
this.observer = new FileSystemObserver(async (records: any[]) => { this.observer = new FileSystemObserver(async (records: any[]) => {
for (const record of records) { for (const record of records) {
+4 -3
View File
@@ -12,6 +12,7 @@
import { LiveSyncWebApp } from "./main"; import { LiveSyncWebApp } from "./main";
import type { ObsidianLiveSyncSettings } from "@lib/common/types"; import type { ObsidianLiveSyncSettings } from "@lib/common/types";
import type { FilePathWithPrefix } from "@lib/common/types"; import type { FilePathWithPrefix } from "@lib/common/types";
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Internal state one app instance per page / browser context // Internal state one app instance per page / browser context
@@ -41,7 +42,7 @@ async function waitForIdle(core: any, timeoutMs = 60_000): Promise<void> {
(core.services?.fileProcessing?.processing?.value ?? 0) + (core.services?.fileProcessing?.processing?.value ?? 0) +
(core.services?.replication?.storageApplyingCount?.value ?? 0); (core.services?.replication?.storageApplyingCount?.value ?? 0);
if (q === 0) return; if (q === 0) return;
await new Promise<void>((r) => setTimeout(r, 300)); await new Promise<void>((r) => compatGlobal.setTimeout(r, 300));
} }
throw new Error(`waitForIdle timed out after ${timeoutMs} ms`); throw new Error(`waitForIdle timed out after ${timeoutMs} ms`);
} }
@@ -116,7 +117,7 @@ export interface LiveSyncTestAPI {
const livesyncTest: LiveSyncTestAPI = { const livesyncTest: LiveSyncTestAPI = {
async init(vaultName: string, settings: Partial<ObsidianLiveSyncSettings>): Promise<void> { async init(vaultName: string, settings: Partial<ObsidianLiveSyncSettings>): Promise<void> {
// Clean up any stale OPFS data from previous runs. // Clean up any stale OPFS data from previous runs.
const opfsRoot = await navigator.storage.getDirectory(); const opfsRoot = await compatGlobal.navigator.storage.getDirectory();
try { try {
await opfsRoot.removeEntry(vaultName, { recursive: true }); await opfsRoot.removeEntry(vaultName, { recursive: true });
} catch { } catch {
@@ -200,4 +201,4 @@ const livesyncTest: LiveSyncTestAPI = {
}; };
// Expose on window for Playwright page.evaluate() calls. // Expose on window for Playwright page.evaluate() calls.
(window as any).livesyncTest = livesyncTest; (compatGlobal as any).livesyncTest = livesyncTest;
+1 -1
View File
@@ -21,7 +21,7 @@
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
/* Path mapping */ /* Path mapping */
"baseUrl": ".", // "baseUrl": ".",
"paths": { "paths": {
"@/*": ["../../*"], "@/*": ["../../*"],
"@lib/*": ["../../lib/src/*"] "@lib/*": ["../../lib/src/*"]
+3 -1
View File
@@ -1,3 +1,5 @@
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
const HANDLE_DB_NAME = "livesync-webapp-handles"; const HANDLE_DB_NAME = "livesync-webapp-handles";
const HANDLE_STORE_NAME = "handles"; const HANDLE_STORE_NAME = "handles";
const LAST_USED_KEY = "meta:lastUsedVaultId"; const LAST_USED_KEY = "meta:lastUsedVaultId";
@@ -170,7 +172,7 @@ export class VaultHistoryStore {
} }
async pickNewVault(): Promise<FileSystemDirectoryHandle> { async pickNewVault(): Promise<FileSystemDirectoryHandle> {
const picker = (window as any).showDirectoryPicker; const picker = (compatGlobal as any).showDirectoryPicker;
if (typeof picker !== "function") { if (typeof picker !== "function") {
throw new Error("FileSystem API showDirectoryPicker is not supported in this browser"); throw new Error("FileSystem API showDirectoryPicker is not supported in this browser");
} }
+7 -4
View File
@@ -17,7 +17,9 @@ import {
type PeerStatus, type PeerStatus,
type PluginShim, type PluginShim,
} from "@lib/replication/trystero/P2PReplicatorPaneCommon"; } from "@lib/replication/trystero/P2PReplicatorPaneCommon";
import { P2PLogCollector, type P2PReplicatorBase, useP2PReplicator } from "@lib/replication/trystero/P2PReplicatorCore"; import { useP2PReplicator } from "@lib/replication/trystero/P2PReplicatorCore";
import { P2PLogCollector } from "@lib/replication/trystero/P2PLogCollector";
import type { P2PReplicatorBase } from "@lib/replication/trystero/P2PReplicatorBase.ts";
import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase"; import type { SimpleStore } from "octagonal-wheels/databases/SimpleStoreBase";
import { reactiveSource } from "octagonal-wheels/dataobject/reactive_v2"; import { reactiveSource } from "octagonal-wheels/dataobject/reactive_v2";
import { EVENT_SETTING_SAVED } from "@lib/events/coreEvents"; import { EVENT_SETTING_SAVED } from "@lib/events/coreEvents";
@@ -31,6 +33,7 @@ import { SimpleStoreIDBv2 } from "octagonal-wheels/databases/SimpleStoreIDBv2";
import type { BrowserAPIService } from "@lib/services/implements/browser/BrowserAPIService"; import type { BrowserAPIService } from "@lib/services/implements/browser/BrowserAPIService";
import type { InjectableSettingService } from "@lib/services/implements/injectable/InjectableSettingService"; import type { InjectableSettingService } from "@lib/services/implements/injectable/InjectableSettingService";
import { LiveSyncTrysteroReplicator } from "@lib/replication/trystero/LiveSyncTrysteroReplicator"; import { LiveSyncTrysteroReplicator } from "@lib/replication/trystero/LiveSyncTrysteroReplicator";
import { compatGlobal } from "@lib/common/coreEnvFunctions.ts";
function addToList(item: string, list: string) { function addToList(item: string, list: string) {
return unique( return unique(
@@ -137,7 +140,7 @@ export class P2PReplicatorShim implements P2PReplicatorBase {
this._initP2PReplicator(); this._initP2PReplicator();
setTimeout(() => { compatGlobal.setTimeout(() => {
if (this.settings.P2P_AutoStart && this.settings.P2P_Enabled) { if (this.settings.P2P_AutoStart && this.settings.P2P_Enabled) {
void this.open(); void this.open();
} }
@@ -164,12 +167,12 @@ export class P2PReplicatorShim implements P2PReplicatorBase {
getConfig(key: string) { getConfig(key: string) {
const vaultName = this.services.vault.getVaultName(); const vaultName = this.services.vault.getVaultName();
const dbKey = `${vaultName}-${key}`; const dbKey = `${vaultName}-${key}`;
return localStorage.getItem(dbKey); return compatGlobal.localStorage.getItem(dbKey);
} }
setConfig(key: string, value: string) { setConfig(key: string, value: string) {
const vaultName = this.services.vault.getVaultName(); const vaultName = this.services.vault.getVaultName();
const dbKey = `${vaultName}-${key}`; const dbKey = `${vaultName}-${key}`;
localStorage.setItem(dbKey, value); compatGlobal.localStorage.setItem(dbKey, value);
} }
getDeviceName(): string { getDeviceName(): string {
+2 -1
View File
@@ -1,9 +1,10 @@
import { mount } from "svelte"; import { mount } from "svelte";
import "./app.css"; import "./app.css";
import App from "./App.svelte"; import App from "./App.svelte";
import { _activeDocument } from "@lib/common/coreEnvFunctions.ts";
const app = mount(App, { const app = mount(App, {
target: document.getElementById("app")!, target: _activeDocument.getElementById("app")!,
}); });
export default app; export default app;
+2 -1
View File
@@ -1,9 +1,10 @@
import { mount } from "svelte"; import { mount } from "svelte";
import "./app.css"; import "./app.css";
import App from "./UITest.svelte"; import App from "./UITest.svelte";
import { _activeDocument } from "@lib/common/coreEnvFunctions.ts";
const app = mount(App, { const app = mount(App, {
target: document.getElementById("app")!, target: _activeDocument.getElementById("app")!,
}); });
export default app; export default app;
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"extends": "../../../tsconfig.json", "extends": "../../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"sourceRoot": "../", // "sourceRoot": "../",
"target": "ESNext", "target": "ESNext",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"module": "ESNext", "module": "ESNext",
+1 -1
View File
@@ -19,7 +19,7 @@ export class PluginDialogModal extends Modal {
this.contentEl.setCssStyles({ this.contentEl.setCssStyles({
overflow: "auto", overflow: "auto",
display: "flex", display: "flex",
flexDirection: "column" flexDirection: "column",
}); });
this.titleEl.setText("Customization Sync (Beta3)"); this.titleEl.setText("Customization Sync (Beta3)");
if (!this.component) { if (!this.component) {
+1 -1
Submodule src/lib updated: 3dad9565aa...7157dbc62c
+3 -3
View File
@@ -194,7 +194,7 @@ export class MessageBox<T extends readonly string[]> extends AutoClosableModal {
const div = contentEl.createDiv(); const div = contentEl.createDiv();
div.setCssStyles({ div.setCssStyles({
userSelect: "text", userSelect: "text",
"webkitUserSelect": "text" webkitUserSelect: "text",
}); });
void MarkdownRenderer.render(this.plugin.app, this.contentMd, div, "/", this.plugin); void MarkdownRenderer.render(this.plugin.app, this.contentMd, div, "/", this.plugin);
const buttonSetting = new Setting(contentEl); const buttonSetting = new Setting(contentEl);
@@ -214,7 +214,7 @@ export class MessageBox<T extends readonly string[]> extends AutoClosableModal {
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
flexGrow: "1" flexGrow: "1",
}); });
} }
contentEl.addEventListener("click", () => { contentEl.addEventListener("click", () => {
@@ -244,7 +244,7 @@ export class MessageBox<T extends readonly string[]> extends AutoClosableModal {
if (this.wideButton) { if (this.wideButton) {
btn.buttonEl.setCssStyles({ btn.buttonEl.setCssStyles({
flexGrow: "1", flexGrow: "1",
width: "100%" width: "100%",
}); });
} }
return btn; return btn;
+32 -21
View File
@@ -45,7 +45,7 @@ const TARGET_GLOBALS = new Set([
"navigator", "navigator",
"location", "location",
"document", "document",
"window" "window",
]); ]);
let modifiedFilesCount = 0; let modifiedFilesCount = 0;
@@ -59,22 +59,13 @@ for (const sourceFile of project.getSourceFiles()) {
continue; continue;
} }
// Exclude submodule files under src/lib/ // Exclude coreEnvFunctions.ts to avoid self-referential definitions
if (posixFilePath.startsWith(posixLibSrc)) { if (posixFilePath.endsWith("/coreEnvFunctions.ts") || posixFilePath.endsWith("/coreEnvFunctions")) {
continue;
}
// Exclude independent application modules under src/apps/
if (posixFilePath.startsWith(`${posixSrc}/apps/`)) {
continue; continue;
} }
// Exclude unit and integration test files // Exclude unit and integration test files
if ( if (posixFilePath.endsWith(".spec.ts") || posixFilePath.endsWith(".test.ts") || posixFilePath.includes("/_test/")) {
posixFilePath.endsWith(".spec.ts") ||
posixFilePath.endsWith(".test.ts") ||
posixFilePath.includes("/_test/")
) {
continue; continue;
} }
@@ -101,6 +92,14 @@ for (const sourceFile of project.getSourceFiles()) {
} }
} }
// 1.5. Skip if it is the right-hand side of a QualifiedName (e.g. the "requestAnimationFrame" in "typeof compatGlobal.requestAnimationFrame")
if (parent.getKind() === SyntaxKind.QualifiedName) {
const qualified = parent.asKindOrThrow(SyntaxKind.QualifiedName);
if (qualified.getRight() === idNode) {
continue;
}
}
// 2. Skip if it is the operand of a typeof expression (e.g. "typeof window") // 2. Skip if it is the operand of a typeof expression (e.g. "typeof window")
if (parent.getKind() === SyntaxKind.TypeOfExpression) { if (parent.getKind() === SyntaxKind.TypeOfExpression) {
continue; continue;
@@ -153,6 +152,8 @@ for (const sourceFile of project.getSourceFiles()) {
let replacement = ""; let replacement = "";
if (name === "window" || name === "globalThis") { if (name === "window" || name === "globalThis") {
replacement = "compatGlobal"; replacement = "compatGlobal";
} else if (name === "document") {
replacement = "_activeDocument";
} else { } else {
replacement = `compatGlobal.${name}`; replacement = `compatGlobal.${name}`;
} }
@@ -174,23 +175,33 @@ for (const sourceFile of project.getSourceFiles()) {
node.replaceWithText(replacement); node.replaceWithText(replacement);
} }
// Ensure compatGlobal is imported // Determine what needs to be imported based on replacements
const hasCompatGlobalImport = sourceFile.getImportDeclarations().some((imp) => { const needsCompatGlobal = nodesToReplace.some((r) => r.replacement.includes("compatGlobal"));
return imp.getNamedImports().some((ni) => ni.getName() === "compatGlobal"); const needsActiveDocument = nodesToReplace.some((r) => r.replacement.includes("_activeDocument"));
});
if (!hasCompatGlobalImport) { const requiredImports: string[] = [];
if (needsCompatGlobal) requiredImports.push("compatGlobal");
if (needsActiveDocument) requiredImports.push("_activeDocument");
if (requiredImports.length > 0) {
const existingImport = sourceFile.getImportDeclarations().find((imp) => { const existingImport = sourceFile.getImportDeclarations().find((imp) => {
const spec = imp.getModuleSpecifierValue(); const spec = imp.getModuleSpecifierValue();
return spec === "@lib/common/coreEnvFunctions" || spec === "@lib/common/coreEnvFunctions.ts"; return spec === "@lib/common/coreEnvFunctions" || spec === "@lib/common/coreEnvFunctions.ts";
}); });
if (existingImport) { if (existingImport) {
existingImport.addNamedImport("compatGlobal"); for (const nameToImport of requiredImports) {
const alreadyImported = existingImport
.getNamedImports()
.some((ni) => ni.getName() === nameToImport);
if (!alreadyImported) {
existingImport.addNamedImport(nameToImport);
}
}
} else { } else {
sourceFile.addImportDeclaration({ sourceFile.addImportDeclaration({
namedImports: ["compatGlobal"], namedImports: requiredImports,
moduleSpecifier: "@lib/common/coreEnvFunctions.ts" moduleSpecifier: "@lib/common/coreEnvFunctions.ts",
}); });
} }
} }
+19 -16
View File
@@ -41,7 +41,7 @@ function matchStyleAccess(node: Node): { element: Node; propertyName: string; is
return { return {
element: expr.getExpression(), element: expr.getExpression(),
propertyName: node.getName(), propertyName: node.getName(),
isComputed: false isComputed: false,
}; };
} }
} else if (Node.isElementAccessExpression(node)) { } else if (Node.isElementAccessExpression(node)) {
@@ -52,7 +52,7 @@ function matchStyleAccess(node: Node): { element: Node; propertyName: string; is
return { return {
element: expr.getExpression(), element: expr.getExpression(),
propertyName: arg.getText(), propertyName: arg.getText(),
isComputed: true isComputed: true,
}; };
} }
} }
@@ -74,7 +74,7 @@ function getStyleAssignment(statement: Node) {
property: styleAccess.propertyName, property: styleAccess.propertyName,
valueText: expr.getRight().getText(), valueText: expr.getRight().getText(),
isComputed: styleAccess.isComputed, isComputed: styleAccess.isComputed,
statementNode: statement statementNode: statement,
}; };
} }
@@ -100,11 +100,7 @@ for (const sourceFile of project.getSourceFiles()) {
} }
// Exclude unit and integration test files // Exclude unit and integration test files
if ( if (posixFilePath.endsWith(".spec.ts") || posixFilePath.endsWith(".test.ts") || posixFilePath.includes("/_test/")) {
posixFilePath.endsWith(".spec.ts") ||
posixFilePath.endsWith(".test.ts") ||
posixFilePath.includes("/_test/")
) {
continue; continue;
} }
@@ -126,12 +122,14 @@ for (const sourceFile of project.getSourceFiles()) {
if (assignment) { if (assignment) {
const currentGroup: StyleGroup = { const currentGroup: StyleGroup = {
elementText: assignment.elementText, elementText: assignment.elementText,
assignments: [{ assignments: [
property: assignment.property, {
valueText: assignment.valueText, property: assignment.property,
isComputed: assignment.isComputed, valueText: assignment.valueText,
statementNode: assignment.statementNode isComputed: assignment.isComputed,
}] statementNode: assignment.statementNode,
},
],
}; };
// Look ahead to collect consecutive assignments to the same element // Look ahead to collect consecutive assignments to the same element
@@ -143,7 +141,7 @@ for (const sourceFile of project.getSourceFiles()) {
property: nextAssignment.property, property: nextAssignment.property,
valueText: nextAssignment.valueText, valueText: nextAssignment.valueText,
isComputed: nextAssignment.isComputed, isComputed: nextAssignment.isComputed,
statementNode: nextAssignment.statementNode statementNode: nextAssignment.statementNode,
}); });
j++; j++;
} else { } else {
@@ -190,7 +188,12 @@ for (const sourceFile of project.getSourceFiles()) {
const { line } = sourceFile.getLineAndColumnAtPos(firstNode.getStart()); const { line } = sourceFile.getLineAndColumnAtPos(firstNode.getStart());
console.log(` Line ${line}: Replacing consecutive style assignments on "${group.elementText}" with:`); console.log(` Line ${line}: Replacing consecutive style assignments on "${group.elementText}" with:`);
console.log(newText.split("\n").map((l) => ` ${l}`).join("\n")); console.log(
newText
.split("\n")
.map((l) => ` ${l}`)
.join("\n")
);
if (!isDryRun) { if (!isDryRun) {
firstNode.replaceWithText(newText); firstNode.replaceWithText(newText);