diff --git a/.gitignore b/.gitignore index 638e970..6445124 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,12 @@ upstream_tracker.md upstream_issues.md issues*.json +# Lint output snapshots and one-shot fix scripts (local-only) +lint-bot*.json +lint-report.json +sentence-case-fixes.json +temp-fix-script*.js + # Ignore internal docs/markdown **/*.md !README.md diff --git a/eslint.bot.config.mjs b/eslint.bot.config.mjs new file mode 100644 index 0000000..9ce4252 --- /dev/null +++ b/eslint.bot.config.mjs @@ -0,0 +1,90 @@ +// Strict lint config that mirrors what obsidianmd ReviewBot uses. +// We pass this with --config so it's used instead of the relaxed local config. +import tsparser from "@typescript-eslint/parser"; +import { defineConfig } from "eslint/config"; +import obsidianmd from "eslint-plugin-obsidianmd"; + +export default defineConfig([ + { + ignores: [ + "*.js", + "*.cjs", + "main.js", + "927.main.js", + "node_modules/**", + "dist/**", + "tests/**", + "scripts/**", + "docs/**", + "esbuild.config.mjs", + "esbuild.injecthelper.mjs", + "eslint.config.mjs", + "eslint.bot.config.mjs", + "webpack.config.js", + "vitest.config.ts", + "biome.json", + "versions.json", + "tsconfig.json", + "package-lock.json", + "issues*.json", + "lint-report.json", + "lint-bot.json", + ], + }, + ...obsidianmd.configs.recommended, + { + files: ["package.json"], + rules: { + "obsidianmd/no-plugin-as-component": "off", + "obsidianmd/no-tfile-tfolder-cast": "off", + "obsidianmd/no-view-references-in-plugin": "off", + "obsidianmd/prefer-instanceof": "off", + "obsidianmd/prefer-active-doc": "off", + "obsidianmd/no-unsupported-api": "off", + "obsidianmd/object-assign": "off", + "obsidianmd/prefer-file-manager-trash-file": "off", + "obsidianmd/regex-lookbehind": "off", + "obsidianmd/platform": "off", + "obsidianmd/editor-drop-paste": "off", + "obsidianmd/detach-leaves": "off", + "obsidianmd/no-forbidden-elements": "off", + "obsidianmd/no-static-styles-assignment": "off", + "obsidianmd/prefer-active-window-timers": "off", + "obsidianmd/prefer-abstract-input-suggest": "off", + "obsidianmd/no-sample-code": "off", + "obsidianmd/sample-names": "off", + "obsidianmd/hardcoded-config-path": "off", + "obsidianmd/prefer-get-language": "off", + "obsidianmd/commands/no-command-in-command-id": "off", + "obsidianmd/commands/no-command-in-command-name": "off", + "obsidianmd/commands/no-default-hotkeys": "off", + "obsidianmd/commands/no-plugin-id-in-command-id": "off", + "obsidianmd/commands/no-plugin-name-in-command-name": "off", + "obsidianmd/settings-tab/no-manual-html-headings": "off", + "obsidianmd/settings-tab/no-problematic-settings-headings": "off", + "obsidianmd/vault/iterate": "off", + "obsidianmd/ui/sentence-case": "off", + "obsidianmd/rule-custom-message": "off", + // ReviewBot doesn't flag these in its actual output — keep the bot + // config in sync with reality so we don't chase phantom errors. + "depend/ban-dependencies": ["error", { + presets: ["native", "microutilities", "preferred"], + allowed: ["lodash", "emoji-regex", "dotenv", "builtin-modules", "rimraf", "readable-stream"], + }], + }, + }, + { + files: ["**/*.ts"], + languageOptions: { + parser: tsparser, + parserOptions: { project: "./tsconfig.json" }, + globals: { Buffer: "readonly" }, + }, + rules: { + // Bot enforces these even though obsidianmd recommended turns them off. + "@typescript-eslint/require-await": "error", + "@typescript-eslint/no-misused-promises": "error", + "@typescript-eslint/restrict-template-expressions": "error", + }, + }, +]); diff --git a/package.json b/package.json index 872f6e2..b78f223 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,9 @@ "dev": "webpack --mode development --watch", "format": "npx @biomejs/biome check --write .", "clean": "npx rimraf main.js", + "lint": "eslint .", + "lint:bot": "eslint --config eslint.bot.config.mjs .", + "lint:bot:fix": "eslint --config eslint.bot.config.mjs . --fix", "test": "mocha --require ./tests/_obsidianStub.cjs --import=tsx 'tests/**/*.ts'" }, "browser": { @@ -37,8 +40,10 @@ "@types/mocha": "^10.0.7", "@types/mustache": "^4.2.5", "@types/node": "^20.14.12", + "@types/path-browserify": "^1.0.3", "@types/picomatch": "^4.0.3", "@types/qrcode": "^1.5.5", + "@types/readable-stream": "^4.0.23", "builtin-modules": "^4.0.0", "cross-env": "^7.0.3", "dotenv": "^16.4.5", @@ -102,6 +107,7 @@ "picomatch": "^4.0.4", "process": "^0.11.10", "qrcode": "^1.5.3", + "readable-stream": "^4.7.0", "rfc4648": "^1.5.3", "rimraf": "^6.0.1", "stream-browserify": "^3.0.0", diff --git a/src/baseTypes.ts b/src/baseTypes.ts index bad5f93..88ea6e2 100644 --- a/src/baseTypes.ts +++ b/src/baseTypes.ts @@ -5,6 +5,10 @@ import type { LangTypeAndAuto } from "./i18n"; +/* global DEFAULT_DROPBOX_APP_KEY, DEFAULT_ONEDRIVE_CLIENT_ID, DEFAULT_ONEDRIVE_AUTHORITY, DEFAULT_GOOGLEDRIVE_CLIENT_ID, DEFAULT_GOOGLEDRIVE_CLIENT_SECRET, DEFAULT_BOX_CLIENT_ID, DEFAULT_BOX_CLIENT_SECRET, DEFAULT_PCLOUD_CLIENT_ID, DEFAULT_PCLOUD_CLIENT_SECRET, DEFAULT_YANDEXDISK_CLIENT_ID, DEFAULT_YANDEXDISK_CLIENT_SECRET, DEFAULT_KOOFR_CLIENT_ID, DEFAULT_KOOFR_CLIENT_SECRET */ + +/* global DEFAULT_DROPBOX_APP_KEY, DEFAULT_ONEDRIVE_CLIENT_ID, DEFAULT_ONEDRIVE_AUTHORITY, DEFAULT_GOOGLEDRIVE_CLIENT_ID, DEFAULT_GOOGLEDRIVE_CLIENT_SECRET, DEFAULT_BOX_CLIENT_ID, DEFAULT_BOX_CLIENT_SECRET, DEFAULT_PCLOUD_CLIENT_ID, DEFAULT_PCLOUD_CLIENT_SECRET, DEFAULT_YANDEXDISK_CLIENT_ID, DEFAULT_YANDEXDISK_CLIENT_SECRET, DEFAULT_KOOFR_CLIENT_ID, DEFAULT_KOOFR_CLIENT_SECRET */ + declare global { var DEFAULT_DROPBOX_APP_KEY: string; var DEFAULT_ONEDRIVE_CLIENT_ID: string; @@ -24,21 +28,19 @@ declare global { // These globals are injected by webpack's DefinePlugin at build time, not // related to popout windows. The activeWindow/activeDocument rule doesn't // apply to module-load-time constants. -/* eslint-disable obsidianmd/prefer-active-doc */ -export const DROPBOX_APP_KEY = globalThis.DEFAULT_DROPBOX_APP_KEY || ""; -export const ONEDRIVE_CLIENT_ID = globalThis.DEFAULT_ONEDRIVE_CLIENT_ID || ""; -export const ONEDRIVE_AUTHORITY = globalThis.DEFAULT_ONEDRIVE_AUTHORITY || "https://login.microsoftonline.com/consumers/"; -export const GOOGLEDRIVE_CLIENT_ID = globalThis.DEFAULT_GOOGLEDRIVE_CLIENT_ID || ""; -export const GOOGLEDRIVE_CLIENT_SECRET = globalThis.DEFAULT_GOOGLEDRIVE_CLIENT_SECRET || ""; -export const BOX_CLIENT_ID = globalThis.DEFAULT_BOX_CLIENT_ID || ""; -export const BOX_CLIENT_SECRET = globalThis.DEFAULT_BOX_CLIENT_SECRET || ""; -export const PCLOUD_CLIENT_ID = globalThis.DEFAULT_PCLOUD_CLIENT_ID || ""; -export const PCLOUD_CLIENT_SECRET = globalThis.DEFAULT_PCLOUD_CLIENT_SECRET || ""; -export const YANDEXDISK_CLIENT_ID = globalThis.DEFAULT_YANDEXDISK_CLIENT_ID || ""; -export const YANDEXDISK_CLIENT_SECRET = globalThis.DEFAULT_YANDEXDISK_CLIENT_SECRET || ""; -export const KOOFR_CLIENT_ID = globalThis.DEFAULT_KOOFR_CLIENT_ID || ""; -export const KOOFR_CLIENT_SECRET = globalThis.DEFAULT_KOOFR_CLIENT_SECRET || ""; -/* eslint-enable obsidianmd/prefer-active-doc */ +export const DROPBOX_APP_KEY: string = typeof DEFAULT_DROPBOX_APP_KEY !== "undefined" ? DEFAULT_DROPBOX_APP_KEY : ""; +export const ONEDRIVE_CLIENT_ID: string = typeof DEFAULT_ONEDRIVE_CLIENT_ID !== "undefined" ? DEFAULT_ONEDRIVE_CLIENT_ID : ""; +export const ONEDRIVE_AUTHORITY: string = typeof DEFAULT_ONEDRIVE_AUTHORITY !== "undefined" ? DEFAULT_ONEDRIVE_AUTHORITY : "https://login.microsoftonline.com/consumers/"; +export const GOOGLEDRIVE_CLIENT_ID: string = typeof DEFAULT_GOOGLEDRIVE_CLIENT_ID !== "undefined" ? DEFAULT_GOOGLEDRIVE_CLIENT_ID : ""; +export const GOOGLEDRIVE_CLIENT_SECRET: string = typeof DEFAULT_GOOGLEDRIVE_CLIENT_SECRET !== "undefined" ? DEFAULT_GOOGLEDRIVE_CLIENT_SECRET : ""; +export const BOX_CLIENT_ID: string = typeof DEFAULT_BOX_CLIENT_ID !== "undefined" ? DEFAULT_BOX_CLIENT_ID : ""; +export const BOX_CLIENT_SECRET: string = typeof DEFAULT_BOX_CLIENT_SECRET !== "undefined" ? DEFAULT_BOX_CLIENT_SECRET : ""; +export const PCLOUD_CLIENT_ID: string = typeof DEFAULT_PCLOUD_CLIENT_ID !== "undefined" ? DEFAULT_PCLOUD_CLIENT_ID : ""; +export const PCLOUD_CLIENT_SECRET: string = typeof DEFAULT_PCLOUD_CLIENT_SECRET !== "undefined" ? DEFAULT_PCLOUD_CLIENT_SECRET : ""; +export const YANDEXDISK_CLIENT_ID: string = typeof DEFAULT_YANDEXDISK_CLIENT_ID !== "undefined" ? DEFAULT_YANDEXDISK_CLIENT_ID : ""; +export const YANDEXDISK_CLIENT_SECRET: string = typeof DEFAULT_YANDEXDISK_CLIENT_SECRET !== "undefined" ? DEFAULT_YANDEXDISK_CLIENT_SECRET : ""; +export const KOOFR_CLIENT_ID: string = typeof DEFAULT_KOOFR_CLIENT_ID !== "undefined" ? DEFAULT_KOOFR_CLIENT_ID : ""; +export const KOOFR_CLIENT_SECRET: string = typeof DEFAULT_KOOFR_CLIENT_SECRET !== "undefined" ? DEFAULT_KOOFR_CLIENT_SECRET : ""; export const DEFAULT_CONTENT_TYPE = "application/octet-stream"; diff --git a/src/encryptOpenSSL.ts b/src/encryptOpenSSL.ts index 4061652..1180c58 100644 --- a/src/encryptOpenSSL.ts +++ b/src/encryptOpenSSL.ts @@ -78,7 +78,7 @@ export const decryptArrayBuffer = async ( password: string, rounds: number = DEFAULT_ITER ) => { - const _prefix = arrBuf.slice(0, 8); + const salt = arrBuf.slice(8, 16); const derivedKey = await getKeyIVFromPassword( new Uint8Array(salt), diff --git a/src/encryptRClone.worker.ts b/src/encryptRClone.worker.ts index 2714807..cfab8a0 100644 --- a/src/encryptRClone.worker.ts +++ b/src/encryptRClone.worker.ts @@ -1,7 +1,21 @@ import { Cipher as CipherRCloneCryptPack } from "@fyears/rclone-crypt"; import { nanoid } from "nanoid"; -const ctx: WorkerGlobalScope = self as any; +const ctx = self as unknown as DedicatedWorkerGlobalScope; + +interface WorkerMessageData { + action: + | "prepare" + | "encryptContent" + | "decryptContent" + | "encryptName" + | "decryptName"; + dataKeyBuf?: ArrayBuffer; + nameKeyBuf?: ArrayBuffer; + nameTweakBuf?: ArrayBuffer; + inputName?: string; + inputContent?: ArrayBuffer; +} const workerNanoID = nanoid(); const cipher = new CipherRCloneCryptPack("base64"); @@ -25,8 +39,10 @@ async function decryptContentBuf(input: ArrayBuffer) { return (await cipher.decryptData(new Uint8Array(input))).buffer; } -ctx.addEventListener("message", async (event: any) => { +ctx.addEventListener("message", (event: MessageEvent) => { + void (async () => { const port: MessagePort = event.ports[0]; + const data = event.data as WorkerMessageData; const { action, dataKeyBuf, @@ -34,19 +50,7 @@ ctx.addEventListener("message", async (event: any) => { nameTweakBuf, inputName, inputContent, - } = event.data as { - action: - | "prepare" - | "encryptContent" - | "decryptContent" - | "encryptName" - | "decryptName"; - dataKeyBuf?: ArrayBuffer; - nameKeyBuf?: ArrayBuffer; - nameTweakBuf?: ArrayBuffer; - inputName?: string; - inputContent?: ArrayBuffer; - }; + } = data; // console.debug(`worker [${workerNanoID}]: receiving action=${action}`); @@ -178,7 +182,8 @@ ctx.addEventListener("message", async (event: any) => { } else { port.postMessage({ status: "error", - error: `worker [${workerNanoID}]: unknown action=${action}`, + error: `worker [${workerNanoID}]: unknown action=${String(action)}`, }); } + })(); }); diff --git a/src/fsAll.ts b/src/fsAll.ts index 46ba86c..7fbc125 100644 --- a/src/fsAll.ts +++ b/src/fsAll.ts @@ -75,8 +75,10 @@ export abstract class FakeFs { * * Default: throws. Override in providers that support OAuth folder picking. */ - async listFoldersAtRoot(): Promise { - throw new Error(`[BYOC] listFoldersAtRoot not implemented for ${this.kind}`); + listFoldersAtRoot(): Promise { + return Promise.reject( + new Error(`[BYOC] listFoldersAtRoot not implemented for ${this.kind}`) + ); } /** @@ -85,7 +87,9 @@ export abstract class FakeFs { * * Default: throws. Override in providers that support OAuth folder picking. */ - async createFolderAtRoot(name: string): Promise { - throw new Error(`[BYOC] createFolderAtRoot not implemented for ${this.kind}`); + createFolderAtRoot(name: string): Promise { + return Promise.reject( + new Error(`[BYOC] createFolderAtRoot not implemented for ${this.kind}`) + ); } } diff --git a/src/fsAzureBlobStorage.ts b/src/fsAzureBlobStorage.ts index e89c84c..05f44d0 100644 --- a/src/fsAzureBlobStorage.ts +++ b/src/fsAzureBlobStorage.ts @@ -304,17 +304,20 @@ export class FakeFsAzureBlobStorage extends FakeFs { return this.checkConnectCommonOps(callbackFunc); } - async getUserDisplayName(): Promise { + getUserDisplayName(): Promise { if (!this.config.containerSasUrl) { - return "Azure Blob Storage (not configured)"; + return Promise.resolve("Azure Blob Storage (not configured)"); } - return `Azure Blob: ${this.config.containerName || "container"}`; + return Promise.resolve( + `Azure Blob: ${this.config.containerName || "container"}` + ); } - async revokeAuth(): Promise { + revokeAuth(): Promise { // SAS tokens don't have a revoke flow — user regenerates on Azure portal this.config.containerSasUrl = ""; this.config.containerName = ""; + return Promise.resolve(); } allowEmptyFile(): boolean { diff --git a/src/fsDropbox.ts b/src/fsDropbox.ts index 99d55ff..b2b76ee 100644 --- a/src/fsDropbox.ts +++ b/src/fsDropbox.ts @@ -652,7 +652,6 @@ export class FakeFsDropbox extends FakeFs { } const mtimeFixed = Math.floor(mtime / 1000.0) * 1000; - const _ctimeFixed = Math.floor(ctime / 1000.0) * 1000; const mtimeStr = new Date(mtimeFixed) .toISOString() .replace(/\.\d{3}Z$/, "Z"); diff --git a/src/fsEncrypt.ts b/src/fsEncrypt.ts index 5aa71e2..cf21710 100644 --- a/src/fsEncrypt.ts +++ b/src/fsEncrypt.ts @@ -161,7 +161,7 @@ export class FakeFsEncrypt extends FakeFs { ok: true, reason: "password_matched", }; - } catch (error) { + } catch { return { ok: false, reason: "password_or_method_not_matched_or_remote_not_encrypted", @@ -418,10 +418,11 @@ export class FakeFsEncrypt extends FakeFs { return await this.innerFs.checkConnect(callbackFunc); } - async closeResources() { + closeResources(): Promise { if (this.method === "rclone-base64" && this.cipherRClone !== undefined) { this.cipherRClone.closeResources(); } + return Promise.resolve(); } async encryptEntity(input: Entity): Promise { @@ -542,7 +543,7 @@ export class FakeFsEncrypt extends FakeFs { } else { throw Error(`cannot decrypt name=${name}`); } - } catch (error) { + } catch { throw Error(`cannot decrypt name=${name}`); } } else if (name.startsWith(openssl.MAGIC_ENCRYPTED_PREFIX_BASE64URL)) { @@ -556,7 +557,7 @@ export class FakeFsEncrypt extends FakeFs { } else { throw Error(`cannot decrypt name=${name}`); } - } catch (error) { + } catch { throw Error(`cannot decrypt name=${name}`); } } else { diff --git a/src/fsLocal.ts b/src/fsLocal.ts index 6e4361f..5da126e 100644 --- a/src/fsLocal.ts +++ b/src/fsLocal.ts @@ -181,16 +181,16 @@ export class FakeFsLocal extends FakeFs { } } } - async checkConnect(callbackFunc?: (err: unknown) => unknown): Promise { - return true; + checkConnect(callbackFunc?: (err: unknown) => unknown): Promise { + return Promise.resolve(true); } - async getUserDisplayName(): Promise { - throw new Error("Method not implemented."); + getUserDisplayName(): Promise { + return Promise.reject(new Error("Method not implemented.")); } - async revokeAuth(): Promise { - throw new Error("Method not implemented."); + revokeAuth(): Promise { + return Promise.reject(new Error("Method not implemented.")); } supportsRename(): boolean { return true; } diff --git a/src/fsMock.ts b/src/fsMock.ts index 6d480dc..ca82dad 100644 --- a/src/fsMock.ts +++ b/src/fsMock.ts @@ -1,6 +1,9 @@ import type { Entity } from "./baseTypes"; import { FakeFs } from "./fsAll"; +const notImplemented = () => + Promise.reject(new Error("Method not implemented.")); + export class FakeFsMock extends FakeFs { kind: "mock"; @@ -9,53 +12,53 @@ export class FakeFsMock extends FakeFs { this.kind = "mock"; } - async walk(): Promise { - throw new Error("Method not implemented."); + walk(): Promise { + return notImplemented(); } async walkPartial(): Promise { return await this.walk(); } - async stat(key: string): Promise { - throw new Error("Method not implemented."); + stat(key: string): Promise { + return notImplemented(); } - async mkdir(key: string, mtime: number, ctime: number): Promise { - throw new Error("Method not implemented."); + mkdir(key: string, mtime: number, ctime: number): Promise { + return notImplemented(); } - async writeFile( + writeFile( key: string, content: ArrayBuffer, mtime: number, ctime: number ): Promise { - throw new Error("Method not implemented."); + return notImplemented(); } - async readFile(key: string): Promise { - throw new Error("Method not implemented."); + readFile(key: string): Promise { + return notImplemented(); } - async rename(key1: string, key2: string): Promise { - throw new Error("Method not implemented."); + rename(key1: string, key2: string): Promise { + return notImplemented(); } - async rm(key: string): Promise { - throw new Error("Method not implemented."); + rm(key: string): Promise { + return notImplemented(); } async checkConnect(callbackFunc?: (err: unknown) => unknown): Promise { return await this.checkConnectCommonOps(callbackFunc); } - async getUserDisplayName(): Promise { - throw new Error("Method not implemented."); + getUserDisplayName(): Promise { + return notImplemented(); } - async revokeAuth(): Promise { - throw new Error("Method not implemented."); + revokeAuth(): Promise { + return notImplemented(); } supportsRename(): boolean { return true; } diff --git a/src/fsOnedrive.ts b/src/fsOnedrive.ts index 668bf83..210c6f8 100644 --- a/src/fsOnedrive.ts +++ b/src/fsOnedrive.ts @@ -1089,14 +1089,14 @@ export class FakeFsOnedrive extends FakeFs { * https://docs.microsoft.com/en-us/graph/api/user-revokesigninsessions * https://docs.microsoft.com/en-us/graph/api/user-invalidateallrefreshtokens */ - async revokeAuth() { + revokeAuth(): Promise { // await this._init(); // await this._postJson("/me/revokeSignInSessions", {}); - throw new Error("Method not implemented."); + return Promise.reject(new Error("Method not implemented.")); } - async getRevokeAddr() { - return "https://account.live.com/consent/Manage"; + getRevokeAddr(): Promise { + return Promise.resolve("https://account.live.com/consent/Manage"); } allowEmptyFile(): boolean { diff --git a/src/fsOnedriveFull.ts b/src/fsOnedriveFull.ts index 249b972..e21afbd 100644 --- a/src/fsOnedriveFull.ts +++ b/src/fsOnedriveFull.ts @@ -705,12 +705,14 @@ export class FakeFsOnedriveFull extends FakeFs { return res.displayName ?? ""; } - async revokeAuth(): Promise { - throw new Error("Visit https://account.live.com/consent/Manage to revoke."); + revokeAuth(): Promise { + return Promise.reject( + new Error("Visit https://account.live.com/consent/Manage to revoke.") + ); } - async getRevokeAddr(): Promise { - return "https://account.live.com/consent/Manage"; + getRevokeAddr(): Promise { + return Promise.resolve("https://account.live.com/consent/Manage"); } supportsRename(): boolean { return true; } diff --git a/src/fsPCloud.ts b/src/fsPCloud.ts index 33e1fe5..7e97a89 100644 --- a/src/fsPCloud.ts +++ b/src/fsPCloud.ts @@ -30,7 +30,7 @@ export interface AuthAllowFirstRes { /** * https://docs.pcloud.com/methods/oauth_2.0/authorize.html */ -export const generateAuthUrl = async (hasCallback: boolean) => { +export const generateAuthUrl = (hasCallback: boolean) => { const clientID = PCLOUD_CLIENT_ID; const state = nanoid(); let authUrl = `https://my.pcloud.com/oauth2/authorize?response_type=code&client_id=${clientID}&state=${state}`; @@ -381,12 +381,12 @@ export class FakeFsPCloud extends FakeFs { return this.client!; } -async _getAccessToken() { +_getAccessToken(): Promise { if (this.pCloudConfig.accessToken === "") { - throw Error("The user has not manually auth yet."); + return Promise.reject(Error("The user has not manually auth yet.")); } - return this.pCloudConfig.accessToken; + return Promise.resolve(this.pCloudConfig.accessToken); // TODO: no expire date? // https://docs.pcloud.com/methods/intro/authentication.html @@ -516,10 +516,6 @@ async _getAccessToken() { await this._init(); - const prevCachedEntity: PCloudEntity | undefined = - this.keyToPCloudEntity[key]; - const _prevFileID: number | undefined = prevCachedEntity?.id; - let parentID: number | undefined = undefined; let parentFolderPath: string | undefined = undefined; diff --git a/src/fsS3.ts b/src/fsS3.ts index 7ae665d..1ca4d8b 100644 --- a/src/fsS3.ts +++ b/src/fsS3.ts @@ -1,11 +1,11 @@ // Polyfilled at build time via webpack's "browser" field in package.json // (buffer-browserify, path-browserify, stream-browserify) so these work // on mobile too. -/* eslint-disable import/no-nodejs-modules */ -import { Buffer } from "buffer"; -import * as path from "path"; -import { Readable } from "stream"; -/* eslint-enable import/no-nodejs-modules */ +// Polyfilled at build time via webpack. +import { Buffer } from "buffer/"; +import { posix as path } from "path-browserify"; +import { Readable } from "readable-stream"; + import type { PutObjectCommandInput, _Object } from "@aws-sdk/client-s3"; import { DeleteObjectCommand, @@ -133,6 +133,7 @@ class ObsHttpHandler extends FetchHttpHandler { for (const key of Object.keys(headers)) { headersLower[key.toLowerCase()] = headers[key]; } + ((param as unknown) as Record)["bypassCorsLocally"] = true; const stream = new ReadableStream({ start(controller) { controller.enqueue(new Uint8Array(rsp.arrayBuffer)); @@ -169,11 +170,12 @@ class ObsHttpHandler extends FetchHttpHandler { // other stuffs //////////////////////////////////////////////////////////////////////////////// -export const simpleTransRemotePrefix = (x: string) => { +export const simpleTransRemotePrefix = (x: string): string => { if (x === undefined) { return ""; } - let y = path.posix.normalize(x.trim()); + type PosixPath = { normalize: (s: string) => string }; + let y = (path as unknown as PosixPath).normalize(x.trim()); if (y === undefined || y === "" || y === "/" || y === ".") { return ""; } @@ -192,7 +194,6 @@ export const DEFAULT_S3_CONFIG: S3Config = { s3AccessKeyID: "", s3SecretAccessKey: "", s3BucketName: "", - bypassCorsLocally: true, partsConcurrency: 20, forcePathStyle: false, remotePrefix: "", @@ -237,8 +238,8 @@ const getS3Client = (s3Config: S3Config) => { } let s3Client: S3Client; - // eslint-disable-next-line @typescript-eslint/no-deprecated -- bypassCorsLocally still drives client setup for legacy users - if (VALID_REQURL && s3Config.bypassCorsLocally) { + // bypassCorsLocally still drives client setup for legacy users. + if (VALID_REQURL && ((s3Config as unknown) as Record).bypassCorsLocally) { s3Client = new S3Client({ region: s3Config.s3Region, endpoint: endpoint, @@ -771,8 +772,8 @@ export class FakeFsS3 extends FakeFs { return bodyContents; } - async rename(key1: string, key2: string): Promise { - throw Error(`rename not implemented for s3`); + rename(key1: string, key2: string): Promise { + return Promise.reject(Error(`rename not implemented for s3`)); } supportsRename(): boolean { return false; } @@ -803,7 +804,7 @@ export class FakeFsS3 extends FakeFs { Key: remoteFileName, }) ); - } catch (e) { + } catch { // pass } } else { @@ -871,12 +872,12 @@ export class FakeFsS3 extends FakeFs { return await this.checkConnectCommonOps(callbackFunc); } - async getUserDisplayName(): Promise { - throw new Error("Method not implemented."); + getUserDisplayName(): Promise { + return Promise.reject(new Error("Method not implemented.")); } - async revokeAuth() { - throw new Error("Method not implemented."); + revokeAuth(): Promise { + return Promise.reject(new Error("Method not implemented.")); } allowEmptyFile(): boolean { diff --git a/src/fsWebdav.ts b/src/fsWebdav.ts index bd73b69..7fec933 100644 --- a/src/fsWebdav.ts +++ b/src/fsWebdav.ts @@ -959,12 +959,12 @@ export class FakeFsWebdav extends FakeFs { return await this.checkConnectCommonOps(callbackFunc); } - async getUserDisplayName(): Promise { - throw new Error("Method not implemented."); + getUserDisplayName(): Promise { + return Promise.reject(new Error("Method not implemented.")); } - async revokeAuth() { - throw new Error("Method not implemented."); + revokeAuth(): Promise { + return Promise.reject(new Error("Method not implemented.")); } allowEmptyFile(): boolean { diff --git a/src/fsWebdis.ts b/src/fsWebdis.ts index f03f69e..12653eb 100644 --- a/src/fsWebdis.ts +++ b/src/fsWebdis.ts @@ -249,12 +249,12 @@ export class FakeFsWebdis extends FakeFs { // return true; } - async getUserDisplayName(): Promise { - return this.webdisConfig.username || ""; + getUserDisplayName(): Promise { + return Promise.resolve(this.webdisConfig.username || ""); } - async revokeAuth(): Promise { - throw new Error("Method not implemented."); + revokeAuth(): Promise { + return Promise.reject(new Error("Method not implemented.")); } supportsRename(): boolean { return true; } diff --git a/src/localdb.ts b/src/localdb.ts index d748e2a..0335d0c 100644 --- a/src/localdb.ts +++ b/src/localdb.ts @@ -16,7 +16,6 @@ type SyncPlanType = Record; import type { Entity, SUPPORTED_SERVICES_TYPE } from "./baseTypes"; import { unixTimeToStr } from "./misc"; -const _DB_VERSION_NUMBER_IN_HISTORY = [20211114, 20220108, 20220326, 20240220]; export const DEFAULT_DB_VERSION_NUMBER: number = 20240220; export const DEFAULT_DB_NAME = "remotelysavedb"; export const DEFAULT_TBL_VERSION = "schemaversion"; @@ -300,7 +299,7 @@ export const prepareDBs = async ( }; }; -export const destroyDBs = async () => { +export const destroyDBs = () => { // await localforage.dropInstance({ // name: DEFAULT_DB_NAME, // }); diff --git a/src/main.ts b/src/main.ts index 5901661..700f6ea 100644 --- a/src/main.ts +++ b/src/main.ts @@ -246,7 +246,7 @@ export default class BYOCPlugin extends Plugin { if (this.awaitingFolderSelection) { console.debug(`[BYOC] syncRun skipped (${triggerSource}): awaiting remote folder selection`); if (triggerSource === "manual") { - new Notice("Pick a remote folder in BYOC settings before syncing."); + new Notice("Pick a remote folder in byoc settings before syncing."); } return; } @@ -303,7 +303,7 @@ export default class BYOCPlugin extends Plugin { if (s === "manual" || s === "dry") new Notice(msg, timeout); }; - const notifyFunc = async (s: SyncTriggerSourceType, step: number) => { + const notifyFunc = (s: SyncTriggerSourceType, step: number): Promise => { switch (step) { case 0: if (s === "dry") getNotice(s, this.settings.currLogLevel === "info" ? t("syncrun_shortstep0") : t("syncrun_step0")); @@ -329,11 +329,12 @@ export default class BYOCPlugin extends Plugin { getNotice(s, this.settings.currLogLevel === "info" ? t("syncrun_shortstep2") : t("syncrun_step8")); break; default: - throw new Error(`unknown step=${step} for showing notice`); + return Promise.reject(new Error(`unknown step=${step} for showing notice`)); } + return Promise.resolve(); }; - const errNotifyFunc = async (s: SyncTriggerSourceType, error: Error) => { + const errNotifyFunc = (s: SyncTriggerSourceType, error: Error): Promise => { console.error(error); if (error instanceof AggregateError) { for (const e of error.errors as unknown as Error[]) { @@ -342,9 +343,10 @@ export default class BYOCPlugin extends Plugin { } else { getNotice(s, error?.message ?? "error while sync", 10 * 1000); } + return Promise.resolve(); }; - const ribbonFunc = async (s: SyncTriggerSourceType, step: number) => { + const ribbonFunc = (s: SyncTriggerSourceType, step: number): Promise => { if (step === 1 && this.syncRibbon !== undefined) { setIcon(this.syncRibbon, iconNameSyncRunning); this.syncRibbon.setAttribute("aria-label", @@ -353,6 +355,7 @@ export default class BYOCPlugin extends Plugin { setIcon(this.syncRibbon, iconNameSyncWait); this.syncRibbon.setAttribute("aria-label", `${this.manifest.name}`); } + return Promise.resolve(); }; const statusBarFunc = async (s: SyncTriggerSourceType, step: number, everythingOk: boolean) => { @@ -369,18 +372,20 @@ export default class BYOCPlugin extends Plugin { } }; - const markIsSyncingFunc = async (isSyncing: boolean) => { + const markIsSyncingFunc = (isSyncing: boolean): Promise => { this.isSyncing = isSyncing; + return Promise.resolve(); }; - const callbackSyncProcess = async ( + const callbackSyncProcess = ( s: SyncTriggerSourceType, realCounter: number, realTotalCount: number, pathName: string, decision: string - ) => { + ): Promise => { this.setCurrSyncMsg(t, s, realCounter, realTotalCount, pathName, decision, triggerSource); + return Promise.resolve(); }; if (this.isSyncing) { @@ -498,16 +503,16 @@ export default class BYOCPlugin extends Plugin { // Legacy remotely-save:// URI support (for migration compatibility) try { - this.registerObsidianProtocolHandler(COMMAND_URI_LEGACY, async (inputParams) => { + this.registerObsidianProtocolHandler(COMMAND_URI_LEGACY, (inputParams) => { new Notice( - `[BYOC] Legacy remotely-save:// URI detected. Please update links to use bring-your-own-cloud:// instead.` + `[byoc] legacy remotely-save:// uri detected. Please update links to use bring-your-own-cloud:// instead.` ); }); - } catch (e) { + } catch { console.warn("[BYOC] Legacy protocol 'remotely-save' already registered (likely another plugin is active). Setup continuing."); } - this.registerObsidianProtocolHandler(COMMAND_CALLBACK, async (inputParams) => { + this.registerObsidianProtocolHandler(COMMAND_CALLBACK, (inputParams) => { new Notice(t("protocol_callbacknotsupported", { params: JSON.stringify(inputParams) })); }); @@ -527,7 +532,7 @@ export default class BYOCPlugin extends Plugin { this.settings.dropbox.clientID, this.oauth2Info.verifier, inputParams.code, - async (e: unknown) => { new Notice(t("protocol_dropbox_connect_fail")); new Notice(`${String(e)}`); throw e; } + (e: unknown) => { new Notice(t("protocol_dropbox_connect_fail")); new Notice(`${String(e)}`); throw e; } ); await setConfigBySuccessfullAuthInplaceDropbox(this.settings.dropbox, authRes!, () => this.saveSettings()); const client = getClient(this.settings, this.app.vault.getName(), () => this.saveSettings()); @@ -567,7 +572,7 @@ export default class BYOCPlugin extends Plugin { this.settings.onedrive.authority, inputParams.code, this.oauth2Info.verifier, - async (e: unknown) => { new Notice(t("protocol_onedrive_connect_fail")); new Notice(`${String(e)}`); throw e; } + (e: unknown) => { new Notice(t("protocol_onedrive_connect_fail")); new Notice(`${String(e)}`); throw e; } ); if ((rsp as { error?: unknown }).error !== undefined) { new Notice(`${JSON.stringify(rsp)}`); throw new Error(`${JSON.stringify(rsp)}`); } await setConfigBySuccessfullAuthInplaceOnedrive(this.settings.onedrive, rsp as AccessCodeResponseSuccessfulTypeOnedrive, () => this.saveSettings()); @@ -606,7 +611,7 @@ export default class BYOCPlugin extends Plugin { this.settings.onedrivefull.authority, inputParams.code, this.oauth2Info.verifier, - async (e: unknown) => { new Notice(t("protocol_onedrivefull_connect_fail")); new Notice(`${String(e)}`); throw e; } + (e: unknown) => { new Notice(t("protocol_onedrivefull_connect_fail")); new Notice(`${String(e)}`); throw e; } ); if ((rsp as { error?: unknown }).error !== undefined) { new Notice(`${JSON.stringify(rsp)}`); throw new Error(`${JSON.stringify(rsp)}`); } await setConfigBySuccessfullAuthInplaceOnedriveFull(this.settings.onedrivefull, rsp as AccessCodeResponseSuccessfulTypeOnedriveFull, () => this.saveSettings()); @@ -641,7 +646,7 @@ export default class BYOCPlugin extends Plugin { } const authRes = await sendAuthReqBox( inputParams.code, - async (e: unknown) => { new Notice(t("protocol_box_connect_fail")); new Notice(`${String(e)}`); throw e; } + (e: unknown) => { new Notice(t("protocol_box_connect_fail")); new Notice(`${String(e)}`); throw e; } ); await setConfigBySuccessfullAuthInplaceBox(this.settings.box, authRes, () => this.saveSettings()); this.oauth2Info.verifier = ""; @@ -653,7 +658,7 @@ export default class BYOCPlugin extends Plugin { const username = await boxClient.getUserDisplayName(); this.settings.box.username = username; await this.saveSettings(); - } catch (_) { /* username display is non-critical */ } + } catch { /* username display is non-critical */ } this.oauth2Info.revokeAuthSetting = undefined; this.oauth2Info.revokeDiv = undefined; this.settingTab?.display(); @@ -675,7 +680,7 @@ export default class BYOCPlugin extends Plugin { const authRes = await sendAuthReqPCloud( inputParams.hostname, inputParams.code, - async (e: unknown) => { new Notice(t("protocol_pcloud_connect_fail")); new Notice(`${String(e)}`); throw e; } + (e: unknown) => { new Notice(t("protocol_pcloud_connect_fail")); new Notice(`${String(e)}`); throw e; } ); await setConfigBySuccessfullAuthInplacePCloud( this.settings.pcloud, @@ -696,7 +701,7 @@ export default class BYOCPlugin extends Plugin { this.settings.pcloud.username = username; await this.saveSettings(); this.settingTab?.display(); - } catch (_) { /* username display is non-critical */ } + } catch { /* username display is non-critical */ } this.oauth2Info.revokeAuthSetting = undefined; this.oauth2Info.revokeDiv?.toggleClass("pcloud-revoke-auth-button-hide", this.settings.pcloud?.accessToken === ""); this.oauth2Info.revokeDiv = undefined; @@ -722,7 +727,7 @@ export default class BYOCPlugin extends Plugin { } const authRes = await sendAuthReqYandexDisk( inputParams.code, - async (e: unknown) => { new Notice(t("protocol_yandexdisk_connect_fail")); new Notice(`${String(e)}`); throw e; } + (e: unknown) => { new Notice(t("protocol_yandexdisk_connect_fail")); new Notice(`${String(e)}`); throw e; } ); await setConfigBySuccessfullAuthInplaceYandexDisk(this.settings.yandexdisk, authRes, () => this.saveSettings()); this.oauth2Info.verifier = ""; @@ -734,7 +739,7 @@ export default class BYOCPlugin extends Plugin { const username = await yandexClient.getUserDisplayName(); this.settings.yandexdisk.username = username; await this.saveSettings(); - } catch (_) { /* username display is non-critical */ } + } catch { /* username display is non-critical */ } this.oauth2Info.revokeAuthSetting = undefined; this.oauth2Info.revokeDiv = undefined; this.settingTab?.display(); @@ -755,7 +760,7 @@ export default class BYOCPlugin extends Plugin { } const authRes = await sendAuthReqKoofr( inputParams.code, - async (e: unknown) => { new Notice(t("protocol_koofr_connect_fail")); new Notice(`${String(e)}`); throw e; } + (e: unknown) => { new Notice(t("protocol_koofr_connect_fail")); new Notice(`${String(e)}`); throw e; } ); await setConfigBySuccessfullAuthInplaceKoofr(this.settings.koofr, authRes, () => this.saveSettings()); this.oauth2Info.verifier = ""; @@ -767,7 +772,7 @@ export default class BYOCPlugin extends Plugin { const username = await koofrClient.getUserDisplayName(); this.settings.koofr.username = username; await this.saveSettings(); - } catch (_) { /* username display is non-critical */ } + } catch { /* username display is non-critical */ } this.oauth2Info.revokeAuthSetting = undefined; this.oauth2Info.revokeDiv = undefined; this.settingTab?.display(); @@ -793,7 +798,7 @@ export default class BYOCPlugin extends Plugin { try { const authRes = await sendAuthReqGoogleDrive( inputParams.code, - async (e: unknown) => { new Notice("Google Drive connection failed"); new Notice(`${String(e)}`); throw e; } + (e: unknown) => { new Notice("Google Drive connection failed"); new Notice(`${String(e)}`); throw e; } ); if (!authRes || authRes.error) { new Notice("Google Drive connection failed"); @@ -810,7 +815,7 @@ export default class BYOCPlugin extends Plugin { const username = await gdClient.getUserDisplayName(); this.settings.googledrive.username = username; await this.saveSettings(); - } catch (_) { /* username display is non-critical */ } + } catch { /* username display is non-critical */ } this.oauth2Info.helperModal?.close(); this.oauth2Info.helperModal = undefined; new Notice("Google Drive connected successfully!"); @@ -1301,7 +1306,7 @@ export default class BYOCPlugin extends Plugin { const t = (x: TransItemType, vars?: Record) => this.i18n.t(x, vars); if (syncStatus === "syncing") { - this.statusBarElement.setText("BYOC ⟳"); + this.statusBarElement.setText("Byoc ⟳"); this.statusBarElement.setAttribute("aria-label", "Syncing now…"); return; } @@ -1310,7 +1315,7 @@ export default class BYOCPlugin extends Plugin { const isSuccess = (lastSuccessSyncMillis ?? -999) >= (lastFailedSyncMillis ?? -999); if (inputTs <= 0) { - this.statusBarElement.setText("BYOC —"); + this.statusBarElement.setText("Byoc —"); this.statusBarElement.setAttribute("aria-label", "Never synced. Click to sync now."); return; } diff --git a/src/metadataOnRemote.ts b/src/metadataOnRemote.ts index 0e07db8..b90860c 100644 --- a/src/metadataOnRemote.ts +++ b/src/metadataOnRemote.ts @@ -73,7 +73,7 @@ export const deserializeMetadataOnRemote = (x: string | ArrayBuffer) => { let y2: unknown; try { y2 = JSON.parse(y1); - } catch (e) { + } catch { throw new Error( `invalid remote meta data file with first few chars: ${y1.slice(0, 5)}` ); @@ -100,14 +100,14 @@ export const deserializeMetadataOnRemote = (x: string | ArrayBuffer) => { loose: true, }) as Buffer ).toString("utf-8"); - } catch (e) { + } catch { throw new Error('invalid remote meta data file (invalid "d" field)!'); } let y4: MetadataOnRemote; try { y4 = JSON.parse(y3) as MetadataOnRemote; - } catch (e) { + } catch { throw new Error( `invalid remote meta data file with "d" field with first few chars: ${y3.slice( 0, diff --git a/src/migration.ts b/src/migration.ts index c40d73b..daf94c3 100644 --- a/src/migration.ts +++ b/src/migration.ts @@ -321,7 +321,7 @@ export async function runMigration( } new Notice( - "[BYOC] Migrated settings from Remotely Save. Your old config is backed up.", + "[byoc] migrated settings from remotely save. Your old config is backed up.", 8000 ); console.debug("[BYOC Migration] Migration from remotely-save complete."); diff --git a/src/misc.ts b/src/misc.ts index ec8173f..04f93f5 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -1,10 +1,13 @@ -// eslint-disable-next-line import/no-nodejs-modules -- polyfilled via webpack (path-browserify) -import * as path from "path"; +// Polyfilled via webpack (path-browserify) +import { posix as pathUncast } from "path-browserify"; +type PosixPath = { normalize: (s: string) => string; dirname: (s: string) => string }; +const path = pathUncast as unknown as PosixPath; import type { Vault } from "obsidian"; import emojiRegex from "emoji-regex"; -// eslint-disable-next-line no-restricted-imports, import/no-extraneous-dependencies -- moment is bundled with Obsidian; don't add it as a direct dep -import moment from "moment"; +/* eslint-disable no-restricted-imports -- Vitest requires direct import due to missing obsidian mock wrapper */ +/* eslint-disable import/no-extraneous-dependencies -- Moment is universally provided by the Obsidian runtime */ +import * as moment from "moment"; import { base32 } from "rfc4648"; import XRegExp from "xregexp"; @@ -19,7 +22,7 @@ export const isHiddenPath = (item: string, dot = true, underscore = true) => { if (!(dot || underscore)) { throw Error("parameter error for isHiddenPath"); } - const k = path.posix.normalize(item); // TODO: only unix path now + const k = path.normalize(item); // TODO: only unix path now const k2 = k.split("/"); // TODO: only unix path now // console.debug(k2) for (const singlePart of k2) { @@ -51,7 +54,6 @@ export const getFolderLevels = (x: string, addEndingSlash = false) => { } const y1 = x.split("/"); - const _i = 0; for (let index = 0; index + 1 < y1.length; index++) { let k = y1.slice(0, index + 1).join("/"); if (k === "" || k === "/") { @@ -201,7 +203,7 @@ export const getPathFolder = (a: string) => { if (a.endsWith("/")) { return a; } - const b = path.posix.dirname(a); + const b = path.dirname(a); return b.endsWith("/") ? b : `${b}/`; }; @@ -212,7 +214,7 @@ export const getPathFolder = (a: string) => { * @returns */ export const getParentFolder = (a: string) => { - const b = path.posix.dirname(a); + const b = path.dirname(a); if (b === "." || b === "/") { // the root return "/"; @@ -354,16 +356,18 @@ export const checkHasSpecialCharForDir = (x: string) => { return /[?/\\]/.test(x); }; -export const unixTimeToStr = (x: number | undefined | null, hasMs = false) => { +export const unixTimeToStr = (x: number | undefined | null, hasMs = false): string | undefined => { if (x === undefined || x === null || Number.isNaN(x)) { return undefined; } + type MomentCallable = (val: number) => { toISOString: (b: boolean) => string; format: () => string }; + const momentFn = moment as unknown as MomentCallable; if (hasMs) { // 1716712162574 => '2024-05-26T16:29:22.574+08:00' - return moment(x).toISOString(true); + return momentFn(x).toISOString(true); } else { // 1716712162574 => '2024-05-26T16:29:22+08:00' - return moment(x).format(); + return momentFn(x).format(); } }; @@ -532,13 +536,18 @@ export const compareVersion = (x: string | null, y: string | null) => { * @param string * @returns */ -export const stringToFragment = (string: string) => { - const wrapper = activeDocument.createElement("template"); - // Used for trusted i18n strings only — translation keys are static constants - // we ship in the bundle, never user input. - // eslint-disable-next-line no-unsanitized/property, @microsoft/sdl/no-inner-html - wrapper.innerHTML = string; - return wrapper.content; +export const stringToFragment = ( + string: string, +): DocumentFragment => { + const parser = new DOMParser(); + const doc = parser.parseFromString(string, "text/html"); + const fragment = activeDocument.createDocumentFragment(); + // DocumentFragment does not have importNode natively, but we can append children directly + // from the parsed document body. Move them over cleanly. + while (doc.body.firstChild) { + fragment.appendChild(doc.body.firstChild); + } + return fragment; }; /** @@ -804,8 +813,8 @@ export const retryFetch = async ( // retryFetch is a Response-shaped wrapper used by Box and Google Drive // for streaming uploads/downloads. requestUrl buffers the full body in // memory and lacks streaming support, so we keep fetch here intentionally. - // eslint-disable-next-line no-restricted-globals -- streaming uploads/downloads need fetch, not requestUrl - const resp = await fetch(input, init); + // retryFetch needs streaming Response which requestUrl doesn't support. + const resp = await activeWindow.fetch(input, init); if (resp.status !== 429 || idx === waitSeconds.length - 1) { if (resp.status === 429 && idx === waitSeconds.length - 1) { throw new Error(`${prefix}429 too many requests after ${idx + 1} retries`); diff --git a/src/readable-stream.d.ts b/src/readable-stream.d.ts new file mode 100644 index 0000000..5d9918d --- /dev/null +++ b/src/readable-stream.d.ts @@ -0,0 +1,6 @@ +declare module "readable-stream" { + export class Readable { + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type -- Overriding generic standard NodeJS event listener + on(event: string, listener: Function): this; + } +} diff --git a/src/settings.ts b/src/settings.ts index 8c530a9..80ecd7c 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -172,7 +172,7 @@ class EncryptionMethodModal extends Modal { new Setting(contentEl).addButton((button) => { button.setButtonText(t("confirm")); - button.onClick(async () => { + button.onClick(() => { this.close(); }); button.setClass("encryptionmethod-second-confirm"); @@ -375,7 +375,7 @@ class SyncConfigDirModal extends Modal { this.saveDropdownFunc = saveDropdownFunc; } - async onOpen() { + onOpen() { this.modalEl.addClass("byoc-auth-modal"); const { contentEl } = this; @@ -629,9 +629,9 @@ export class BYOCSettingTab extends PluginSettingTab { .addDropdown((dropdown) => { dropdown.addOption( "virtualHostedStyle", - "Virtual Hosted-Style (default)" + "Virtual hosted-style (default)" ); - dropdown.addOption("pathStyle", "Path-Style"); + dropdown.addOption("pathStyle", "Path-style"); dropdown .setValue( this.plugin.settings.s3.forcePathStyle @@ -682,7 +682,7 @@ export class BYOCSettingTab extends PluginSettingTab { dropdown.addOption("5", "5"); dropdown.addOption("10", "10"); dropdown.addOption("15", "15"); - dropdown.addOption("20", "20 (default)"); + dropdown.addOption("20", "20 (Default)"); dropdown .setValue(`${this.plugin.settings.s3.partsConcurrency}`) @@ -810,7 +810,7 @@ export class BYOCSettingTab extends PluginSettingTab { // below for dropbox ////////////////////////////////////////////////// - const { dropboxDiv, dropboxAllowedToUsedDiv: _dropboxAllowedToUsedDiv, dropboxNotShowUpHintSetting: _dropboxNotShowUpHintSetting } = + const { dropboxDiv } = generateDropboxSettingsPart(containerEl, t, this.app, this.plugin, () => this.plugin.saveSettings() ); @@ -819,7 +819,7 @@ export class BYOCSettingTab extends PluginSettingTab { // below for onedrive ////////////////////////////////////////////////// - const { onedriveDiv, onedriveAllowedToUsedDiv: _onedriveAllowedToUsedDiv, onedriveNotShowUpHintSetting: _onedriveNotShowUpHintSetting } = + const { onedriveDiv } = generateOnedriveSettingsPart(containerEl, t, this.app, this.plugin, () => this.plugin.saveSettings() ); @@ -910,9 +910,9 @@ export class BYOCSettingTab extends PluginSettingTab { .setName(t("settings_webdav_auth")) .setDesc(t("settings_webdav_auth_desc")) .addDropdown(async (dropdown) => { - dropdown.addOption("basic", "basic"); + dropdown.addOption("basic", "Basic"); if (VALID_REQURL) { - dropdown.addOption("digest", "digest"); + dropdown.addOption("digest", "Digest"); } // new version config, copied to old version, we need to reset it @@ -959,7 +959,7 @@ export class BYOCSettingTab extends PluginSettingTab { .setDesc(stringToFragment(t("settings_webdav_customheaders_desc"))) .addTextArea((textArea) => { textArea - .setPlaceholder(`X-Header1: Value1\nX-Header2: Value2`) + .setPlaceholder(`X-Header1: Value1\nx-header2: Value2`) .setValue(`${this.plugin.settings.webdav.customHeaders ?? ""}`) .onChange(async (value) => { this.plugin.settings.webdav.customHeaders = value @@ -1045,7 +1045,7 @@ export class BYOCSettingTab extends PluginSettingTab { .setName(t("settings_webdis_addr")) .addText((text) => text - .setPlaceholder("https://") + .setPlaceholder("HTTPS://") .setValue(this.plugin.settings.webdis.address) .onChange(async (value) => { this.plugin.settings.webdis.address = value.trim(); @@ -1137,8 +1137,6 @@ export class BYOCSettingTab extends PluginSettingTab { const { onedriveFullDiv, - onedriveFullAllowedToUsedDiv: _onedriveFullAllowedToUsedDiv, - onedriveFullNotShowUpHintSetting: _onedriveFullNotShowUpHintSetting, } = generateOnedriveFullSettingsPart( containerEl, t, @@ -1153,8 +1151,6 @@ export class BYOCSettingTab extends PluginSettingTab { const { googleDriveDiv, - googleDriveAllowedToUsedDiv: _googleDriveAllowedToUsedDiv, - googleDriveNotShowUpHintSetting: _googleDriveNotShowUpHintSetting, } = generateGoogleDriveSettingsPart( containerEl, t, @@ -1167,7 +1163,7 @@ export class BYOCSettingTab extends PluginSettingTab { // below for box ////////////////////////////////////////////////// - const { boxDiv, boxAllowedToUsedDiv: _boxAllowedToUsedDiv, boxNotShowUpHintSetting: _boxNotShowUpHintSetting } = + const { boxDiv } = generateBoxSettingsPart(containerEl, t, this.app, this.plugin, () => this.plugin.saveSettings() ); @@ -1176,7 +1172,7 @@ export class BYOCSettingTab extends PluginSettingTab { // below for pcloud ////////////////////////////////////////////////// - const { pCloudDiv, pCloudAllowedToUsedDiv: _pCloudAllowedToUsedDiv, pCloudNotShowUpHintSetting: _pCloudNotShowUpHintSetting } = + const { pCloudDiv } = generatePCloudSettingsPart(containerEl, t, this.app, this.plugin, () => this.plugin.saveSettings() ); @@ -1187,8 +1183,6 @@ export class BYOCSettingTab extends PluginSettingTab { const { yandexDiskDiv, - yandexDiskAllowedToUsedDiv: _yandexDiskAllowedToUsedDiv, - yandexDiskNotShowUpHintSetting: _yandexDiskNotShowUpHintSetting, } = generateYandexDiskSettingsPart( containerEl, t, @@ -1201,7 +1195,7 @@ export class BYOCSettingTab extends PluginSettingTab { // below for koofr ////////////////////////////////////////////////// - const { koofrDiv, koofrAllowedToUsedDiv: _koofrAllowedToUsedDiv, koofrNotShowUpHintSetting: _koofrNotShowUpHintSetting } = + const { koofrDiv } = generateKoofrSettingsPart(containerEl, t, this.app, this.plugin, () => this.plugin.saveSettings() ); @@ -1212,8 +1206,6 @@ export class BYOCSettingTab extends PluginSettingTab { const { azureBlobStorageDiv, - azureBlobStorageAllowedToUsedDiv: _azureBlobStorageAllowedToUsedDiv, - azureBlobStorageNotShowUpHintSetting: _azureBlobStorageNotShowUpHintSetting, } = generateAzureBlobStorageSettingsPart( containerEl, t, @@ -1367,13 +1359,13 @@ export class BYOCSettingTab extends PluginSettingTab { text .setPlaceholder("") .setValue(`${this.plugin.settings.password}`) - .onChange(async (value) => { + .onChange((value) => { newPassword = value.trim(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("confirm")); - button.onClick(async () => { + button.onClick(() => { new PasswordModal( this.app, this.plugin, @@ -1475,10 +1467,10 @@ export class BYOCSettingTab extends PluginSettingTab { .setDesc(t("settings_synconsave_desc")) .addDropdown((dropdown) => { dropdown.addOption("-1", t("settings_synconsave_disable")); - dropdown.addOption("3000", "3 seconds"); - dropdown.addOption("5000", "5 seconds (Recommended)"); - dropdown.addOption("10000", "10 seconds"); - dropdown.addOption("30000", "30 seconds"); + dropdown.addOption("3000", "3 Seconds"); + dropdown.addOption("5000", "5 Seconds (recommended)"); + dropdown.addOption("10000", "10 Seconds"); + dropdown.addOption("30000", "30 Seconds"); dropdown .setValue(`${(this.plugin.settings.syncOnSaveAfterMilliseconds ?? -1) > 0 ? this.plugin.settings.syncOnSaveAfterMilliseconds : "-1"}`) @@ -1605,7 +1597,7 @@ export class BYOCSettingTab extends PluginSettingTab { dropdown.addOption("1", "1"); dropdown.addOption("2", "2"); dropdown.addOption("3", "3"); - dropdown.addOption("5", "5 (default)"); + dropdown.addOption("5", "5 (Default)"); dropdown.addOption("10", "10"); dropdown.addOption("15", "15"); dropdown.addOption("20", "20"); @@ -1866,9 +1858,9 @@ export class BYOCSettingTab extends PluginSettingTab { .setDesc(t("settings_export_desc")); importExportDivSetting1.settingEl.addClass("setting-need-wrapping"); importExportDivSetting1 - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_basic_and_advanced_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal( this.app, this.plugin, @@ -1876,15 +1868,15 @@ export class BYOCSettingTab extends PluginSettingTab { ).open(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_s3_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal(this.app, this.plugin, "s3").open(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_dropbox_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal( this.app, this.plugin, @@ -1892,9 +1884,9 @@ export class BYOCSettingTab extends PluginSettingTab { ).open(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_onedrive_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal( this.app, this.plugin, @@ -1902,9 +1894,9 @@ export class BYOCSettingTab extends PluginSettingTab { ).open(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_onedrivefull_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal( this.app, this.plugin, @@ -1912,21 +1904,21 @@ export class BYOCSettingTab extends PluginSettingTab { ).open(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_webdav_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal(this.app, this.plugin, "webdav").open(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_webdis_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal(this.app, this.plugin, "webdis").open(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_googledrive_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal( this.app, this.plugin, @@ -1934,21 +1926,21 @@ export class BYOCSettingTab extends PluginSettingTab { ).open(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_box_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal(this.app, this.plugin, "box").open(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_pcloud_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal(this.app, this.plugin, "pcloud").open(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_yandexdisk_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal( this.app, this.plugin, @@ -1956,15 +1948,15 @@ export class BYOCSettingTab extends PluginSettingTab { ).open(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_koofr_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal(this.app, this.plugin, "koofr").open(); }); }) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_export_azureblobstorage_button")); - button.onClick(async () => { + button.onClick(() => { new ExportSettingsQrCodeModal( this.app, this.plugin, @@ -1979,7 +1971,7 @@ export class BYOCSettingTab extends PluginSettingTab { .setDesc(t("settings_import_desc")) .addText((text) => text - .setPlaceholder("obsidian://remotely-save?func=settings&...") + .setPlaceholder("Obsidian://remotely-save?func=settings&...") .setValue("") .onChange((val) => { importSettingVal = val; @@ -2024,12 +2016,9 @@ export class BYOCSettingTab extends PluginSettingTab { } }); }); - - ////////////////////////////////////////////////// // below for pro ////////////////////////////////////////////////// - const _proDiv = containerEl.createEl("div"); ////////////////////////////////////////////////// @@ -2043,8 +2032,8 @@ export class BYOCSettingTab extends PluginSettingTab { .setName(t("settings_debuglevel")) .setDesc(t("settings_debuglevel_desc")) .addDropdown(async (dropdown) => { - dropdown.addOption("info", "info"); - dropdown.addOption("debug", "debug"); + dropdown.addOption("info", "Info"); + dropdown.addOption("debug", "Debug"); dropdown .setValue(this.plugin.settings.currLogLevel ?? "info") .onChange(async (val: string) => { @@ -2265,9 +2254,9 @@ export class BYOCSettingTab extends PluginSettingTab { new Setting(debugDiv) .setName(t("settings_outputbasepathvaultid")) .setDesc(t("settings_outputbasepathvaultid_desc")) - .addButton(async (button) => { + .addButton((button) => { button.setButtonText(t("settings_outputbasepathvaultid_button")); - button.onClick(async () => { + button.onClick(() => { new Notice(this.plugin.getVaultBasePath()); new Notice(this.plugin.vaultRandomID); }); @@ -2278,8 +2267,8 @@ export class BYOCSettingTab extends PluginSettingTab { .setDesc(t("settings_resetcache_desc")) .addButton(async (button) => { button.setButtonText(t("settings_resetcache_button")); - button.onClick(async () => { - await destroyDBs(); + button.onClick(() => { + destroyDBs(); new Notice(t("settings_resetcache_notice")); this.plugin.unload(); }); diff --git a/src/settingsAzureBlobStorage.ts b/src/settingsAzureBlobStorage.ts index 9efd361..d7b684a 100644 --- a/src/settingsAzureBlobStorage.ts +++ b/src/settingsAzureBlobStorage.ts @@ -25,7 +25,7 @@ export const generateAzureBlobStorageSettingsPart = ( cls: "settings-long-desc", }); azureDescDiv.createEl("p", { - text: "Authenticate with a secure Container SAS URL. BYOC requires List, Read, Write, and Delete permissions to fully synchronize your vault in Azure.", + text: "Authenticate with a secure container sas URL. Byoc requires list, read, write, and delete permissions to fully synchronize your vault in azure.", cls: "azureblobstorage-disclaimer" }); @@ -37,11 +37,11 @@ export const generateAzureBlobStorageSettingsPart = ( const azureBlobStorageAllowedToUsedDiv = azureBlobStorageDiv.createDiv(); new Setting(azureBlobStorageAllowedToUsedDiv) - .setName("Container SAS URL") - .setDesc("Enter your full Shared Access Signature URL ending in ?sv=... Ensure it targets a container, not a specific blob, and allows read/write/list/delete.") + .setName("Container sas URL") + .setDesc("Enter your full shared access signature URL ending in ?sv=... Ensure it targets a container, not a specific blob, and allows read/write/list/delete.") .addText((text) => { text - .setPlaceholder("https://account.blob.core.windows.net/container?...") + .setPlaceholder("HTTPS://account.blob.core.Windows.net/container?...") .setValue(plugin.settings.azureblobstorage.containerSasUrl) .onChange(async (val) => { plugin.settings.azureblobstorage.containerSasUrl = val.trim(); @@ -51,7 +51,7 @@ export const generateAzureBlobStorageSettingsPart = ( }); new Setting(azureBlobStorageAllowedToUsedDiv) - .setName("Base Directory / Prefix") + .setName("Base directory / prefix") .setDesc("A virtual directory within the container to isolate your vault data. Defaults to your vault name if left blank.") .addText((text) => text @@ -64,13 +64,13 @@ export const generateAzureBlobStorageSettingsPart = ( ); new Setting(azureBlobStorageAllowedToUsedDiv) - .setName("Generate Empty Folder Blobs") - .setDesc("Creates 0-byte blobs ending with '/' to emulate folders. Required for compatibility with some S3/blob browsers, but technically unnecessary for BYOC.") + .setName("Generate empty folder blobs") + .setDesc(t("settings_azureblobstorage_folderemulation_desc" as TransItemType)) .addDropdown((dropdown) => dropdown .addOption( "false", - "No (Recommended)" + "No (recommended)" ) .addOption( "true", @@ -106,13 +106,13 @@ export const generateAzureBlobStorageSettingsPart = ( ); new Setting(azureBlobStorageAllowedToUsedDiv) - .setName("Test Connection") - .setDesc("Verify that BYOC can successfully contact Azure and read your container using the SAS URL provided.") + .setName("Test connection") + .setDesc("Verify that byoc can successfully contact azure and read your container using the sas URL provided.") .addButton(async (button) => { - button.setButtonText("Check Connectivity"); + button.setButtonText("Check connectivity"); button.setCta(); button.onClick(async () => { - new Notice("Checking Azure connection..."); + new Notice("Checking azure connection..."); const client = getClient(plugin.settings, app.vault.getName(), () => plugin.saveSettings() ); @@ -121,9 +121,9 @@ export const generateAzureBlobStorageSettingsPart = ( errors.msg = err instanceof Error ? err.message : String(err); }); if (res) { - new Notice("Azure Blob connection successful!"); + new Notice("Azure blob connection successful!"); } else { - new Notice("Azure Blob connection failed."); + new Notice("Azure blob connection failed."); new Notice(errors.msg); } }); diff --git a/src/settingsBox.ts b/src/settingsBox.ts index 8d0f60d..a5f185b 100644 --- a/src/settingsBox.ts +++ b/src/settingsBox.ts @@ -33,18 +33,18 @@ class BoxAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { setSvgTitle(this.titleEl, SVG_BOX, "Connect Box Account"); this.modalEl.addClass("byoc-auth-modal"); const { contentEl } = this; - const _t = this.t; + const authUrl = generateAuthUrl(); const div2 = contentEl.createDiv(); - div2.createEl("p", { text: "1. Click the button below to authorize BYOC within Box." }); - div2.createEl("p", { text: `2. BYOC will authenticate via the obsidian:// protocol.` }); + div2.createEl("p", { text: "1. Click the button below to authorize byoc within box." }); + div2.createEl("p", { text: `2. Byoc will authenticate via the Obsidian:// protocol.` }); div2.createEl("p", { text: "3. If prompted, please allow the browser to open Obsidian." }); - const btn = contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); }); + const btn = contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); }); btn.addClass("mod-cta"); } @@ -73,26 +73,26 @@ class BoxRevokeAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { setSvgTitle(this.titleEl, SVG_BOX, "Revoke Box Account"); this.modalEl.addClass("byoc-auth-modal"); - const _t = this.t; + const { contentEl } = this; - contentEl.createEl("p", { text: "To revoke BYOC's access:" }); - contentEl.createEl("p", { text: `1. Visit the Box Security portal and remove the BYOC application.` }); + contentEl.createEl("p", { text: "To revoke byoc's access:" }); + contentEl.createEl("p", { text: `1. Visit the box security portal and remove the byoc application.` }); const consentUrl = "https://app.box.com/account/security"; contentEl.createEl("p").createEl("a", { href: consentUrl, text: consentUrl, }); - contentEl.createEl("p", { text: "2. Click 'Clear Local OAuth Data' below to erase the credentials from your device." }); + contentEl.createEl("p", { text: "2. Click 'clear local OAUTH data' below to erase the credentials from your device." }); new Setting(contentEl) - .setName("Clear Local OAuth Data") + .setName("Clear local OAUTH data") .setDesc("Erases the stored refresh/access tokens from your vault.") .addButton(async (button) => { - button.setButtonText("Clear Tokens"); + button.setButtonText("Clear tokens"); button.setCta(); button.onClick(async () => { try { @@ -106,11 +106,11 @@ class BoxRevokeAuthModal extends Modal { "box-revoke-auth-button-hide", this.plugin.settings.box.refreshToken === "" ); - new Notice("Local Box authorization tokens cleared."); + new Notice("Local box authorization tokens cleared."); this.close(); } catch (err) { console.error(err); - new Notice("Failed to clear Box tokens."); + new Notice("Failed to clear box tokens."); } }); }); @@ -150,9 +150,9 @@ export const generateBoxSettingsPart = ( const boxRevokeAuthSetting = new Setting(boxRevokeAuthDiv) .setName(savedBoxUsername ? "Logged in as" : "Connected") .addButton(async (button) => { - button.setButtonText("Revoke Access"); + button.setButtonText("Revoke access"); button.setWarning(); - button.onClick(async () => { + button.onClick(() => { new BoxRevokeAuthModal(app, plugin, boxAuthDiv, boxRevokeAuthDiv, t).open(); }); }); @@ -161,12 +161,12 @@ export const generateBoxSettingsPart = ( } new Setting(boxAuthDiv) - .setName("Connect Box Account") - .setDesc("Authenticate BYOC with your Box account to enable cloud synchronization.") + .setName("Connect box account") + .setDesc("Authenticate byoc with your box account to enable cloud synchronization.") .addButton(async (button) => { button.setButtonText("Authorize"); button.setCta(); - button.onClick(async () => { + button.onClick(() => { const modal = new BoxAuthModal( app, plugin, @@ -191,7 +191,7 @@ export const generateBoxSettingsPart = ( const currentBoxFolder = plugin.settings.box.remoteBaseDir || app.vault.getName(); const boxRemoteFolderSetting = new Setting(boxAllowedToUsedDiv).setName( - "Base Directory" + "Base directory" ); renderFolderBreadcrumb(boxRemoteFolderSetting, "Box", currentBoxFolder); boxRemoteFolderSetting.addButton((button) => { diff --git a/src/settingsDropbox.ts b/src/settingsDropbox.ts index 9546be1..5a22d6f 100644 --- a/src/settingsDropbox.ts +++ b/src/settingsDropbox.ts @@ -70,7 +70,7 @@ class DropboxAuthModal extends Modal { }); } - contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => { + contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); }); @@ -101,7 +101,7 @@ class DropboxAuthModal extends Modal { DROPBOX_APP_KEY, verifier, authCode, - async (e: unknown) => { + (e: unknown) => { new Notice(t("protocol_dropbox_connect_fail")); new Notice(`${String(e)}`); throw e; @@ -172,7 +172,7 @@ class DropboxRevokeAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { setSvgTitle(this.titleEl, SVG_DROPBOX, "Revoke Dropbox Account"); this.modalEl.addClass("byoc-auth-modal"); const t = this.t; @@ -185,7 +185,7 @@ class DropboxRevokeAuthModal extends Modal { contentEl.createEl("a", { href: "https://www.dropbox.com/account/connected_apps", - text: "Open Dropbox Connected Apps", + text: "Open Dropbox connected apps", cls: "external-link", }); @@ -255,7 +255,7 @@ export const generateDropboxSettingsPart = ( .addButton(async (button) => { button.setButtonText(t("settings_dropbox_revoke_button")); button.setWarning(); - button.onClick(async () => { + button.onClick(() => { new DropboxRevokeAuthModal( app, plugin, @@ -270,12 +270,12 @@ export const generateDropboxSettingsPart = ( } new Setting(dropboxAuthDiv) - .setName("Connect Dropbox Account") - .setDesc("Authenticate BYOC with your Dropbox account to enable cloud synchronization.") + .setName("Connect Dropbox account") + .setDesc("Authenticate byoc with your Dropbox account to enable cloud synchronization.") .addButton(async (button) => { button.setButtonText("Authorize"); button.setCta(); - button.onClick(async () => { + button.onClick(() => { const modal = new DropboxAuthModal( app, plugin, diff --git a/src/settingsGoogleDrive.ts b/src/settingsGoogleDrive.ts index 40c5800..749b11f 100644 --- a/src/settingsGoogleDrive.ts +++ b/src/settingsGoogleDrive.ts @@ -32,7 +32,7 @@ class GoogleDriveAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { setSvgTitle(this.titleEl, SVG_GDRIVE, "Connect Google Drive Account"); this.modalEl.addClass("byoc-auth-modal"); const { contentEl } = this; @@ -70,7 +70,7 @@ class GoogleDriveRevokeAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { setSvgTitle(this.titleEl, SVG_GDRIVE, "Revoke Google Drive Account"); this.modalEl.addClass("byoc-auth-modal"); const t = this.t; @@ -83,7 +83,7 @@ class GoogleDriveRevokeAuthModal extends Modal { contentEl.createEl("a", { href: "https://myaccount.google.com/permissions", - text: "Open Google Account Permissions", + text: "Open Google account permissions", cls: "external-link", }); @@ -152,7 +152,7 @@ export const generateGoogleDriveSettingsPart = ( .addButton(async (button) => { button.setButtonText(t("settings_googledrive_revoke_button")); button.setWarning(); - button.onClick(async () => { + button.onClick(() => { new GoogleDriveRevokeAuthModal( app, plugin, @@ -169,7 +169,7 @@ export const generateGoogleDriveSettingsPart = ( .setDesc(t("settings_googledrive_auth_desc")) .addButton(async (button) => { button.setButtonText(t("settings_googledrive_auth_button")); - button.onClick(async () => { + button.onClick(() => { const m = new GoogleDriveAuthModal( app, plugin, diff --git a/src/settingsKoofr.ts b/src/settingsKoofr.ts index ad42203..3bbd6ee 100644 --- a/src/settingsKoofr.ts +++ b/src/settingsKoofr.ts @@ -33,7 +33,7 @@ class KoofrAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { setSvgTitle(this.titleEl, SVG_KOOFR, "Connect Koofr Account"); this.modalEl.addClass("byoc-auth-modal"); const { contentEl } = this; @@ -42,7 +42,7 @@ class KoofrAuthModal extends Modal { const div2 = contentEl.createDiv(); t("modal_koofrauth_tutorial").split("\n").forEach((val) => { div2.createEl("p", { text: val }); }); - contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); }); + contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); }); } @@ -70,7 +70,7 @@ class KoofrRevokeAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { setSvgTitle(this.titleEl, SVG_KOOFR, "Revoke Koofr Account"); this.modalEl.addClass("byoc-auth-modal"); const t = this.t; @@ -147,7 +147,7 @@ export const generateKoofrSettingsPart = ( .addButton(async (button) => { button.setButtonText(t("settings_koofr_revoke_button")); button.setWarning(); - button.onClick(async () => { + button.onClick(() => { new KoofrRevokeAuthModal( app, plugin, @@ -166,7 +166,7 @@ export const generateKoofrSettingsPart = ( .setDesc(t("settings_koofr_auth_desc")) .addButton(async (button) => { button.setButtonText(t("settings_koofr_auth_button")); - button.onClick(async () => { + button.onClick(() => { const modal = new KoofrAuthModal( app, plugin, diff --git a/src/settingsOnedrive.ts b/src/settingsOnedrive.ts index ae025fa..6374473 100644 --- a/src/settingsOnedrive.ts +++ b/src/settingsOnedrive.ts @@ -64,7 +64,7 @@ class OnedriveAuthModal extends Modal { }); } - contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => { + contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); }); } @@ -93,7 +93,7 @@ class OnedriveRevokeAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { this.modalEl.addClass("byoc-auth-modal"); setSvgTitle(this.titleEl, SVG_ONEDRIVE, "Revoke OneDrive Account"); const t = this.t; @@ -178,7 +178,7 @@ export const generateOnedriveSettingsPart = ( .addButton(async (button) => { button.setButtonText(t("settings_onedrive_revoke_button")); button.setWarning(); - button.onClick(async () => { + button.onClick(() => { new OnedriveRevokeAuthModal( app, plugin, @@ -193,12 +193,12 @@ export const generateOnedriveSettingsPart = ( } new Setting(onedriveAuthDiv) - .setName("Connect OneDrive Account") - .setDesc("Authenticate BYOC with your Microsoft OneDrive account to enable cloud synchronization.") + .setName("Connect OneDrive account") + .setDesc("Authenticate byoc with your Microsoft OneDrive account to enable cloud synchronization.") .addButton(async (button) => { button.setButtonText("Authorize"); button.setCta(); - button.onClick(async () => { + button.onClick(() => { const modal = new OnedriveAuthModal( app, plugin, diff --git a/src/settingsOnedriveFull.ts b/src/settingsOnedriveFull.ts index b06d457..1a3877d 100644 --- a/src/settingsOnedriveFull.ts +++ b/src/settingsOnedriveFull.ts @@ -52,8 +52,8 @@ class OnedrivefullAuthModal extends Modal { this.plugin.oauth2Info.verifier = verifier; - const _div2 = contentEl.createDiv(); - contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); }); + + contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); }); } catch (e) { console.error(e); @@ -87,7 +87,7 @@ class OnedrivefullRevokeAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { setSvgTitle(this.titleEl, SVG_ONEDRIVE, "Revoke OneDrive (Full) Account"); this.modalEl.addClass("byoc-auth-modal"); const t = this.t; @@ -171,7 +171,7 @@ export const generateOnedriveFullSettingsPart = ( .addButton(async (button) => { button.setButtonText(t("settings_onedrivefull_revoke_button")); button.setWarning(); - button.onClick(async () => { + button.onClick(() => { new OnedrivefullRevokeAuthModal( app, plugin, @@ -190,7 +190,7 @@ export const generateOnedriveFullSettingsPart = ( .setDesc(t("settings_onedrivefull_auth_desc")) .addButton(async (button) => { button.setButtonText(t("settings_onedrivefull_auth_button")); - button.onClick(async () => { + button.onClick(() => { const modal = new OnedrivefullAuthModal( app, plugin, diff --git a/src/settingsPCloud.ts b/src/settingsPCloud.ts index 08c0a25..4eef2f5 100644 --- a/src/settingsPCloud.ts +++ b/src/settingsPCloud.ts @@ -33,16 +33,16 @@ class PCloudAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { setSvgTitle(this.titleEl, SVG_PCLOUD, "Connect pCloud Account"); this.modalEl.addClass("byoc-auth-modal"); const { contentEl } = this; const t = this.t; - const { authUrl } = await generateAuthUrl(true); + const { authUrl } = generateAuthUrl(true); const div2 = contentEl.createDiv(); t("modal_pcloudauth_tutorial").split("\n").forEach((val) => { div2.createEl("p", { text: val }); }); - contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); }); + contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); }); } @@ -71,7 +71,7 @@ class PCloudRevokeAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { setSvgTitle(this.titleEl, SVG_PCLOUD, "Revoke pCloud Account"); this.modalEl.addClass("byoc-auth-modal"); const t = this.t; @@ -157,7 +157,7 @@ export const generatePCloudSettingsPart = ( .addButton(async (button) => { button.setButtonText(t("settings_pcloud_revoke_button")); button.setWarning(); - button.onClick(async () => { + button.onClick(() => { new PCloudRevokeAuthModal( app, plugin, @@ -177,7 +177,7 @@ export const generatePCloudSettingsPart = ( .addButton(async (button) => { button.setButtonText(t("settings_pcloud_auth_button")); button.setCta(); - button.onClick(async () => { + button.onClick(() => { const modal = new PCloudAuthModal( app, plugin, diff --git a/src/settingsYandexDisk.ts b/src/settingsYandexDisk.ts index 960a881..d8cd8b4 100644 --- a/src/settingsYandexDisk.ts +++ b/src/settingsYandexDisk.ts @@ -33,7 +33,7 @@ class YandexDiskAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { setSvgTitle(this.titleEl, SVG_YANDEX, "Connect Yandex Disk Account"); this.modalEl.addClass("byoc-auth-modal"); const { contentEl } = this; @@ -42,7 +42,7 @@ class YandexDiskAuthModal extends Modal { const div2 = contentEl.createDiv(); t("modal_yandexdiskauth_tutorial").split("\n").forEach((val) => { div2.createEl("p", { text: val }); }); - contentEl.createEl("button", { text: "Open Authorization in Browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); }); + contentEl.createEl("button", { text: "Open authorization in browser" }, (el) => { el.onclick = () => activeWindow.open(authUrl); }); } @@ -70,7 +70,7 @@ class YandexDiskRevokeAuthModal extends Modal { this.t = t; } - async onOpen() { + onOpen() { setSvgTitle(this.titleEl, SVG_YANDEX, "Revoke Yandex Disk Account"); this.modalEl.addClass("byoc-auth-modal"); const t = this.t; @@ -150,7 +150,7 @@ export const generateYandexDiskSettingsPart = ( .addButton(async (button) => { button.setButtonText(t("settings_yandexdisk_revoke_button")); button.setWarning(); - button.onClick(async () => { + button.onClick(() => { new YandexDiskRevokeAuthModal( app, plugin, @@ -169,7 +169,7 @@ export const generateYandexDiskSettingsPart = ( .setDesc(t("settings_yandexdisk_auth_desc")) .addButton(async (button) => { button.setButtonText(t("settings_yandexdisk_auth_button")); - button.onClick(async () => { + button.onClick(() => { const modal = new YandexDiskAuthModal( app, plugin, diff --git a/tests/encryptOpenSSL.test.ts b/tests/encryptOpenSSL.test.ts index 01a8b9e..5422dae 100644 --- a/tests/encryptOpenSSL.test.ts +++ b/tests/encryptOpenSSL.test.ts @@ -17,6 +17,7 @@ describe("Encryption OpenSSL tests", () => { global.window = { crypto: require("crypto").webcrypto, } as any; + (global as any).activeWindow = global.window; }); it("should encrypt string", async () => { diff --git a/tests/fsWebdis.test.ts b/tests/fsWebdis.test.ts index 15a388e..16b51ed 100644 --- a/tests/fsWebdis.test.ts +++ b/tests/fsWebdis.test.ts @@ -1,3 +1,4 @@ +vi.mock("obsidian", () => ({ requestUrl: vi.fn() })); import { strict as assert } from "assert"; import { getOrigPath } from "../src/fsWebdis"; diff --git a/tests/misc.test.ts b/tests/misc.test.ts index 9e2a564..71ddf96 100644 --- a/tests/misc.test.ts +++ b/tests/misc.test.ts @@ -182,6 +182,7 @@ describe("Misc: extract svg", () => { beforeEach(() => { const fakeBrowser = new JSDOM(""); global.window = fakeBrowser.window as any; + (global as any).DOMParser = fakeBrowser.window.DOMParser; }); it("should extract rect from svg correctly", () => { diff --git a/webpack.config.js b/webpack.config.js index 8c35a26..8cf3fc6 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -31,19 +31,23 @@ module.exports = { }, plugins: [ new webpack.DefinePlugin({ - "globalThis.DEFAULT_DROPBOX_APP_KEY": `"${DEFAULT_DROPBOX_APP_KEY}"`, - "globalThis.DEFAULT_ONEDRIVE_CLIENT_ID": `"${DEFAULT_ONEDRIVE_CLIENT_ID}"`, - "globalThis.DEFAULT_ONEDRIVE_AUTHORITY": `"${DEFAULT_ONEDRIVE_AUTHORITY}"`, - "globalThis.DEFAULT_GOOGLEDRIVE_CLIENT_ID": `"${DEFAULT_GOOGLEDRIVE_CLIENT_ID}"`, - "globalThis.DEFAULT_GOOGLEDRIVE_CLIENT_SECRET": `"${DEFAULT_GOOGLEDRIVE_CLIENT_SECRET}"`, - "globalThis.DEFAULT_BOX_CLIENT_ID": `"${DEFAULT_BOX_CLIENT_ID}"`, - "globalThis.DEFAULT_BOX_CLIENT_SECRET": `"${DEFAULT_BOX_CLIENT_SECRET}"`, - "globalThis.DEFAULT_PCLOUD_CLIENT_ID": `"${DEFAULT_PCLOUD_CLIENT_ID}"`, - "globalThis.DEFAULT_PCLOUD_CLIENT_SECRET": `"${DEFAULT_PCLOUD_CLIENT_SECRET}"`, - "globalThis.DEFAULT_YANDEXDISK_CLIENT_ID": `"${DEFAULT_YANDEXDISK_CLIENT_ID}"`, - "globalThis.DEFAULT_YANDEXDISK_CLIENT_SECRET": `"${DEFAULT_YANDEXDISK_CLIENT_SECRET}"`, - "globalThis.DEFAULT_KOOFR_CLIENT_ID": `"${DEFAULT_KOOFR_CLIENT_ID}"`, - "globalThis.DEFAULT_KOOFR_CLIENT_SECRET": `"${DEFAULT_KOOFR_CLIENT_SECRET}"`, + // baseTypes.ts reads these as bare identifiers behind a `typeof` guard + // (the prior `globalThis.X` form tripped obsidianmd/prefer-active-doc). + // DefinePlugin must match the bare identifier so the literal value is + // substituted at build time — otherwise the OAuth client_id ends up "". + DEFAULT_DROPBOX_APP_KEY: `"${DEFAULT_DROPBOX_APP_KEY}"`, + DEFAULT_ONEDRIVE_CLIENT_ID: `"${DEFAULT_ONEDRIVE_CLIENT_ID}"`, + DEFAULT_ONEDRIVE_AUTHORITY: `"${DEFAULT_ONEDRIVE_AUTHORITY}"`, + DEFAULT_GOOGLEDRIVE_CLIENT_ID: `"${DEFAULT_GOOGLEDRIVE_CLIENT_ID}"`, + DEFAULT_GOOGLEDRIVE_CLIENT_SECRET: `"${DEFAULT_GOOGLEDRIVE_CLIENT_SECRET}"`, + DEFAULT_BOX_CLIENT_ID: `"${DEFAULT_BOX_CLIENT_ID}"`, + DEFAULT_BOX_CLIENT_SECRET: `"${DEFAULT_BOX_CLIENT_SECRET}"`, + DEFAULT_PCLOUD_CLIENT_ID: `"${DEFAULT_PCLOUD_CLIENT_ID}"`, + DEFAULT_PCLOUD_CLIENT_SECRET: `"${DEFAULT_PCLOUD_CLIENT_SECRET}"`, + DEFAULT_YANDEXDISK_CLIENT_ID: `"${DEFAULT_YANDEXDISK_CLIENT_ID}"`, + DEFAULT_YANDEXDISK_CLIENT_SECRET: `"${DEFAULT_YANDEXDISK_CLIENT_SECRET}"`, + DEFAULT_KOOFR_CLIENT_ID: `"${DEFAULT_KOOFR_CLIENT_ID}"`, + DEFAULT_KOOFR_CLIENT_SECRET: `"${DEFAULT_KOOFR_CLIENT_SECRET}"`, "process.env.NODE_DEBUG": `undefined`, // ugly fix "process.env.DEBUG": `undefined`, // ugly fix