chore(lint): mirror ReviewBot config + fix OAuth DefinePlugin regression

Add eslint.bot.config.mjs that runs the obsidianmd ReviewBot's stricter
ruleset locally (require-await, no-misused-promises,
restrict-template-expressions, ui/sentence-case, undescribed-disable
checks, plus its disallow-list of eslint-disable rules). Sweeps every
remaining Required-tier finding the bot flagged: async-no-await across
fs*/settings*/main.ts, sentence-case UI text, undescribed eslint-disable
directives, encryptRClone.worker.ts any-typing and never-template,
plus the worker's async-callback-as-event-listener-return mismatch.

Also fixes a build regression introduced by the same sweep: rewriting
baseTypes.ts from `globalThis.DEFAULT_X` access to a bare-identifier
`typeof` guard broke webpack's DefinePlugin substitution because the
plugin keys still pattern-matched on "globalThis.X". Result was every
OAuth client_id (Dropbox, OneDrive, OneDriveFull, GoogleDrive, Box,
pCloud, Yandex, Koofr) shipped as "" and OAuth flows returned
"invalid client_id". DefinePlugin keys now match the bare identifier.
Verified by grepping the deployed main.js for the pCloud client_id
literal post-build.
This commit is contained in:
winters27 2026-04-27 06:30:32 -07:00
parent 4e55cd761b
commit 203238db84
38 changed files with 449 additions and 316 deletions

6
.gitignore vendored
View file

@ -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

90
eslint.bot.config.mjs Normal file
View file

@ -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",
},
},
]);

View file

@ -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",

View file

@ -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";

View file

@ -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),

View file

@ -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)}`,
});
}
})();
});

View file

@ -75,8 +75,10 @@ export abstract class FakeFs {
*
* Default: throws. Override in providers that support OAuth folder picking.
*/
async listFoldersAtRoot(): Promise<string[]> {
throw new Error(`[BYOC] listFoldersAtRoot not implemented for ${this.kind}`);
listFoldersAtRoot(): Promise<string[]> {
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<void> {
throw new Error(`[BYOC] createFolderAtRoot not implemented for ${this.kind}`);
createFolderAtRoot(name: string): Promise<void> {
return Promise.reject(
new Error(`[BYOC] createFolderAtRoot not implemented for ${this.kind}`)
);
}
}

View file

@ -304,17 +304,20 @@ export class FakeFsAzureBlobStorage extends FakeFs {
return this.checkConnectCommonOps(callbackFunc);
}
async getUserDisplayName(): Promise<string> {
getUserDisplayName(): Promise<string> {
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<void> {
revokeAuth(): Promise<void> {
// SAS tokens don't have a revoke flow — user regenerates on Azure portal
this.config.containerSasUrl = "";
this.config.containerName = "";
return Promise.resolve();
}
allowEmptyFile(): boolean {

View file

@ -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");

View file

@ -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<void> {
if (this.method === "rclone-base64" && this.cipherRClone !== undefined) {
this.cipherRClone.closeResources();
}
return Promise.resolve();
}
async encryptEntity(input: Entity): Promise<Entity> {
@ -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 {

View file

@ -181,16 +181,16 @@ export class FakeFsLocal extends FakeFs {
}
}
}
async checkConnect(callbackFunc?: (err: unknown) => unknown): Promise<boolean> {
return true;
checkConnect(callbackFunc?: (err: unknown) => unknown): Promise<boolean> {
return Promise.resolve(true);
}
async getUserDisplayName(): Promise<string> {
throw new Error("Method not implemented.");
getUserDisplayName(): Promise<string> {
return Promise.reject(new Error("Method not implemented."));
}
async revokeAuth(): Promise<void> {
throw new Error("Method not implemented.");
revokeAuth(): Promise<void> {
return Promise.reject(new Error("Method not implemented."));
}
supportsRename(): boolean { return true; }

View file

@ -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<Entity[]> {
throw new Error("Method not implemented.");
walk(): Promise<Entity[]> {
return notImplemented();
}
async walkPartial(): Promise<Entity[]> {
return await this.walk();
}
async stat(key: string): Promise<Entity> {
throw new Error("Method not implemented.");
stat(key: string): Promise<Entity> {
return notImplemented();
}
async mkdir(key: string, mtime: number, ctime: number): Promise<Entity> {
throw new Error("Method not implemented.");
mkdir(key: string, mtime: number, ctime: number): Promise<Entity> {
return notImplemented();
}
async writeFile(
writeFile(
key: string,
content: ArrayBuffer,
mtime: number,
ctime: number
): Promise<Entity> {
throw new Error("Method not implemented.");
return notImplemented();
}
async readFile(key: string): Promise<ArrayBuffer> {
throw new Error("Method not implemented.");
readFile(key: string): Promise<ArrayBuffer> {
return notImplemented();
}
async rename(key1: string, key2: string): Promise<void> {
throw new Error("Method not implemented.");
rename(key1: string, key2: string): Promise<void> {
return notImplemented();
}
async rm(key: string): Promise<void> {
throw new Error("Method not implemented.");
rm(key: string): Promise<void> {
return notImplemented();
}
async checkConnect(callbackFunc?: (err: unknown) => unknown): Promise<boolean> {
return await this.checkConnectCommonOps(callbackFunc);
}
async getUserDisplayName(): Promise<string> {
throw new Error("Method not implemented.");
getUserDisplayName(): Promise<string> {
return notImplemented();
}
async revokeAuth(): Promise<void> {
throw new Error("Method not implemented.");
revokeAuth(): Promise<void> {
return notImplemented();
}
supportsRename(): boolean { return true; }

View file

@ -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<void> {
// 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<string> {
return Promise.resolve("https://account.live.com/consent/Manage");
}
allowEmptyFile(): boolean {

View file

@ -705,12 +705,14 @@ export class FakeFsOnedriveFull extends FakeFs {
return res.displayName ?? "<unknown>";
}
async revokeAuth(): Promise<void> {
throw new Error("Visit https://account.live.com/consent/Manage to revoke.");
revokeAuth(): Promise<void> {
return Promise.reject(
new Error("Visit https://account.live.com/consent/Manage to revoke.")
);
}
async getRevokeAddr(): Promise<string> {
return "https://account.live.com/consent/Manage";
getRevokeAddr(): Promise<string> {
return Promise.resolve("https://account.live.com/consent/Manage");
}
supportsRename(): boolean { return true; }

View file

@ -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<string> {
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;

View file

@ -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<string, unknown>)["bypassCorsLocally"] = true;
const stream = new ReadableStream<Uint8Array>({
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<string, unknown>).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<void> {
throw Error(`rename not implemented for s3`);
rename(key1: string, key2: string): Promise<void> {
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<string> {
throw new Error("Method not implemented.");
getUserDisplayName(): Promise<string> {
return Promise.reject(new Error("Method not implemented."));
}
async revokeAuth() {
throw new Error("Method not implemented.");
revokeAuth(): Promise<void> {
return Promise.reject(new Error("Method not implemented."));
}
allowEmptyFile(): boolean {

View file

@ -959,12 +959,12 @@ export class FakeFsWebdav extends FakeFs {
return await this.checkConnectCommonOps(callbackFunc);
}
async getUserDisplayName(): Promise<string> {
throw new Error("Method not implemented.");
getUserDisplayName(): Promise<string> {
return Promise.reject(new Error("Method not implemented."));
}
async revokeAuth() {
throw new Error("Method not implemented.");
revokeAuth(): Promise<void> {
return Promise.reject(new Error("Method not implemented."));
}
allowEmptyFile(): boolean {

View file

@ -249,12 +249,12 @@ export class FakeFsWebdis extends FakeFs {
// return true;
}
async getUserDisplayName(): Promise<string> {
return this.webdisConfig.username || "<no usernme>";
getUserDisplayName(): Promise<string> {
return Promise.resolve(this.webdisConfig.username || "<no usernme>");
}
async revokeAuth(): Promise<void> {
throw new Error("Method not implemented.");
revokeAuth(): Promise<void> {
return Promise.reject(new Error("Method not implemented."));
}
supportsRename(): boolean { return true; }

View file

@ -16,7 +16,6 @@ type SyncPlanType = Record<string, unknown>;
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,
// });

View file

@ -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<void> => {
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<void> => {
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<void> => {
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<void> => {
this.isSyncing = isSyncing;
return Promise.resolve();
};
const callbackSyncProcess = async (
const callbackSyncProcess = (
s: SyncTriggerSourceType,
realCounter: number,
realTotalCount: number,
pathName: string,
decision: string
) => {
): Promise<void> => {
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<string, string>) => 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;
}

View file

@ -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,

View file

@ -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.");

View file

@ -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`);

6
src/readable-stream.d.ts vendored Normal file
View file

@ -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;
}
}

View file

@ -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();
});

View file

@ -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);
}
});

View file

@ -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) => {

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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 () => {

View file

@ -1,3 +1,4 @@
vi.mock("obsidian", () => ({ requestUrl: vi.fn() }));
import { strict as assert } from "assert";
import { getOrigPath } from "../src/fsWebdis";

View file

@ -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", () => {

View file

@ -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