mirror of
https://github.com/remotely-save/remotely-save.git
synced 2024-06-07 21:10:45 +00:00
strict anywhere
This commit is contained in:
parent
480cf224c6
commit
8dde6c59d2
@ -48,6 +48,7 @@ export interface DropboxConfig {
|
||||
|
||||
export type WebdavAuthType = "digest" | "basic";
|
||||
export type WebdavDepthType =
|
||||
| "auto"
|
||||
| "auto_unknown"
|
||||
| "auto_1"
|
||||
| "auto_infinity"
|
||||
@ -112,7 +113,7 @@ export interface RemotelySavePluginSettings {
|
||||
|
||||
export interface RemoteItem {
|
||||
key: string;
|
||||
lastModified: number;
|
||||
lastModified?: number;
|
||||
size: number;
|
||||
remoteType: SUPPORTED_SERVICES_TYPE;
|
||||
etag?: string;
|
||||
|
@ -14,7 +14,7 @@ export const exportQrCodeUri = async (
|
||||
currentVaultName: string,
|
||||
pluginVersion: string
|
||||
) => {
|
||||
const settings2 = cloneDeep(settings);
|
||||
const settings2: Partial<RemotelySavePluginSettings> = cloneDeep(settings);
|
||||
delete settings2.dropbox;
|
||||
delete settings2.onedrive;
|
||||
delete settings2.vaultRandomID;
|
||||
|
@ -436,8 +436,8 @@ export const insertRenameRecordByVault = async (
|
||||
vaultRandomID: string
|
||||
) => {
|
||||
// log.info(fileOrFolder);
|
||||
let k1: FileFolderHistoryRecord;
|
||||
let k2: FileFolderHistoryRecord;
|
||||
let k1: FileFolderHistoryRecord | undefined;
|
||||
let k2: FileFolderHistoryRecord | undefined;
|
||||
const actionWhen = Date.now();
|
||||
if (fileOrFolder instanceof TFile) {
|
||||
k1 = {
|
||||
@ -473,8 +473,10 @@ export const insertRenameRecordByVault = async (
|
||||
// TAbstractFile does not contain these info
|
||||
// but from API_VER_STAT_FOLDER we can manually stat them by path.
|
||||
const s = await statFix(fileOrFolder.vault, fileOrFolder.path);
|
||||
ctime = s.ctime;
|
||||
mtime = s.mtime;
|
||||
if (s !== undefined && s !== null) {
|
||||
ctime = s.ctime;
|
||||
mtime = s.mtime;
|
||||
}
|
||||
}
|
||||
k1 = {
|
||||
key: key,
|
||||
@ -500,8 +502,8 @@ export const insertRenameRecordByVault = async (
|
||||
};
|
||||
}
|
||||
await Promise.all([
|
||||
db.fileHistoryTbl.setItem(`${vaultRandomID}\t${k1.key}`, k1),
|
||||
db.fileHistoryTbl.setItem(`${vaultRandomID}\t${k2.key}`, k2),
|
||||
db.fileHistoryTbl.setItem(`${vaultRandomID}\t${k1!.key}`, k1),
|
||||
db.fileHistoryTbl.setItem(`${vaultRandomID}\t${k2!.key}`, k2),
|
||||
]);
|
||||
};
|
||||
|
||||
|
82
src/main.ts
82
src/main.ts
@ -133,18 +133,18 @@ const getIconSvg = () => {
|
||||
};
|
||||
|
||||
export default class RemotelySavePlugin extends Plugin {
|
||||
settings: RemotelySavePluginSettings;
|
||||
db: InternalDBs;
|
||||
syncStatus: SyncStatusType;
|
||||
statusBarElement: HTMLSpanElement;
|
||||
oauth2Info: OAuth2Info;
|
||||
currLogLevel: string;
|
||||
settings!: RemotelySavePluginSettings;
|
||||
db!: InternalDBs;
|
||||
syncStatus!: SyncStatusType;
|
||||
statusBarElement!: HTMLSpanElement;
|
||||
oauth2Info!: OAuth2Info;
|
||||
currLogLevel!: string;
|
||||
currSyncMsg?: string;
|
||||
syncRibbon?: HTMLElement;
|
||||
autoRunIntervalID?: number;
|
||||
syncOnSaveIntervalID?: number;
|
||||
i18n: I18n;
|
||||
vaultRandomID: string;
|
||||
i18n!: I18n;
|
||||
vaultRandomID!: string;
|
||||
debugServerTemp?: string;
|
||||
|
||||
async syncRun(triggerSource: SyncTriggerSourceType = "manual") {
|
||||
@ -175,7 +175,7 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
|
||||
let originLabel = `${this.manifest.name}`;
|
||||
if (this.syncRibbon !== undefined) {
|
||||
originLabel = this.syncRibbon.getAttribute("aria-label");
|
||||
originLabel = this.syncRibbon.getAttribute("aria-label") as string;
|
||||
}
|
||||
|
||||
try {
|
||||
@ -286,7 +286,8 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
this.db,
|
||||
this.vaultRandomID
|
||||
);
|
||||
let localConfigDirContents: ObsConfigDirFileType[] = undefined;
|
||||
let localConfigDirContents: ObsConfigDirFileType[] | undefined =
|
||||
undefined;
|
||||
if (this.settings.syncConfigDir) {
|
||||
localConfigDirContents = await listFilesInObsFolder(
|
||||
this.app.vault.configDir,
|
||||
@ -312,11 +313,11 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
client.serviceType,
|
||||
triggerSource,
|
||||
this.app.vault,
|
||||
this.settings.syncConfigDir,
|
||||
this.settings.syncConfigDir ?? false,
|
||||
this.app.vault.configDir,
|
||||
this.settings.syncUnderscoreItems,
|
||||
this.settings.skipSizeLargerThan,
|
||||
this.settings.ignorePaths,
|
||||
this.settings.syncUnderscoreItems ?? false,
|
||||
this.settings.skipSizeLargerThan ?? -1,
|
||||
this.settings.ignorePaths ?? [],
|
||||
this.settings.password
|
||||
);
|
||||
log.info(plan.mixedStates); // for debugging
|
||||
@ -350,7 +351,7 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
new SizesConflictModal(
|
||||
self.app,
|
||||
self,
|
||||
this.settings.skipSizeLargerThan,
|
||||
this.settings.skipSizeLargerThan ?? -1,
|
||||
ss,
|
||||
this.settings.password !== ""
|
||||
).open();
|
||||
@ -397,7 +398,7 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
this.manifest.id
|
||||
}-${Date.now()}: finish sync, triggerSource=${triggerSource}`
|
||||
);
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
const msg = t("syncrun_abort", {
|
||||
manifestID: this.manifest.id,
|
||||
theDate: `${Date.now()}`,
|
||||
@ -412,7 +413,7 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
getNotice(e.message, 10 * 1000);
|
||||
}
|
||||
} else {
|
||||
getNotice(error.message, 10 * 1000);
|
||||
getNotice(error?.message ?? "error while sync", 10 * 1000);
|
||||
}
|
||||
this.syncStatus = "idle";
|
||||
if (this.syncRibbon !== undefined) {
|
||||
@ -445,7 +446,7 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
await this.checkIfPresetRulesFollowed();
|
||||
|
||||
// lang should be load early, but after settings
|
||||
this.i18n = new I18n(this.settings.lang, async (lang: LangTypeAndAuto) => {
|
||||
this.i18n = new I18n(this.settings.lang!, async (lang: LangTypeAndAuto) => {
|
||||
this.settings.lang = lang;
|
||||
await this.saveSettings();
|
||||
});
|
||||
@ -475,8 +476,11 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
vaultBasePath,
|
||||
vaultRandomIDFromOldConfigFile
|
||||
);
|
||||
} catch (err) {
|
||||
new Notice(err.message, 10 * 1000);
|
||||
} catch (err: any) {
|
||||
new Notice(
|
||||
err?.message ?? "error of prepareDBAndVaultRandomID",
|
||||
10 * 1000
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
|
||||
@ -566,14 +570,18 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
this.registerObsidianProtocolHandler(
|
||||
COMMAND_CALLBACK_DROPBOX,
|
||||
async (inputParams) => {
|
||||
if (inputParams.code !== undefined) {
|
||||
if (
|
||||
inputParams.code !== undefined &&
|
||||
this.oauth2Info?.verifier !== undefined
|
||||
) {
|
||||
if (this.oauth2Info.helperModal !== undefined) {
|
||||
this.oauth2Info.helperModal.contentEl.empty();
|
||||
const k = this.oauth2Info.helperModal.contentEl;
|
||||
k.empty();
|
||||
|
||||
t("protocol_dropbox_connecting")
|
||||
.split("\n")
|
||||
.forEach((val) => {
|
||||
this.oauth2Info.helperModal.contentEl.createEl("p", {
|
||||
k.createEl("p", {
|
||||
text: val,
|
||||
});
|
||||
});
|
||||
@ -596,7 +604,7 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
const self = this;
|
||||
setConfigBySuccessfullAuthInplaceDropbox(
|
||||
this.settings.dropbox,
|
||||
authRes,
|
||||
authRes!,
|
||||
() => self.saveSettings()
|
||||
);
|
||||
|
||||
@ -655,14 +663,18 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
this.registerObsidianProtocolHandler(
|
||||
COMMAND_CALLBACK_ONEDRIVE,
|
||||
async (inputParams) => {
|
||||
if (inputParams.code !== undefined) {
|
||||
if (
|
||||
inputParams.code !== undefined &&
|
||||
this.oauth2Info?.verifier !== undefined
|
||||
) {
|
||||
if (this.oauth2Info.helperModal !== undefined) {
|
||||
this.oauth2Info.helperModal.contentEl.empty();
|
||||
const k = this.oauth2Info.helperModal.contentEl;
|
||||
k.empty();
|
||||
|
||||
t("protocol_onedrive_connecting")
|
||||
.split("\n")
|
||||
.forEach((val) => {
|
||||
this.oauth2Info.helperModal.contentEl.createEl("p", {
|
||||
k.createEl("p", {
|
||||
text: val,
|
||||
});
|
||||
});
|
||||
@ -824,7 +836,13 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
this.syncRibbon = undefined;
|
||||
if (this.oauth2Info !== undefined) {
|
||||
this.oauth2Info.helperModal = undefined;
|
||||
this.oauth2Info = undefined;
|
||||
this.oauth2Info = {
|
||||
verifier: "",
|
||||
helperModal: undefined,
|
||||
authDiv: undefined,
|
||||
revokeDiv: undefined,
|
||||
revokeAuthSetting: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -932,7 +950,7 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
let dropboxExpired = false;
|
||||
if (
|
||||
this.settings.dropbox.refreshToken !== "" &&
|
||||
current >= this.settings.dropbox.credentialsShouldBeDeletedAtTime
|
||||
current >= this.settings!.dropbox!.credentialsShouldBeDeletedAtTime!
|
||||
) {
|
||||
dropboxExpired = true;
|
||||
this.settings.dropbox = cloneDeep(DEFAULT_DROPBOX_CONFIG);
|
||||
@ -942,7 +960,7 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
let onedriveExpired = false;
|
||||
if (
|
||||
this.settings.onedrive.refreshToken !== "" &&
|
||||
current >= this.settings.onedrive.credentialsShouldBeDeletedAtTime
|
||||
current >= this.settings!.onedrive!.credentialsShouldBeDeletedAtTime!
|
||||
) {
|
||||
onedriveExpired = true;
|
||||
this.settings.onedrive = cloneDeep(DEFAULT_ONEDRIVE_CONFIG);
|
||||
@ -1078,11 +1096,11 @@ export default class RemotelySavePlugin extends Plugin {
|
||||
// );
|
||||
if (
|
||||
currentTime - lastModified <
|
||||
this.settings.syncOnSaveAfterMilliseconds
|
||||
this.settings!.syncOnSaveAfterMilliseconds!
|
||||
) {
|
||||
if (!runScheduled) {
|
||||
const scheduleTimeFromNow =
|
||||
this.settings.syncOnSaveAfterMilliseconds -
|
||||
this.settings!.syncOnSaveAfterMilliseconds! -
|
||||
(currentTime - lastModified);
|
||||
log.info(
|
||||
`schedule a run for ${scheduleTimeFromNow} milliseconds later`
|
||||
|
@ -26,8 +26,8 @@ export interface MetadataOnRemote {
|
||||
}
|
||||
|
||||
export const isEqualMetadataOnRemote = (
|
||||
a: MetadataOnRemote,
|
||||
b: MetadataOnRemote
|
||||
a: MetadataOnRemote | undefined,
|
||||
b: MetadataOnRemote | undefined
|
||||
) => {
|
||||
const m1 = a === undefined ? { deletions: [] } : a;
|
||||
const m2 = b === undefined ? { deletions: [] } : b;
|
||||
|
17
src/misc.ts
17
src/misc.ts
@ -126,8 +126,12 @@ export const base64ToArrayBuffer = (b64text: string) => {
|
||||
* @returns
|
||||
*/
|
||||
export const hexStringToTypedArray = (hex: string) => {
|
||||
const f = hex.match(/[\da-f]{2}/gi);
|
||||
if (f === null) {
|
||||
throw Error(`input ${hex} is not hex, no way to transform`);
|
||||
}
|
||||
return new Uint8Array(
|
||||
hex.match(/[\da-f]{2}/gi).map(function (h) {
|
||||
f.map(function (h) {
|
||||
return parseInt(h, 16);
|
||||
})
|
||||
);
|
||||
@ -236,7 +240,7 @@ export const setToString = (a: Set<string>, delimiter: string = ",") => {
|
||||
export const extractSvgSub = (x: string, subEl: string = "rect") => {
|
||||
const parser = new window.DOMParser();
|
||||
const dom = parser.parseFromString(x, "image/svg+xml");
|
||||
const svg = dom.querySelector("svg");
|
||||
const svg = dom.querySelector("svg")!;
|
||||
svg.setAttribute("viewbox", "0 0 10 10");
|
||||
return svg.innerHTML;
|
||||
};
|
||||
@ -317,7 +321,7 @@ export const getTypeName = (obj: any) => {
|
||||
* @param x
|
||||
* @returns
|
||||
*/
|
||||
export const atWhichLevel = (x: string) => {
|
||||
export const atWhichLevel = (x: string | undefined) => {
|
||||
if (
|
||||
x === undefined ||
|
||||
x === "" ||
|
||||
@ -413,11 +417,14 @@ export const toText = (x: any) => {
|
||||
*/
|
||||
export const statFix = async (vault: Vault, path: string) => {
|
||||
const s = await vault.adapter.stat(path);
|
||||
if (s === undefined || s === null) {
|
||||
return s;
|
||||
}
|
||||
if (s.ctime === undefined || s.ctime === null || Number.isNaN(s.ctime)) {
|
||||
s.ctime = undefined;
|
||||
s.ctime = undefined as any; // force assignment
|
||||
}
|
||||
if (s.mtime === undefined || s.mtime === null || Number.isNaN(s.mtime)) {
|
||||
s.mtime = undefined;
|
||||
s.mtime = undefined as any; // force assignment
|
||||
}
|
||||
if (
|
||||
(s.size === undefined || s.size === null || Number.isNaN(s.size)) &&
|
||||
|
@ -53,9 +53,9 @@ export const listFilesInObsFolder = async (
|
||||
const CHUNK_SIZE = 10;
|
||||
const contents: ObsConfigDirFileType[] = [];
|
||||
while (q.length > 0) {
|
||||
const itemsToFetch = [];
|
||||
const itemsToFetch: string[] = [];
|
||||
while (q.length > 0) {
|
||||
itemsToFetch.push(q.pop());
|
||||
itemsToFetch.push(q.pop()!);
|
||||
}
|
||||
|
||||
const itemsToFetchChunks = chunk(itemsToFetch, CHUNK_SIZE);
|
||||
@ -63,8 +63,11 @@ export const listFilesInObsFolder = async (
|
||||
const r = singleChunk.map(async (x) => {
|
||||
const statRes = await statFix(vault, x);
|
||||
|
||||
if (statRes === undefined || statRes === null) {
|
||||
throw Error("something goes wrong while listing hidden folder");
|
||||
}
|
||||
const isFolder = statRes.type === "folder";
|
||||
let children: ListedFiles = undefined;
|
||||
let children: ListedFiles | undefined = undefined;
|
||||
if (isFolder) {
|
||||
children = await vault.adapter.list(x);
|
||||
}
|
||||
|
@ -43,10 +43,10 @@ export class RemoteClient {
|
||||
"remember to provide vault name and callback while init webdav client"
|
||||
);
|
||||
}
|
||||
const remoteBaseDir = webdavConfig.remoteBaseDir || vaultName;
|
||||
const remoteBaseDir = webdavConfig!.remoteBaseDir || vaultName;
|
||||
this.webdavConfig = webdavConfig;
|
||||
this.webdavClient = webdav.getWebdavClient(
|
||||
this.webdavConfig,
|
||||
this.webdavConfig!,
|
||||
remoteBaseDir,
|
||||
saveUpdatedConfigFunc
|
||||
);
|
||||
@ -56,10 +56,10 @@ export class RemoteClient {
|
||||
"remember to provide vault name and callback while init dropbox client"
|
||||
);
|
||||
}
|
||||
const remoteBaseDir = dropboxConfig.remoteBaseDir || vaultName;
|
||||
const remoteBaseDir = dropboxConfig!.remoteBaseDir || vaultName;
|
||||
this.dropboxConfig = dropboxConfig;
|
||||
this.dropboxClient = dropbox.getDropboxClient(
|
||||
this.dropboxConfig,
|
||||
this.dropboxConfig!,
|
||||
remoteBaseDir,
|
||||
saveUpdatedConfigFunc
|
||||
);
|
||||
@ -69,10 +69,10 @@ export class RemoteClient {
|
||||
"remember to provide vault name and callback while init onedrive client"
|
||||
);
|
||||
}
|
||||
const remoteBaseDir = onedriveConfig.remoteBaseDir || vaultName;
|
||||
const remoteBaseDir = onedriveConfig!.remoteBaseDir || vaultName;
|
||||
this.onedriveConfig = onedriveConfig;
|
||||
this.onedriveClient = onedrive.getOnedriveClient(
|
||||
this.onedriveConfig,
|
||||
this.onedriveConfig!,
|
||||
remoteBaseDir,
|
||||
saveUpdatedConfigFunc
|
||||
);
|
||||
@ -84,17 +84,17 @@ export class RemoteClient {
|
||||
getRemoteMeta = async (fileOrFolderPath: string) => {
|
||||
if (this.serviceType === "s3") {
|
||||
return await s3.getRemoteMeta(
|
||||
s3.getS3Client(this.s3Config),
|
||||
this.s3Config,
|
||||
s3.getS3Client(this.s3Config!),
|
||||
this.s3Config!,
|
||||
fileOrFolderPath
|
||||
);
|
||||
} else if (this.serviceType === "webdav") {
|
||||
return await webdav.getRemoteMeta(this.webdavClient, fileOrFolderPath);
|
||||
return await webdav.getRemoteMeta(this.webdavClient!, fileOrFolderPath);
|
||||
} else if (this.serviceType === "dropbox") {
|
||||
return await dropbox.getRemoteMeta(this.dropboxClient, fileOrFolderPath);
|
||||
return await dropbox.getRemoteMeta(this.dropboxClient!, fileOrFolderPath);
|
||||
} else if (this.serviceType === "onedrive") {
|
||||
return await onedrive.getRemoteMeta(
|
||||
this.onedriveClient,
|
||||
this.onedriveClient!,
|
||||
fileOrFolderPath
|
||||
);
|
||||
} else {
|
||||
@ -104,7 +104,7 @@ export class RemoteClient {
|
||||
|
||||
uploadToRemote = async (
|
||||
fileOrFolderPath: string,
|
||||
vault: Vault,
|
||||
vault: Vault | undefined,
|
||||
isRecursively: boolean = false,
|
||||
password: string = "",
|
||||
remoteEncryptedKey: string = "",
|
||||
@ -114,8 +114,8 @@ export class RemoteClient {
|
||||
) => {
|
||||
if (this.serviceType === "s3") {
|
||||
return await s3.uploadToRemote(
|
||||
s3.getS3Client(this.s3Config),
|
||||
this.s3Config,
|
||||
s3.getS3Client(this.s3Config!),
|
||||
this.s3Config!,
|
||||
fileOrFolderPath,
|
||||
vault,
|
||||
isRecursively,
|
||||
@ -126,7 +126,7 @@ export class RemoteClient {
|
||||
);
|
||||
} else if (this.serviceType === "webdav") {
|
||||
return await webdav.uploadToRemote(
|
||||
this.webdavClient,
|
||||
this.webdavClient!,
|
||||
fileOrFolderPath,
|
||||
vault,
|
||||
isRecursively,
|
||||
@ -137,7 +137,7 @@ export class RemoteClient {
|
||||
);
|
||||
} else if (this.serviceType === "dropbox") {
|
||||
return await dropbox.uploadToRemote(
|
||||
this.dropboxClient,
|
||||
this.dropboxClient!,
|
||||
fileOrFolderPath,
|
||||
vault,
|
||||
isRecursively,
|
||||
@ -149,7 +149,7 @@ export class RemoteClient {
|
||||
);
|
||||
} else if (this.serviceType === "onedrive") {
|
||||
return await onedrive.uploadToRemote(
|
||||
this.onedriveClient,
|
||||
this.onedriveClient!,
|
||||
fileOrFolderPath,
|
||||
vault,
|
||||
isRecursively,
|
||||
@ -167,15 +167,15 @@ export class RemoteClient {
|
||||
listAllFromRemote = async () => {
|
||||
if (this.serviceType === "s3") {
|
||||
return await s3.listAllFromRemote(
|
||||
s3.getS3Client(this.s3Config),
|
||||
this.s3Config
|
||||
s3.getS3Client(this.s3Config!),
|
||||
this.s3Config!
|
||||
);
|
||||
} else if (this.serviceType === "webdav") {
|
||||
return await webdav.listAllFromRemote(this.webdavClient);
|
||||
return await webdav.listAllFromRemote(this.webdavClient!);
|
||||
} else if (this.serviceType === "dropbox") {
|
||||
return await dropbox.listAllFromRemote(this.dropboxClient);
|
||||
return await dropbox.listAllFromRemote(this.dropboxClient!);
|
||||
} else if (this.serviceType === "onedrive") {
|
||||
return await onedrive.listAllFromRemote(this.onedriveClient);
|
||||
return await onedrive.listAllFromRemote(this.onedriveClient!);
|
||||
} else {
|
||||
throw Error(`not supported service type ${this.serviceType}`);
|
||||
}
|
||||
@ -191,8 +191,8 @@ export class RemoteClient {
|
||||
) => {
|
||||
if (this.serviceType === "s3") {
|
||||
return await s3.downloadFromRemote(
|
||||
s3.getS3Client(this.s3Config),
|
||||
this.s3Config,
|
||||
s3.getS3Client(this.s3Config!),
|
||||
this.s3Config!,
|
||||
fileOrFolderPath,
|
||||
vault,
|
||||
mtime,
|
||||
@ -202,7 +202,7 @@ export class RemoteClient {
|
||||
);
|
||||
} else if (this.serviceType === "webdav") {
|
||||
return await webdav.downloadFromRemote(
|
||||
this.webdavClient,
|
||||
this.webdavClient!,
|
||||
fileOrFolderPath,
|
||||
vault,
|
||||
mtime,
|
||||
@ -212,7 +212,7 @@ export class RemoteClient {
|
||||
);
|
||||
} else if (this.serviceType === "dropbox") {
|
||||
return await dropbox.downloadFromRemote(
|
||||
this.dropboxClient,
|
||||
this.dropboxClient!,
|
||||
fileOrFolderPath,
|
||||
vault,
|
||||
mtime,
|
||||
@ -222,7 +222,7 @@ export class RemoteClient {
|
||||
);
|
||||
} else if (this.serviceType === "onedrive") {
|
||||
return await onedrive.downloadFromRemote(
|
||||
this.onedriveClient,
|
||||
this.onedriveClient!,
|
||||
fileOrFolderPath,
|
||||
vault,
|
||||
mtime,
|
||||
@ -242,29 +242,29 @@ export class RemoteClient {
|
||||
) => {
|
||||
if (this.serviceType === "s3") {
|
||||
return await s3.deleteFromRemote(
|
||||
s3.getS3Client(this.s3Config),
|
||||
this.s3Config,
|
||||
s3.getS3Client(this.s3Config!),
|
||||
this.s3Config!,
|
||||
fileOrFolderPath,
|
||||
password,
|
||||
remoteEncryptedKey
|
||||
);
|
||||
} else if (this.serviceType === "webdav") {
|
||||
return await webdav.deleteFromRemote(
|
||||
this.webdavClient,
|
||||
this.webdavClient!,
|
||||
fileOrFolderPath,
|
||||
password,
|
||||
remoteEncryptedKey
|
||||
);
|
||||
} else if (this.serviceType === "dropbox") {
|
||||
return await dropbox.deleteFromRemote(
|
||||
this.dropboxClient,
|
||||
this.dropboxClient!,
|
||||
fileOrFolderPath,
|
||||
password,
|
||||
remoteEncryptedKey
|
||||
);
|
||||
} else if (this.serviceType === "onedrive") {
|
||||
return await onedrive.deleteFromRemote(
|
||||
this.onedriveClient,
|
||||
this.onedriveClient!,
|
||||
fileOrFolderPath,
|
||||
password,
|
||||
remoteEncryptedKey
|
||||
@ -277,17 +277,17 @@ export class RemoteClient {
|
||||
checkConnectivity = async (callbackFunc?: any) => {
|
||||
if (this.serviceType === "s3") {
|
||||
return await s3.checkConnectivity(
|
||||
s3.getS3Client(this.s3Config),
|
||||
this.s3Config,
|
||||
s3.getS3Client(this.s3Config!),
|
||||
this.s3Config!,
|
||||
callbackFunc
|
||||
);
|
||||
} else if (this.serviceType === "webdav") {
|
||||
return await webdav.checkConnectivity(this.webdavClient, callbackFunc);
|
||||
return await webdav.checkConnectivity(this.webdavClient!, callbackFunc);
|
||||
} else if (this.serviceType === "dropbox") {
|
||||
return await dropbox.checkConnectivity(this.dropboxClient, callbackFunc);
|
||||
return await dropbox.checkConnectivity(this.dropboxClient!, callbackFunc);
|
||||
} else if (this.serviceType === "onedrive") {
|
||||
return await onedrive.checkConnectivity(
|
||||
this.onedriveClient,
|
||||
this.onedriveClient!,
|
||||
callbackFunc
|
||||
);
|
||||
} else {
|
||||
@ -297,9 +297,9 @@ export class RemoteClient {
|
||||
|
||||
getUser = async () => {
|
||||
if (this.serviceType === "dropbox") {
|
||||
return await dropbox.getUserDisplayName(this.dropboxClient);
|
||||
return await dropbox.getUserDisplayName(this.dropboxClient!);
|
||||
} else if (this.serviceType === "onedrive") {
|
||||
return await onedrive.getUserDisplayName(this.onedriveClient);
|
||||
return await onedrive.getUserDisplayName(this.onedriveClient!);
|
||||
} else {
|
||||
throw Error(`not supported service type ${this.serviceType}`);
|
||||
}
|
||||
@ -307,7 +307,7 @@ export class RemoteClient {
|
||||
|
||||
revokeAuth = async () => {
|
||||
if (this.serviceType === "dropbox") {
|
||||
return await dropbox.revokeAuth(this.dropboxClient);
|
||||
return await dropbox.revokeAuth(this.dropboxClient!);
|
||||
} else {
|
||||
throw Error(`not supported service type ${this.serviceType}`);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import { log } from "./moreOnLog";
|
||||
|
||||
export const DEFAULT_DROPBOX_CONFIG: DropboxConfig = {
|
||||
accessToken: "",
|
||||
clientID: process.env.DEFAULT_DROPBOX_APP_KEY,
|
||||
clientID: process.env.DEFAULT_DROPBOX_APP_KEY ?? "",
|
||||
refreshToken: "",
|
||||
accessTokenExpiresInSeconds: 0,
|
||||
accessTokenExpiresAtTime: 0,
|
||||
@ -76,7 +76,7 @@ const fromDropboxItemToRemoteItem = (
|
||||
| files.DeletedMetadataReference,
|
||||
remoteBaseDir: string
|
||||
): RemoteItem => {
|
||||
let key = getNormPath(x.path_display, remoteBaseDir);
|
||||
let key = getNormPath(x.path_display!, remoteBaseDir);
|
||||
if (x[".tag"] === "folder" && !key.endsWith("/")) {
|
||||
key = `${key}/`;
|
||||
}
|
||||
@ -101,7 +101,8 @@ const fromDropboxItemToRemoteItem = (
|
||||
remoteType: "dropbox",
|
||||
etag: `${x.id}\t${x.content_hash}`,
|
||||
} as RemoteItem;
|
||||
} else if (x[".tag"] === "deleted") {
|
||||
} else {
|
||||
// x[".tag"] === "deleted"
|
||||
throw Error("do not support deleted tag");
|
||||
}
|
||||
};
|
||||
@ -188,7 +189,7 @@ export const getAuthUrlAndVerifier = async (
|
||||
: `obsidian://${COMMAND_CALLBACK_DROPBOX}`;
|
||||
const authUrl = (
|
||||
await auth.getAuthenticationUrl(
|
||||
callback,
|
||||
callback as any,
|
||||
undefined,
|
||||
"code",
|
||||
"offline",
|
||||
@ -282,9 +283,7 @@ export const setConfigBySuccessfullAuthInplace = async (
|
||||
|
||||
if (authRes.refresh_token !== undefined) {
|
||||
config.refreshToken = authRes.refresh_token;
|
||||
}
|
||||
if (authRes.refresh_token !== undefined) {
|
||||
config.accountID = authRes.account_id;
|
||||
config.accountID = authRes.account_id!;
|
||||
}
|
||||
|
||||
if (saveUpdatedConfigFunc !== undefined) {
|
||||
@ -307,7 +306,7 @@ interface ErrSubType {
|
||||
async function retryReq<T>(
|
||||
reqFunc: () => Promise<DropboxResponse<T>>,
|
||||
extraHint: string = ""
|
||||
): Promise<DropboxResponse<T>> {
|
||||
): Promise<DropboxResponse<T> | undefined> {
|
||||
const waitSeconds = [1, 2, 4, 8]; // hard code exponential backoff
|
||||
for (let idx = 0; idx < waitSeconds.length; ++idx) {
|
||||
try {
|
||||
@ -369,7 +368,7 @@ export class WrappedDropboxClient {
|
||||
dropboxConfig: DropboxConfig;
|
||||
remoteBaseDir: string;
|
||||
saveUpdatedConfigFunc: () => Promise<any>;
|
||||
dropbox: Dropbox;
|
||||
dropbox!: Dropbox;
|
||||
vaultFolderExists: boolean;
|
||||
constructor(
|
||||
dropboxConfig: DropboxConfig,
|
||||
@ -507,6 +506,9 @@ export const getRemoteMeta = async (
|
||||
path: remotePath,
|
||||
})
|
||||
);
|
||||
if (rsp === undefined) {
|
||||
throw Error("dropbox.filesGetMetadata undefinded");
|
||||
}
|
||||
if (rsp.status !== 200) {
|
||||
throw Error(JSON.stringify(rsp));
|
||||
}
|
||||
@ -516,7 +518,7 @@ export const getRemoteMeta = async (
|
||||
export const uploadToRemote = async (
|
||||
client: WrappedDropboxClient,
|
||||
fileOrFolderPath: string,
|
||||
vault: Vault,
|
||||
vault: Vault | undefined,
|
||||
isRecursively: boolean = false,
|
||||
password: string = "",
|
||||
remoteEncryptedKey: string = "",
|
||||
@ -611,6 +613,11 @@ export const uploadToRemote = async (
|
||||
localContent = rawContent;
|
||||
}
|
||||
} else {
|
||||
if (vault === undefined) {
|
||||
throw new Error(
|
||||
`the vault variable is not passed but we want to read ${fileOrFolderPath} for Dropbox`
|
||||
);
|
||||
}
|
||||
localContent = await vault.adapter.readBinary(fileOrFolderPath);
|
||||
}
|
||||
let remoteContent = localContent;
|
||||
@ -700,6 +707,9 @@ const downloadFromRemoteRaw = async (
|
||||
}),
|
||||
`downloadFromRemoteRaw=${remotePath}`
|
||||
);
|
||||
if (rsp === undefined) {
|
||||
throw Error(`unknown rsp from dropbox download: ${rsp}`);
|
||||
}
|
||||
if ((rsp.result as any).fileBlob !== undefined) {
|
||||
// we get a Blob
|
||||
const content = (rsp.result as any).fileBlob as Blob;
|
||||
|
@ -31,8 +31,8 @@ const REDIRECT_URI = `obsidian://${COMMAND_CALLBACK_ONEDRIVE}`;
|
||||
|
||||
export const DEFAULT_ONEDRIVE_CONFIG: OnedriveConfig = {
|
||||
accessToken: "",
|
||||
clientID: process.env.DEFAULT_ONEDRIVE_CLIENT_ID,
|
||||
authority: process.env.DEFAULT_ONEDRIVE_AUTHORITY,
|
||||
clientID: process.env.DEFAULT_ONEDRIVE_CLIENT_ID ?? "",
|
||||
authority: process.env.DEFAULT_ONEDRIVE_AUTHORITY ?? "",
|
||||
refreshToken: "",
|
||||
accessTokenExpiresInSeconds: 0,
|
||||
accessTokenExpiresAtTime: 0,
|
||||
@ -199,7 +199,7 @@ export const setConfigBySuccessfullAuthInplace = async (
|
||||
config.accessTokenExpiresAtTime =
|
||||
Date.now() + authRes.expires_in - 5 * 60 * 1000;
|
||||
config.accessTokenExpiresInSeconds = authRes.expires_in;
|
||||
config.refreshToken = authRes.refresh_token;
|
||||
config.refreshToken = authRes.refresh_token!;
|
||||
|
||||
// manually set it expired after 80 days;
|
||||
config.credentialsShouldBeDeletedAtTime =
|
||||
@ -256,7 +256,9 @@ const getNormPath = (fileOrFolderPath: string, remoteBaseDir: string) => {
|
||||
};
|
||||
|
||||
const constructFromDriveItemToRemoteItemError = (x: DriveItem) => {
|
||||
return `parentPath="${x.parentReference.path}", selfName="${x.name}"`;
|
||||
return `parentPath="${
|
||||
x.parentReference?.path ?? "(no parentReference or path)"
|
||||
}", selfName="${x.name}"`;
|
||||
};
|
||||
|
||||
const fromDriveItemToRemoteItem = (
|
||||
@ -281,6 +283,14 @@ const fromDriveItemToRemoteItem = (
|
||||
// another possibile prefix
|
||||
const FOURTH_COMMON_PREFIX_RAW = `/drive/items/`;
|
||||
|
||||
if (
|
||||
x.parentReference === undefined ||
|
||||
x.parentReference === null ||
|
||||
x.parentReference.path === undefined ||
|
||||
x.parentReference.path === null
|
||||
) {
|
||||
throw Error("x.parentReference.path is undefinded or null");
|
||||
}
|
||||
const fullPathOriginal = `${x.parentReference.path}/${x.name}`;
|
||||
const matchFirstPrefixRes = fullPathOriginal.match(FIRST_COMMON_PREFIX_REGEX);
|
||||
const matchSecondPrefixRes = fullPathOriginal.match(
|
||||
@ -309,6 +319,11 @@ const fromDriveItemToRemoteItem = (
|
||||
// it's something like
|
||||
// /drive/items/<some_id>!<another_id>:/${remoteBaseDir}/<subfolder>
|
||||
// with uri encoded!
|
||||
if (x.name === undefined || x.name === null) {
|
||||
throw Error(
|
||||
`OneDrive item no name variable while matching ${FOURTH_COMMON_PREFIX_RAW}`
|
||||
);
|
||||
}
|
||||
const parPath = decodeURIComponent(x.parentReference.path);
|
||||
key = parPath.substring(parPath.indexOf(":") + 1);
|
||||
if (key.startsWith(`/${remoteBaseDir}/`)) {
|
||||
@ -337,8 +352,8 @@ const fromDriveItemToRemoteItem = (
|
||||
}
|
||||
return {
|
||||
key: key,
|
||||
lastModified: Date.parse(x.fileSystemInfo.lastModifiedDateTime),
|
||||
size: isFolder ? 0 : x.size,
|
||||
lastModified: Date.parse(x!.fileSystemInfo!.lastModifiedDateTime!),
|
||||
size: isFolder ? 0 : x.size!,
|
||||
remoteType: "onedrive",
|
||||
etag: x.cTag || "", // do NOT use x.eTag because it changes if meta changes
|
||||
};
|
||||
@ -381,7 +396,7 @@ class MyAuthProvider implements AuthenticationProvider {
|
||||
}
|
||||
const r2 = r as AccessCodeResponseSuccessfulType;
|
||||
this.onedriveConfig.accessToken = r2.access_token;
|
||||
this.onedriveConfig.refreshToken = r2.refresh_token;
|
||||
this.onedriveConfig.refreshToken = r2.refresh_token!;
|
||||
this.onedriveConfig.accessTokenExpiresInSeconds = r2.expires_in;
|
||||
this.onedriveConfig.accessTokenExpiresAtTime =
|
||||
currentTs + r2.expires_in * 1000 - 60 * 2 * 1000;
|
||||
@ -680,7 +695,7 @@ export const getRemoteMeta = async (
|
||||
export const uploadToRemote = async (
|
||||
client: WrappedOnedriveClient,
|
||||
fileOrFolderPath: string,
|
||||
vault: Vault,
|
||||
vault: Vault | undefined,
|
||||
isRecursively: boolean = false,
|
||||
password: string = "",
|
||||
remoteEncryptedKey: string = "",
|
||||
@ -784,6 +799,11 @@ export const uploadToRemote = async (
|
||||
localContent = rawContent;
|
||||
}
|
||||
} else {
|
||||
if (vault === undefined) {
|
||||
throw new Error(
|
||||
`the vault variable is not passed but we want to read ${fileOrFolderPath} for OneDrive`
|
||||
);
|
||||
}
|
||||
localContent = await vault.adapter.readBinary(fileOrFolderPath);
|
||||
}
|
||||
let remoteContent = localContent;
|
||||
@ -842,7 +862,7 @@ export const uploadToRemote = async (
|
||||
`${uploadFile}:/createUploadSession`,
|
||||
k
|
||||
);
|
||||
const uploadUrl = s.uploadUrl;
|
||||
const uploadUrl = s.uploadUrl!;
|
||||
log.debug("uploadSession = ");
|
||||
log.debug(s);
|
||||
|
||||
|
@ -55,7 +55,7 @@ import PQueue from "p-queue";
|
||||
* But this uses Obsidian requestUrl instead.
|
||||
*/
|
||||
class ObsHttpHandler extends FetchHttpHandler {
|
||||
requestTimeoutInMs: number;
|
||||
requestTimeoutInMs: number | undefined;
|
||||
constructor(options?: FetchHttpHandlerOptions) {
|
||||
super(options);
|
||||
this.requestTimeoutInMs =
|
||||
@ -95,7 +95,7 @@ class ObsHttpHandler extends FetchHttpHandler {
|
||||
transformedHeaders[keyLower] = request.headers[key];
|
||||
}
|
||||
|
||||
let contentType: string = undefined;
|
||||
let contentType: string | undefined = undefined;
|
||||
if (transformedHeaders["content-type"] !== undefined) {
|
||||
contentType = transformedHeaders["content-type"];
|
||||
}
|
||||
@ -226,17 +226,17 @@ const fromS3ObjectToRemoteItem = (
|
||||
mtimeRecords: Record<string, number>,
|
||||
ctimeRecords: Record<string, number>
|
||||
) => {
|
||||
let mtime = x.LastModified.valueOf();
|
||||
if (x.Key in mtimeRecords) {
|
||||
const m2 = mtimeRecords[x.Key];
|
||||
let mtime = x.LastModified!.valueOf();
|
||||
if (x.Key! in mtimeRecords) {
|
||||
const m2 = mtimeRecords[x.Key!];
|
||||
if (m2 !== 0) {
|
||||
mtime = m2;
|
||||
}
|
||||
}
|
||||
const r: RemoteItem = {
|
||||
key: getLocalNoPrefixPath(x.Key, remotePrefix),
|
||||
key: getLocalNoPrefixPath(x.Key!, remotePrefix),
|
||||
lastModified: mtime,
|
||||
size: x.Size,
|
||||
size: x.Size!,
|
||||
remoteType: "s3",
|
||||
etag: x.ETag,
|
||||
};
|
||||
@ -249,7 +249,7 @@ const fromS3HeadObjectToRemoteItem = (
|
||||
remotePrefix: string,
|
||||
useAccurateMTime: boolean
|
||||
) => {
|
||||
let mtime = x.LastModified.valueOf();
|
||||
let mtime = x.LastModified!.valueOf();
|
||||
if (useAccurateMTime && x.Metadata !== undefined) {
|
||||
const m2 = Math.round(
|
||||
parseFloat(x.Metadata.mtime || x.Metadata.MTime || "0")
|
||||
@ -317,6 +317,7 @@ export const getRemoteMeta = async (
|
||||
fileOrFolderPathWithRemotePrefix: string
|
||||
) => {
|
||||
if (
|
||||
s3Config.remotePrefix !== undefined &&
|
||||
s3Config.remotePrefix !== "" &&
|
||||
!fileOrFolderPathWithRemotePrefix.startsWith(s3Config.remotePrefix)
|
||||
) {
|
||||
@ -332,8 +333,8 @@ export const getRemoteMeta = async (
|
||||
return fromS3HeadObjectToRemoteItem(
|
||||
fileOrFolderPathWithRemotePrefix,
|
||||
res,
|
||||
s3Config.remotePrefix,
|
||||
s3Config.useAccurateMTime
|
||||
s3Config.remotePrefix ?? "",
|
||||
s3Config.useAccurateMTime ?? false
|
||||
);
|
||||
};
|
||||
|
||||
@ -341,7 +342,7 @@ export const uploadToRemote = async (
|
||||
s3Client: S3Client,
|
||||
s3Config: S3Config,
|
||||
fileOrFolderPath: string,
|
||||
vault: Vault,
|
||||
vault: Vault | undefined,
|
||||
isRecursively: boolean = false,
|
||||
password: string = "",
|
||||
remoteEncryptedKey: string = "",
|
||||
@ -354,7 +355,7 @@ export const uploadToRemote = async (
|
||||
if (password !== "") {
|
||||
uploadFile = remoteEncryptedKey;
|
||||
}
|
||||
uploadFile = getRemoteWithPrefixPath(uploadFile, s3Config.remotePrefix);
|
||||
uploadFile = getRemoteWithPrefixPath(uploadFile, s3Config.remotePrefix ?? "");
|
||||
const isFolder = fileOrFolderPath.endsWith("/");
|
||||
|
||||
if (isFolder && isRecursively) {
|
||||
@ -407,8 +408,13 @@ export const uploadToRemote = async (
|
||||
mtime = rawContentMTime;
|
||||
ctime = rawContentCTime;
|
||||
} else {
|
||||
if (vault === undefined) {
|
||||
throw new Error(
|
||||
`the vault variable is not passed but we want to read ${fileOrFolderPath} for S3`
|
||||
);
|
||||
}
|
||||
localContent = await vault.adapter.readBinary(fileOrFolderPath);
|
||||
const s = await vault?.adapter?.stat(fileOrFolderPath);
|
||||
const s = await vault.adapter.stat(fileOrFolderPath);
|
||||
if (s !== undefined && s !== null) {
|
||||
mtime = s.mtime;
|
||||
ctime = s.ctime;
|
||||
@ -500,12 +506,12 @@ const listFromRemoteRaw = async (
|
||||
if (rspHead.Metadata === undefined) {
|
||||
// pass
|
||||
} else {
|
||||
mtimeRecords[content.Key] = Math.round(
|
||||
mtimeRecords[content.Key!] = Math.round(
|
||||
parseFloat(
|
||||
rspHead.Metadata.mtime || rspHead.Metadata.MTime || "0"
|
||||
)
|
||||
);
|
||||
ctimeRecords[content.Key] = Math.round(
|
||||
ctimeRecords[content.Key!] = Math.round(
|
||||
parseFloat(
|
||||
rspHead.Metadata.ctime || rspHead.Metadata.CTime || "0"
|
||||
)
|
||||
@ -515,7 +521,7 @@ const listFromRemoteRaw = async (
|
||||
}
|
||||
}
|
||||
|
||||
isTruncated = rsp.IsTruncated;
|
||||
isTruncated = rsp.IsTruncated ?? false;
|
||||
confCmd.ContinuationToken = rsp.NextContinuationToken;
|
||||
if (
|
||||
isTruncated &&
|
||||
@ -536,7 +542,7 @@ const listFromRemoteRaw = async (
|
||||
Contents: contents.map((x) =>
|
||||
fromS3ObjectToRemoteItem(
|
||||
x,
|
||||
s3Config.remotePrefix,
|
||||
s3Config.remotePrefix ?? "",
|
||||
mtimeRecords,
|
||||
ctimeRecords
|
||||
)
|
||||
@ -559,8 +565,11 @@ export const listAllFromRemote = async (
|
||||
* @returns Promise<ArrayBuffer>
|
||||
*/
|
||||
const getObjectBodyToArrayBuffer = async (
|
||||
b: Readable | ReadableStream | Blob
|
||||
b: Readable | ReadableStream | Blob | undefined
|
||||
) => {
|
||||
if (b === undefined) {
|
||||
throw Error(`ObjectBody is undefined and don't know how to deal with it`);
|
||||
}
|
||||
if (b instanceof Readable) {
|
||||
return (await new Promise((resolve, reject) => {
|
||||
const chunks: Uint8Array[] = [];
|
||||
@ -583,6 +592,7 @@ const downloadFromRemoteRaw = async (
|
||||
fileOrFolderPathWithRemotePrefix: string
|
||||
) => {
|
||||
if (
|
||||
s3Config.remotePrefix !== undefined &&
|
||||
s3Config.remotePrefix !== "" &&
|
||||
!fileOrFolderPathWithRemotePrefix.startsWith(s3Config.remotePrefix)
|
||||
) {
|
||||
@ -626,7 +636,10 @@ export const downloadFromRemote = async (
|
||||
if (password !== "") {
|
||||
downloadFile = remoteEncryptedKey;
|
||||
}
|
||||
downloadFile = getRemoteWithPrefixPath(downloadFile, s3Config.remotePrefix);
|
||||
downloadFile = getRemoteWithPrefixPath(
|
||||
downloadFile,
|
||||
s3Config.remotePrefix ?? ""
|
||||
);
|
||||
const remoteContent = await downloadFromRemoteRaw(
|
||||
s3Client,
|
||||
s3Config,
|
||||
@ -668,7 +681,7 @@ export const deleteFromRemote = async (
|
||||
}
|
||||
remoteFileName = getRemoteWithPrefixPath(
|
||||
remoteFileName,
|
||||
s3Config.remotePrefix
|
||||
s3Config.remotePrefix ?? ""
|
||||
);
|
||||
await s3Client.send(
|
||||
new DeleteObjectCommand({
|
||||
@ -734,7 +747,7 @@ export const checkConnectivity = async (
|
||||
return false;
|
||||
}
|
||||
return results.$metadata.httpStatusCode === 200;
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
log.debug(err);
|
||||
if (callbackFunc !== undefined) {
|
||||
if (s3Config.s3Endpoint.contains(s3Config.s3BucketName)) {
|
||||
|
@ -39,12 +39,16 @@ if (VALID_REQURL) {
|
||||
});
|
||||
|
||||
let contentType: string | undefined =
|
||||
r.headers["Content-Type"] ||
|
||||
r.headers["content-type"] ||
|
||||
options.headers["Content-Type"] ||
|
||||
options.headers["content-type"] ||
|
||||
options.headers["Accept"] ||
|
||||
options.headers["accept"];
|
||||
r.headers["Content-Type"] || r.headers["content-type"];
|
||||
if (options.headers !== undefined) {
|
||||
contentType =
|
||||
contentType ||
|
||||
options.headers["Content-Type"] ||
|
||||
options.headers["content-type"] ||
|
||||
options.headers["Accept"] ||
|
||||
options.headers["accept"];
|
||||
}
|
||||
|
||||
if (contentType !== undefined) {
|
||||
contentType = contentType.toLowerCase();
|
||||
}
|
||||
@ -106,7 +110,7 @@ if (VALID_REQURL) {
|
||||
// );
|
||||
// }
|
||||
|
||||
let r2: Response = undefined;
|
||||
let r2: Response | undefined = undefined;
|
||||
if ([101, 103, 204, 205, 304].includes(r.status)) {
|
||||
// A null body status is a status that is 101, 103, 204, 205, or 304.
|
||||
// https://fetch.spec.whatwg.org/#statuses
|
||||
@ -193,7 +197,7 @@ const fromWebdavItemToRemoteItem = (x: FileStat, remoteBaseDir: string) => {
|
||||
export class WrappedWebdavClient {
|
||||
webdavConfig: WebdavConfig;
|
||||
remoteBaseDir: string;
|
||||
client: WebDAVClient;
|
||||
client!: WebDAVClient;
|
||||
vaultFolderExists: boolean;
|
||||
saveUpdatedConfigFunc: () => Promise<any>;
|
||||
constructor(
|
||||
@ -340,7 +344,7 @@ export const getRemoteMeta = async (
|
||||
export const uploadToRemote = async (
|
||||
client: WrappedWebdavClient,
|
||||
fileOrFolderPath: string,
|
||||
vault: Vault,
|
||||
vault: Vault | undefined,
|
||||
isRecursively: boolean = false,
|
||||
password: string = "",
|
||||
remoteEncryptedKey: string = "",
|
||||
@ -392,6 +396,11 @@ export const uploadToRemote = async (
|
||||
localContent = rawContent;
|
||||
}
|
||||
} else {
|
||||
if (vault == undefined) {
|
||||
throw new Error(
|
||||
`the vault variable is not passed but we want to read ${fileOrFolderPath} for webdav`
|
||||
);
|
||||
}
|
||||
localContent = await vault.adapter.readBinary(fileOrFolderPath);
|
||||
}
|
||||
let remoteContent = localContent;
|
||||
@ -428,9 +437,9 @@ export const listAllFromRemote = async (client: WrappedWebdavClient) => {
|
||||
const q = new Queue([`/${client.remoteBaseDir}`]);
|
||||
const CHUNK_SIZE = 10;
|
||||
while (q.length > 0) {
|
||||
const itemsToFetch = [];
|
||||
const itemsToFetch: string[] = [];
|
||||
while (q.length > 0) {
|
||||
itemsToFetch.push(q.pop());
|
||||
itemsToFetch.push(q.pop()!);
|
||||
}
|
||||
const itemsToFetchChunks = chunk(itemsToFetch, CHUNK_SIZE);
|
||||
// log.debug(itemsToFetchChunks);
|
||||
|
@ -416,7 +416,7 @@ class DropboxAuthModal extends Modal {
|
||||
const self = this;
|
||||
setConfigBySuccessfullAuthInplace(
|
||||
this.plugin.settings.dropbox,
|
||||
authRes,
|
||||
authRes!,
|
||||
() => self.plugin.saveSettings()
|
||||
);
|
||||
const client = new RemoteClient(
|
||||
@ -784,7 +784,7 @@ const getEyesElements = () => {
|
||||
|
||||
const wrapTextWithPasswordHide = (text: TextComponent) => {
|
||||
const { eye, eyeOff } = getEyesElements();
|
||||
const hider = text.inputEl.insertAdjacentElement("afterend", createSpan());
|
||||
const hider = text.inputEl.insertAdjacentElement("afterend", createSpan())!;
|
||||
// the init type of hider is "hidden" === eyeOff === password
|
||||
hider.innerHTML = eyeOff;
|
||||
hider.addEventListener("click", (e) => {
|
||||
@ -1534,8 +1534,8 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
||||
|
||||
dropdown
|
||||
.setValue(this.plugin.settings.webdav.authType)
|
||||
.onChange(async (val: WebdavAuthType) => {
|
||||
this.plugin.settings.webdav.authType = val;
|
||||
.onChange(async (val) => {
|
||||
this.plugin.settings.webdav.authType = val as WebdavAuthType;
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
@ -1548,20 +1548,20 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
||||
dropdown.addOption("manual_1", t("settings_webdav_depth_1"));
|
||||
dropdown.addOption("manual_infinity", t("settings_webdav_depth_inf"));
|
||||
|
||||
let initVal = "auto";
|
||||
let initVal: WebdavDepthType = "auto";
|
||||
const autoOptions: Set<WebdavDepthType> = new Set([
|
||||
"auto_unknown",
|
||||
"auto_1",
|
||||
"auto_infinity",
|
||||
]);
|
||||
if (autoOptions.has(this.plugin.settings.webdav.depth)) {
|
||||
if (autoOptions.has(this.plugin.settings.webdav.depth as any)) {
|
||||
initVal = "auto";
|
||||
} else {
|
||||
initVal = this.plugin.settings.webdav.depth || "auto";
|
||||
}
|
||||
|
||||
type DepthOption = "auto" | "manual_1" | "manual_infinity";
|
||||
dropdown.setValue(initVal).onChange(async (val: DepthOption) => {
|
||||
dropdown.setValue(initVal).onChange(async (val) => {
|
||||
if (val === "auto") {
|
||||
this.plugin.settings.webdav.depth = "auto_unknown";
|
||||
this.plugin.settings.webdav.manualRecursive = false;
|
||||
@ -1656,8 +1656,8 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
||||
dropdown.addOption("onedrive", t("settings_chooseservice_onedrive"));
|
||||
dropdown
|
||||
.setValue(this.plugin.settings.serviceType)
|
||||
.onChange(async (val: SUPPORTED_SERVICES_TYPE) => {
|
||||
this.plugin.settings.serviceType = val;
|
||||
.onChange(async (val) => {
|
||||
this.plugin.settings.serviceType = val as SUPPORTED_SERVICES_TYPE;
|
||||
s3Div.toggleClass(
|
||||
"s3-hide",
|
||||
this.plugin.settings.serviceType !== "s3"
|
||||
@ -1815,11 +1815,11 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
||||
// );
|
||||
if (
|
||||
currentTime - lastModified <
|
||||
this.plugin.settings.syncOnSaveAfterMilliseconds
|
||||
this.plugin.settings.syncOnSaveAfterMilliseconds!
|
||||
) {
|
||||
if (!runScheduled) {
|
||||
const scheduleTimeFromNow =
|
||||
this.plugin.settings.syncOnSaveAfterMilliseconds -
|
||||
this.plugin.settings.syncOnSaveAfterMilliseconds! -
|
||||
(currentTime - lastModified);
|
||||
log.info(
|
||||
`schedule a run for ${scheduleTimeFromNow} milliseconds later`
|
||||
@ -1864,7 +1864,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
||||
.setDesc(t("settings_enablestatusbar_info_desc"))
|
||||
.addToggle((toggle) => {
|
||||
toggle
|
||||
.setValue(this.plugin.settings.enableStatusBarInfo)
|
||||
.setValue(this.plugin.settings.enableStatusBarInfo ?? false)
|
||||
.onChange(async (val) => {
|
||||
this.plugin.settings.enableStatusBarInfo = val;
|
||||
await this.plugin.saveSettings();
|
||||
@ -1897,7 +1897,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
||||
|
||||
.addTextArea((textArea) => {
|
||||
textArea
|
||||
.setValue(`${this.plugin.settings.ignorePaths.join("\n")}`)
|
||||
.setValue(`${(this.plugin.settings.ignorePaths ?? []).join("\n")}`)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.ignorePaths = value
|
||||
.trim()
|
||||
@ -1999,9 +1999,9 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
||||
t("settings_deletetowhere_obsidian_trash")
|
||||
);
|
||||
dropdown
|
||||
.setValue(this.plugin.settings.deleteToWhere)
|
||||
.onChange(async (val: "system" | "obsidian") => {
|
||||
this.plugin.settings.deleteToWhere = val;
|
||||
.setValue(this.plugin.settings.deleteToWhere ?? "system")
|
||||
.onChange(async (val) => {
|
||||
this.plugin.settings.deleteToWhere = val as "system" | "obsidian";
|
||||
await this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
@ -2044,7 +2044,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
|
||||
dropdown.addOption("info", "info");
|
||||
dropdown.addOption("debug", "debug");
|
||||
dropdown
|
||||
.setValue(this.plugin.settings.currLogLevel)
|
||||
.setValue(this.plugin.settings.currLogLevel ?? "info")
|
||||
.onChange(async (val: string) => {
|
||||
this.plugin.settings.currLogLevel = val;
|
||||
log.setLevel(val as any);
|
||||
|
115
src/sync.ts
115
src/sync.ts
@ -186,7 +186,7 @@ export const parseRemoteItems = async (
|
||||
password: string = ""
|
||||
) => {
|
||||
const remoteStates = [] as FileOrFolderMixedState[];
|
||||
let metadataFile: FileOrFolderMixedState = undefined;
|
||||
let metadataFile: FileOrFolderMixedState | undefined = undefined;
|
||||
if (remote === undefined) {
|
||||
return {
|
||||
remoteStates: remoteStates,
|
||||
@ -212,8 +212,8 @@ export const parseRemoteItems = async (
|
||||
remoteType,
|
||||
db,
|
||||
key,
|
||||
entry.lastModified,
|
||||
entry.etag,
|
||||
entry.lastModified ?? Date.now(),
|
||||
entry.etag ?? "",
|
||||
vaultRandomID
|
||||
);
|
||||
|
||||
@ -268,7 +268,7 @@ export const parseRemoteItems = async (
|
||||
};
|
||||
|
||||
export const fetchMetadataFile = async (
|
||||
metadataFile: FileOrFolderMixedState,
|
||||
metadataFile: FileOrFolderMixedState | undefined,
|
||||
client: RemoteClient,
|
||||
vault: Vault,
|
||||
password: string = ""
|
||||
@ -283,7 +283,7 @@ export const fetchMetadataFile = async (
|
||||
const buf = await client.downloadFromRemote(
|
||||
metadataFile.key,
|
||||
vault,
|
||||
metadataFile.mtimeRemote,
|
||||
metadataFile.mtimeRemote ?? Date.now(),
|
||||
password,
|
||||
metadataFile.remoteEncryptedKey,
|
||||
true
|
||||
@ -325,8 +325,8 @@ const ensembleMixedStates = async (
|
||||
remoteStates: FileOrFolderMixedState[],
|
||||
local: TAbstractFile[],
|
||||
localConfigDirContents: ObsConfigDirFileType[] | undefined,
|
||||
remoteDeleteHistory: DeletionOnRemote[],
|
||||
localFileHistory: FileFolderHistoryRecord[],
|
||||
remoteDeleteHistory: DeletionOnRemote[] | undefined,
|
||||
localFileHistory: FileFolderHistoryRecord[] | undefined,
|
||||
syncConfigDir: boolean,
|
||||
configDir: string,
|
||||
syncUnderscoreItems: boolean,
|
||||
@ -413,7 +413,10 @@ const ensembleMixedStates = async (
|
||||
if (syncConfigDir && localConfigDirContents !== undefined) {
|
||||
for (const entry of localConfigDirContents) {
|
||||
const key = entry.key;
|
||||
let mtimeLocal = Math.max(entry.mtime ?? 0, entry.ctime ?? 0);
|
||||
let mtimeLocal: number | undefined = Math.max(
|
||||
entry.mtime ?? 0,
|
||||
entry.ctime ?? 0
|
||||
);
|
||||
if (Number.isNaN(mtimeLocal) || mtimeLocal === 0) {
|
||||
mtimeLocal = undefined;
|
||||
}
|
||||
@ -453,7 +456,7 @@ const ensembleMixedStates = async (
|
||||
}
|
||||
}
|
||||
|
||||
for (const entry of remoteDeleteHistory) {
|
||||
for (const entry of remoteDeleteHistory ?? []) {
|
||||
const key = entry.key;
|
||||
const r = {
|
||||
key: key,
|
||||
@ -485,7 +488,7 @@ const ensembleMixedStates = async (
|
||||
}
|
||||
}
|
||||
|
||||
for (const entry of localFileHistory) {
|
||||
for (const entry of localFileHistory ?? []) {
|
||||
let key = entry.key;
|
||||
if (entry.keyType === "folder") {
|
||||
if (!entry.key.endsWith("/")) {
|
||||
@ -532,7 +535,7 @@ const ensembleMixedStates = async (
|
||||
changeLocalMtimeUsingMapping: true,
|
||||
};
|
||||
if (results.hasOwnProperty(key)) {
|
||||
let mtimeLocal = Math.max(
|
||||
let mtimeLocal: number | undefined = Math.max(
|
||||
r.mtimeLocal ?? 0,
|
||||
results[key].mtimeLocal ?? 0
|
||||
);
|
||||
@ -624,13 +627,13 @@ const assignOperationToFileInplace = (
|
||||
|
||||
// 1. mtimeLocal
|
||||
if (r.existLocal) {
|
||||
const mtimeRemote = r.existRemote ? r.mtimeRemote : -1;
|
||||
const mtimeRemote = r.existRemote ? r.mtimeRemote! : -1;
|
||||
const deltimeRemote = r.deltimeRemote !== undefined ? r.deltimeRemote : -1;
|
||||
const deltimeLocal = r.deltimeLocal !== undefined ? r.deltimeLocal : -1;
|
||||
if (
|
||||
r.mtimeLocal >= mtimeRemote &&
|
||||
r.mtimeLocal >= deltimeLocal &&
|
||||
r.mtimeLocal >= deltimeRemote
|
||||
r.mtimeLocal! >= mtimeRemote &&
|
||||
r.mtimeLocal! >= deltimeLocal &&
|
||||
r.mtimeLocal! >= deltimeRemote
|
||||
) {
|
||||
if (sizeLocalComp === undefined) {
|
||||
throw new Error(
|
||||
@ -654,7 +657,7 @@ const assignOperationToFileInplace = (
|
||||
} else {
|
||||
// limit the sizes
|
||||
if (sizeLocalComp <= skipSizeLargerThan) {
|
||||
if (sizeRemoteComp <= skipSizeLargerThan) {
|
||||
if (sizeRemoteComp! <= skipSizeLargerThan) {
|
||||
r.decision = "uploadLocalToRemote";
|
||||
r.decisionBranch = 18;
|
||||
} else {
|
||||
@ -662,7 +665,7 @@ const assignOperationToFileInplace = (
|
||||
r.decisionBranch = 19;
|
||||
}
|
||||
} else {
|
||||
if (sizeRemoteComp <= skipSizeLargerThan) {
|
||||
if (sizeRemoteComp! <= skipSizeLargerThan) {
|
||||
r.decision = "errorLocalTooLargeConflictRemote";
|
||||
r.decisionBranch = 20;
|
||||
} else {
|
||||
@ -713,13 +716,13 @@ const assignOperationToFileInplace = (
|
||||
|
||||
// 2. mtimeRemote
|
||||
if (r.existRemote) {
|
||||
const mtimeLocal = r.existLocal ? r.mtimeLocal : -1;
|
||||
const mtimeLocal = r.existLocal ? r.mtimeLocal! : -1;
|
||||
const deltimeRemote = r.deltimeRemote !== undefined ? r.deltimeRemote : -1;
|
||||
const deltimeLocal = r.deltimeLocal !== undefined ? r.deltimeLocal : -1;
|
||||
if (
|
||||
r.mtimeRemote > mtimeLocal &&
|
||||
r.mtimeRemote >= deltimeLocal &&
|
||||
r.mtimeRemote >= deltimeRemote
|
||||
r.mtimeRemote! > mtimeLocal &&
|
||||
r.mtimeRemote! >= deltimeLocal &&
|
||||
r.mtimeRemote! >= deltimeRemote
|
||||
) {
|
||||
// we have remote laregest mtime,
|
||||
// and the local not existing or smaller mtime
|
||||
@ -771,8 +774,8 @@ const assignOperationToFileInplace = (
|
||||
|
||||
// 3. deltimeLocal
|
||||
if (r.deltimeLocal !== undefined && r.deltimeLocal !== 0) {
|
||||
const mtimeLocal = r.existLocal ? r.mtimeLocal : -1;
|
||||
const mtimeRemote = r.existRemote ? r.mtimeRemote : -1;
|
||||
const mtimeLocal = r.existLocal ? r.mtimeLocal! : -1;
|
||||
const mtimeRemote = r.existRemote ? r.mtimeRemote! : -1;
|
||||
const deltimeRemote = r.deltimeRemote !== undefined ? r.deltimeRemote : -1;
|
||||
if (
|
||||
r.deltimeLocal >= mtimeLocal &&
|
||||
@ -787,9 +790,9 @@ const assignOperationToFileInplace = (
|
||||
}
|
||||
} else {
|
||||
const localTooLargeToDelete =
|
||||
r.existLocal && sizeLocalComp > skipSizeLargerThan;
|
||||
r.existLocal && sizeLocalComp! > skipSizeLargerThan;
|
||||
const remoteTooLargeToDelete =
|
||||
r.existRemote && sizeRemoteComp > skipSizeLargerThan;
|
||||
r.existRemote && sizeRemoteComp! > skipSizeLargerThan;
|
||||
if (localTooLargeToDelete) {
|
||||
if (remoteTooLargeToDelete) {
|
||||
r.decision = "skipUsingLocalDelTooLarge";
|
||||
@ -824,8 +827,8 @@ const assignOperationToFileInplace = (
|
||||
|
||||
// 4. deltimeRemote
|
||||
if (r.deltimeRemote !== undefined && r.deltimeRemote !== 0) {
|
||||
const mtimeLocal = r.existLocal ? r.mtimeLocal : -1;
|
||||
const mtimeRemote = r.existRemote ? r.mtimeRemote : -1;
|
||||
const mtimeLocal = r.existLocal ? r.mtimeLocal! : -1;
|
||||
const mtimeRemote = r.existRemote ? r.mtimeRemote! : -1;
|
||||
const deltimeLocal = r.deltimeLocal !== undefined ? r.deltimeLocal : -1;
|
||||
if (
|
||||
r.deltimeRemote >= mtimeLocal &&
|
||||
@ -840,9 +843,9 @@ const assignOperationToFileInplace = (
|
||||
}
|
||||
} else {
|
||||
const localTooLargeToDelete =
|
||||
r.existLocal && sizeLocalComp > skipSizeLargerThan;
|
||||
r.existLocal && sizeLocalComp! > skipSizeLargerThan;
|
||||
const remoteTooLargeToDelete =
|
||||
r.existRemote && sizeRemoteComp > skipSizeLargerThan;
|
||||
r.existRemote && sizeRemoteComp! > skipSizeLargerThan;
|
||||
if (localTooLargeToDelete) {
|
||||
if (remoteTooLargeToDelete) {
|
||||
r.decision = "skipUsingRemoteDelTooLarge";
|
||||
@ -905,7 +908,13 @@ const assignOperationToFolderInplace = async (
|
||||
// if it was created after deletion, we should keep it as is
|
||||
if (requireApiVersion(API_VER_STAT_FOLDER)) {
|
||||
if (r.existLocal) {
|
||||
const { ctime, mtime } = await statFix(vault, r.key);
|
||||
let ctime = 0;
|
||||
let mtime = 0;
|
||||
const s = await statFix(vault, r.key);
|
||||
if (s !== undefined && s !== null) {
|
||||
ctime = s.ctime;
|
||||
mtime = s.mtime;
|
||||
}
|
||||
const cmtime = Math.max(ctime ?? 0, mtime ?? 0);
|
||||
if (
|
||||
!Number.isNaN(cmtime) &&
|
||||
@ -936,9 +945,9 @@ const assignOperationToFolderInplace = async (
|
||||
if (
|
||||
r.existLocal &&
|
||||
r.changeLocalMtimeUsingMapping &&
|
||||
r.mtimeLocal > 0 &&
|
||||
r.mtimeLocal > deltimeLocal &&
|
||||
r.mtimeLocal > deltimeRemote
|
||||
r.mtimeLocal! > 0 &&
|
||||
r.mtimeLocal! > deltimeLocal &&
|
||||
r.mtimeLocal! > deltimeRemote
|
||||
) {
|
||||
keptFolder.add(getParentFolder(r.key));
|
||||
if (r.existLocal && r.existRemote) {
|
||||
@ -1018,8 +1027,8 @@ export const getSyncPlan = async (
|
||||
remoteStates: FileOrFolderMixedState[],
|
||||
local: TAbstractFile[],
|
||||
localConfigDirContents: ObsConfigDirFileType[] | undefined,
|
||||
remoteDeleteHistory: DeletionOnRemote[],
|
||||
localFileHistory: FileFolderHistoryRecord[],
|
||||
remoteDeleteHistory: DeletionOnRemote[] | undefined,
|
||||
localFileHistory: FileFolderHistoryRecord[] | undefined,
|
||||
remoteType: SUPPORTED_SERVICES_TYPE,
|
||||
triggerSource: SyncTriggerSourceType,
|
||||
vault: Vault,
|
||||
@ -1071,30 +1080,30 @@ export const getSyncPlan = async (
|
||||
);
|
||||
}
|
||||
|
||||
if (SIZES_GO_WRONG_DECISIONS.has(val.decision)) {
|
||||
if (SIZES_GO_WRONG_DECISIONS.has(val.decision!)) {
|
||||
sizesGoWrong.push(val);
|
||||
}
|
||||
|
||||
if (DELETION_DECISIONS.has(val.decision)) {
|
||||
if (DELETION_DECISIONS.has(val.decision!)) {
|
||||
if (val.decision === "uploadLocalDelHistToRemote") {
|
||||
deletions.push({
|
||||
key: key,
|
||||
actionWhen: val.deltimeLocal,
|
||||
actionWhen: val.deltimeLocal!,
|
||||
});
|
||||
} else if (val.decision === "keepRemoteDelHist") {
|
||||
deletions.push({
|
||||
key: key,
|
||||
actionWhen: val.deltimeRemote,
|
||||
actionWhen: val.deltimeRemote!,
|
||||
});
|
||||
} else if (val.decision === "uploadLocalDelHistToRemoteFolder") {
|
||||
deletions.push({
|
||||
key: key,
|
||||
actionWhen: val.deltimeLocal,
|
||||
actionWhen: val.deltimeLocal!,
|
||||
});
|
||||
} else if (val.decision === "keepRemoteDelHistFolder") {
|
||||
deletions.push({
|
||||
key: key,
|
||||
actionWhen: val.deltimeRemote,
|
||||
actionWhen: val.deltimeRemote!,
|
||||
});
|
||||
} else {
|
||||
throw Error(`do not know how to delete for decision ${val.decision}`);
|
||||
@ -1131,7 +1140,7 @@ const uploadExtraMeta = async (
|
||||
}
|
||||
|
||||
const key = DEFAULT_FILE_NAME_FOR_METADATAONREMOTE;
|
||||
let remoteEncryptedKey = key;
|
||||
let remoteEncryptedKey: string | undefined = key;
|
||||
|
||||
if (password !== "") {
|
||||
if (metadataFile === undefined) {
|
||||
@ -1180,7 +1189,7 @@ const dispatchOperationToActual = async (
|
||||
localDeleteFunc: any,
|
||||
password: string = ""
|
||||
) => {
|
||||
let remoteEncryptedKey = key;
|
||||
let remoteEncryptedKey: string | undefined = key;
|
||||
if (password !== "") {
|
||||
remoteEncryptedKey = r.remoteEncryptedKey;
|
||||
if (remoteEncryptedKey === undefined || remoteEncryptedKey === "") {
|
||||
@ -1233,12 +1242,12 @@ const dispatchOperationToActual = async (
|
||||
client.serviceType,
|
||||
db,
|
||||
r.key,
|
||||
r.mtimeLocal,
|
||||
r.sizeLocal,
|
||||
r.mtimeLocal!,
|
||||
r.sizeLocal!,
|
||||
r.key,
|
||||
remoteObjMeta.lastModified,
|
||||
remoteObjMeta.lastModified ?? Date.now(),
|
||||
remoteObjMeta.size,
|
||||
remoteObjMeta.etag,
|
||||
remoteObjMeta.etag ?? "",
|
||||
vaultRandomID
|
||||
);
|
||||
}
|
||||
@ -1248,7 +1257,7 @@ const dispatchOperationToActual = async (
|
||||
await client.downloadFromRemote(
|
||||
r.key,
|
||||
vault,
|
||||
r.mtimeRemote,
|
||||
r.mtimeRemote!,
|
||||
password,
|
||||
remoteEncryptedKey
|
||||
);
|
||||
@ -1269,12 +1278,12 @@ const dispatchOperationToActual = async (
|
||||
client.serviceType,
|
||||
db,
|
||||
r.key,
|
||||
r.mtimeLocal,
|
||||
r.sizeLocal,
|
||||
r.mtimeLocal!,
|
||||
r.sizeLocal!,
|
||||
r.key,
|
||||
remoteObjMeta.lastModified,
|
||||
remoteObjMeta.lastModified ?? Date.now(),
|
||||
remoteObjMeta.size,
|
||||
remoteObjMeta.etag,
|
||||
remoteObjMeta.etag ?? "",
|
||||
vaultRandomID
|
||||
);
|
||||
}
|
||||
@ -1388,7 +1397,7 @@ export const doActualSync = async (
|
||||
vault: Vault,
|
||||
syncPlan: SyncPlanType,
|
||||
sortedKeys: string[],
|
||||
metadataFile: FileOrFolderMixedState,
|
||||
metadataFile: FileOrFolderMixedState | undefined,
|
||||
origMetadata: MetadataOnRemote,
|
||||
sizesGoWrong: FileOrFolderMixedState[],
|
||||
deletions: DeletionOnRemote[],
|
||||
|
@ -45,8 +45,8 @@ describe("Metadata operations tests", () => {
|
||||
});
|
||||
|
||||
it("should treat undefined correctly", async () => {
|
||||
const a: MetadataOnRemote = undefined;
|
||||
let b: MetadataOnRemote = {
|
||||
const a: MetadataOnRemote | undefined = undefined;
|
||||
let b: MetadataOnRemote | undefined = {
|
||||
deletions: [
|
||||
{ key: "xxx", actionWhen: 1 },
|
||||
{ key: "yyy", actionWhen: 2 },
|
||||
|
@ -5,6 +5,7 @@
|
||||
"inlineSources": true,
|
||||
"module": "ESNext",
|
||||
"target": "es6",
|
||||
"strict": true,
|
||||
"allowJs": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
|
Loading…
Reference in New Issue
Block a user