Compare commits

..
10 Commits
Author SHA1 Message Date
fyears d1e30e3536 bump to 0.4.21
Release A New Version / build (16.x) (push) Failing after 38s
2024-05-09 00:02:07 +08:00
fyears 36079fc1d0 add profiler 2024-05-09 00:01:30 +08:00
fyears 67467a5034 fail statusbar 2024-05-08 22:50:18 +08:00
fyears 2a3df8ab53 fix onedrive issue in enc 2024-05-08 22:37:34 +08:00
fyears e66b0c71c4 fix format again 2024-05-08 22:04:21 +08:00
fyears a081d09212 safe lint from biome 2024-05-08 00:20:15 +08:00
fyears 6ed6122bb6 rm prettier 2024-05-07 23:51:26 +08:00
fyears 235e346d2f slightly format 2024-05-07 23:48:37 +08:00
fyears 084cbc8391 switch formater to biome for speed 2024-05-07 23:48:29 +08:00
fyears dc0c1db779 enable git lfs cache to avoid billing according to https://github.com/actions/checkout/issues/165 2024-05-07 23:22:51 +08:00
41 changed files with 610 additions and 302 deletions
+11 -3
View File
@@ -29,10 +29,18 @@ jobs:
- name: Checkout codes - name: Checkout codes
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
lfs: true
submodules: recursive submodules: recursive
- name: Checkout LFS - name: Checkout LFS file list
run: git lfs checkout run: git lfs ls-files --long | cut -d ' ' -f1 | sort > .lfs-assets-id
- name: LFS Cache
uses: actions/cache@v3
with:
path: .git/lfs/objects
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}
restore-keys: |
${{ runner.os }}-lfs-
- name: Git LFS Pull
run: git lfs pull
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
+11 -3
View File
@@ -32,10 +32,18 @@ jobs:
- name: Checkout codes - name: Checkout codes
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
lfs: true
submodules: recursive submodules: recursive
- name: Checkout LFS - name: Checkout LFS file list
run: git lfs checkout run: git lfs ls-files --long | cut -d ' ' -f1 | sort > .lfs-assets-id
- name: LFS Cache
uses: actions/cache@v3
with:
path: .git/lfs/objects
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}
restore-keys: |
${{ runner.os }}-lfs-
- name: Git LFS Pull
run: git lfs pull
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
-3
View File
@@ -1,3 +0,0 @@
node_modules/
main.js
data.json
+55
View File
@@ -0,0 +1,55 @@
{
"$schema": "https://biomejs.dev/schemas/1.7.3/schema.json",
"organizeImports": {
"enabled": true
},
"files": {
"ignore": ["main.js"]
},
"formatter": {
"enabled": true,
"formatWithErrors": false,
"ignore": [],
"attributePosition": "auto",
"indentStyle": "space",
"indentWidth": 2,
"lineEnding": "lf",
"lineWidth": 80
},
"javascript": {
"formatter": {
"arrowParentheses": "always",
"bracketSameLine": false,
"bracketSpacing": true,
"jsxQuoteStyle": "double",
"quoteProperties": "asNeeded",
"semicolons": "always",
"trailingComma": "es5"
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noExplicitAny": "off",
"noPrototypeBuiltins": "off",
"noControlCharactersInRegex": "off"
},
"style": {
"noUselessElse": "off",
"useNodejsImportProtocol": "off",
"noUnusedTemplateLiteral": "off",
"useTemplate": "off",
"noNonNullAssertion": "off"
},
"performance": {
"noDelete": "off"
},
"complexity": {
"noForEach": "off",
"useLiteralKeys": "off"
}
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
const http = require("http"); const http = require("http");
const requestHandler = (req, res) => { const requestHandler = (req, res) => {
let body = []; const body = [];
req req
.on("data", (chunk) => { .on("data", (chunk) => {
body.push(chunk); body.push(chunk);
+1 -2
View File
@@ -1,7 +1,6 @@
import dotenv from "dotenv/config";
import esbuild from "esbuild"; import esbuild from "esbuild";
import process from "process";
import inlineWorkerPlugin from "esbuild-plugin-inline-worker"; import inlineWorkerPlugin from "esbuild-plugin-inline-worker";
import process from "process";
// import builtins from 'builtin-modules' // import builtins from 'builtin-modules'
const banner = `/* const banner = `/*
+2 -2
View File
@@ -1,2 +1,2 @@
export let Buffer = require("buffer").Buffer; export const Buffer = require("buffer").Buffer;
export let process = require("process/browser"); export const process = require("process/browser");
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"id": "remotely-save", "id": "remotely-save",
"name": "Remotely Save", "name": "Remotely Save",
"version": "0.4.20", "version": "0.4.21",
"minAppVersion": "0.13.21", "minAppVersion": "0.13.21",
"description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.", "description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.",
"author": "fyears", "author": "fyears",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"id": "remotely-save", "id": "remotely-save",
"name": "Remotely Save", "name": "Remotely Save",
"version": "0.4.20", "version": "0.4.21",
"minAppVersion": "0.13.21", "minAppVersion": "0.13.21",
"description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.", "description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.",
"author": "fyears", "author": "fyears",
+3 -3
View File
@@ -1,13 +1,13 @@
{ {
"name": "remotely-save", "name": "remotely-save",
"version": "0.4.20", "version": "0.4.21",
"description": "This is yet another sync plugin for Obsidian app.", "description": "This is yet another sync plugin for Obsidian app.",
"scripts": { "scripts": {
"dev2": "node esbuild.config.mjs --watch", "dev2": "node esbuild.config.mjs --watch",
"build2": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", "build2": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"build": "webpack --mode production", "build": "webpack --mode production",
"dev": "webpack --mode development --watch", "dev": "webpack --mode development --watch",
"format": "npx prettier --trailing-comma es5 --write .", "format": "npx @biomejs/biome check --apply .",
"clean": "npx rimraf main.js", "clean": "npx rimraf main.js",
"test": "cross-env TS_NODE_COMPILER_OPTIONS={\\\"module\\\":\\\"commonjs\\\"} mocha -r ts-node/register 'tests/**/*.ts'" "test": "cross-env TS_NODE_COMPILER_OPTIONS={\\\"module\\\":\\\"commonjs\\\"} mocha -r ts-node/register 'tests/**/*.ts'"
}, },
@@ -25,6 +25,7 @@
"author": "", "author": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.7.3",
"@microsoft/microsoft-graph-types": "^2.40.0", "@microsoft/microsoft-graph-types": "^2.40.0",
"@types/chai": "^4.3.14", "@types/chai": "^4.3.14",
"@types/chai-as-promised": "^7.1.8", "@types/chai-as-promised": "^7.1.8",
@@ -44,7 +45,6 @@
"mocha": "^10.4.0", "mocha": "^10.4.0",
"npm-check-updates": "^16.14.20", "npm-check-updates": "^16.14.20",
"obsidian": "^1.5.7", "obsidian": "^1.5.7",
"prettier": "^3.2.5",
"ts-loader": "^9.5.1", "ts-loader": "^9.5.1",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tslib": "^2.6.2", "tslib": "^2.6.2",
+8 -1
View File
@@ -3,7 +3,7 @@
* To avoid circular dependency. * To avoid circular dependency.
*/ */
import type { LangType, LangTypeAndAuto } from "./i18n"; import type { LangTypeAndAuto } from "./i18n";
export const DEFAULT_CONTENT_TYPE = "application/octet-stream"; export const DEFAULT_CONTENT_TYPE = "application/octet-stream";
@@ -107,6 +107,11 @@ export type CipherMethodType = "rclone-base64" | "openssl-base64" | "unknown";
export type QRExportType = "all_but_oauth2" | "dropbox" | "onedrive"; export type QRExportType = "all_but_oauth2" | "dropbox" | "onedrive";
export interface ProfilerConfig {
enablePrinting?: boolean;
recordSize?: boolean;
}
export interface RemotelySavePluginSettings { export interface RemotelySavePluginSettings {
s3: S3Config; s3: S3Config;
webdav: WebdavConfig; webdav: WebdavConfig;
@@ -141,6 +146,8 @@ export interface RemotelySavePluginSettings {
encryptionMethod?: CipherMethodType; encryptionMethod?: CipherMethodType;
profiler?: ProfilerConfig;
/** /**
* @deprecated * @deprecated
*/ */
+1 -1
View File
@@ -1,4 +1,4 @@
import { base64, base64url } from "rfc4648"; import { base64url } from "rfc4648";
import { reverseString } from "./misc"; import { reverseString } from "./misc";
import type { RemotelySavePluginSettings } from "./baseTypes"; import type { RemotelySavePluginSettings } from "./baseTypes";
+7 -7
View File
@@ -1,16 +1,16 @@
import { TAbstractFile, TFolder, TFile, Vault } from "obsidian"; import type { Vault } from "obsidian";
import {
readAllProfilerResultsByVault,
readAllSyncPlanRecordTextsByVault,
} from "./localdb";
import type { InternalDBs } from "./localdb";
import { mkdirpInVault, unixTimeToStr } from "./misc";
import { import {
DEFAULT_DEBUG_FOLDER, DEFAULT_DEBUG_FOLDER,
DEFAULT_PROFILER_RESULT_FILE_PREFIX, DEFAULT_PROFILER_RESULT_FILE_PREFIX,
DEFAULT_SYNC_PLANS_HISTORY_FILE_PREFIX, DEFAULT_SYNC_PLANS_HISTORY_FILE_PREFIX,
} from "./baseTypes"; } from "./baseTypes";
import {
readAllProfilerResultsByVault,
readAllSyncPlanRecordTextsByVault,
} from "./localdb";
import type { InternalDBs } from "./localdb";
import { mkdirpInVault } from "./misc";
export const exportVaultSyncPlansToFiles = async ( export const exportVaultSyncPlansToFiles = async (
db: InternalDBs, db: InternalDBs,
+3 -3
View File
@@ -39,7 +39,7 @@ export const encryptArrayBuffer = async (
arrBuf: ArrayBuffer, arrBuf: ArrayBuffer,
password: string, password: string,
rounds: number = DEFAULT_ITER, rounds: number = DEFAULT_ITER,
saltHex: string = "" saltHex = ""
) => { ) => {
let salt: Uint8Array; let salt: Uint8Array;
if (saltHex !== "") { if (saltHex !== "") {
@@ -109,7 +109,7 @@ export const encryptStringToBase32 = async (
text: string, text: string,
password: string, password: string,
rounds: number = DEFAULT_ITER, rounds: number = DEFAULT_ITER,
saltHex: string = "" saltHex = ""
) => { ) => {
const enc = await encryptArrayBuffer( const enc = await encryptArrayBuffer(
bufferToArrayBuffer(new TextEncoder().encode(text)), bufferToArrayBuffer(new TextEncoder().encode(text)),
@@ -138,7 +138,7 @@ export const encryptStringToBase64url = async (
text: string, text: string,
password: string, password: string,
rounds: number = DEFAULT_ITER, rounds: number = DEFAULT_ITER,
saltHex: string = "" saltHex = ""
) => { ) => {
const enc = await encryptArrayBuffer( const enc = await encryptArrayBuffer(
bufferToArrayBuffer(new TextEncoder().encode(text)), bufferToArrayBuffer(new TextEncoder().encode(text)),
+1 -1
View File
@@ -1,5 +1,5 @@
import { nanoid } from "nanoid";
import { Cipher as CipherRCloneCryptPack } from "@fyears/rclone-crypt"; import { Cipher as CipherRCloneCryptPack } from "@fyears/rclone-crypt";
import { nanoid } from "nanoid";
const ctx: WorkerGlobalScope = self as any; const ctx: WorkerGlobalScope = self as any;
+2 -1
View File
@@ -1,4 +1,4 @@
import { Entity } from "./baseTypes"; import type { Entity } from "./baseTypes";
export abstract class FakeFs { export abstract class FakeFs {
abstract kind: string; abstract kind: string;
@@ -16,4 +16,5 @@ export abstract class FakeFs {
abstract checkConnect(callbackFunc?: any): Promise<boolean>; abstract checkConnect(callbackFunc?: any): Promise<boolean>;
abstract getUserDisplayName(): Promise<string>; abstract getUserDisplayName(): Promise<string>;
abstract revokeAuth(): Promise<any>; abstract revokeAuth(): Promise<any>;
abstract allowEmptyFile(): boolean;
} }
+19 -15
View File
@@ -1,13 +1,13 @@
import { FakeFs } from "./fsAll";
import { Dropbox, DropboxAuth } from "dropbox"; import { Dropbox, DropboxAuth } from "dropbox";
import type { files, DropboxResponseError, DropboxResponse } from "dropbox"; import type { DropboxResponse, DropboxResponseError, files } from "dropbox";
import {
DropboxConfig,
COMMAND_CALLBACK_DROPBOX,
OAUTH2_FORCE_EXPIRE_MILLISECONDS,
Entity,
} from "./baseTypes";
import random from "lodash/random"; import random from "lodash/random";
import {
COMMAND_CALLBACK_DROPBOX,
type DropboxConfig,
type Entity,
OAUTH2_FORCE_EXPIRE_MILLISECONDS,
} from "./baseTypes";
import { FakeFs } from "./fsAll";
import { import {
bufferToArrayBuffer, bufferToArrayBuffer,
delay, delay,
@@ -134,7 +134,7 @@ export const fixEntityListCasesInplace = (entities: { key?: string }[]) => {
caseMapping[newKey.toLocaleLowerCase()] = newKey; caseMapping[newKey.toLocaleLowerCase()] = newKey;
e.key = newKey; e.key = newKey;
// console.log(JSON.stringify(caseMapping,null,2)); // console.log(JSON.stringify(caseMapping,null,2));
continue; // continue;
} else { } else {
throw Error(`${parentFolder} doesn't have cases record??`); throw Error(`${parentFolder} doesn't have cases record??`);
} }
@@ -145,7 +145,7 @@ export const fixEntityListCasesInplace = (entities: { key?: string }[]) => {
.slice(-1) .slice(-1)
.join("/")}`; .join("/")}`;
e.key = newKey; e.key = newKey;
continue; // continue;
} else { } else {
throw Error(`${parentFolder} doesn't have cases record??`); throw Error(`${parentFolder} doesn't have cases record??`);
} }
@@ -167,7 +167,7 @@ interface ErrSubType {
async function retryReq<T>( async function retryReq<T>(
reqFunc: () => Promise<DropboxResponse<T>>, reqFunc: () => Promise<DropboxResponse<T>>,
extraHint: string = "" extraHint = ""
): Promise<DropboxResponse<T> | undefined> { ): Promise<DropboxResponse<T> | undefined> {
const waitSeconds = [1, 2, 4, 8]; // hard code exponential backoff const waitSeconds = [1, 2, 4, 8]; // hard code exponential backoff
for (let idx = 0; idx < waitSeconds.length; ++idx) { for (let idx = 0; idx < waitSeconds.length; ++idx) {
@@ -205,7 +205,7 @@ async function retryReq<T>(
const headers = headersToRecord(err.headers); const headers = headersToRecord(err.headers);
const svrSec = const svrSec =
err.error.error.retry_after || err.error.error.retry_after ||
parseInt(headers["retry-after"] || "1") || Number.parseInt(headers["retry-after"] || "1") ||
1; 1;
const fallbackSec = waitSeconds[idx]; const fallbackSec = waitSeconds[idx];
const secMin = Math.max(svrSec, fallbackSec); const secMin = Math.max(svrSec, fallbackSec);
@@ -233,7 +233,7 @@ async function retryReq<T>(
export const getAuthUrlAndVerifier = async ( export const getAuthUrlAndVerifier = async (
appKey: string, appKey: string,
needManualPatse: boolean = false needManualPatse = false
) => { ) => {
const auth = new DropboxAuth({ const auth = new DropboxAuth({
clientId: appKey, clientId: appKey,
@@ -328,9 +328,9 @@ export const setConfigBySuccessfullAuthInplace = async (
console.info("start updating local info of Dropbox token"); console.info("start updating local info of Dropbox token");
config.accessToken = authRes.access_token; config.accessToken = authRes.access_token;
config.accessTokenExpiresInSeconds = parseInt(authRes.expires_in); config.accessTokenExpiresInSeconds = Number.parseInt(authRes.expires_in);
config.accessTokenExpiresAtTime = config.accessTokenExpiresAtTime =
Date.now() + parseInt(authRes.expires_in) * 1000 - 10 * 1000; Date.now() + Number.parseInt(authRes.expires_in) * 1000 - 10 * 1000;
// manually set it expired after 80 days; // manually set it expired after 80 days;
config.credentialsShouldBeDeletedAtTime = config.credentialsShouldBeDeletedAtTime =
@@ -736,4 +736,8 @@ export class FakeFsDropbox extends FakeFs {
return false; return false;
} }
} }
allowEmptyFile(): boolean {
return true;
}
} }
+11 -3
View File
@@ -1,10 +1,10 @@
import { CipherMethodType, Entity } from "./baseTypes"; import type { CipherMethodType, Entity } from "./baseTypes";
import * as openssl from "./encryptOpenSSL"; import * as openssl from "./encryptOpenSSL";
import * as rclone from "./encryptRClone"; import * as rclone from "./encryptRClone";
import { isVaildText } from "./misc"; import { isVaildText } from "./misc";
import { FakeFs } from "./fsAll";
import cloneDeep from "lodash/cloneDeep"; import cloneDeep from "lodash/cloneDeep";
import { FakeFs } from "./fsAll";
/** /**
* quick guess, no actual decryption here * quick guess, no actual decryption here
@@ -273,9 +273,13 @@ export class FakeFsEncrypt extends FakeFs {
return copyEntityAndCopyKeyEncSizeEnc(innerEntity); return copyEntityAndCopyKeyEncSizeEnc(innerEntity);
} else { } else {
const now = Date.now(); const now = Date.now();
let content = new ArrayBuffer(0);
if (!this.innerFs.allowEmptyFile()) {
content = new ArrayBuffer(1);
}
const innerEntity = await this.innerFs.writeFile( const innerEntity = await this.innerFs.writeFile(
keyEnc, keyEnc,
new ArrayBuffer(0), content,
mtime ?? now, mtime ?? now,
ctime ?? now ctime ?? now
); );
@@ -554,4 +558,8 @@ export class FakeFsEncrypt extends FakeFs {
async revokeAuth(): Promise<any> { async revokeAuth(): Promise<any> {
return await this.innerFs.revokeAuth(); return await this.innerFs.revokeAuth();
} }
allowEmptyFile(): boolean {
return true;
}
} }
+2 -8
View File
@@ -1,5 +1,5 @@
import { RemotelySavePluginSettings } from "./baseTypes"; import type { RemotelySavePluginSettings } from "./baseTypes";
import { FakeFs } from "./fsAll"; import type { FakeFs } from "./fsAll";
import { FakeFsDropbox } from "./fsDropbox"; import { FakeFsDropbox } from "./fsDropbox";
import { FakeFsOnedrive } from "./fsOnedrive"; import { FakeFsOnedrive } from "./fsOnedrive";
import { FakeFsS3 } from "./fsS3"; import { FakeFsS3 } from "./fsS3";
@@ -17,37 +17,31 @@ export function getClient(
switch (settings.serviceType) { switch (settings.serviceType) {
case "s3": case "s3":
return new FakeFsS3(settings.s3); return new FakeFsS3(settings.s3);
break;
case "webdav": case "webdav":
return new FakeFsWebdav( return new FakeFsWebdav(
settings.webdav, settings.webdav,
vaultName, vaultName,
saveUpdatedConfigFunc saveUpdatedConfigFunc
); );
break;
case "dropbox": case "dropbox":
return new FakeFsDropbox( return new FakeFsDropbox(
settings.dropbox, settings.dropbox,
vaultName, vaultName,
saveUpdatedConfigFunc saveUpdatedConfigFunc
); );
break;
case "onedrive": case "onedrive":
return new FakeFsOnedrive( return new FakeFsOnedrive(
settings.onedrive, settings.onedrive,
vaultName, vaultName,
saveUpdatedConfigFunc saveUpdatedConfigFunc
); );
break;
case "webdis": case "webdis":
return new FakeFsWebdis( return new FakeFsWebdis(
settings.webdis, settings.webdis,
vaultName, vaultName,
saveUpdatedConfigFunc saveUpdatedConfigFunc
); );
break;
default: default:
throw Error(`cannot init client for serviceType=${settings.serviceType}`); throw Error(`cannot init client for serviceType=${settings.serviceType}`);
break;
} }
} }
+8 -4
View File
@@ -1,10 +1,10 @@
import { DEFAULT_DEBUG_FOLDER, Entity } from "./baseTypes"; import { DEFAULT_DEBUG_FOLDER, type Entity } from "./baseTypes";
import { FakeFs } from "./fsAll"; import { FakeFs } from "./fsAll";
import { TFile, TFolder, type Vault } from "obsidian"; import { TFile, TFolder, type Vault } from "obsidian";
import { mkdirpInVault, statFix, unixTimeToStr } from "./misc";
import { listFilesInObsFolder } from "./obsFolderLister"; import { listFilesInObsFolder } from "./obsFolderLister";
import { Profiler } from "./profiler"; import type { Profiler } from "./profiler";
import { getFolderLevels, mkdirpInVault, statFix, unixTimeToStr } from "./misc";
export class FakeFsLocal extends FakeFs { export class FakeFsLocal extends FakeFs {
vault: Vault; vault: Vault;
@@ -83,7 +83,7 @@ export class FakeFsLocal extends FakeFs {
if (r.keyRaw.startsWith(DEFAULT_DEBUG_FOLDER)) { if (r.keyRaw.startsWith(DEFAULT_DEBUG_FOLDER)) {
// skip listing the debug folder, // skip listing the debug folder,
// which should always not involved in sync // which should always not involved in sync
continue; // continue;
} else { } else {
local.push(r); local.push(r);
} }
@@ -170,4 +170,8 @@ export class FakeFsLocal extends FakeFs {
async revokeAuth(): Promise<any> { async revokeAuth(): Promise<any> {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
allowEmptyFile(): boolean {
return true;
}
} }
+5 -1
View File
@@ -1,4 +1,4 @@
import { Entity } from "./baseTypes"; import type { Entity } from "./baseTypes";
import { FakeFs } from "./fsAll"; import { FakeFs } from "./fsAll";
export class FakeFsMock extends FakeFs { export class FakeFsMock extends FakeFs {
@@ -49,4 +49,8 @@ export class FakeFsMock extends FakeFs {
async revokeAuth(): Promise<any> { async revokeAuth(): Promise<any> {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
allowEmptyFile(): boolean {
throw new Error("Method not implemented.");
}
} }
+12 -6
View File
@@ -1,19 +1,19 @@
import { CryptoProvider, PublicClientApplication } from "@azure/msal-node";
import type { AuthenticationProvider } from "@microsoft/microsoft-graph-client";
import type { import type {
DriveItem, DriveItem,
FileSystemInfo, FileSystemInfo,
UploadSession, UploadSession,
User, User,
} from "@microsoft/microsoft-graph-types"; } from "@microsoft/microsoft-graph-types";
import { CryptoProvider, PublicClientApplication } from "@azure/msal-node";
import { AuthenticationProvider } from "@microsoft/microsoft-graph-client";
import cloneDeep from "lodash/cloneDeep"; import cloneDeep from "lodash/cloneDeep";
import { request, requestUrl } from "obsidian"; import { request, requestUrl } from "obsidian";
import { import {
COMMAND_CALLBACK_ONEDRIVE, COMMAND_CALLBACK_ONEDRIVE,
DEFAULT_CONTENT_TYPE, DEFAULT_CONTENT_TYPE,
Entity, type Entity,
OAUTH2_FORCE_EXPIRE_MILLISECONDS, OAUTH2_FORCE_EXPIRE_MILLISECONDS,
OnedriveConfig, type OnedriveConfig,
} from "./baseTypes"; } from "./baseTypes";
import { VALID_REQURL } from "./baseTypesObs"; import { VALID_REQURL } from "./baseTypesObs";
import { FakeFs } from "./fsAll"; import { FakeFs } from "./fsAll";
@@ -567,6 +567,7 @@ export class FakeFsOnedrive extends FakeFs {
// TODO: // TODO:
// 20220401: On Android, requestUrl has issue that text becomes base64. // 20220401: On Android, requestUrl has issue that text becomes base64.
// Use fetch everywhere instead! // Use fetch everywhere instead!
// biome-ignore lint/correctness/noConstantCondition: hard code
if (false /*VALID_REQURL*/) { if (false /*VALID_REQURL*/) {
const res = await requestUrl({ const res = await requestUrl({
url: theUrl, url: theUrl,
@@ -617,6 +618,7 @@ export class FakeFsOnedrive extends FakeFs {
// TODO: // TODO:
// 20220401: On Android, requestUrl has issue that text becomes base64. // 20220401: On Android, requestUrl has issue that text becomes base64.
// Use fetch everywhere instead! // Use fetch everywhere instead!
// biome-ignore lint/correctness/noConstantCondition: hard code
if (false /*VALID_REQURL*/) { if (false /*VALID_REQURL*/) {
const res = await requestUrl({ const res = await requestUrl({
url: theUrl, url: theUrl,
@@ -658,7 +660,7 @@ export class FakeFsOnedrive extends FakeFs {
let res = await this._getJson( let res = await this._getJson(
`/drive/special/approot:/${this.remoteBaseDir}:/delta` `/drive/special/approot:/${this.remoteBaseDir}:/delta`
); );
let driveItems = res.value as DriveItem[]; const driveItems = res.value as DriveItem[];
// console.debug(driveItems); // console.debug(driveItems);
while (NEXT_LINK_KEY in res) { while (NEXT_LINK_KEY in res) {
@@ -719,7 +721,7 @@ export class FakeFsOnedrive extends FakeFs {
} else { } else {
// https://stackoverflow.com/questions/56479865/creating-nested-folders-in-one-go-onedrive-api // https://stackoverflow.com/questions/56479865/creating-nested-folders-in-one-go-onedrive-api
// use PATCH to create folder recursively!!! // use PATCH to create folder recursively!!!
let playload: any = { const playload: any = {
folder: {}, folder: {},
"@microsoft.graph.conflictBehavior": "replace", "@microsoft.graph.conflictBehavior": "replace",
}; };
@@ -926,4 +928,8 @@ export class FakeFsOnedrive extends FakeFs {
async getRevokeAddr() { async getRevokeAddr() {
return "https://account.live.com/consent/Manage"; return "https://account.live.com/consent/Manage";
} }
allowEmptyFile(): boolean {
return false;
}
} }
+22 -17
View File
@@ -1,36 +1,37 @@
import type { _Object, PutObjectCommandInput } from "@aws-sdk/client-s3"; import { Buffer } from "buffer";
import * as path from "path";
import { Readable } from "stream";
import type { PutObjectCommandInput, _Object } from "@aws-sdk/client-s3";
import { import {
DeleteObjectCommand, DeleteObjectCommand,
GetObjectCommand, GetObjectCommand,
HeadObjectCommand, HeadObjectCommand,
HeadObjectCommandOutput, type HeadObjectCommandOutput,
ListObjectsV2Command, ListObjectsV2Command,
ListObjectsV2CommandInput, type ListObjectsV2CommandInput,
PutObjectCommand, PutObjectCommand,
S3Client, S3Client,
} from "@aws-sdk/client-s3"; } from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage"; import { Upload } from "@aws-sdk/lib-storage";
import { HttpRequest, HttpResponse } from "@smithy/protocol-http"; import type { HttpHandlerOptions } from "@aws-sdk/types";
import { import {
FetchHttpHandler, FetchHttpHandler,
FetchHttpHandlerOptions, type FetchHttpHandlerOptions,
} from "@smithy/fetch-http-handler"; } from "@smithy/fetch-http-handler";
// @ts-ignore // @ts-ignore
import { requestTimeout } from "@smithy/fetch-http-handler/dist-es/request-timeout"; import { requestTimeout } from "@smithy/fetch-http-handler/dist-es/request-timeout";
import { type HttpRequest, HttpResponse } from "@smithy/protocol-http";
import { buildQueryString } from "@smithy/querystring-builder"; import { buildQueryString } from "@smithy/querystring-builder";
import { HttpHandlerOptions } from "@aws-sdk/types"; // biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
import { Buffer } from "buffer";
import * as mime from "mime-types";
import { Platform, requestUrl, RequestUrlParam } from "obsidian";
import { Readable } from "stream";
import * as path from "path";
import AggregateError from "aggregate-error"; import AggregateError from "aggregate-error";
import { DEFAULT_CONTENT_TYPE, S3Config } from "./baseTypes"; import * as mime from "mime-types";
import { Platform, type RequestUrlParam, requestUrl } from "obsidian";
import PQueue from "p-queue";
import { DEFAULT_CONTENT_TYPE, type S3Config } from "./baseTypes";
import { VALID_REQURL } from "./baseTypesObs"; import { VALID_REQURL } from "./baseTypesObs";
import { bufferToArrayBuffer, getFolderLevels } from "./misc"; import { bufferToArrayBuffer, getFolderLevels } from "./misc";
import PQueue from "p-queue";
import { Entity } from "./baseTypes"; import type { Entity } from "./baseTypes";
import { FakeFs } from "./fsAll"; import { FakeFs } from "./fsAll";
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -349,7 +350,7 @@ const fromS3HeadObjectToEntity = (
let mtimeCli = mtimeSvr; let mtimeCli = mtimeSvr;
if (useAccurateMTime && x.Metadata !== undefined) { if (useAccurateMTime && x.Metadata !== undefined) {
const m2 = Math.floor( const m2 = Math.floor(
parseFloat(x.Metadata.mtime || x.Metadata.MTime || "0") Number.parseFloat(x.Metadata.mtime || x.Metadata.MTime || "0")
); );
if (m2 !== 0) { if (m2 !== 0) {
// to be compatible with RClone, we read and store the time in seconds in new version! // to be compatible with RClone, we read and store the time in seconds in new version!
@@ -458,12 +459,12 @@ export class FakeFsS3 extends FakeFs {
// pass // pass
} else { } else {
mtimeRecords[content.Key!] = Math.floor( mtimeRecords[content.Key!] = Math.floor(
parseFloat( Number.parseFloat(
rspHead.Metadata.mtime || rspHead.Metadata.MTime || "0" rspHead.Metadata.mtime || rspHead.Metadata.MTime || "0"
) )
); );
ctimeRecords[content.Key!] = Math.floor( ctimeRecords[content.Key!] = Math.floor(
parseFloat( Number.parseFloat(
rspHead.Metadata.ctime || rspHead.Metadata.CTime || "0" rspHead.Metadata.ctime || rspHead.Metadata.CTime || "0"
) )
); );
@@ -816,4 +817,8 @@ export class FakeFsS3 extends FakeFs {
async revokeAuth() { async revokeAuth() {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
allowEmptyFile(): boolean {
return true;
}
} }
+13 -8
View File
@@ -1,21 +1,21 @@
import { getReasonPhrase } from "http-status-codes/build/cjs/utils-functions";
import { Buffer } from "buffer"; import { Buffer } from "buffer";
import cloneDeep from "lodash/cloneDeep";
import { Queue } from "@fyears/tsqueue"; import { Queue } from "@fyears/tsqueue";
import { getReasonPhrase } from "http-status-codes/build/cjs/utils-functions";
import chunk from "lodash/chunk"; import chunk from "lodash/chunk";
import cloneDeep from "lodash/cloneDeep";
import flatten from "lodash/flatten"; import flatten from "lodash/flatten";
import { Platform, requestUrl } from "obsidian"; import { Platform, requestUrl } from "obsidian";
import { FakeFs } from "./fsAll";
import { bufferToArrayBuffer } from "./misc";
import { Entity, WebdavConfig } from "./baseTypes";
import { VALID_REQURL } from "./baseTypesObs";
import type { import type {
FileStat, FileStat,
WebDAVClient,
RequestOptionsWithState, RequestOptionsWithState,
WebDAVClient,
// Response, // Response,
// ResponseDataDetailed, // ResponseDataDetailed,
} from "webdav"; } from "webdav";
import type { Entity, WebdavConfig } from "./baseTypes";
import { VALID_REQURL } from "./baseTypesObs";
import { FakeFs } from "./fsAll";
import { bufferToArrayBuffer } from "./misc";
/** /**
* https://stackoverflow.com/questions/32850898/how-to-check-if-a-string-has-any-non-iso-8859-1-characters-with-javascript * https://stackoverflow.com/questions/32850898/how-to-check-if-a-string-has-any-non-iso-8859-1-characters-with-javascript
@@ -95,7 +95,7 @@ if (VALID_REQURL) {
console.debug(`after request:`); console.debug(`after request:`);
const rspHeaders = objKeyToLower({ ...r.headers }); const rspHeaders = objKeyToLower({ ...r.headers });
console.debug(`rspHeaders: ${JSON.stringify(rspHeaders, null, 2)}`); console.debug(`rspHeaders: ${JSON.stringify(rspHeaders, null, 2)}`);
for (let key in rspHeaders) { for (const key in rspHeaders) {
if (rspHeaders.hasOwnProperty(key)) { if (rspHeaders.hasOwnProperty(key)) {
// avoid the error: // avoid the error:
// Failed to read the 'headers' property from 'ResponseInit': String contains non ISO-8859-1 code point. // Failed to read the 'headers' property from 'ResponseInit': String contains non ISO-8859-1 code point.
@@ -143,6 +143,7 @@ if (VALID_REQURL) {
} }
// @ts-ignore // @ts-ignore
// biome-ignore lint: we want to ts-ignore the next line
import { AuthType, BufferLike, createClient } from "webdav/dist/web/index.js"; import { AuthType, BufferLike, createClient } from "webdav/dist/web/index.js";
export const DEFAULT_WEBDAV_CONFIG = { export const DEFAULT_WEBDAV_CONFIG = {
@@ -492,4 +493,8 @@ export class FakeFsWebdav extends FakeFs {
async revokeAuth() { async revokeAuth() {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
allowEmptyFile(): boolean {
return true;
}
} }
+14 -6
View File
@@ -1,5 +1,9 @@
import { cloneDeep, isEqual } from "lodash"; import { isEqual } from "lodash";
import { DEFAULT_CONTENT_TYPE, Entity, WebdisConfig } from "./baseTypes"; import {
DEFAULT_CONTENT_TYPE,
type Entity,
type WebdisConfig,
} from "./baseTypes";
import { FakeFs } from "./fsAll"; import { FakeFs } from "./fsAll";
export const DEFAULT_WEBDIS_CONFIG: WebdisConfig = { export const DEFAULT_WEBDIS_CONFIG: WebdisConfig = {
@@ -149,10 +153,10 @@ export class FakeFsWebdis extends FakeFs {
return { return {
key: realKey, key: realKey,
keyRaw: realKey, keyRaw: realKey,
mtimeCli: parseInt(rsp["mtime"]), mtimeCli: Number.parseInt(rsp["mtime"]),
mtimeSvr: parseInt(rsp["mtime"]), mtimeSvr: Number.parseInt(rsp["mtime"]),
size: parseInt(rsp["size"]), size: Number.parseInt(rsp["size"]),
sizeRaw: parseInt(rsp["size"]), sizeRaw: Number.parseInt(rsp["size"]),
}; };
} }
@@ -235,4 +239,8 @@ export class FakeFsWebdis extends FakeFs {
async revokeAuth(): Promise<any> { async revokeAuth(): Promise<any> {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
allowEmptyFile(): boolean {
return true;
}
} }
+5 -5
View File
@@ -1,11 +1,11 @@
import QRCode from "qrcode";
import cloneDeep from "lodash/cloneDeep"; import cloneDeep from "lodash/cloneDeep";
import QRCode from "qrcode";
import { import {
COMMAND_URI, COMMAND_URI,
UriParams, type QRExportType,
RemotelySavePluginSettings, type RemotelySavePluginSettings,
QRExportType, type UriParams,
} from "./baseTypes"; } from "./baseTypes";
import { getShrinkedSettings } from "./fsOnedrive"; import { getShrinkedSettings } from "./fsOnedrive";
@@ -64,7 +64,7 @@ export const importQrCodeUri = (
inputParams: any, inputParams: any,
currentVaultName: string currentVaultName: string
): ProcessQrCodeResultType => { ): ProcessQrCodeResultType => {
let params = inputParams as UriParams; const params = inputParams as UriParams;
if ( if (
params.func === undefined || params.func === undefined ||
params.func !== "settings" || params.func !== "settings" ||
+5
View File
@@ -53,6 +53,7 @@
"statusbar_time_lessminute": "Synced last minute ago", "statusbar_time_lessminute": "Synced last minute ago",
"statusbar_lastsync": "Synced {{time}} ago", "statusbar_lastsync": "Synced {{time}} ago",
"statusbar_syncing": "Syncing...", "statusbar_syncing": "Syncing...",
"statusbar_failed": "Last sync failed",
"statusbar_now": "Synced just now", "statusbar_now": "Synced just now",
"statusbar_lastsync_label": "Last successful Sync on {{date}}", "statusbar_lastsync_label": "Last successful Sync on {{date}}",
"statusbar_lastsync_never": "Never Synced", "statusbar_lastsync_never": "Never Synced",
@@ -329,6 +330,10 @@
"settings_profiler_results_desc": "The plugin records the time cost of each steps. Here you can export them to know which step is slow.", "settings_profiler_results_desc": "The plugin records the time cost of each steps. Here you can export them to know which step is slow.",
"settings_profiler_results_notice": "Profiler results exported.", "settings_profiler_results_notice": "Profiler results exported.",
"settings_profiler_results_button_all": "Export All", "settings_profiler_results_button_all": "Export All",
"settings_profiler_enabledebugprint": "Enable Profiler Printing",
"settings_profiler_enabledebugprint_desc": "Print profiler result in each insertion to console or not?",
"settings_profiler_recordsize": "Enable Profiler Recording Size",
"settings_profiler_recordsize_desc": "Let profiler record object sizes or not?",
"settings_outputbasepathvaultid": "Output Vault Base Path And Randomly Assigned ID", "settings_outputbasepathvaultid": "Output Vault Base Path And Randomly Assigned ID",
"settings_outputbasepathvaultid_desc": "For debugging purposes.", "settings_outputbasepathvaultid_desc": "For debugging purposes.",
"settings_outputbasepathvaultid_button": "Output", "settings_outputbasepathvaultid_button": "Output",
+1
View File
@@ -53,6 +53,7 @@
"statusbar_time_lessminute": "一分钟之内同步", "statusbar_time_lessminute": "一分钟之内同步",
"statusbar_lastsync": "上一次同步于:{{time}}", "statusbar_lastsync": "上一次同步于:{{time}}",
"statusbar_syncing": "正在同步", "statusbar_syncing": "正在同步",
"statusbar_failed": "上次同步失败了",
"statusbar_now": "刚同步完", "statusbar_now": "刚同步完",
"statusbar_lastsync_label": "上一次同步于:{{date}}", "statusbar_lastsync_label": "上一次同步于:{{date}}",
"statusbar_lastsync_never": "没触发过同步", "statusbar_lastsync_never": "没触发过同步",
+1
View File
@@ -52,6 +52,7 @@
"statusbar_time_lessminute": "一分鐘之內同步", "statusbar_time_lessminute": "一分鐘之內同步",
"statusbar_lastsync": "上一次同步於:{{time}}", "statusbar_lastsync": "上一次同步於:{{time}}",
"statusbar_syncing": "正在同步", "statusbar_syncing": "正在同步",
"statusbar_failed": "上次同步失敗了",
"statusbar_now": "剛同步完", "statusbar_now": "剛同步完",
"statusbar_lastsync_label": "上一次同步於:{{date}}", "statusbar_lastsync_label": "上一次同步於:{{date}}",
"statusbar_lastsync_never": "沒觸發過同步", "statusbar_lastsync_never": "沒觸發過同步",
+5 -5
View File
@@ -4,9 +4,9 @@ extendPrototype(localforage);
export type LocalForage = typeof localforage; export type LocalForage = typeof localforage;
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import type { Entity, MixedEntity, SUPPORTED_SERVICES_TYPE } from "./baseTypes"; import type { Entity, SUPPORTED_SERVICES_TYPE } from "./baseTypes";
import type { SyncPlanType } from "./sync";
import { unixTimeToStr } from "./misc"; import { unixTimeToStr } from "./misc";
import type { SyncPlanType } from "./sync";
const DB_VERSION_NUMBER_IN_HISTORY = [20211114, 20220108, 20220326, 20240220]; const DB_VERSION_NUMBER_IN_HISTORY = [20211114, 20220108, 20220326, 20240220];
export const DEFAULT_DB_VERSION_NUMBER: number = 20240220; export const DEFAULT_DB_VERSION_NUMBER: number = 20240220;
@@ -400,7 +400,7 @@ export const clearExpiredSyncPlanRecords = async (db: InternalDBs) => {
const expiredTs = currTs - MILLISECONDS_OLD; const expiredTs = currTs - MILLISECONDS_OLD;
let records = (await db.syncPlansTbl.keys()).map((key) => { let records = (await db.syncPlansTbl.keys()).map((key) => {
const ts = parseInt(key.split("\t")[1]); const ts = Number.parseInt(key.split("\t")[1]);
const expired = ts <= expiredTs; const expired = ts <= expiredTs;
return { return {
ts: ts, ts: ts,
@@ -542,7 +542,7 @@ export const insertProfilerResultByVault = async (
// clear older one while writing // clear older one while writing
const records = (await db.profilerResultsTbl.keys()) const records = (await db.profilerResultsTbl.keys())
.filter((x) => x.startsWith(`${vaultRandomID}\t`)) .filter((x) => x.startsWith(`${vaultRandomID}\t`))
.map((x) => parseInt(x.split("\t")[1])); .map((x) => Number.parseInt(x.split("\t")[1]));
records.sort((a, b) => -(a - b)); // descending records.sort((a, b) => -(a - b)); // descending
while (records.length > 5) { while (records.length > 5) {
const ts = records.pop()!; const ts = records.pop()!;
@@ -559,7 +559,7 @@ export const readAllProfilerResultsByVault = async (
if (key.startsWith(`${vaultRandomID}\t`)) { if (key.startsWith(`${vaultRandomID}\t`)) {
records.push({ records.push({
val: value as string, val: value as string,
ts: parseInt(key.split("\t")[1]), ts: Number.parseInt(key.split("\t")[1]),
}); });
} }
}); });
+70 -44
View File
@@ -1,68 +1,69 @@
import {
Modal,
Notice,
Plugin,
Setting,
addIcon,
setIcon,
FileSystemAdapter,
Platform,
requireApiVersion,
Events,
TFolder,
} from "obsidian";
import cloneDeep from "lodash/cloneDeep"; import cloneDeep from "lodash/cloneDeep";
import { createElement, RotateCcw, RefreshCcw, FileText } from "lucide"; import { FileText, RefreshCcw, RotateCcw, createElement } from "lucide";
import {
Events,
FileSystemAdapter,
type Modal,
Notice,
Platform,
Plugin,
type Setting,
TFolder,
addIcon,
requireApiVersion,
setIcon,
} from "obsidian";
import type { import type {
RemotelySavePluginSettings, RemotelySavePluginSettings,
SyncTriggerSourceType, SyncTriggerSourceType,
} from "./baseTypes"; } from "./baseTypes";
import { import {
COMMAND_CALLBACK, COMMAND_CALLBACK,
COMMAND_CALLBACK_ONEDRIVE,
COMMAND_CALLBACK_DROPBOX, COMMAND_CALLBACK_DROPBOX,
COMMAND_CALLBACK_ONEDRIVE,
COMMAND_URI, COMMAND_URI,
} from "./baseTypes"; } from "./baseTypes";
import { API_VER_ENSURE_REQURL_OK } from "./baseTypesObs"; import { API_VER_ENSURE_REQURL_OK } from "./baseTypesObs";
import { importQrCodeUri } from "./importExport"; import { messyConfigToNormal, normalConfigToMessy } from "./configPersist";
import {
prepareDBs,
InternalDBs,
clearExpiredSyncPlanRecords,
upsertPluginVersionByVault,
clearAllLoggerOutputRecords,
upsertLastSuccessSyncTimeByVault,
getLastSuccessSyncTimeByVault,
} from "./localdb";
import { import {
DEFAULT_DROPBOX_CONFIG, DEFAULT_DROPBOX_CONFIG,
sendAuthReq as sendAuthReqDropbox, sendAuthReq as sendAuthReqDropbox,
setConfigBySuccessfullAuthInplace as setConfigBySuccessfullAuthInplaceDropbox, setConfigBySuccessfullAuthInplace as setConfigBySuccessfullAuthInplaceDropbox,
} from "./fsDropbox"; } from "./fsDropbox";
import { import {
AccessCodeResponseSuccessfulType, type AccessCodeResponseSuccessfulType,
DEFAULT_ONEDRIVE_CONFIG, DEFAULT_ONEDRIVE_CONFIG,
sendAuthReq as sendAuthReqOnedrive, sendAuthReq as sendAuthReqOnedrive,
setConfigBySuccessfullAuthInplace as setConfigBySuccessfullAuthInplaceOnedrive, setConfigBySuccessfullAuthInplace as setConfigBySuccessfullAuthInplaceOnedrive,
} from "./fsOnedrive"; } from "./fsOnedrive";
import { DEFAULT_S3_CONFIG } from "./fsS3"; import { DEFAULT_S3_CONFIG } from "./fsS3";
import { DEFAULT_WEBDAV_CONFIG } from "./fsWebdav"; import { DEFAULT_WEBDAV_CONFIG } from "./fsWebdav";
import { RemotelySaveSettingTab } from "./settings";
import { messyConfigToNormal, normalConfigToMessy } from "./configPersist";
import { I18n } from "./i18n"; import { I18n } from "./i18n";
import type { LangTypeAndAuto, TransItemType } from "./i18n"; import type { LangTypeAndAuto, TransItemType } from "./i18n";
import { importQrCodeUri } from "./importExport";
import {
type InternalDBs,
clearAllLoggerOutputRecords,
clearExpiredSyncPlanRecords,
getLastSuccessSyncTimeByVault,
prepareDBs,
upsertLastSuccessSyncTimeByVault,
upsertPluginVersionByVault,
} from "./localdb";
import { RemotelySaveSettingTab } from "./settings";
import { SyncAlgoV3Modal } from "./syncAlgoV3Notice"; import { SyncAlgoV3Modal } from "./syncAlgoV3Notice";
// biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
import AggregateError from "aggregate-error"; import AggregateError from "aggregate-error";
import { exportVaultSyncPlansToFiles } from "./debugMode";
import { changeMobileStatusBar } from "./misc";
import { Profiler } from "./profiler";
import { FakeFsLocal } from "./fsLocal";
import { FakeFsEncrypt } from "./fsEncrypt";
import { syncer } from "./sync";
import { getClient } from "./fsGetter";
import throttle from "lodash/throttle"; import throttle from "lodash/throttle";
import { exportVaultSyncPlansToFiles } from "./debugMode";
import { FakeFsEncrypt } from "./fsEncrypt";
import { getClient } from "./fsGetter";
import { FakeFsLocal } from "./fsLocal";
import { DEFAULT_WEBDIS_CONFIG } from "./fsWebdis"; import { DEFAULT_WEBDIS_CONFIG } from "./fsWebdis";
import { changeMobileStatusBar } from "./misc";
import { DEFAULT_PROFILER_CONFIG, Profiler } from "./profiler";
import { syncer } from "./sync";
const DEFAULT_SETTINGS: RemotelySavePluginSettings = { const DEFAULT_SETTINGS: RemotelySavePluginSettings = {
s3: DEFAULT_S3_CONFIG, s3: DEFAULT_S3_CONFIG,
@@ -95,6 +96,7 @@ const DEFAULT_SETTINGS: RemotelySavePluginSettings = {
obfuscateSettingFile: true, obfuscateSettingFile: true,
enableMobileStatusBar: false, enableMobileStatusBar: false,
encryptionMethod: "unknown", encryptionMethod: "unknown",
profiler: DEFAULT_PROFILER_CONFIG,
}; };
interface OAuth2Info { interface OAuth2Info {
@@ -150,7 +152,11 @@ export default class RemotelySavePlugin extends Plugin {
appContainerObserver?: MutationObserver; appContainerObserver?: MutationObserver;
async syncRun(triggerSource: SyncTriggerSourceType = "manual") { async syncRun(triggerSource: SyncTriggerSourceType = "manual") {
const profiler = new Profiler(); const profiler = new Profiler(
undefined,
this.settings.profiler?.enablePrinting ?? false,
this.settings.profiler?.recordSize ?? false
);
const fsLocal = new FakeFsLocal( const fsLocal = new FakeFsLocal(
this.app.vault, this.app.vault,
this.settings.syncConfigDir ?? false, this.settings.syncConfigDir ?? false,
@@ -300,7 +306,6 @@ export default class RemotelySavePlugin extends Plugin {
default: default:
throw Error(`unknown step=${step} for showing notice`); throw Error(`unknown step=${step} for showing notice`);
break;
} }
}; };
@@ -331,17 +336,21 @@ export default class RemotelySavePlugin extends Plugin {
// last step // last step
if (this.syncRibbon !== undefined) { if (this.syncRibbon !== undefined) {
setIcon(this.syncRibbon, iconNameSyncWait); setIcon(this.syncRibbon, iconNameSyncWait);
let originLabel = `${this.manifest.name}`; const originLabel = `${this.manifest.name}`;
this.syncRibbon.setAttribute("aria-label", originLabel); this.syncRibbon.setAttribute("aria-label", originLabel);
} }
} }
}; };
const statusBarFunc = async (s: SyncTriggerSourceType, step: number) => { const statusBarFunc = async (
s: SyncTriggerSourceType,
step: number,
everythingOk: boolean
) => {
if (step === 1) { if (step === 1) {
// change status to "syncing..." on statusbar // change status to "syncing..." on statusbar
this.updateLastSuccessSyncMsg(-1); this.updateLastSuccessSyncMsg(-1);
} else if (step === 8) { } else if (step === 8 && everythingOk) {
const lastSuccessSyncMillis = Date.now(); const lastSuccessSyncMillis = Date.now();
await upsertLastSuccessSyncTimeByVault( await upsertLastSuccessSyncTimeByVault(
this.db, this.db,
@@ -349,6 +358,8 @@ export default class RemotelySavePlugin extends Plugin {
lastSuccessSyncMillis lastSuccessSyncMillis
); );
this.updateLastSuccessSyncMsg(lastSuccessSyncMillis); this.updateLastSuccessSyncMsg(lastSuccessSyncMillis);
} else if (!everythingOk) {
this.updateLastSuccessSyncMsg(-2); // magic number
} }
}; };
@@ -535,7 +546,7 @@ export default class RemotelySavePlugin extends Plugin {
return; return;
} }
let authRes = await sendAuthReqDropbox( const authRes = await sendAuthReqDropbox(
this.settings.dropbox.clientID, this.settings.dropbox.clientID,
this.oauth2Info.verifier, this.oauth2Info.verifier,
inputParams.code, inputParams.code,
@@ -620,7 +631,7 @@ export default class RemotelySavePlugin extends Plugin {
}); });
} }
let rsp = await sendAuthReqOnedrive( const rsp = await sendAuthReqOnedrive(
this.settings.onedrive.clientID, this.settings.onedrive.clientID,
this.settings.onedrive.authority, this.settings.onedrive.authority,
inputParams.code, inputParams.code,
@@ -934,6 +945,16 @@ export default class RemotelySavePlugin extends Plugin {
} }
} }
if (this.settings.profiler === undefined) {
this.settings.profiler = DEFAULT_PROFILER_CONFIG;
}
if (this.settings.profiler.enablePrinting === undefined) {
this.settings.profiler.enablePrinting = false;
}
if (this.settings.profiler.recordSize === undefined) {
this.settings.profiler.recordSize = false;
}
await this.saveSettings(); await this.saveSettings();
} }
@@ -957,7 +978,7 @@ export default class RemotelySavePlugin extends Plugin {
} }
async checkIfOauthExpires() { async checkIfOauthExpires() {
let needSave: boolean = false; let needSave = false;
const current = Date.now(); const current = Date.now();
// fullfill old version settings // fullfill old version settings
@@ -1266,6 +1287,11 @@ export default class RemotelySavePlugin extends Plugin {
lastSyncMsg = t("statusbar_syncing"); lastSyncMsg = t("statusbar_syncing");
} }
if (lastSuccessSyncMillis !== undefined && lastSuccessSyncMillis === -2) {
lastSyncMsg = t("statusbar_failed");
lastSyncLabelMsg = t("statusbar_failed");
}
if (lastSuccessSyncMillis !== undefined && lastSuccessSyncMillis > 0) { if (lastSuccessSyncMillis !== undefined && lastSuccessSyncMillis > 0) {
const deltaTime = Date.now() - lastSuccessSyncMillis; const deltaTime = Date.now() - lastSuccessSyncMillis;
@@ -1298,7 +1324,7 @@ export default class RemotelySavePlugin extends Plugin {
timeText = t("statusbar_now"); timeText = t("statusbar_now");
} }
let dateText = new Date(lastSuccessSyncMillis).toLocaleTimeString( const dateText = new Date(lastSuccessSyncMillis).toLocaleTimeString(
navigator.language, navigator.language,
{ {
weekday: "long", weekday: "long",
+58 -30
View File
@@ -1,9 +1,9 @@
import { Platform, Vault } from "obsidian";
import * as path from "path"; import * as path from "path";
import type { Vault } from "obsidian";
import { base32, base64url } from "rfc4648";
import XRegExp from "xregexp";
import emojiRegex from "emoji-regex"; import emojiRegex from "emoji-regex";
import { base32 } from "rfc4648";
import XRegExp from "xregexp";
declare global { declare global {
interface Window { interface Window {
@@ -18,11 +18,7 @@ declare global {
* @param underscore * @param underscore
* @returns * @returns
*/ */
export const isHiddenPath = ( export const isHiddenPath = (item: string, dot = true, underscore = true) => {
item: string,
dot: boolean = true,
underscore: boolean = true
) => {
if (!(dot || underscore)) { if (!(dot || underscore)) {
throw Error("parameter error for isHiddenPath"); throw Error("parameter error for isHiddenPath");
} }
@@ -50,7 +46,7 @@ export const isHiddenPath = (
* @param x string * @param x string
* @returns string[] might be empty * @returns string[] might be empty
*/ */
export const getFolderLevels = (x: string, addEndingSlash: boolean = false) => { export const getFolderLevels = (x: string, addEndingSlash = false) => {
const res: string[] = []; const res: string[] = [];
if (x === "" || x === "/") { if (x === "" || x === "/") {
@@ -58,7 +54,7 @@ export const getFolderLevels = (x: string, addEndingSlash: boolean = false) => {
} }
const y1 = x.split("/"); const y1 = x.split("/");
let i = 0; const i = 0;
for (let index = 0; index + 1 < y1.length; index++) { for (let index = 0; index + 1 < y1.length; index++) {
let k = y1.slice(0, index + 1).join("/"); let k = y1.slice(0, index + 1).join("/");
if (k === "" || k === "/") { if (k === "" || k === "/") {
@@ -119,7 +115,7 @@ export const base64ToArrayBuffer = (b64text: string) => {
}; };
export const copyArrayBuffer = (src: ArrayBuffer) => { export const copyArrayBuffer = (src: ArrayBuffer) => {
var dst = new ArrayBuffer(src.byteLength); const dst = new ArrayBuffer(src.byteLength);
new Uint8Array(dst).set(new Uint8Array(src)); new Uint8Array(dst).set(new Uint8Array(src));
return dst; return dst;
}; };
@@ -134,18 +130,14 @@ export const hexStringToTypedArray = (hex: string) => {
if (f === null) { if (f === null) {
throw Error(`input ${hex} is not hex, no way to transform`); throw Error(`input ${hex} is not hex, no way to transform`);
} }
return new Uint8Array( return new Uint8Array(f.map((h) => Number.parseInt(h, 16)));
f.map(function (h) {
return parseInt(h, 16);
})
);
}; };
export const base64ToBase32 = (a: string) => { export const base64ToBase32 = (a: string) => {
return base32.stringify(Buffer.from(a, "base64")); return base32.stringify(Buffer.from(a, "base64"));
}; };
export const base64ToBase64url = (a: string, pad: boolean = false) => { export const base64ToBase64url = (a: string, pad = false) => {
let b = a.replace(/\+/g, "-").replace(/\//g, "_"); let b = a.replace(/\+/g, "-").replace(/\//g, "_");
if (!pad) { if (!pad) {
b = b.replace(/=/g, ""); b = b.replace(/=/g, "");
@@ -190,7 +182,7 @@ export const hasEmojiInText = (a: string) => {
* @param toLower * @param toLower
* @returns * @returns
*/ */
export const headersToRecord = (h: Headers, toLower: boolean = true) => { export const headersToRecord = (h: Headers, toLower = true) => {
const res: Record<string, string> = {}; const res: Record<string, string> = {};
h.forEach((v, k) => { h.forEach((v, k) => {
if (toLower) { if (toLower) {
@@ -240,11 +232,11 @@ export const getParentFolder = (a: string) => {
* @param delimiter * @param delimiter
* @returns * @returns
*/ */
export const setToString = (a: Set<string>, delimiter: string = ",") => { export const setToString = (a: Set<string>, delimiter = ",") => {
return [...a].join(delimiter); return [...a].join(delimiter);
}; };
export const extractSvgSub = (x: string, subEl: string = "rect") => { export const extractSvgSub = (x: string, subEl = "rect") => {
const parser = new window.DOMParser(); const parser = new window.DOMParser();
const dom = parser.parseFromString(x, "image/svg+xml"); const dom = parser.parseFromString(x, "image/svg+xml");
const svg = dom.querySelector("svg")!; const svg = dom.querySelector("svg")!;
@@ -261,10 +253,10 @@ export const extractSvgSub = (x: string, subEl: string = "rect") => {
export const getRandomIntInclusive = (min: number, max: number) => { export const getRandomIntInclusive = (min: number, max: number) => {
const randomBuffer = new Uint32Array(1); const randomBuffer = new Uint32Array(1);
window.crypto.getRandomValues(randomBuffer); window.crypto.getRandomValues(randomBuffer);
let randomNumber = randomBuffer[0] / (0xffffffff + 1); const randomNumber = randomBuffer[0] / (0xffffffff + 1);
min = Math.ceil(min); const min2 = Math.ceil(min);
max = Math.floor(max); const max2 = Math.floor(max);
return Math.floor(randomNumber * (max - min + 1)) + min; return Math.floor(randomNumber * (max2 - min2 + 1)) + min2;
}; };
/** /**
@@ -397,9 +389,8 @@ export const toText = (x: any) => {
if ( if (
x instanceof Error || x instanceof Error ||
(x && (x?.stack &&
x.stack && x?.message &&
x.message &&
typeof x.stack === "string" && typeof x.stack === "string" &&
typeof x.message === "string") typeof x.message === "string")
) { ) {
@@ -446,7 +437,7 @@ export const isSpecialFolderNameToSkip = (
x: string, x: string,
more: string[] | undefined more: string[] | undefined
) => { ) => {
let specialFolders = [ const specialFolders = [
".git", ".git",
".github", ".github",
".gitlab", ".gitlab",
@@ -595,6 +586,7 @@ export const changeMobileStatusBar = (
if (oldAppContainerObserver !== undefined) { if (oldAppContainerObserver !== undefined) {
console.debug(`disconnect oldAppContainerObserver`); console.debug(`disconnect oldAppContainerObserver`);
oldAppContainerObserver.disconnect(); oldAppContainerObserver.disconnect();
// biome-ignore lint/style/noParameterAssign: we want gc
oldAppContainerObserver = undefined; oldAppContainerObserver = undefined;
} }
statusbar.style.removeProperty("display"); statusbar.style.removeProperty("display");
@@ -631,7 +623,6 @@ export const fixEntityListCasesInplace = (entities: { keyRaw: string }[]) => {
caseMapping[newKeyRaw.toLocaleLowerCase()] = newKeyRaw; caseMapping[newKeyRaw.toLocaleLowerCase()] = newKeyRaw;
e.keyRaw = newKeyRaw; e.keyRaw = newKeyRaw;
// console.log(JSON.stringify(caseMapping,null,2)); // console.log(JSON.stringify(caseMapping,null,2));
continue;
} else { } else {
throw Error(`${parentFolder} doesn't have cases record??`); throw Error(`${parentFolder} doesn't have cases record??`);
} }
@@ -642,7 +633,6 @@ export const fixEntityListCasesInplace = (entities: { keyRaw: string }[]) => {
.slice(-1) .slice(-1)
.join("/")}`; .join("/")}`;
e.keyRaw = newKeyRaw; e.keyRaw = newKeyRaw;
continue;
} else { } else {
throw Error(`${parentFolder} doesn't have cases record??`); throw Error(`${parentFolder} doesn't have cases record??`);
} }
@@ -651,3 +641,41 @@ export const fixEntityListCasesInplace = (entities: { keyRaw: string }[]) => {
return entities; return entities;
}; };
/**
* https://stackoverflow.com/questions/1248302/how-to-get-the-size-of-a-javascript-object
* @param object
* @returns bytes
*/
export const roughSizeOfObject = (object: any) => {
const objectList: any[] = [];
const stack = [object];
let bytes = 0;
while (stack.length) {
const value = stack.pop();
switch (typeof value) {
case "boolean":
bytes += 4;
break;
case "string":
bytes += value.length * 2;
break;
case "number":
bytes += 8;
break;
case "object":
if (!objectList.includes(value)) {
objectList.push(value);
for (const prop in value) {
if (value.hasOwnProperty(prop)) {
stack.push(value[prop]);
}
}
}
break;
}
}
return bytes;
};
+3 -3
View File
@@ -1,10 +1,10 @@
import type { Vault, Stat, ListedFiles } from "obsidian"; import type { ListedFiles, Vault } from "obsidian";
import type { Entity, MixedEntity } from "./baseTypes"; import type { Entity } from "./baseTypes";
import { Queue } from "@fyears/tsqueue"; import { Queue } from "@fyears/tsqueue";
import chunk from "lodash/chunk"; import chunk from "lodash/chunk";
import flatten from "lodash/flatten"; import flatten from "lodash/flatten";
import { statFix, isSpecialFolderNameToSkip } from "./misc"; import { isSpecialFolderNameToSkip, statFix } from "./misc";
const isPluginDirItself = (x: string, pluginId: string) => { const isPluginDirItself = (x: string, pluginId: string) => {
return ( return (
+73 -18
View File
@@ -1,29 +1,43 @@
import { SUPPORTED_SERVICES_TYPE } from "./baseTypes"; import type { ProfilerConfig, SUPPORTED_SERVICES_TYPE } from "./baseTypes";
import { InternalDBs, insertProfilerResultByVault } from "./localdb"; import { type InternalDBs, insertProfilerResultByVault } from "./localdb";
import { unixTimeToStr } from "./misc"; import { roughSizeOfObject, unixTimeToStr } from "./misc";
interface BreakPoint { interface BreakPoint {
label: string; label: string;
fakeTimeMilli: number; // it's NOT a unix timestamp fakeTimeMilli: number; // it's NOT a unix timestamp
indent: number; indent: number;
size?: number;
} }
export const DEFAULT_PROFILER_CONFIG: ProfilerConfig = {
enablePrinting: false,
recordSize: false,
};
export class Profiler { export class Profiler {
startTime: number; startTime: number;
breakPoints: BreakPoint[]; breakPoints: BreakPoint[];
indent: number; indent: number;
constructor(label?: string) { enablePrinting: boolean;
recordSize: boolean;
constructor(label?: string, enablePrinting?: boolean, recordSize?: boolean) {
this.breakPoints = []; this.breakPoints = [];
this.indent = 0; this.indent = 0;
this.startTime = 0; this.startTime = 0;
this.enablePrinting = enablePrinting ?? false;
this.recordSize = recordSize ?? false;
if (label !== undefined) { if (label !== undefined) {
this.startTime = Date.now(); this.startTime = Date.now();
this.breakPoints.push({ const p = {
label: label, label: label,
fakeTimeMilli: performance.now(), fakeTimeMilli: performance.now(),
indent: this.indent, indent: this.indent,
}); };
this.breakPoints.push(p);
if (this.enablePrinting) {
console.debug(this.toString(-1));
}
} }
} }
@@ -31,11 +45,36 @@ export class Profiler {
if (this.breakPoints.length === 0) { if (this.breakPoints.length === 0) {
this.startTime = Date.now(); this.startTime = Date.now();
} }
this.breakPoints.push({ const p = {
label: label, label: label,
fakeTimeMilli: performance.now(), fakeTimeMilli: performance.now(),
indent: this.indent, indent: this.indent,
}); };
this.breakPoints.push(p);
if (this.enablePrinting) {
console.debug(this.toString(-1));
}
return this;
}
insertSize(label: string, obj: any) {
if (!this.recordSize) {
return;
}
if (this.breakPoints.length === 0) {
this.startTime = Date.now();
}
const p = {
label: label,
fakeTimeMilli: performance.now(),
indent: this.indent,
size: roughSizeOfObject(obj),
};
this.breakPoints.push(p);
if (this.enablePrinting) {
console.debug(this.toString(-1));
}
return this; return this;
} }
@@ -57,7 +96,31 @@ export class Profiler {
return this; return this;
} }
toString() { toString(idx?: number) {
if (idx !== undefined) {
let i = idx;
if (idx < 0) {
i = this.breakPoints.length + idx;
}
const label = this.breakPoints?.[i]["label"];
const indent = this.breakPoints?.[i]["indent"];
let millsec = 0;
if (i >= 1) {
millsec =
Math.round(
(this.breakPoints?.[i]["fakeTimeMilli"] -
this.breakPoints?.[i - 1]["fakeTimeMilli"]) *
10
) / 10.0;
}
let res = `${" ".repeat(indent)}[${label}]: ${millsec}ms`;
if (this.breakPoints[i].hasOwnProperty("size")) {
const size = this.breakPoints[i].size as number;
res += `, size=${size}`;
}
return res;
}
if (this.breakPoints.length === 0) { if (this.breakPoints.length === 0) {
return "nothing in profiler"; return "nothing in profiler";
} }
@@ -67,15 +130,7 @@ export class Profiler {
if (i === 0) { if (i === 0) {
res += `\n[${this.breakPoints[i]["label"]}]: start`; res += `\n[${this.breakPoints[i]["label"]}]: start`;
} else { } else {
const label = this.breakPoints[i]["label"]; res += `\n${this.toString(i)}`;
const indent = this.breakPoints[i]["indent"];
const millsec =
Math.round(
(this.breakPoints[i]["fakeTimeMilli"] -
this.breakPoints[i - 1]["fakeTimeMilli"]) *
10
) / 10.0;
res += `\n${" ".repeat(indent)}[${label}]: ${millsec}ms`;
} }
} }
+94 -63
View File
@@ -1,37 +1,45 @@
import { Eye, EyeOff, createElement } from "lucide";
import { import {
App, type App,
Modal, Modal,
Notice, Notice,
Platform,
PluginSettingTab, PluginSettingTab,
Setting, Setting,
Platform,
requireApiVersion, requireApiVersion,
requestUrl,
} from "obsidian"; } from "obsidian";
import type { TextComponent } from "obsidian"; import type { TextComponent } from "obsidian";
import { createElement, Eye, EyeOff } from "lucide"; import type {
import { CipherMethodType,
ConflictActionType, ConflictActionType,
DEFAULT_DEBUG_FOLDER,
EmptyFolderCleanType, EmptyFolderCleanType,
QRExportType,
SUPPORTED_SERVICES_TYPE, SUPPORTED_SERVICES_TYPE,
SUPPORTED_SERVICES_TYPE_WITH_REMOTE_BASE_DIR, SUPPORTED_SERVICES_TYPE_WITH_REMOTE_BASE_DIR,
SyncDirectionType, SyncDirectionType,
WebdavAuthType, WebdavAuthType,
WebdavDepthType,
CipherMethodType,
QRExportType,
} from "./baseTypes"; } from "./baseTypes";
import { import cloneDeep from "lodash/cloneDeep";
API_VER_ENSURE_REQURL_OK, import { API_VER_ENSURE_REQURL_OK, VALID_REQURL } from "./baseTypesObs";
API_VER_REQURL, import { messyConfigToNormal } from "./configPersist";
VALID_REQURL,
} from "./baseTypesObs";
import { import {
exportVaultProfilerResultsToFiles, exportVaultProfilerResultsToFiles,
exportVaultSyncPlansToFiles, exportVaultSyncPlansToFiles,
} from "./debugMode"; } from "./debugMode";
import {
DEFAULT_DROPBOX_CONFIG,
getAuthUrlAndVerifier as getAuthUrlAndVerifierDropbox,
sendAuthReq as sendAuthReqDropbox,
setConfigBySuccessfullAuthInplace,
} from "./fsDropbox";
import { getClient } from "./fsGetter";
import {
DEFAULT_ONEDRIVE_CONFIG,
getAuthUrlAndVerifier as getAuthUrlAndVerifierOnedrive,
} from "./fsOnedrive";
import { simpleTransRemotePrefix } from "./fsS3";
import type { TransItemType } from "./i18n";
import { import {
exportQrCodeUri, exportQrCodeUri,
importQrCodeUri, importQrCodeUri,
@@ -44,27 +52,12 @@ import {
upsertLastSuccessSyncTimeByVault, upsertLastSuccessSyncTimeByVault,
} from "./localdb"; } from "./localdb";
import type RemotelySavePlugin from "./main"; // unavoidable import type RemotelySavePlugin from "./main"; // unavoidable
import { FakeFs } from "./fsAll";
import {
DEFAULT_DROPBOX_CONFIG,
getAuthUrlAndVerifier as getAuthUrlAndVerifierDropbox,
sendAuthReq as sendAuthReqDropbox,
setConfigBySuccessfullAuthInplace,
} from "./fsDropbox";
import {
DEFAULT_ONEDRIVE_CONFIG,
getAuthUrlAndVerifier as getAuthUrlAndVerifierOnedrive,
} from "./fsOnedrive";
import { messyConfigToNormal } from "./configPersist";
import type { TransItemType } from "./i18n";
import { import {
changeMobileStatusBar, changeMobileStatusBar,
checkHasSpecialCharForDir, checkHasSpecialCharForDir,
stringToFragment, stringToFragment,
} from "./misc"; } from "./misc";
import { simpleTransRemotePrefix } from "./fsS3"; import { DEFAULT_PROFILER_CONFIG } from "./profiler";
import cloneDeep from "lodash/cloneDeep";
import { getClient } from "./fsGetter";
class PasswordModal extends Modal { class PasswordModal extends Modal {
plugin: RemotelySavePlugin; plugin: RemotelySavePlugin;
@@ -76,7 +69,7 @@ class PasswordModal extends Modal {
} }
onOpen() { onOpen() {
let { contentEl } = this; const { contentEl } = this;
const t = (x: TransItemType, vars?: any) => { const t = (x: TransItemType, vars?: any) => {
return this.plugin.i18n.t(x, vars); return this.plugin.i18n.t(x, vars);
@@ -131,7 +124,7 @@ class PasswordModal extends Modal {
} }
onClose() { onClose() {
let { contentEl } = this; const { contentEl } = this;
contentEl.empty(); contentEl.empty();
} }
} }
@@ -144,7 +137,7 @@ class EncryptionMethodModal extends Modal {
} }
onOpen() { onOpen() {
let { contentEl } = this; const { contentEl } = this;
const t = (x: TransItemType, vars?: any) => { const t = (x: TransItemType, vars?: any) => {
return this.plugin.i18n.t(x, vars); return this.plugin.i18n.t(x, vars);
@@ -170,7 +163,7 @@ class EncryptionMethodModal extends Modal {
} }
onClose() { onClose() {
let { contentEl } = this; const { contentEl } = this;
contentEl.empty(); contentEl.empty();
} }
} }
@@ -192,7 +185,7 @@ class ChangeRemoteBaseDirModal extends Modal {
} }
onOpen() { onOpen() {
let { contentEl } = this; const { contentEl } = this;
const t = (x: TransItemType, vars?: any) => { const t = (x: TransItemType, vars?: any) => {
return this.plugin.i18n.t(x, vars); return this.plugin.i18n.t(x, vars);
@@ -264,7 +257,7 @@ class ChangeRemoteBaseDirModal extends Modal {
} }
onClose() { onClose() {
let { contentEl } = this; const { contentEl } = this;
contentEl.empty(); contentEl.empty();
} }
} }
@@ -283,7 +276,7 @@ class ChangeRemotePrefixModal extends Modal {
} }
onOpen() { onOpen() {
let { contentEl } = this; const { contentEl } = this;
const t = (x: TransItemType, vars?: any) => { const t = (x: TransItemType, vars?: any) => {
return this.plugin.i18n.t(x, vars); return this.plugin.i18n.t(x, vars);
@@ -346,7 +339,7 @@ class ChangeRemotePrefixModal extends Modal {
} }
onClose() { onClose() {
let { contentEl } = this; const { contentEl } = this;
contentEl.empty(); contentEl.empty();
} }
} }
@@ -371,7 +364,7 @@ class DropboxAuthModal extends Modal {
} }
async onOpen() { async onOpen() {
let { contentEl } = this; const { contentEl } = this;
const t = (x: TransItemType, vars?: any) => { const t = (x: TransItemType, vars?: any) => {
return this.plugin.i18n.t(x, vars); return this.plugin.i18n.t(x, vars);
@@ -509,7 +502,7 @@ class DropboxAuthModal extends Modal {
} }
onClose() { onClose() {
let { contentEl } = this; const { contentEl } = this;
contentEl.empty(); contentEl.empty();
} }
} }
@@ -534,7 +527,7 @@ export class OnedriveAuthModal extends Modal {
} }
async onOpen() { async onOpen() {
let { contentEl } = this; const { contentEl } = this;
const { authUrl, verifier } = await getAuthUrlAndVerifierOnedrive( const { authUrl, verifier } = await getAuthUrlAndVerifierOnedrive(
this.plugin.settings.onedrive.clientID, this.plugin.settings.onedrive.clientID,
@@ -583,7 +576,7 @@ export class OnedriveAuthModal extends Modal {
} }
onClose() { onClose() {
let { contentEl } = this; const { contentEl } = this;
contentEl.empty(); contentEl.empty();
} }
} }
@@ -605,7 +598,7 @@ export class OnedriveRevokeAuthModal extends Modal {
} }
async onOpen() { async onOpen() {
let { contentEl } = this; const { contentEl } = this;
const t = (x: TransItemType, vars?: any) => { const t = (x: TransItemType, vars?: any) => {
return this.plugin.i18n.t(x, vars); return this.plugin.i18n.t(x, vars);
}; };
@@ -653,7 +646,7 @@ export class OnedriveRevokeAuthModal extends Modal {
} }
onClose() { onClose() {
let { contentEl } = this; const { contentEl } = this;
contentEl.empty(); contentEl.empty();
} }
} }
@@ -672,7 +665,7 @@ class SyncConfigDirModal extends Modal {
} }
async onOpen() { async onOpen() {
let { contentEl } = this; const { contentEl } = this;
const t = (x: TransItemType, vars?: any) => { const t = (x: TransItemType, vars?: any) => {
return this.plugin.i18n.t(x, vars); return this.plugin.i18n.t(x, vars);
@@ -706,7 +699,7 @@ class SyncConfigDirModal extends Modal {
} }
onClose() { onClose() {
let { contentEl } = this; const { contentEl } = this;
contentEl.empty(); contentEl.empty();
} }
} }
@@ -721,7 +714,7 @@ class ExportSettingsQrCodeModal extends Modal {
} }
async onOpen() { async onOpen() {
let { contentEl } = this; const { contentEl } = this;
const t = (x: TransItemType, vars?: any) => { const t = (x: TransItemType, vars?: any) => {
return this.plugin.i18n.t(x, vars); return this.plugin.i18n.t(x, vars);
@@ -770,7 +763,7 @@ class ExportSettingsQrCodeModal extends Modal {
} }
onClose() { onClose() {
let { contentEl } = this; const { contentEl } = this;
contentEl.empty(); contentEl.empty();
} }
} }
@@ -810,7 +803,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
} }
display(): void { display(): void {
let { containerEl } = this; const { containerEl } = this;
containerEl.style.setProperty("overflow-wrap", "break-word"); containerEl.style.setProperty("overflow-wrap", "break-word");
containerEl.empty(); containerEl.empty();
@@ -1007,7 +1000,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
dropdown dropdown
.setValue(`${this.plugin.settings.s3.partsConcurrency}`) .setValue(`${this.plugin.settings.s3.partsConcurrency}`)
.onChange(async (val) => { .onChange(async (val) => {
const realVal = parseInt(val); const realVal = Number.parseInt(val);
this.plugin.settings.s3.partsConcurrency = realVal; this.plugin.settings.s3.partsConcurrency = realVal;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}); });
@@ -1086,7 +1079,11 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
dropdown dropdown
.setValue( .setValue(
`${this.plugin.settings.s3.generateFolderObject ? "generate" : "notgenerate"}` `${
this.plugin.settings.s3.generateFolderObject
? "generate"
: "notgenerate"
}`
) )
.onChange(async (val) => { .onChange(async (val) => {
if (val === "generate") { if (val === "generate") {
@@ -1174,7 +1171,6 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
button.setButtonText(t("settings_dropbox_revoke_button")); button.setButtonText(t("settings_dropbox_revoke_button"));
button.onClick(async () => { button.onClick(async () => {
try { try {
const self = this;
const client = getClient( const client = getClient(
this.plugin.settings, this.plugin.settings,
this.app.vault.getName(), this.app.vault.getName(),
@@ -1285,7 +1281,6 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
button.setButtonText(t("settings_checkonnectivity_button")); button.setButtonText(t("settings_checkonnectivity_button"));
button.onClick(async () => { button.onClick(async () => {
new Notice(t("settings_checkonnectivity_checking")); new Notice(t("settings_checkonnectivity_checking"));
const self = this;
const client = getClient( const client = getClient(
this.plugin.settings, this.plugin.settings,
this.app.vault.getName(), this.app.vault.getName(),
@@ -1430,7 +1425,6 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
button.setButtonText(t("settings_checkonnectivity_button")); button.setButtonText(t("settings_checkonnectivity_button"));
button.onClick(async () => { button.onClick(async () => {
new Notice(t("settings_checkonnectivity_checking")); new Notice(t("settings_checkonnectivity_checking"));
const self = this;
const client = getClient( const client = getClient(
this.plugin.settings, this.plugin.settings,
this.app.vault.getName(), this.app.vault.getName(),
@@ -1635,7 +1629,6 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
button.setButtonText(t("settings_checkonnectivity_button")); button.setButtonText(t("settings_checkonnectivity_button"));
button.onClick(async () => { button.onClick(async () => {
new Notice(t("settings_checkonnectivity_checking")); new Notice(t("settings_checkonnectivity_checking"));
const self = this;
const client = getClient( const client = getClient(
this.plugin.settings, this.plugin.settings,
this.app.vault.getName(), this.app.vault.getName(),
@@ -1765,7 +1758,6 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
button.setButtonText(t("settings_checkonnectivity_button")); button.setButtonText(t("settings_checkonnectivity_button"));
button.onClick(async () => { button.onClick(async () => {
new Notice(t("settings_checkonnectivity_checking")); new Notice(t("settings_checkonnectivity_checking"));
const self = this;
const client = getClient( const client = getClient(
this.plugin.settings, this.plugin.settings,
this.app.vault.getName(), this.app.vault.getName(),
@@ -1885,7 +1877,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
dropdown dropdown
.setValue(`${this.plugin.settings.autoRunEveryMilliseconds}`) .setValue(`${this.plugin.settings.autoRunEveryMilliseconds}`)
.onChange(async (val: string) => { .onChange(async (val: string) => {
const realVal = parseInt(val); const realVal = Number.parseInt(val);
this.plugin.settings.autoRunEveryMilliseconds = realVal; this.plugin.settings.autoRunEveryMilliseconds = realVal;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
if ( if (
@@ -1930,7 +1922,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
dropdown dropdown
.setValue(`${this.plugin.settings.initRunAfterMilliseconds}`) .setValue(`${this.plugin.settings.initRunAfterMilliseconds}`)
.onChange(async (val: string) => { .onChange(async (val: string) => {
const realVal = parseInt(val); const realVal = Number.parseInt(val);
this.plugin.settings.initRunAfterMilliseconds = realVal; this.plugin.settings.initRunAfterMilliseconds = realVal;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}); });
@@ -1950,7 +1942,8 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
dropdown dropdown
.setValue(`${syncOnSaveEnabled ? "1000" : "-1"}`) .setValue(`${syncOnSaveEnabled ? "1000" : "-1"}`)
.onChange(async (val: string) => { .onChange(async (val: string) => {
this.plugin.settings.syncOnSaveAfterMilliseconds = parseInt(val); this.plugin.settings.syncOnSaveAfterMilliseconds =
Number.parseInt(val);
await this.plugin.saveSettings(); await this.plugin.saveSettings();
this.plugin.toggleSyncOnSaveIfSet(); this.plugin.toggleSyncOnSaveIfSet();
}); });
@@ -1969,7 +1962,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
dropdown dropdown
.setValue(`${this.plugin.settings.skipSizeLargerThan}`) .setValue(`${this.plugin.settings.skipSizeLargerThan}`)
.onChange(async (val) => { .onChange(async (val) => {
this.plugin.settings.skipSizeLargerThan = parseInt(val); this.plugin.settings.skipSizeLargerThan = Number.parseInt(val);
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}); });
}); });
@@ -2051,7 +2044,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
dropdown dropdown
.setValue(`${this.plugin.settings.concurrency}`) .setValue(`${this.plugin.settings.concurrency}`)
.onChange(async (val) => { .onChange(async (val) => {
const realVal = parseInt(val); const realVal = Number.parseInt(val);
this.plugin.settings.concurrency = realVal; this.plugin.settings.concurrency = realVal;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}); });
@@ -2179,7 +2172,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
dropdown dropdown
.setValue(`${this.plugin.settings.protectModifyPercentage ?? 50}`) .setValue(`${this.plugin.settings.protectModifyPercentage ?? 50}`)
.onChange(async (val) => { .onChange(async (val) => {
this.plugin.settings.protectModifyPercentage = parseInt(val); this.plugin.settings.protectModifyPercentage = Number.parseInt(val);
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}); });
}); });
@@ -2486,6 +2479,44 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
}); });
}); });
new Setting(debugDiv)
.setName(t("settings_profiler_enabledebugprint"))
.setDesc(t("settings_profiler_enabledebugprint_desc"))
.addDropdown((dropdown) => {
dropdown.addOption("enable", t("enable"));
dropdown.addOption("disable", t("disable"));
dropdown
.setValue(
this.plugin.settings.profiler?.enablePrinting ? "enable" : "disable"
)
.onChange(async (val: string) => {
if (this.plugin.settings.profiler === undefined) {
this.plugin.settings.profiler = DEFAULT_PROFILER_CONFIG;
}
this.plugin.settings.profiler.enablePrinting = val === "enable";
await this.plugin.saveSettings();
});
});
new Setting(debugDiv)
.setName(t("settings_profiler_recordsize"))
.setDesc(t("settings_profiler_recordsize_desc"))
.addDropdown((dropdown) => {
dropdown.addOption("enable", t("enable"));
dropdown.addOption("disable", t("disable"));
dropdown
.setValue(
this.plugin.settings.profiler?.recordSize ? "enable" : "disable"
)
.onChange(async (val: string) => {
if (this.plugin.settings.profiler === undefined) {
this.plugin.settings.profiler = DEFAULT_PROFILER_CONFIG;
}
this.plugin.settings.profiler.recordSize = val === "enable";
await this.plugin.saveSettings();
});
});
new Setting(debugDiv) new Setting(debugDiv)
.setName(t("settings_outputbasepathvaultid")) .setName(t("settings_outputbasepathvaultid"))
.setDesc(t("settings_outputbasepathvaultid_desc")) .setDesc(t("settings_outputbasepathvaultid_desc"))
@@ -2511,7 +2542,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
} }
hide() { hide() {
let { containerEl } = this; const { containerEl } = this;
containerEl.empty(); containerEl.empty();
super.hide(); super.hide();
} }
+72 -24
View File
@@ -1,5 +1,8 @@
// biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
import AggregateError from "aggregate-error";
import PQueue from "p-queue";
import XRegExp from "xregexp"; import XRegExp from "xregexp";
import { import type {
ConflictActionType, ConflictActionType,
EmptyFolderCleanType, EmptyFolderCleanType,
Entity, Entity,
@@ -9,29 +12,28 @@ import {
SyncDirectionType, SyncDirectionType,
SyncTriggerSourceType, SyncTriggerSourceType,
} from "./baseTypes"; } from "./baseTypes";
import { FakeFs } from "./fsAll"; import type { FakeFs } from "./fsAll";
import { FakeFsEncrypt } from "./fsEncrypt"; import type { FakeFsEncrypt } from "./fsEncrypt";
import { import {
InternalDBs, type InternalDBs,
clearPrevSyncRecordByVaultAndProfile, clearPrevSyncRecordByVaultAndProfile,
getAllPrevSyncRecordsByVaultAndProfile, getAllPrevSyncRecordsByVaultAndProfile,
insertSyncPlanRecordByVault, insertSyncPlanRecordByVault,
upsertPrevSyncRecordByVaultAndProfile, upsertPrevSyncRecordByVaultAndProfile,
} from "./localdb"; } from "./localdb";
import {
DEFAULT_FILE_NAME_FOR_METADATAONREMOTE,
DEFAULT_FILE_NAME_FOR_METADATAONREMOTE2,
} from "./metadataOnRemote";
import { import {
atWhichLevel, atWhichLevel,
getParentFolder, getParentFolder,
isHiddenPath, isHiddenPath,
isSpecialFolderNameToSkip, isSpecialFolderNameToSkip,
roughSizeOfObject,
unixTimeToStr, unixTimeToStr,
} from "./misc"; } from "./misc";
import { Profiler } from "./profiler"; import type { Profiler } from "./profiler";
import {
DEFAULT_FILE_NAME_FOR_METADATAONREMOTE,
DEFAULT_FILE_NAME_FOR_METADATAONREMOTE2,
} from "./metadataOnRemote";
import AggregateError from "aggregate-error";
import PQueue from "p-queue";
const copyEntityAndFixTimeFormat = ( const copyEntityAndFixTimeFormat = (
src: Entity, src: Entity,
@@ -157,6 +159,9 @@ const ensembleMixedEnties = async (
): Promise<SyncPlanType> => { ): Promise<SyncPlanType> => {
profiler.addIndent(); profiler.addIndent();
profiler.insert("ensembleMixedEnties: enter"); profiler.insert("ensembleMixedEnties: enter");
profiler.insertSize("sizeof localEntityList", localEntityList);
profiler.insertSize("sizeof prevSyncEntityList", prevSyncEntityList);
profiler.insertSize("sizeof remoteEntityList", remoteEntityList);
const finalMappings: SyncPlanType = {}; const finalMappings: SyncPlanType = {};
@@ -186,6 +191,7 @@ const ensembleMixedEnties = async (
} }
profiler.insert("ensembleMixedEnties: finish remote"); profiler.insert("ensembleMixedEnties: finish remote");
profiler.insertSize("sizeof finalMappings", finalMappings);
if (Object.keys(finalMappings).length === 0 || localEntityList.length === 0) { if (Object.keys(finalMappings).length === 0 || localEntityList.length === 0) {
// Special checking: // Special checking:
@@ -226,6 +232,7 @@ const ensembleMixedEnties = async (
} }
profiler.insert("ensembleMixedEnties: finish prevSync"); profiler.insert("ensembleMixedEnties: finish prevSync");
profiler.insertSize("sizeof finalMappings", finalMappings);
// local has to be last // local has to be last
// because we want to get keyEnc based on the remote // because we want to get keyEnc based on the remote
@@ -259,6 +266,7 @@ const ensembleMixedEnties = async (
} }
profiler.insert("ensembleMixedEnties: finish local"); profiler.insert("ensembleMixedEnties: finish local");
profiler.insertSize("sizeof finalMappings", finalMappings);
// console.debug("in the end of ensembleMixedEnties, finalMappings is:"); // console.debug("in the end of ensembleMixedEnties, finalMappings is:");
// console.debug(finalMappings); // console.debug(finalMappings);
@@ -279,7 +287,9 @@ const getSyncPlanInplace = async (
skipSizeLargerThan: number, skipSizeLargerThan: number,
conflictAction: ConflictActionType, conflictAction: ConflictActionType,
syncDirection: SyncDirectionType, syncDirection: SyncDirectionType,
profiler: Profiler profiler: Profiler,
settings: RemotelySavePluginSettings,
triggerSource: SyncTriggerSourceType
) => { ) => {
profiler.addIndent(); profiler.addIndent();
profiler.insert("getSyncPlanInplace: enter"); profiler.insert("getSyncPlanInplace: enter");
@@ -288,10 +298,17 @@ const getSyncPlanInplace = async (
(k1, k2) => k2.length - k1.length (k1, k2) => k2.length - k1.length
); );
profiler.insert("getSyncPlanInplace: finish sorting"); profiler.insert("getSyncPlanInplace: finish sorting");
profiler.insertSize("sizeof sortedKeys", sortedKeys);
const keptFolder = new Set<string>(); const keptFolder = new Set<string>();
for (let i = 0; i < sortedKeys.length; ++i) { for (let i = 0; i < sortedKeys.length; ++i) {
if (i % 100 === 0) {
profiler.insertSize(
`sizeof sortedKeys in the beginning of i=${i}`,
mixedEntityMappings
);
}
const key = sortedKeys[i]; const key = sortedKeys[i];
const mixedEntry = mixedEntityMappings[key]; const mixedEntry = mixedEntityMappings[key];
const { local, prevSync, remote } = mixedEntry; const { local, prevSync, remote } = mixedEntry;
@@ -702,16 +719,28 @@ const getSyncPlanInplace = async (
const currTimeFmt = unixTimeToStr(currTime); const currTimeFmt = unixTimeToStr(currTime);
// because the path should not as / in the beginning, // because the path should not as / in the beginning,
// we should be safe to add these keys: // we should be safe to add these keys:
const sizeofmixedEntityMappings = roughSizeOfObject(mixedEntityMappings);
mixedEntityMappings["/$@meta"] = { mixedEntityMappings["/$@meta"] = {
key: "/$@meta", // don't mess up with the types key: "/$@meta", // don't mess up with the types
sideNotes: { sideNotes: {
version: "2024047 fs version", version: "20240508 fs version",
generateTime: currTime, generateTime: currTime,
generateTimeFmt: currTimeFmt, generateTimeFmt: currTimeFmt,
service: settings.serviceType,
hasPassword: settings.password !== "",
syncConfigDir: settings.syncConfigDir,
conflictAction: conflictAction,
syncDirection: syncDirection,
triggerSource: triggerSource,
sizeof: sizeofmixedEntityMappings
}, },
}; };
profiler.insert("getSyncPlanInplace: exit"); profiler.insert("getSyncPlanInplace: exit");
profiler.insertSize(
"sizeof mixedEntityMappings in the end of getSyncPlanInplace",
mixedEntityMappings
);
profiler.removeIndent(); profiler.removeIndent();
return mixedEntityMappings; return mixedEntityMappings;
@@ -1125,6 +1154,16 @@ export const doActualSync = async (
console.debug(`realTotalCount: ${realTotalCount}`); console.debug(`realTotalCount: ${realTotalCount}`);
profiler.insert("doActualSync: finish splitting steps"); profiler.insert("doActualSync: finish splitting steps");
profiler.insertSize(
"doActualSync: sizeof onlyMarkSyncedOps",
onlyMarkSyncedOps
);
profiler.insertSize(
"doActualSync: sizeof folderCreationOps",
folderCreationOps
);
profiler.insertSize("doActualSync: sizeof realTotalCount", deletionOps);
console.debug(`protectModifyPercentage: ${protectModifyPercentage}`); console.debug(`protectModifyPercentage: ${protectModifyPercentage}`);
if ( if (
@@ -1282,26 +1321,32 @@ export async function syncer(
notifyFunc?: (s: SyncTriggerSourceType, step: number) => Promise<any>, notifyFunc?: (s: SyncTriggerSourceType, step: number) => Promise<any>,
errNotifyFunc?: (s: SyncTriggerSourceType, error: Error) => Promise<any>, errNotifyFunc?: (s: SyncTriggerSourceType, error: Error) => Promise<any>,
ribboonFunc?: (s: SyncTriggerSourceType, step: number) => Promise<any>, ribboonFunc?: (s: SyncTriggerSourceType, step: number) => Promise<any>,
statusBarFunc?: (s: SyncTriggerSourceType, step: number) => any, statusBarFunc?: (
s: SyncTriggerSourceType,
step: number,
everythingOk: boolean
) => any,
callbackSyncProcess?: any callbackSyncProcess?: any
) { ) {
console.info(`startting sync.`); console.info(`startting sync.`);
markIsSyncingFunc(true); markIsSyncingFunc(true);
let everythingOk = true;
let step = 0; // dry mode only let step = 0; // dry mode only
await notifyFunc?.(triggerSource, step); await notifyFunc?.(triggerSource, step);
step = 1; step = 1;
await notifyFunc?.(triggerSource, step); await notifyFunc?.(triggerSource, step);
await ribboonFunc?.(triggerSource, step); await ribboonFunc?.(triggerSource, step);
await statusBarFunc?.(triggerSource, step); await statusBarFunc?.(triggerSource, step, everythingOk);
profiler.insert("start big sync func"); profiler.insert("start big sync func");
try { try {
step = 2; step = 2;
await notifyFunc?.(triggerSource, step); await notifyFunc?.(triggerSource, step);
await ribboonFunc?.(triggerSource, step); await ribboonFunc?.(triggerSource, step);
await statusBarFunc?.(triggerSource, step); await statusBarFunc?.(triggerSource, step, everythingOk);
if (fsEncrypt.innerFs !== fsRemote) { if (fsEncrypt.innerFs !== fsRemote) {
throw Error(`your enc should has inner of the remote`); throw Error(`your enc should has inner of the remote`);
} }
@@ -1316,7 +1361,7 @@ export async function syncer(
step = 3; step = 3;
await notifyFunc?.(triggerSource, step); await notifyFunc?.(triggerSource, step);
await ribboonFunc?.(triggerSource, step); await ribboonFunc?.(triggerSource, step);
await statusBarFunc?.(triggerSource, step); await statusBarFunc?.(triggerSource, step, everythingOk);
const remoteEntityList = await fsEncrypt.walk(); const remoteEntityList = await fsEncrypt.walk();
// console.debug(`remoteEntityList:`); // console.debug(`remoteEntityList:`);
// console.debug(remoteEntityList); // console.debug(remoteEntityList);
@@ -1325,7 +1370,7 @@ export async function syncer(
step = 4; step = 4;
await notifyFunc?.(triggerSource, step); await notifyFunc?.(triggerSource, step);
await ribboonFunc?.(triggerSource, step); await ribboonFunc?.(triggerSource, step);
await statusBarFunc?.(triggerSource, step); await statusBarFunc?.(triggerSource, step, everythingOk);
const localEntityList = await fsLocal.walk(); const localEntityList = await fsLocal.walk();
// console.debug(`localEntityList:`); // console.debug(`localEntityList:`);
// console.debug(localEntityList); // console.debug(localEntityList);
@@ -1334,7 +1379,7 @@ export async function syncer(
step = 5; step = 5;
await notifyFunc?.(triggerSource, step); await notifyFunc?.(triggerSource, step);
await ribboonFunc?.(triggerSource, step); await ribboonFunc?.(triggerSource, step);
await statusBarFunc?.(triggerSource, step); await statusBarFunc?.(triggerSource, step, everythingOk);
const prevSyncEntityList = await getAllPrevSyncRecordsByVaultAndProfile( const prevSyncEntityList = await getAllPrevSyncRecordsByVaultAndProfile(
db, db,
vaultRandomID, vaultRandomID,
@@ -1347,7 +1392,7 @@ export async function syncer(
step = 6; step = 6;
await notifyFunc?.(triggerSource, step); await notifyFunc?.(triggerSource, step);
await ribboonFunc?.(triggerSource, step); await ribboonFunc?.(triggerSource, step);
await statusBarFunc?.(triggerSource, step); await statusBarFunc?.(triggerSource, step, everythingOk);
let mixedEntityMappings = await ensembleMixedEnties( let mixedEntityMappings = await ensembleMixedEnties(
localEntityList, localEntityList,
prevSyncEntityList, prevSyncEntityList,
@@ -1368,7 +1413,9 @@ export async function syncer(
settings.skipSizeLargerThan ?? -1, settings.skipSizeLargerThan ?? -1,
settings.conflictAction ?? "keep_newer", settings.conflictAction ?? "keep_newer",
settings.syncDirection ?? "bidirectional", settings.syncDirection ?? "bidirectional",
profiler profiler,
settings,
triggerSource
); );
console.debug(`mixedEntityMappings:`); console.debug(`mixedEntityMappings:`);
console.debug(mixedEntityMappings); // for debugging console.debug(mixedEntityMappings); // for debugging
@@ -1390,7 +1437,7 @@ export async function syncer(
if (triggerSource !== "dry") { if (triggerSource !== "dry") {
await notifyFunc?.(triggerSource, step); await notifyFunc?.(triggerSource, step);
await ribboonFunc?.(triggerSource, step); await ribboonFunc?.(triggerSource, step);
await statusBarFunc?.(triggerSource, step); await statusBarFunc?.(triggerSource, step, everythingOk);
await doActualSync( await doActualSync(
mixedEntityMappings, mixedEntityMappings,
fsLocal, fsLocal,
@@ -1408,13 +1455,14 @@ export async function syncer(
} else { } else {
await notifyFunc?.(triggerSource, step); await notifyFunc?.(triggerSource, step);
await ribboonFunc?.(triggerSource, step); await ribboonFunc?.(triggerSource, step);
await statusBarFunc?.(triggerSource, step); await statusBarFunc?.(triggerSource, step, everythingOk);
profiler.insert( profiler.insert(
`finish step${step} (skip actual sync because of dry run)` `finish step${step} (skip actual sync because of dry run)`
); );
} }
} catch (error: any) { } catch (error: any) {
profiler.insert("start error branch"); profiler.insert("start error branch");
everythingOk = false;
await errNotifyFunc?.(triggerSource, error as Error); await errNotifyFunc?.(triggerSource, error as Error);
profiler.insert("finish error branch"); profiler.insert("finish error branch");
@@ -1428,7 +1476,7 @@ export async function syncer(
step = 8; step = 8;
await notifyFunc?.(triggerSource, step); await notifyFunc?.(triggerSource, step);
await ribboonFunc?.(triggerSource, step); await ribboonFunc?.(triggerSource, step);
await statusBarFunc?.(triggerSource, step); await statusBarFunc?.(triggerSource, step, everythingOk);
console.info(`endding sync.`); console.info(`endding sync.`);
markIsSyncingFunc(false); markIsSyncingFunc(false);
+4 -4
View File
@@ -1,6 +1,6 @@
import { App, Modal, Notice, PluginSettingTab, Setting } from "obsidian"; import { type App, Modal } from "obsidian";
import type RemotelySavePlugin from "./main"; // unavoidable
import type { TransItemType } from "./i18n"; import type { TransItemType } from "./i18n";
import type RemotelySavePlugin from "./main"; // unavoidable
import { stringToFragment } from "./misc"; import { stringToFragment } from "./misc";
@@ -17,7 +17,7 @@ export class SyncAlgoV3Modal extends Modal {
this.requireUpdateAllDev = false; this.requireUpdateAllDev = false;
} }
onOpen() { onOpen() {
let { contentEl } = this; const { contentEl } = this;
const t = (x: TransItemType, vars?: any) => { const t = (x: TransItemType, vars?: any) => {
return this.plugin.i18n.t(x, vars); return this.plugin.i18n.t(x, vars);
}; };
@@ -112,7 +112,7 @@ export class SyncAlgoV3Modal extends Modal {
} }
onClose() { onClose() {
let { contentEl } = this; const { contentEl } = this;
contentEl.empty(); contentEl.empty();
if (this.agree) { if (this.agree) {
console.info("agree to use the new algorithm"); console.info("agree to use the new algorithm");
+1 -1
View File
@@ -1,6 +1,6 @@
import { strict as assert } from "assert"; import { strict as assert } from "assert";
import { RemotelySavePluginSettings } from "../src/baseTypes"; import type { RemotelySavePluginSettings } from "../src/baseTypes";
import { messyConfigToNormal, normalConfigToMessy } from "../src/configPersist"; import { messyConfigToNormal, normalConfigToMessy } from "../src/configPersist";
const DEFAULT_SETTINGS: RemotelySavePluginSettings = { const DEFAULT_SETTINGS: RemotelySavePluginSettings = {
+2 -2
View File
@@ -13,7 +13,7 @@ import {
import { base64ToBase64url, bufferToArrayBuffer } from "../src/misc"; import { base64ToBase64url, bufferToArrayBuffer } from "../src/misc";
describe("Encryption OpenSSL tests", () => { describe("Encryption OpenSSL tests", () => {
beforeEach(function () { beforeEach(() => {
global.window = { global.window = {
crypto: require("crypto").webcrypto, crypto: require("crypto").webcrypto,
} as any; } as any;
@@ -157,7 +157,7 @@ describe("Encryption OpenSSL tests", () => {
assert.throws(() => getSizeFromEncToOrig(14787231)); assert.throws(() => getSizeFromEncToOrig(14787231));
let { minSize, maxSize } = getSizeFromEncToOrig(14787232); const { minSize, maxSize } = getSizeFromEncToOrig(14787232);
assert.ok(minSize <= 14787203 && 14787203 <= maxSize); assert.ok(minSize <= 14787203 && 14787203 <= maxSize);
}); });
}); });
+1 -1
View File
@@ -1,7 +1,7 @@
import { strict as assert } from "assert"; import { strict as assert } from "assert";
import { import {
type MetadataOnRemote,
isEqualMetadataOnRemote, isEqualMetadataOnRemote,
MetadataOnRemote,
} from "../src/metadataOnRemote"; } from "../src/metadataOnRemote";
describe("Metadata operations tests", () => { describe("Metadata operations tests", () => {
+1 -1
View File
@@ -179,7 +179,7 @@ describe("Misc: get dirname", () => {
}); });
describe("Misc: extract svg", () => { describe("Misc: extract svg", () => {
beforeEach(function () { beforeEach(() => {
const fakeBrowser = new JSDOM(""); const fakeBrowser = new JSDOM("");
global.window = fakeBrowser.window as any; global.window = fakeBrowser.window as any;
}); });