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