strict anywhere

This commit is contained in:
fyears 2024-01-14 22:13:10 +08:00
parent 480cf224c6
commit 8dde6c59d2
16 changed files with 306 additions and 213 deletions

View File

@ -48,6 +48,7 @@ export interface DropboxConfig {
export type WebdavAuthType = "digest" | "basic"; export type WebdavAuthType = "digest" | "basic";
export type WebdavDepthType = export type WebdavDepthType =
| "auto"
| "auto_unknown" | "auto_unknown"
| "auto_1" | "auto_1"
| "auto_infinity" | "auto_infinity"
@ -112,7 +113,7 @@ export interface RemotelySavePluginSettings {
export interface RemoteItem { export interface RemoteItem {
key: string; key: string;
lastModified: number; lastModified?: number;
size: number; size: number;
remoteType: SUPPORTED_SERVICES_TYPE; remoteType: SUPPORTED_SERVICES_TYPE;
etag?: string; etag?: string;

View File

@ -14,7 +14,7 @@ export const exportQrCodeUri = async (
currentVaultName: string, currentVaultName: string,
pluginVersion: string pluginVersion: string
) => { ) => {
const settings2 = cloneDeep(settings); const settings2: Partial<RemotelySavePluginSettings> = cloneDeep(settings);
delete settings2.dropbox; delete settings2.dropbox;
delete settings2.onedrive; delete settings2.onedrive;
delete settings2.vaultRandomID; delete settings2.vaultRandomID;

View File

@ -436,8 +436,8 @@ export const insertRenameRecordByVault = async (
vaultRandomID: string vaultRandomID: string
) => { ) => {
// log.info(fileOrFolder); // log.info(fileOrFolder);
let k1: FileFolderHistoryRecord; let k1: FileFolderHistoryRecord | undefined;
let k2: FileFolderHistoryRecord; let k2: FileFolderHistoryRecord | undefined;
const actionWhen = Date.now(); const actionWhen = Date.now();
if (fileOrFolder instanceof TFile) { if (fileOrFolder instanceof TFile) {
k1 = { k1 = {
@ -473,9 +473,11 @@ export const insertRenameRecordByVault = async (
// TAbstractFile does not contain these info // TAbstractFile does not contain these info
// but from API_VER_STAT_FOLDER we can manually stat them by path. // but from API_VER_STAT_FOLDER we can manually stat them by path.
const s = await statFix(fileOrFolder.vault, fileOrFolder.path); const s = await statFix(fileOrFolder.vault, fileOrFolder.path);
if (s !== undefined && s !== null) {
ctime = s.ctime; ctime = s.ctime;
mtime = s.mtime; mtime = s.mtime;
} }
}
k1 = { k1 = {
key: key, key: key,
ctime: ctime, ctime: ctime,
@ -500,8 +502,8 @@ export const insertRenameRecordByVault = async (
}; };
} }
await Promise.all([ await Promise.all([
db.fileHistoryTbl.setItem(`${vaultRandomID}\t${k1.key}`, k1), db.fileHistoryTbl.setItem(`${vaultRandomID}\t${k1!.key}`, k1),
db.fileHistoryTbl.setItem(`${vaultRandomID}\t${k2.key}`, k2), db.fileHistoryTbl.setItem(`${vaultRandomID}\t${k2!.key}`, k2),
]); ]);
}; };

View File

@ -133,18 +133,18 @@ const getIconSvg = () => {
}; };
export default class RemotelySavePlugin extends Plugin { export default class RemotelySavePlugin extends Plugin {
settings: RemotelySavePluginSettings; settings!: RemotelySavePluginSettings;
db: InternalDBs; db!: InternalDBs;
syncStatus: SyncStatusType; syncStatus!: SyncStatusType;
statusBarElement: HTMLSpanElement; statusBarElement!: HTMLSpanElement;
oauth2Info: OAuth2Info; oauth2Info!: OAuth2Info;
currLogLevel: string; currLogLevel!: string;
currSyncMsg?: string; currSyncMsg?: string;
syncRibbon?: HTMLElement; syncRibbon?: HTMLElement;
autoRunIntervalID?: number; autoRunIntervalID?: number;
syncOnSaveIntervalID?: number; syncOnSaveIntervalID?: number;
i18n: I18n; i18n!: I18n;
vaultRandomID: string; vaultRandomID!: string;
debugServerTemp?: string; debugServerTemp?: string;
async syncRun(triggerSource: SyncTriggerSourceType = "manual") { async syncRun(triggerSource: SyncTriggerSourceType = "manual") {
@ -175,7 +175,7 @@ export default class RemotelySavePlugin extends Plugin {
let originLabel = `${this.manifest.name}`; let originLabel = `${this.manifest.name}`;
if (this.syncRibbon !== undefined) { if (this.syncRibbon !== undefined) {
originLabel = this.syncRibbon.getAttribute("aria-label"); originLabel = this.syncRibbon.getAttribute("aria-label") as string;
} }
try { try {
@ -286,7 +286,8 @@ export default class RemotelySavePlugin extends Plugin {
this.db, this.db,
this.vaultRandomID this.vaultRandomID
); );
let localConfigDirContents: ObsConfigDirFileType[] = undefined; let localConfigDirContents: ObsConfigDirFileType[] | undefined =
undefined;
if (this.settings.syncConfigDir) { if (this.settings.syncConfigDir) {
localConfigDirContents = await listFilesInObsFolder( localConfigDirContents = await listFilesInObsFolder(
this.app.vault.configDir, this.app.vault.configDir,
@ -312,11 +313,11 @@ export default class RemotelySavePlugin extends Plugin {
client.serviceType, client.serviceType,
triggerSource, triggerSource,
this.app.vault, this.app.vault,
this.settings.syncConfigDir, this.settings.syncConfigDir ?? false,
this.app.vault.configDir, this.app.vault.configDir,
this.settings.syncUnderscoreItems, this.settings.syncUnderscoreItems ?? false,
this.settings.skipSizeLargerThan, this.settings.skipSizeLargerThan ?? -1,
this.settings.ignorePaths, this.settings.ignorePaths ?? [],
this.settings.password this.settings.password
); );
log.info(plan.mixedStates); // for debugging log.info(plan.mixedStates); // for debugging
@ -350,7 +351,7 @@ export default class RemotelySavePlugin extends Plugin {
new SizesConflictModal( new SizesConflictModal(
self.app, self.app,
self, self,
this.settings.skipSizeLargerThan, this.settings.skipSizeLargerThan ?? -1,
ss, ss,
this.settings.password !== "" this.settings.password !== ""
).open(); ).open();
@ -397,7 +398,7 @@ export default class RemotelySavePlugin extends Plugin {
this.manifest.id this.manifest.id
}-${Date.now()}: finish sync, triggerSource=${triggerSource}` }-${Date.now()}: finish sync, triggerSource=${triggerSource}`
); );
} catch (error) { } catch (error: any) {
const msg = t("syncrun_abort", { const msg = t("syncrun_abort", {
manifestID: this.manifest.id, manifestID: this.manifest.id,
theDate: `${Date.now()}`, theDate: `${Date.now()}`,
@ -412,7 +413,7 @@ export default class RemotelySavePlugin extends Plugin {
getNotice(e.message, 10 * 1000); getNotice(e.message, 10 * 1000);
} }
} else { } else {
getNotice(error.message, 10 * 1000); getNotice(error?.message ?? "error while sync", 10 * 1000);
} }
this.syncStatus = "idle"; this.syncStatus = "idle";
if (this.syncRibbon !== undefined) { if (this.syncRibbon !== undefined) {
@ -445,7 +446,7 @@ export default class RemotelySavePlugin extends Plugin {
await this.checkIfPresetRulesFollowed(); await this.checkIfPresetRulesFollowed();
// lang should be load early, but after settings // 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; this.settings.lang = lang;
await this.saveSettings(); await this.saveSettings();
}); });
@ -475,8 +476,11 @@ export default class RemotelySavePlugin extends Plugin {
vaultBasePath, vaultBasePath,
vaultRandomIDFromOldConfigFile vaultRandomIDFromOldConfigFile
); );
} catch (err) { } catch (err: any) {
new Notice(err.message, 10 * 1000); new Notice(
err?.message ?? "error of prepareDBAndVaultRandomID",
10 * 1000
);
throw err; throw err;
} }
@ -566,14 +570,18 @@ export default class RemotelySavePlugin extends Plugin {
this.registerObsidianProtocolHandler( this.registerObsidianProtocolHandler(
COMMAND_CALLBACK_DROPBOX, COMMAND_CALLBACK_DROPBOX,
async (inputParams) => { async (inputParams) => {
if (inputParams.code !== undefined) { if (
inputParams.code !== undefined &&
this.oauth2Info?.verifier !== undefined
) {
if (this.oauth2Info.helperModal !== undefined) { if (this.oauth2Info.helperModal !== undefined) {
this.oauth2Info.helperModal.contentEl.empty(); const k = this.oauth2Info.helperModal.contentEl;
k.empty();
t("protocol_dropbox_connecting") t("protocol_dropbox_connecting")
.split("\n") .split("\n")
.forEach((val) => { .forEach((val) => {
this.oauth2Info.helperModal.contentEl.createEl("p", { k.createEl("p", {
text: val, text: val,
}); });
}); });
@ -596,7 +604,7 @@ export default class RemotelySavePlugin extends Plugin {
const self = this; const self = this;
setConfigBySuccessfullAuthInplaceDropbox( setConfigBySuccessfullAuthInplaceDropbox(
this.settings.dropbox, this.settings.dropbox,
authRes, authRes!,
() => self.saveSettings() () => self.saveSettings()
); );
@ -655,14 +663,18 @@ export default class RemotelySavePlugin extends Plugin {
this.registerObsidianProtocolHandler( this.registerObsidianProtocolHandler(
COMMAND_CALLBACK_ONEDRIVE, COMMAND_CALLBACK_ONEDRIVE,
async (inputParams) => { async (inputParams) => {
if (inputParams.code !== undefined) { if (
inputParams.code !== undefined &&
this.oauth2Info?.verifier !== undefined
) {
if (this.oauth2Info.helperModal !== undefined) { if (this.oauth2Info.helperModal !== undefined) {
this.oauth2Info.helperModal.contentEl.empty(); const k = this.oauth2Info.helperModal.contentEl;
k.empty();
t("protocol_onedrive_connecting") t("protocol_onedrive_connecting")
.split("\n") .split("\n")
.forEach((val) => { .forEach((val) => {
this.oauth2Info.helperModal.contentEl.createEl("p", { k.createEl("p", {
text: val, text: val,
}); });
}); });
@ -824,7 +836,13 @@ export default class RemotelySavePlugin extends Plugin {
this.syncRibbon = undefined; this.syncRibbon = undefined;
if (this.oauth2Info !== undefined) { if (this.oauth2Info !== undefined) {
this.oauth2Info.helperModal = 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; let dropboxExpired = false;
if ( if (
this.settings.dropbox.refreshToken !== "" && this.settings.dropbox.refreshToken !== "" &&
current >= this.settings.dropbox.credentialsShouldBeDeletedAtTime current >= this.settings!.dropbox!.credentialsShouldBeDeletedAtTime!
) { ) {
dropboxExpired = true; dropboxExpired = true;
this.settings.dropbox = cloneDeep(DEFAULT_DROPBOX_CONFIG); this.settings.dropbox = cloneDeep(DEFAULT_DROPBOX_CONFIG);
@ -942,7 +960,7 @@ export default class RemotelySavePlugin extends Plugin {
let onedriveExpired = false; let onedriveExpired = false;
if ( if (
this.settings.onedrive.refreshToken !== "" && this.settings.onedrive.refreshToken !== "" &&
current >= this.settings.onedrive.credentialsShouldBeDeletedAtTime current >= this.settings!.onedrive!.credentialsShouldBeDeletedAtTime!
) { ) {
onedriveExpired = true; onedriveExpired = true;
this.settings.onedrive = cloneDeep(DEFAULT_ONEDRIVE_CONFIG); this.settings.onedrive = cloneDeep(DEFAULT_ONEDRIVE_CONFIG);
@ -1078,11 +1096,11 @@ export default class RemotelySavePlugin extends Plugin {
// ); // );
if ( if (
currentTime - lastModified < currentTime - lastModified <
this.settings.syncOnSaveAfterMilliseconds this.settings!.syncOnSaveAfterMilliseconds!
) { ) {
if (!runScheduled) { if (!runScheduled) {
const scheduleTimeFromNow = const scheduleTimeFromNow =
this.settings.syncOnSaveAfterMilliseconds - this.settings!.syncOnSaveAfterMilliseconds! -
(currentTime - lastModified); (currentTime - lastModified);
log.info( log.info(
`schedule a run for ${scheduleTimeFromNow} milliseconds later` `schedule a run for ${scheduleTimeFromNow} milliseconds later`

View File

@ -26,8 +26,8 @@ export interface MetadataOnRemote {
} }
export const isEqualMetadataOnRemote = ( export const isEqualMetadataOnRemote = (
a: MetadataOnRemote, a: MetadataOnRemote | undefined,
b: MetadataOnRemote b: MetadataOnRemote | undefined
) => { ) => {
const m1 = a === undefined ? { deletions: [] } : a; const m1 = a === undefined ? { deletions: [] } : a;
const m2 = b === undefined ? { deletions: [] } : b; const m2 = b === undefined ? { deletions: [] } : b;

View File

@ -126,8 +126,12 @@ export const base64ToArrayBuffer = (b64text: string) => {
* @returns * @returns
*/ */
export const hexStringToTypedArray = (hex: string) => { 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( return new Uint8Array(
hex.match(/[\da-f]{2}/gi).map(function (h) { f.map(function (h) {
return parseInt(h, 16); return parseInt(h, 16);
}) })
); );
@ -236,7 +240,7 @@ export const setToString = (a: Set<string>, delimiter: string = ",") => {
export const extractSvgSub = (x: string, subEl: string = "rect") => { export const extractSvgSub = (x: string, subEl: string = "rect") => {
const parser = new window.DOMParser(); const parser = new window.DOMParser();
const dom = parser.parseFromString(x, "image/svg+xml"); const dom = parser.parseFromString(x, "image/svg+xml");
const svg = dom.querySelector("svg"); const svg = dom.querySelector("svg")!;
svg.setAttribute("viewbox", "0 0 10 10"); svg.setAttribute("viewbox", "0 0 10 10");
return svg.innerHTML; return svg.innerHTML;
}; };
@ -317,7 +321,7 @@ export const getTypeName = (obj: any) => {
* @param x * @param x
* @returns * @returns
*/ */
export const atWhichLevel = (x: string) => { export const atWhichLevel = (x: string | undefined) => {
if ( if (
x === undefined || x === undefined ||
x === "" || x === "" ||
@ -413,11 +417,14 @@ export const toText = (x: any) => {
*/ */
export const statFix = async (vault: Vault, path: string) => { export const statFix = async (vault: Vault, path: string) => {
const s = await vault.adapter.stat(path); 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)) { 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)) { if (s.mtime === undefined || s.mtime === null || Number.isNaN(s.mtime)) {
s.mtime = undefined; s.mtime = undefined as any; // force assignment
} }
if ( if (
(s.size === undefined || s.size === null || Number.isNaN(s.size)) && (s.size === undefined || s.size === null || Number.isNaN(s.size)) &&

View File

@ -53,9 +53,9 @@ export const listFilesInObsFolder = async (
const CHUNK_SIZE = 10; const CHUNK_SIZE = 10;
const contents: ObsConfigDirFileType[] = []; const contents: ObsConfigDirFileType[] = [];
while (q.length > 0) { while (q.length > 0) {
const itemsToFetch = []; const itemsToFetch: string[] = [];
while (q.length > 0) { while (q.length > 0) {
itemsToFetch.push(q.pop()); itemsToFetch.push(q.pop()!);
} }
const itemsToFetchChunks = chunk(itemsToFetch, CHUNK_SIZE); const itemsToFetchChunks = chunk(itemsToFetch, CHUNK_SIZE);
@ -63,8 +63,11 @@ export const listFilesInObsFolder = async (
const r = singleChunk.map(async (x) => { const r = singleChunk.map(async (x) => {
const statRes = await statFix(vault, 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"; const isFolder = statRes.type === "folder";
let children: ListedFiles = undefined; let children: ListedFiles | undefined = undefined;
if (isFolder) { if (isFolder) {
children = await vault.adapter.list(x); children = await vault.adapter.list(x);
} }

View File

@ -43,10 +43,10 @@ export class RemoteClient {
"remember to provide vault name and callback while init webdav client" "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.webdavConfig = webdavConfig;
this.webdavClient = webdav.getWebdavClient( this.webdavClient = webdav.getWebdavClient(
this.webdavConfig, this.webdavConfig!,
remoteBaseDir, remoteBaseDir,
saveUpdatedConfigFunc saveUpdatedConfigFunc
); );
@ -56,10 +56,10 @@ export class RemoteClient {
"remember to provide vault name and callback while init dropbox client" "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.dropboxConfig = dropboxConfig;
this.dropboxClient = dropbox.getDropboxClient( this.dropboxClient = dropbox.getDropboxClient(
this.dropboxConfig, this.dropboxConfig!,
remoteBaseDir, remoteBaseDir,
saveUpdatedConfigFunc saveUpdatedConfigFunc
); );
@ -69,10 +69,10 @@ export class RemoteClient {
"remember to provide vault name and callback while init onedrive client" "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.onedriveConfig = onedriveConfig;
this.onedriveClient = onedrive.getOnedriveClient( this.onedriveClient = onedrive.getOnedriveClient(
this.onedriveConfig, this.onedriveConfig!,
remoteBaseDir, remoteBaseDir,
saveUpdatedConfigFunc saveUpdatedConfigFunc
); );
@ -84,17 +84,17 @@ export class RemoteClient {
getRemoteMeta = async (fileOrFolderPath: string) => { getRemoteMeta = async (fileOrFolderPath: string) => {
if (this.serviceType === "s3") { if (this.serviceType === "s3") {
return await s3.getRemoteMeta( return await s3.getRemoteMeta(
s3.getS3Client(this.s3Config), s3.getS3Client(this.s3Config!),
this.s3Config, this.s3Config!,
fileOrFolderPath fileOrFolderPath
); );
} else if (this.serviceType === "webdav") { } else if (this.serviceType === "webdav") {
return await webdav.getRemoteMeta(this.webdavClient, fileOrFolderPath); return await webdav.getRemoteMeta(this.webdavClient!, fileOrFolderPath);
} else if (this.serviceType === "dropbox") { } else if (this.serviceType === "dropbox") {
return await dropbox.getRemoteMeta(this.dropboxClient, fileOrFolderPath); return await dropbox.getRemoteMeta(this.dropboxClient!, fileOrFolderPath);
} else if (this.serviceType === "onedrive") { } else if (this.serviceType === "onedrive") {
return await onedrive.getRemoteMeta( return await onedrive.getRemoteMeta(
this.onedriveClient, this.onedriveClient!,
fileOrFolderPath fileOrFolderPath
); );
} else { } else {
@ -104,7 +104,7 @@ export class RemoteClient {
uploadToRemote = async ( uploadToRemote = async (
fileOrFolderPath: string, fileOrFolderPath: string,
vault: Vault, vault: Vault | undefined,
isRecursively: boolean = false, isRecursively: boolean = false,
password: string = "", password: string = "",
remoteEncryptedKey: string = "", remoteEncryptedKey: string = "",
@ -114,8 +114,8 @@ export class RemoteClient {
) => { ) => {
if (this.serviceType === "s3") { if (this.serviceType === "s3") {
return await s3.uploadToRemote( return await s3.uploadToRemote(
s3.getS3Client(this.s3Config), s3.getS3Client(this.s3Config!),
this.s3Config, this.s3Config!,
fileOrFolderPath, fileOrFolderPath,
vault, vault,
isRecursively, isRecursively,
@ -126,7 +126,7 @@ export class RemoteClient {
); );
} else if (this.serviceType === "webdav") { } else if (this.serviceType === "webdav") {
return await webdav.uploadToRemote( return await webdav.uploadToRemote(
this.webdavClient, this.webdavClient!,
fileOrFolderPath, fileOrFolderPath,
vault, vault,
isRecursively, isRecursively,
@ -137,7 +137,7 @@ export class RemoteClient {
); );
} else if (this.serviceType === "dropbox") { } else if (this.serviceType === "dropbox") {
return await dropbox.uploadToRemote( return await dropbox.uploadToRemote(
this.dropboxClient, this.dropboxClient!,
fileOrFolderPath, fileOrFolderPath,
vault, vault,
isRecursively, isRecursively,
@ -149,7 +149,7 @@ export class RemoteClient {
); );
} else if (this.serviceType === "onedrive") { } else if (this.serviceType === "onedrive") {
return await onedrive.uploadToRemote( return await onedrive.uploadToRemote(
this.onedriveClient, this.onedriveClient!,
fileOrFolderPath, fileOrFolderPath,
vault, vault,
isRecursively, isRecursively,
@ -167,15 +167,15 @@ export class RemoteClient {
listAllFromRemote = async () => { listAllFromRemote = async () => {
if (this.serviceType === "s3") { if (this.serviceType === "s3") {
return await s3.listAllFromRemote( return await s3.listAllFromRemote(
s3.getS3Client(this.s3Config), s3.getS3Client(this.s3Config!),
this.s3Config this.s3Config!
); );
} else if (this.serviceType === "webdav") { } else if (this.serviceType === "webdav") {
return await webdav.listAllFromRemote(this.webdavClient); return await webdav.listAllFromRemote(this.webdavClient!);
} else if (this.serviceType === "dropbox") { } else if (this.serviceType === "dropbox") {
return await dropbox.listAllFromRemote(this.dropboxClient); return await dropbox.listAllFromRemote(this.dropboxClient!);
} else if (this.serviceType === "onedrive") { } else if (this.serviceType === "onedrive") {
return await onedrive.listAllFromRemote(this.onedriveClient); return await onedrive.listAllFromRemote(this.onedriveClient!);
} else { } else {
throw Error(`not supported service type ${this.serviceType}`); throw Error(`not supported service type ${this.serviceType}`);
} }
@ -191,8 +191,8 @@ export class RemoteClient {
) => { ) => {
if (this.serviceType === "s3") { if (this.serviceType === "s3") {
return await s3.downloadFromRemote( return await s3.downloadFromRemote(
s3.getS3Client(this.s3Config), s3.getS3Client(this.s3Config!),
this.s3Config, this.s3Config!,
fileOrFolderPath, fileOrFolderPath,
vault, vault,
mtime, mtime,
@ -202,7 +202,7 @@ export class RemoteClient {
); );
} else if (this.serviceType === "webdav") { } else if (this.serviceType === "webdav") {
return await webdav.downloadFromRemote( return await webdav.downloadFromRemote(
this.webdavClient, this.webdavClient!,
fileOrFolderPath, fileOrFolderPath,
vault, vault,
mtime, mtime,
@ -212,7 +212,7 @@ export class RemoteClient {
); );
} else if (this.serviceType === "dropbox") { } else if (this.serviceType === "dropbox") {
return await dropbox.downloadFromRemote( return await dropbox.downloadFromRemote(
this.dropboxClient, this.dropboxClient!,
fileOrFolderPath, fileOrFolderPath,
vault, vault,
mtime, mtime,
@ -222,7 +222,7 @@ export class RemoteClient {
); );
} else if (this.serviceType === "onedrive") { } else if (this.serviceType === "onedrive") {
return await onedrive.downloadFromRemote( return await onedrive.downloadFromRemote(
this.onedriveClient, this.onedriveClient!,
fileOrFolderPath, fileOrFolderPath,
vault, vault,
mtime, mtime,
@ -242,29 +242,29 @@ export class RemoteClient {
) => { ) => {
if (this.serviceType === "s3") { if (this.serviceType === "s3") {
return await s3.deleteFromRemote( return await s3.deleteFromRemote(
s3.getS3Client(this.s3Config), s3.getS3Client(this.s3Config!),
this.s3Config, this.s3Config!,
fileOrFolderPath, fileOrFolderPath,
password, password,
remoteEncryptedKey remoteEncryptedKey
); );
} else if (this.serviceType === "webdav") { } else if (this.serviceType === "webdav") {
return await webdav.deleteFromRemote( return await webdav.deleteFromRemote(
this.webdavClient, this.webdavClient!,
fileOrFolderPath, fileOrFolderPath,
password, password,
remoteEncryptedKey remoteEncryptedKey
); );
} else if (this.serviceType === "dropbox") { } else if (this.serviceType === "dropbox") {
return await dropbox.deleteFromRemote( return await dropbox.deleteFromRemote(
this.dropboxClient, this.dropboxClient!,
fileOrFolderPath, fileOrFolderPath,
password, password,
remoteEncryptedKey remoteEncryptedKey
); );
} else if (this.serviceType === "onedrive") { } else if (this.serviceType === "onedrive") {
return await onedrive.deleteFromRemote( return await onedrive.deleteFromRemote(
this.onedriveClient, this.onedriveClient!,
fileOrFolderPath, fileOrFolderPath,
password, password,
remoteEncryptedKey remoteEncryptedKey
@ -277,17 +277,17 @@ export class RemoteClient {
checkConnectivity = async (callbackFunc?: any) => { checkConnectivity = async (callbackFunc?: any) => {
if (this.serviceType === "s3") { if (this.serviceType === "s3") {
return await s3.checkConnectivity( return await s3.checkConnectivity(
s3.getS3Client(this.s3Config), s3.getS3Client(this.s3Config!),
this.s3Config, this.s3Config!,
callbackFunc callbackFunc
); );
} else if (this.serviceType === "webdav") { } else if (this.serviceType === "webdav") {
return await webdav.checkConnectivity(this.webdavClient, callbackFunc); return await webdav.checkConnectivity(this.webdavClient!, callbackFunc);
} else if (this.serviceType === "dropbox") { } else if (this.serviceType === "dropbox") {
return await dropbox.checkConnectivity(this.dropboxClient, callbackFunc); return await dropbox.checkConnectivity(this.dropboxClient!, callbackFunc);
} else if (this.serviceType === "onedrive") { } else if (this.serviceType === "onedrive") {
return await onedrive.checkConnectivity( return await onedrive.checkConnectivity(
this.onedriveClient, this.onedriveClient!,
callbackFunc callbackFunc
); );
} else { } else {
@ -297,9 +297,9 @@ export class RemoteClient {
getUser = async () => { getUser = async () => {
if (this.serviceType === "dropbox") { if (this.serviceType === "dropbox") {
return await dropbox.getUserDisplayName(this.dropboxClient); return await dropbox.getUserDisplayName(this.dropboxClient!);
} else if (this.serviceType === "onedrive") { } else if (this.serviceType === "onedrive") {
return await onedrive.getUserDisplayName(this.onedriveClient); return await onedrive.getUserDisplayName(this.onedriveClient!);
} else { } else {
throw Error(`not supported service type ${this.serviceType}`); throw Error(`not supported service type ${this.serviceType}`);
} }
@ -307,7 +307,7 @@ export class RemoteClient {
revokeAuth = async () => { revokeAuth = async () => {
if (this.serviceType === "dropbox") { if (this.serviceType === "dropbox") {
return await dropbox.revokeAuth(this.dropboxClient); return await dropbox.revokeAuth(this.dropboxClient!);
} else { } else {
throw Error(`not supported service type ${this.serviceType}`); throw Error(`not supported service type ${this.serviceType}`);
} }

View File

@ -24,7 +24,7 @@ import { log } from "./moreOnLog";
export const DEFAULT_DROPBOX_CONFIG: DropboxConfig = { export const DEFAULT_DROPBOX_CONFIG: DropboxConfig = {
accessToken: "", accessToken: "",
clientID: process.env.DEFAULT_DROPBOX_APP_KEY, clientID: process.env.DEFAULT_DROPBOX_APP_KEY ?? "",
refreshToken: "", refreshToken: "",
accessTokenExpiresInSeconds: 0, accessTokenExpiresInSeconds: 0,
accessTokenExpiresAtTime: 0, accessTokenExpiresAtTime: 0,
@ -76,7 +76,7 @@ const fromDropboxItemToRemoteItem = (
| files.DeletedMetadataReference, | files.DeletedMetadataReference,
remoteBaseDir: string remoteBaseDir: string
): RemoteItem => { ): RemoteItem => {
let key = getNormPath(x.path_display, remoteBaseDir); let key = getNormPath(x.path_display!, remoteBaseDir);
if (x[".tag"] === "folder" && !key.endsWith("/")) { if (x[".tag"] === "folder" && !key.endsWith("/")) {
key = `${key}/`; key = `${key}/`;
} }
@ -101,7 +101,8 @@ const fromDropboxItemToRemoteItem = (
remoteType: "dropbox", remoteType: "dropbox",
etag: `${x.id}\t${x.content_hash}`, etag: `${x.id}\t${x.content_hash}`,
} as RemoteItem; } as RemoteItem;
} else if (x[".tag"] === "deleted") { } else {
// x[".tag"] === "deleted"
throw Error("do not support deleted tag"); throw Error("do not support deleted tag");
} }
}; };
@ -188,7 +189,7 @@ export const getAuthUrlAndVerifier = async (
: `obsidian://${COMMAND_CALLBACK_DROPBOX}`; : `obsidian://${COMMAND_CALLBACK_DROPBOX}`;
const authUrl = ( const authUrl = (
await auth.getAuthenticationUrl( await auth.getAuthenticationUrl(
callback, callback as any,
undefined, undefined,
"code", "code",
"offline", "offline",
@ -282,9 +283,7 @@ export const setConfigBySuccessfullAuthInplace = async (
if (authRes.refresh_token !== undefined) { if (authRes.refresh_token !== undefined) {
config.refreshToken = authRes.refresh_token; config.refreshToken = authRes.refresh_token;
} config.accountID = authRes.account_id!;
if (authRes.refresh_token !== undefined) {
config.accountID = authRes.account_id;
} }
if (saveUpdatedConfigFunc !== undefined) { if (saveUpdatedConfigFunc !== undefined) {
@ -307,7 +306,7 @@ interface ErrSubType {
async function retryReq<T>( async function retryReq<T>(
reqFunc: () => Promise<DropboxResponse<T>>, reqFunc: () => Promise<DropboxResponse<T>>,
extraHint: string = "" extraHint: string = ""
): Promise<DropboxResponse<T>> { ): Promise<DropboxResponse<T> | undefined> {
const waitSeconds = [1, 2, 4, 8]; // hard code exponential backoff const waitSeconds = [1, 2, 4, 8]; // hard code exponential backoff
for (let idx = 0; idx < waitSeconds.length; ++idx) { for (let idx = 0; idx < waitSeconds.length; ++idx) {
try { try {
@ -369,7 +368,7 @@ export class WrappedDropboxClient {
dropboxConfig: DropboxConfig; dropboxConfig: DropboxConfig;
remoteBaseDir: string; remoteBaseDir: string;
saveUpdatedConfigFunc: () => Promise<any>; saveUpdatedConfigFunc: () => Promise<any>;
dropbox: Dropbox; dropbox!: Dropbox;
vaultFolderExists: boolean; vaultFolderExists: boolean;
constructor( constructor(
dropboxConfig: DropboxConfig, dropboxConfig: DropboxConfig,
@ -507,6 +506,9 @@ export const getRemoteMeta = async (
path: remotePath, path: remotePath,
}) })
); );
if (rsp === undefined) {
throw Error("dropbox.filesGetMetadata undefinded");
}
if (rsp.status !== 200) { if (rsp.status !== 200) {
throw Error(JSON.stringify(rsp)); throw Error(JSON.stringify(rsp));
} }
@ -516,7 +518,7 @@ export const getRemoteMeta = async (
export const uploadToRemote = async ( export const uploadToRemote = async (
client: WrappedDropboxClient, client: WrappedDropboxClient,
fileOrFolderPath: string, fileOrFolderPath: string,
vault: Vault, vault: Vault | undefined,
isRecursively: boolean = false, isRecursively: boolean = false,
password: string = "", password: string = "",
remoteEncryptedKey: string = "", remoteEncryptedKey: string = "",
@ -611,6 +613,11 @@ export const uploadToRemote = async (
localContent = rawContent; localContent = rawContent;
} }
} else { } 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); localContent = await vault.adapter.readBinary(fileOrFolderPath);
} }
let remoteContent = localContent; let remoteContent = localContent;
@ -700,6 +707,9 @@ const downloadFromRemoteRaw = async (
}), }),
`downloadFromRemoteRaw=${remotePath}` `downloadFromRemoteRaw=${remotePath}`
); );
if (rsp === undefined) {
throw Error(`unknown rsp from dropbox download: ${rsp}`);
}
if ((rsp.result as any).fileBlob !== undefined) { if ((rsp.result as any).fileBlob !== undefined) {
// we get a Blob // we get a Blob
const content = (rsp.result as any).fileBlob as Blob; const content = (rsp.result as any).fileBlob as Blob;

View File

@ -31,8 +31,8 @@ const REDIRECT_URI = `obsidian://${COMMAND_CALLBACK_ONEDRIVE}`;
export const DEFAULT_ONEDRIVE_CONFIG: OnedriveConfig = { export const DEFAULT_ONEDRIVE_CONFIG: OnedriveConfig = {
accessToken: "", accessToken: "",
clientID: process.env.DEFAULT_ONEDRIVE_CLIENT_ID, clientID: process.env.DEFAULT_ONEDRIVE_CLIENT_ID ?? "",
authority: process.env.DEFAULT_ONEDRIVE_AUTHORITY, authority: process.env.DEFAULT_ONEDRIVE_AUTHORITY ?? "",
refreshToken: "", refreshToken: "",
accessTokenExpiresInSeconds: 0, accessTokenExpiresInSeconds: 0,
accessTokenExpiresAtTime: 0, accessTokenExpiresAtTime: 0,
@ -199,7 +199,7 @@ export const setConfigBySuccessfullAuthInplace = async (
config.accessTokenExpiresAtTime = config.accessTokenExpiresAtTime =
Date.now() + authRes.expires_in - 5 * 60 * 1000; Date.now() + authRes.expires_in - 5 * 60 * 1000;
config.accessTokenExpiresInSeconds = authRes.expires_in; config.accessTokenExpiresInSeconds = authRes.expires_in;
config.refreshToken = authRes.refresh_token; config.refreshToken = authRes.refresh_token!;
// manually set it expired after 80 days; // manually set it expired after 80 days;
config.credentialsShouldBeDeletedAtTime = config.credentialsShouldBeDeletedAtTime =
@ -256,7 +256,9 @@ const getNormPath = (fileOrFolderPath: string, remoteBaseDir: string) => {
}; };
const constructFromDriveItemToRemoteItemError = (x: DriveItem) => { 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 = ( const fromDriveItemToRemoteItem = (
@ -281,6 +283,14 @@ const fromDriveItemToRemoteItem = (
// another possibile prefix // another possibile prefix
const FOURTH_COMMON_PREFIX_RAW = `/drive/items/`; 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 fullPathOriginal = `${x.parentReference.path}/${x.name}`;
const matchFirstPrefixRes = fullPathOriginal.match(FIRST_COMMON_PREFIX_REGEX); const matchFirstPrefixRes = fullPathOriginal.match(FIRST_COMMON_PREFIX_REGEX);
const matchSecondPrefixRes = fullPathOriginal.match( const matchSecondPrefixRes = fullPathOriginal.match(
@ -309,6 +319,11 @@ const fromDriveItemToRemoteItem = (
// it's something like // it's something like
// /drive/items/<some_id>!<another_id>:/${remoteBaseDir}/<subfolder> // /drive/items/<some_id>!<another_id>:/${remoteBaseDir}/<subfolder>
// with uri encoded! // 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); const parPath = decodeURIComponent(x.parentReference.path);
key = parPath.substring(parPath.indexOf(":") + 1); key = parPath.substring(parPath.indexOf(":") + 1);
if (key.startsWith(`/${remoteBaseDir}/`)) { if (key.startsWith(`/${remoteBaseDir}/`)) {
@ -337,8 +352,8 @@ const fromDriveItemToRemoteItem = (
} }
return { return {
key: key, key: key,
lastModified: Date.parse(x.fileSystemInfo.lastModifiedDateTime), lastModified: Date.parse(x!.fileSystemInfo!.lastModifiedDateTime!),
size: isFolder ? 0 : x.size, size: isFolder ? 0 : x.size!,
remoteType: "onedrive", remoteType: "onedrive",
etag: x.cTag || "", // do NOT use x.eTag because it changes if meta changes 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; const r2 = r as AccessCodeResponseSuccessfulType;
this.onedriveConfig.accessToken = r2.access_token; 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.accessTokenExpiresInSeconds = r2.expires_in;
this.onedriveConfig.accessTokenExpiresAtTime = this.onedriveConfig.accessTokenExpiresAtTime =
currentTs + r2.expires_in * 1000 - 60 * 2 * 1000; currentTs + r2.expires_in * 1000 - 60 * 2 * 1000;
@ -680,7 +695,7 @@ export const getRemoteMeta = async (
export const uploadToRemote = async ( export const uploadToRemote = async (
client: WrappedOnedriveClient, client: WrappedOnedriveClient,
fileOrFolderPath: string, fileOrFolderPath: string,
vault: Vault, vault: Vault | undefined,
isRecursively: boolean = false, isRecursively: boolean = false,
password: string = "", password: string = "",
remoteEncryptedKey: string = "", remoteEncryptedKey: string = "",
@ -784,6 +799,11 @@ export const uploadToRemote = async (
localContent = rawContent; localContent = rawContent;
} }
} else { } 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); localContent = await vault.adapter.readBinary(fileOrFolderPath);
} }
let remoteContent = localContent; let remoteContent = localContent;
@ -842,7 +862,7 @@ export const uploadToRemote = async (
`${uploadFile}:/createUploadSession`, `${uploadFile}:/createUploadSession`,
k k
); );
const uploadUrl = s.uploadUrl; const uploadUrl = s.uploadUrl!;
log.debug("uploadSession = "); log.debug("uploadSession = ");
log.debug(s); log.debug(s);

View File

@ -55,7 +55,7 @@ import PQueue from "p-queue";
* But this uses Obsidian requestUrl instead. * But this uses Obsidian requestUrl instead.
*/ */
class ObsHttpHandler extends FetchHttpHandler { class ObsHttpHandler extends FetchHttpHandler {
requestTimeoutInMs: number; requestTimeoutInMs: number | undefined;
constructor(options?: FetchHttpHandlerOptions) { constructor(options?: FetchHttpHandlerOptions) {
super(options); super(options);
this.requestTimeoutInMs = this.requestTimeoutInMs =
@ -95,7 +95,7 @@ class ObsHttpHandler extends FetchHttpHandler {
transformedHeaders[keyLower] = request.headers[key]; transformedHeaders[keyLower] = request.headers[key];
} }
let contentType: string = undefined; let contentType: string | undefined = undefined;
if (transformedHeaders["content-type"] !== undefined) { if (transformedHeaders["content-type"] !== undefined) {
contentType = transformedHeaders["content-type"]; contentType = transformedHeaders["content-type"];
} }
@ -226,17 +226,17 @@ const fromS3ObjectToRemoteItem = (
mtimeRecords: Record<string, number>, mtimeRecords: Record<string, number>,
ctimeRecords: Record<string, number> ctimeRecords: Record<string, number>
) => { ) => {
let mtime = x.LastModified.valueOf(); let mtime = x.LastModified!.valueOf();
if (x.Key in mtimeRecords) { if (x.Key! in mtimeRecords) {
const m2 = mtimeRecords[x.Key]; const m2 = mtimeRecords[x.Key!];
if (m2 !== 0) { if (m2 !== 0) {
mtime = m2; mtime = m2;
} }
} }
const r: RemoteItem = { const r: RemoteItem = {
key: getLocalNoPrefixPath(x.Key, remotePrefix), key: getLocalNoPrefixPath(x.Key!, remotePrefix),
lastModified: mtime, lastModified: mtime,
size: x.Size, size: x.Size!,
remoteType: "s3", remoteType: "s3",
etag: x.ETag, etag: x.ETag,
}; };
@ -249,7 +249,7 @@ const fromS3HeadObjectToRemoteItem = (
remotePrefix: string, remotePrefix: string,
useAccurateMTime: boolean useAccurateMTime: boolean
) => { ) => {
let mtime = x.LastModified.valueOf(); let mtime = x.LastModified!.valueOf();
if (useAccurateMTime && x.Metadata !== undefined) { if (useAccurateMTime && x.Metadata !== undefined) {
const m2 = Math.round( const m2 = Math.round(
parseFloat(x.Metadata.mtime || x.Metadata.MTime || "0") parseFloat(x.Metadata.mtime || x.Metadata.MTime || "0")
@ -317,6 +317,7 @@ export const getRemoteMeta = async (
fileOrFolderPathWithRemotePrefix: string fileOrFolderPathWithRemotePrefix: string
) => { ) => {
if ( if (
s3Config.remotePrefix !== undefined &&
s3Config.remotePrefix !== "" && s3Config.remotePrefix !== "" &&
!fileOrFolderPathWithRemotePrefix.startsWith(s3Config.remotePrefix) !fileOrFolderPathWithRemotePrefix.startsWith(s3Config.remotePrefix)
) { ) {
@ -332,8 +333,8 @@ export const getRemoteMeta = async (
return fromS3HeadObjectToRemoteItem( return fromS3HeadObjectToRemoteItem(
fileOrFolderPathWithRemotePrefix, fileOrFolderPathWithRemotePrefix,
res, res,
s3Config.remotePrefix, s3Config.remotePrefix ?? "",
s3Config.useAccurateMTime s3Config.useAccurateMTime ?? false
); );
}; };
@ -341,7 +342,7 @@ export const uploadToRemote = async (
s3Client: S3Client, s3Client: S3Client,
s3Config: S3Config, s3Config: S3Config,
fileOrFolderPath: string, fileOrFolderPath: string,
vault: Vault, vault: Vault | undefined,
isRecursively: boolean = false, isRecursively: boolean = false,
password: string = "", password: string = "",
remoteEncryptedKey: string = "", remoteEncryptedKey: string = "",
@ -354,7 +355,7 @@ export const uploadToRemote = async (
if (password !== "") { if (password !== "") {
uploadFile = remoteEncryptedKey; uploadFile = remoteEncryptedKey;
} }
uploadFile = getRemoteWithPrefixPath(uploadFile, s3Config.remotePrefix); uploadFile = getRemoteWithPrefixPath(uploadFile, s3Config.remotePrefix ?? "");
const isFolder = fileOrFolderPath.endsWith("/"); const isFolder = fileOrFolderPath.endsWith("/");
if (isFolder && isRecursively) { if (isFolder && isRecursively) {
@ -407,8 +408,13 @@ export const uploadToRemote = async (
mtime = rawContentMTime; mtime = rawContentMTime;
ctime = rawContentCTime; ctime = rawContentCTime;
} else { } 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); 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) { if (s !== undefined && s !== null) {
mtime = s.mtime; mtime = s.mtime;
ctime = s.ctime; ctime = s.ctime;
@ -500,12 +506,12 @@ const listFromRemoteRaw = async (
if (rspHead.Metadata === undefined) { if (rspHead.Metadata === undefined) {
// pass // pass
} else { } else {
mtimeRecords[content.Key] = Math.round( mtimeRecords[content.Key!] = Math.round(
parseFloat( parseFloat(
rspHead.Metadata.mtime || rspHead.Metadata.MTime || "0" rspHead.Metadata.mtime || rspHead.Metadata.MTime || "0"
) )
); );
ctimeRecords[content.Key] = Math.round( ctimeRecords[content.Key!] = Math.round(
parseFloat( parseFloat(
rspHead.Metadata.ctime || rspHead.Metadata.CTime || "0" 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; confCmd.ContinuationToken = rsp.NextContinuationToken;
if ( if (
isTruncated && isTruncated &&
@ -536,7 +542,7 @@ const listFromRemoteRaw = async (
Contents: contents.map((x) => Contents: contents.map((x) =>
fromS3ObjectToRemoteItem( fromS3ObjectToRemoteItem(
x, x,
s3Config.remotePrefix, s3Config.remotePrefix ?? "",
mtimeRecords, mtimeRecords,
ctimeRecords ctimeRecords
) )
@ -559,8 +565,11 @@ export const listAllFromRemote = async (
* @returns Promise<ArrayBuffer> * @returns Promise<ArrayBuffer>
*/ */
const getObjectBodyToArrayBuffer = async ( 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) { if (b instanceof Readable) {
return (await new Promise((resolve, reject) => { return (await new Promise((resolve, reject) => {
const chunks: Uint8Array[] = []; const chunks: Uint8Array[] = [];
@ -583,6 +592,7 @@ const downloadFromRemoteRaw = async (
fileOrFolderPathWithRemotePrefix: string fileOrFolderPathWithRemotePrefix: string
) => { ) => {
if ( if (
s3Config.remotePrefix !== undefined &&
s3Config.remotePrefix !== "" && s3Config.remotePrefix !== "" &&
!fileOrFolderPathWithRemotePrefix.startsWith(s3Config.remotePrefix) !fileOrFolderPathWithRemotePrefix.startsWith(s3Config.remotePrefix)
) { ) {
@ -626,7 +636,10 @@ export const downloadFromRemote = async (
if (password !== "") { if (password !== "") {
downloadFile = remoteEncryptedKey; downloadFile = remoteEncryptedKey;
} }
downloadFile = getRemoteWithPrefixPath(downloadFile, s3Config.remotePrefix); downloadFile = getRemoteWithPrefixPath(
downloadFile,
s3Config.remotePrefix ?? ""
);
const remoteContent = await downloadFromRemoteRaw( const remoteContent = await downloadFromRemoteRaw(
s3Client, s3Client,
s3Config, s3Config,
@ -668,7 +681,7 @@ export const deleteFromRemote = async (
} }
remoteFileName = getRemoteWithPrefixPath( remoteFileName = getRemoteWithPrefixPath(
remoteFileName, remoteFileName,
s3Config.remotePrefix s3Config.remotePrefix ?? ""
); );
await s3Client.send( await s3Client.send(
new DeleteObjectCommand({ new DeleteObjectCommand({
@ -734,7 +747,7 @@ export const checkConnectivity = async (
return false; return false;
} }
return results.$metadata.httpStatusCode === 200; return results.$metadata.httpStatusCode === 200;
} catch (err) { } catch (err: any) {
log.debug(err); log.debug(err);
if (callbackFunc !== undefined) { if (callbackFunc !== undefined) {
if (s3Config.s3Endpoint.contains(s3Config.s3BucketName)) { if (s3Config.s3Endpoint.contains(s3Config.s3BucketName)) {

View File

@ -39,12 +39,16 @@ if (VALID_REQURL) {
}); });
let contentType: string | undefined = let contentType: string | undefined =
r.headers["Content-Type"] || r.headers["Content-Type"] || r.headers["content-type"];
r.headers["content-type"] || if (options.headers !== undefined) {
contentType =
contentType ||
options.headers["Content-Type"] || options.headers["Content-Type"] ||
options.headers["content-type"] || options.headers["content-type"] ||
options.headers["Accept"] || options.headers["Accept"] ||
options.headers["accept"]; options.headers["accept"];
}
if (contentType !== undefined) { if (contentType !== undefined) {
contentType = contentType.toLowerCase(); 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)) { if ([101, 103, 204, 205, 304].includes(r.status)) {
// A null body status is a status that is 101, 103, 204, 205, or 304. // A null body status is a status that is 101, 103, 204, 205, or 304.
// https://fetch.spec.whatwg.org/#statuses // https://fetch.spec.whatwg.org/#statuses
@ -193,7 +197,7 @@ const fromWebdavItemToRemoteItem = (x: FileStat, remoteBaseDir: string) => {
export class WrappedWebdavClient { export class WrappedWebdavClient {
webdavConfig: WebdavConfig; webdavConfig: WebdavConfig;
remoteBaseDir: string; remoteBaseDir: string;
client: WebDAVClient; client!: WebDAVClient;
vaultFolderExists: boolean; vaultFolderExists: boolean;
saveUpdatedConfigFunc: () => Promise<any>; saveUpdatedConfigFunc: () => Promise<any>;
constructor( constructor(
@ -340,7 +344,7 @@ export const getRemoteMeta = async (
export const uploadToRemote = async ( export const uploadToRemote = async (
client: WrappedWebdavClient, client: WrappedWebdavClient,
fileOrFolderPath: string, fileOrFolderPath: string,
vault: Vault, vault: Vault | undefined,
isRecursively: boolean = false, isRecursively: boolean = false,
password: string = "", password: string = "",
remoteEncryptedKey: string = "", remoteEncryptedKey: string = "",
@ -392,6 +396,11 @@ export const uploadToRemote = async (
localContent = rawContent; localContent = rawContent;
} }
} else { } 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); localContent = await vault.adapter.readBinary(fileOrFolderPath);
} }
let remoteContent = localContent; let remoteContent = localContent;
@ -428,9 +437,9 @@ export const listAllFromRemote = async (client: WrappedWebdavClient) => {
const q = new Queue([`/${client.remoteBaseDir}`]); const q = new Queue([`/${client.remoteBaseDir}`]);
const CHUNK_SIZE = 10; const CHUNK_SIZE = 10;
while (q.length > 0) { while (q.length > 0) {
const itemsToFetch = []; const itemsToFetch: string[] = [];
while (q.length > 0) { while (q.length > 0) {
itemsToFetch.push(q.pop()); itemsToFetch.push(q.pop()!);
} }
const itemsToFetchChunks = chunk(itemsToFetch, CHUNK_SIZE); const itemsToFetchChunks = chunk(itemsToFetch, CHUNK_SIZE);
// log.debug(itemsToFetchChunks); // log.debug(itemsToFetchChunks);

View File

@ -416,7 +416,7 @@ class DropboxAuthModal extends Modal {
const self = this; const self = this;
setConfigBySuccessfullAuthInplace( setConfigBySuccessfullAuthInplace(
this.plugin.settings.dropbox, this.plugin.settings.dropbox,
authRes, authRes!,
() => self.plugin.saveSettings() () => self.plugin.saveSettings()
); );
const client = new RemoteClient( const client = new RemoteClient(
@ -784,7 +784,7 @@ const getEyesElements = () => {
const wrapTextWithPasswordHide = (text: TextComponent) => { const wrapTextWithPasswordHide = (text: TextComponent) => {
const { eye, eyeOff } = getEyesElements(); 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 // the init type of hider is "hidden" === eyeOff === password
hider.innerHTML = eyeOff; hider.innerHTML = eyeOff;
hider.addEventListener("click", (e) => { hider.addEventListener("click", (e) => {
@ -1534,8 +1534,8 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
dropdown dropdown
.setValue(this.plugin.settings.webdav.authType) .setValue(this.plugin.settings.webdav.authType)
.onChange(async (val: WebdavAuthType) => { .onChange(async (val) => {
this.plugin.settings.webdav.authType = val; this.plugin.settings.webdav.authType = val as WebdavAuthType;
await this.plugin.saveSettings(); 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_1", t("settings_webdav_depth_1"));
dropdown.addOption("manual_infinity", t("settings_webdav_depth_inf")); dropdown.addOption("manual_infinity", t("settings_webdav_depth_inf"));
let initVal = "auto"; let initVal: WebdavDepthType = "auto";
const autoOptions: Set<WebdavDepthType> = new Set([ const autoOptions: Set<WebdavDepthType> = new Set([
"auto_unknown", "auto_unknown",
"auto_1", "auto_1",
"auto_infinity", "auto_infinity",
]); ]);
if (autoOptions.has(this.plugin.settings.webdav.depth)) { if (autoOptions.has(this.plugin.settings.webdav.depth as any)) {
initVal = "auto"; initVal = "auto";
} else { } else {
initVal = this.plugin.settings.webdav.depth || "auto"; initVal = this.plugin.settings.webdav.depth || "auto";
} }
type DepthOption = "auto" | "manual_1" | "manual_infinity"; type DepthOption = "auto" | "manual_1" | "manual_infinity";
dropdown.setValue(initVal).onChange(async (val: DepthOption) => { dropdown.setValue(initVal).onChange(async (val) => {
if (val === "auto") { if (val === "auto") {
this.plugin.settings.webdav.depth = "auto_unknown"; this.plugin.settings.webdav.depth = "auto_unknown";
this.plugin.settings.webdav.manualRecursive = false; this.plugin.settings.webdav.manualRecursive = false;
@ -1656,8 +1656,8 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
dropdown.addOption("onedrive", t("settings_chooseservice_onedrive")); dropdown.addOption("onedrive", t("settings_chooseservice_onedrive"));
dropdown dropdown
.setValue(this.plugin.settings.serviceType) .setValue(this.plugin.settings.serviceType)
.onChange(async (val: SUPPORTED_SERVICES_TYPE) => { .onChange(async (val) => {
this.plugin.settings.serviceType = val; this.plugin.settings.serviceType = val as SUPPORTED_SERVICES_TYPE;
s3Div.toggleClass( s3Div.toggleClass(
"s3-hide", "s3-hide",
this.plugin.settings.serviceType !== "s3" this.plugin.settings.serviceType !== "s3"
@ -1815,11 +1815,11 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
// ); // );
if ( if (
currentTime - lastModified < currentTime - lastModified <
this.plugin.settings.syncOnSaveAfterMilliseconds this.plugin.settings.syncOnSaveAfterMilliseconds!
) { ) {
if (!runScheduled) { if (!runScheduled) {
const scheduleTimeFromNow = const scheduleTimeFromNow =
this.plugin.settings.syncOnSaveAfterMilliseconds - this.plugin.settings.syncOnSaveAfterMilliseconds! -
(currentTime - lastModified); (currentTime - lastModified);
log.info( log.info(
`schedule a run for ${scheduleTimeFromNow} milliseconds later` `schedule a run for ${scheduleTimeFromNow} milliseconds later`
@ -1864,7 +1864,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
.setDesc(t("settings_enablestatusbar_info_desc")) .setDesc(t("settings_enablestatusbar_info_desc"))
.addToggle((toggle) => { .addToggle((toggle) => {
toggle toggle
.setValue(this.plugin.settings.enableStatusBarInfo) .setValue(this.plugin.settings.enableStatusBarInfo ?? false)
.onChange(async (val) => { .onChange(async (val) => {
this.plugin.settings.enableStatusBarInfo = val; this.plugin.settings.enableStatusBarInfo = val;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
@ -1897,7 +1897,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
.addTextArea((textArea) => { .addTextArea((textArea) => {
textArea textArea
.setValue(`${this.plugin.settings.ignorePaths.join("\n")}`) .setValue(`${(this.plugin.settings.ignorePaths ?? []).join("\n")}`)
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.ignorePaths = value this.plugin.settings.ignorePaths = value
.trim() .trim()
@ -1999,9 +1999,9 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
t("settings_deletetowhere_obsidian_trash") t("settings_deletetowhere_obsidian_trash")
); );
dropdown dropdown
.setValue(this.plugin.settings.deleteToWhere) .setValue(this.plugin.settings.deleteToWhere ?? "system")
.onChange(async (val: "system" | "obsidian") => { .onChange(async (val) => {
this.plugin.settings.deleteToWhere = val; this.plugin.settings.deleteToWhere = val as "system" | "obsidian";
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}); });
}); });
@ -2044,7 +2044,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab {
dropdown.addOption("info", "info"); dropdown.addOption("info", "info");
dropdown.addOption("debug", "debug"); dropdown.addOption("debug", "debug");
dropdown dropdown
.setValue(this.plugin.settings.currLogLevel) .setValue(this.plugin.settings.currLogLevel ?? "info")
.onChange(async (val: string) => { .onChange(async (val: string) => {
this.plugin.settings.currLogLevel = val; this.plugin.settings.currLogLevel = val;
log.setLevel(val as any); log.setLevel(val as any);

View File

@ -186,7 +186,7 @@ export const parseRemoteItems = async (
password: string = "" password: string = ""
) => { ) => {
const remoteStates = [] as FileOrFolderMixedState[]; const remoteStates = [] as FileOrFolderMixedState[];
let metadataFile: FileOrFolderMixedState = undefined; let metadataFile: FileOrFolderMixedState | undefined = undefined;
if (remote === undefined) { if (remote === undefined) {
return { return {
remoteStates: remoteStates, remoteStates: remoteStates,
@ -212,8 +212,8 @@ export const parseRemoteItems = async (
remoteType, remoteType,
db, db,
key, key,
entry.lastModified, entry.lastModified ?? Date.now(),
entry.etag, entry.etag ?? "",
vaultRandomID vaultRandomID
); );
@ -268,7 +268,7 @@ export const parseRemoteItems = async (
}; };
export const fetchMetadataFile = async ( export const fetchMetadataFile = async (
metadataFile: FileOrFolderMixedState, metadataFile: FileOrFolderMixedState | undefined,
client: RemoteClient, client: RemoteClient,
vault: Vault, vault: Vault,
password: string = "" password: string = ""
@ -283,7 +283,7 @@ export const fetchMetadataFile = async (
const buf = await client.downloadFromRemote( const buf = await client.downloadFromRemote(
metadataFile.key, metadataFile.key,
vault, vault,
metadataFile.mtimeRemote, metadataFile.mtimeRemote ?? Date.now(),
password, password,
metadataFile.remoteEncryptedKey, metadataFile.remoteEncryptedKey,
true true
@ -325,8 +325,8 @@ const ensembleMixedStates = async (
remoteStates: FileOrFolderMixedState[], remoteStates: FileOrFolderMixedState[],
local: TAbstractFile[], local: TAbstractFile[],
localConfigDirContents: ObsConfigDirFileType[] | undefined, localConfigDirContents: ObsConfigDirFileType[] | undefined,
remoteDeleteHistory: DeletionOnRemote[], remoteDeleteHistory: DeletionOnRemote[] | undefined,
localFileHistory: FileFolderHistoryRecord[], localFileHistory: FileFolderHistoryRecord[] | undefined,
syncConfigDir: boolean, syncConfigDir: boolean,
configDir: string, configDir: string,
syncUnderscoreItems: boolean, syncUnderscoreItems: boolean,
@ -413,7 +413,10 @@ const ensembleMixedStates = async (
if (syncConfigDir && localConfigDirContents !== undefined) { if (syncConfigDir && localConfigDirContents !== undefined) {
for (const entry of localConfigDirContents) { for (const entry of localConfigDirContents) {
const key = entry.key; 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) { if (Number.isNaN(mtimeLocal) || mtimeLocal === 0) {
mtimeLocal = undefined; mtimeLocal = undefined;
} }
@ -453,7 +456,7 @@ const ensembleMixedStates = async (
} }
} }
for (const entry of remoteDeleteHistory) { for (const entry of remoteDeleteHistory ?? []) {
const key = entry.key; const key = entry.key;
const r = { const r = {
key: key, key: key,
@ -485,7 +488,7 @@ const ensembleMixedStates = async (
} }
} }
for (const entry of localFileHistory) { for (const entry of localFileHistory ?? []) {
let key = entry.key; let key = entry.key;
if (entry.keyType === "folder") { if (entry.keyType === "folder") {
if (!entry.key.endsWith("/")) { if (!entry.key.endsWith("/")) {
@ -532,7 +535,7 @@ const ensembleMixedStates = async (
changeLocalMtimeUsingMapping: true, changeLocalMtimeUsingMapping: true,
}; };
if (results.hasOwnProperty(key)) { if (results.hasOwnProperty(key)) {
let mtimeLocal = Math.max( let mtimeLocal: number | undefined = Math.max(
r.mtimeLocal ?? 0, r.mtimeLocal ?? 0,
results[key].mtimeLocal ?? 0 results[key].mtimeLocal ?? 0
); );
@ -624,13 +627,13 @@ const assignOperationToFileInplace = (
// 1. mtimeLocal // 1. mtimeLocal
if (r.existLocal) { 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 deltimeRemote = r.deltimeRemote !== undefined ? r.deltimeRemote : -1;
const deltimeLocal = r.deltimeLocal !== undefined ? r.deltimeLocal : -1; const deltimeLocal = r.deltimeLocal !== undefined ? r.deltimeLocal : -1;
if ( if (
r.mtimeLocal >= mtimeRemote && r.mtimeLocal! >= mtimeRemote &&
r.mtimeLocal >= deltimeLocal && r.mtimeLocal! >= deltimeLocal &&
r.mtimeLocal >= deltimeRemote r.mtimeLocal! >= deltimeRemote
) { ) {
if (sizeLocalComp === undefined) { if (sizeLocalComp === undefined) {
throw new Error( throw new Error(
@ -654,7 +657,7 @@ const assignOperationToFileInplace = (
} else { } else {
// limit the sizes // limit the sizes
if (sizeLocalComp <= skipSizeLargerThan) { if (sizeLocalComp <= skipSizeLargerThan) {
if (sizeRemoteComp <= skipSizeLargerThan) { if (sizeRemoteComp! <= skipSizeLargerThan) {
r.decision = "uploadLocalToRemote"; r.decision = "uploadLocalToRemote";
r.decisionBranch = 18; r.decisionBranch = 18;
} else { } else {
@ -662,7 +665,7 @@ const assignOperationToFileInplace = (
r.decisionBranch = 19; r.decisionBranch = 19;
} }
} else { } else {
if (sizeRemoteComp <= skipSizeLargerThan) { if (sizeRemoteComp! <= skipSizeLargerThan) {
r.decision = "errorLocalTooLargeConflictRemote"; r.decision = "errorLocalTooLargeConflictRemote";
r.decisionBranch = 20; r.decisionBranch = 20;
} else { } else {
@ -713,13 +716,13 @@ const assignOperationToFileInplace = (
// 2. mtimeRemote // 2. mtimeRemote
if (r.existRemote) { 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 deltimeRemote = r.deltimeRemote !== undefined ? r.deltimeRemote : -1;
const deltimeLocal = r.deltimeLocal !== undefined ? r.deltimeLocal : -1; const deltimeLocal = r.deltimeLocal !== undefined ? r.deltimeLocal : -1;
if ( if (
r.mtimeRemote > mtimeLocal && r.mtimeRemote! > mtimeLocal &&
r.mtimeRemote >= deltimeLocal && r.mtimeRemote! >= deltimeLocal &&
r.mtimeRemote >= deltimeRemote r.mtimeRemote! >= deltimeRemote
) { ) {
// we have remote laregest mtime, // we have remote laregest mtime,
// and the local not existing or smaller mtime // and the local not existing or smaller mtime
@ -771,8 +774,8 @@ const assignOperationToFileInplace = (
// 3. deltimeLocal // 3. deltimeLocal
if (r.deltimeLocal !== undefined && r.deltimeLocal !== 0) { if (r.deltimeLocal !== undefined && r.deltimeLocal !== 0) {
const mtimeLocal = r.existLocal ? r.mtimeLocal : -1; const mtimeLocal = r.existLocal ? r.mtimeLocal! : -1;
const mtimeRemote = r.existRemote ? r.mtimeRemote : -1; const mtimeRemote = r.existRemote ? r.mtimeRemote! : -1;
const deltimeRemote = r.deltimeRemote !== undefined ? r.deltimeRemote : -1; const deltimeRemote = r.deltimeRemote !== undefined ? r.deltimeRemote : -1;
if ( if (
r.deltimeLocal >= mtimeLocal && r.deltimeLocal >= mtimeLocal &&
@ -787,9 +790,9 @@ const assignOperationToFileInplace = (
} }
} else { } else {
const localTooLargeToDelete = const localTooLargeToDelete =
r.existLocal && sizeLocalComp > skipSizeLargerThan; r.existLocal && sizeLocalComp! > skipSizeLargerThan;
const remoteTooLargeToDelete = const remoteTooLargeToDelete =
r.existRemote && sizeRemoteComp > skipSizeLargerThan; r.existRemote && sizeRemoteComp! > skipSizeLargerThan;
if (localTooLargeToDelete) { if (localTooLargeToDelete) {
if (remoteTooLargeToDelete) { if (remoteTooLargeToDelete) {
r.decision = "skipUsingLocalDelTooLarge"; r.decision = "skipUsingLocalDelTooLarge";
@ -824,8 +827,8 @@ const assignOperationToFileInplace = (
// 4. deltimeRemote // 4. deltimeRemote
if (r.deltimeRemote !== undefined && r.deltimeRemote !== 0) { if (r.deltimeRemote !== undefined && r.deltimeRemote !== 0) {
const mtimeLocal = r.existLocal ? r.mtimeLocal : -1; const mtimeLocal = r.existLocal ? r.mtimeLocal! : -1;
const mtimeRemote = r.existRemote ? r.mtimeRemote : -1; const mtimeRemote = r.existRemote ? r.mtimeRemote! : -1;
const deltimeLocal = r.deltimeLocal !== undefined ? r.deltimeLocal : -1; const deltimeLocal = r.deltimeLocal !== undefined ? r.deltimeLocal : -1;
if ( if (
r.deltimeRemote >= mtimeLocal && r.deltimeRemote >= mtimeLocal &&
@ -840,9 +843,9 @@ const assignOperationToFileInplace = (
} }
} else { } else {
const localTooLargeToDelete = const localTooLargeToDelete =
r.existLocal && sizeLocalComp > skipSizeLargerThan; r.existLocal && sizeLocalComp! > skipSizeLargerThan;
const remoteTooLargeToDelete = const remoteTooLargeToDelete =
r.existRemote && sizeRemoteComp > skipSizeLargerThan; r.existRemote && sizeRemoteComp! > skipSizeLargerThan;
if (localTooLargeToDelete) { if (localTooLargeToDelete) {
if (remoteTooLargeToDelete) { if (remoteTooLargeToDelete) {
r.decision = "skipUsingRemoteDelTooLarge"; r.decision = "skipUsingRemoteDelTooLarge";
@ -905,7 +908,13 @@ const assignOperationToFolderInplace = async (
// if it was created after deletion, we should keep it as is // if it was created after deletion, we should keep it as is
if (requireApiVersion(API_VER_STAT_FOLDER)) { if (requireApiVersion(API_VER_STAT_FOLDER)) {
if (r.existLocal) { 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); const cmtime = Math.max(ctime ?? 0, mtime ?? 0);
if ( if (
!Number.isNaN(cmtime) && !Number.isNaN(cmtime) &&
@ -936,9 +945,9 @@ const assignOperationToFolderInplace = async (
if ( if (
r.existLocal && r.existLocal &&
r.changeLocalMtimeUsingMapping && r.changeLocalMtimeUsingMapping &&
r.mtimeLocal > 0 && r.mtimeLocal! > 0 &&
r.mtimeLocal > deltimeLocal && r.mtimeLocal! > deltimeLocal &&
r.mtimeLocal > deltimeRemote r.mtimeLocal! > deltimeRemote
) { ) {
keptFolder.add(getParentFolder(r.key)); keptFolder.add(getParentFolder(r.key));
if (r.existLocal && r.existRemote) { if (r.existLocal && r.existRemote) {
@ -1018,8 +1027,8 @@ export const getSyncPlan = async (
remoteStates: FileOrFolderMixedState[], remoteStates: FileOrFolderMixedState[],
local: TAbstractFile[], local: TAbstractFile[],
localConfigDirContents: ObsConfigDirFileType[] | undefined, localConfigDirContents: ObsConfigDirFileType[] | undefined,
remoteDeleteHistory: DeletionOnRemote[], remoteDeleteHistory: DeletionOnRemote[] | undefined,
localFileHistory: FileFolderHistoryRecord[], localFileHistory: FileFolderHistoryRecord[] | undefined,
remoteType: SUPPORTED_SERVICES_TYPE, remoteType: SUPPORTED_SERVICES_TYPE,
triggerSource: SyncTriggerSourceType, triggerSource: SyncTriggerSourceType,
vault: Vault, 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); sizesGoWrong.push(val);
} }
if (DELETION_DECISIONS.has(val.decision)) { if (DELETION_DECISIONS.has(val.decision!)) {
if (val.decision === "uploadLocalDelHistToRemote") { if (val.decision === "uploadLocalDelHistToRemote") {
deletions.push({ deletions.push({
key: key, key: key,
actionWhen: val.deltimeLocal, actionWhen: val.deltimeLocal!,
}); });
} else if (val.decision === "keepRemoteDelHist") { } else if (val.decision === "keepRemoteDelHist") {
deletions.push({ deletions.push({
key: key, key: key,
actionWhen: val.deltimeRemote, actionWhen: val.deltimeRemote!,
}); });
} else if (val.decision === "uploadLocalDelHistToRemoteFolder") { } else if (val.decision === "uploadLocalDelHistToRemoteFolder") {
deletions.push({ deletions.push({
key: key, key: key,
actionWhen: val.deltimeLocal, actionWhen: val.deltimeLocal!,
}); });
} else if (val.decision === "keepRemoteDelHistFolder") { } else if (val.decision === "keepRemoteDelHistFolder") {
deletions.push({ deletions.push({
key: key, key: key,
actionWhen: val.deltimeRemote, actionWhen: val.deltimeRemote!,
}); });
} else { } else {
throw Error(`do not know how to delete for decision ${val.decision}`); 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; const key = DEFAULT_FILE_NAME_FOR_METADATAONREMOTE;
let remoteEncryptedKey = key; let remoteEncryptedKey: string | undefined = key;
if (password !== "") { if (password !== "") {
if (metadataFile === undefined) { if (metadataFile === undefined) {
@ -1180,7 +1189,7 @@ const dispatchOperationToActual = async (
localDeleteFunc: any, localDeleteFunc: any,
password: string = "" password: string = ""
) => { ) => {
let remoteEncryptedKey = key; let remoteEncryptedKey: string | undefined = key;
if (password !== "") { if (password !== "") {
remoteEncryptedKey = r.remoteEncryptedKey; remoteEncryptedKey = r.remoteEncryptedKey;
if (remoteEncryptedKey === undefined || remoteEncryptedKey === "") { if (remoteEncryptedKey === undefined || remoteEncryptedKey === "") {
@ -1233,12 +1242,12 @@ const dispatchOperationToActual = async (
client.serviceType, client.serviceType,
db, db,
r.key, r.key,
r.mtimeLocal, r.mtimeLocal!,
r.sizeLocal, r.sizeLocal!,
r.key, r.key,
remoteObjMeta.lastModified, remoteObjMeta.lastModified ?? Date.now(),
remoteObjMeta.size, remoteObjMeta.size,
remoteObjMeta.etag, remoteObjMeta.etag ?? "",
vaultRandomID vaultRandomID
); );
} }
@ -1248,7 +1257,7 @@ const dispatchOperationToActual = async (
await client.downloadFromRemote( await client.downloadFromRemote(
r.key, r.key,
vault, vault,
r.mtimeRemote, r.mtimeRemote!,
password, password,
remoteEncryptedKey remoteEncryptedKey
); );
@ -1269,12 +1278,12 @@ const dispatchOperationToActual = async (
client.serviceType, client.serviceType,
db, db,
r.key, r.key,
r.mtimeLocal, r.mtimeLocal!,
r.sizeLocal, r.sizeLocal!,
r.key, r.key,
remoteObjMeta.lastModified, remoteObjMeta.lastModified ?? Date.now(),
remoteObjMeta.size, remoteObjMeta.size,
remoteObjMeta.etag, remoteObjMeta.etag ?? "",
vaultRandomID vaultRandomID
); );
} }
@ -1388,7 +1397,7 @@ export const doActualSync = async (
vault: Vault, vault: Vault,
syncPlan: SyncPlanType, syncPlan: SyncPlanType,
sortedKeys: string[], sortedKeys: string[],
metadataFile: FileOrFolderMixedState, metadataFile: FileOrFolderMixedState | undefined,
origMetadata: MetadataOnRemote, origMetadata: MetadataOnRemote,
sizesGoWrong: FileOrFolderMixedState[], sizesGoWrong: FileOrFolderMixedState[],
deletions: DeletionOnRemote[], deletions: DeletionOnRemote[],

View File

@ -45,8 +45,8 @@ describe("Metadata operations tests", () => {
}); });
it("should treat undefined correctly", async () => { it("should treat undefined correctly", async () => {
const a: MetadataOnRemote = undefined; const a: MetadataOnRemote | undefined = undefined;
let b: MetadataOnRemote = { let b: MetadataOnRemote | undefined = {
deletions: [ deletions: [
{ key: "xxx", actionWhen: 1 }, { key: "xxx", actionWhen: 1 },
{ key: "yyy", actionWhen: 2 }, { key: "yyy", actionWhen: 2 },

View File

@ -5,6 +5,7 @@
"inlineSources": true, "inlineSources": true,
"module": "ESNext", "module": "ESNext",
"target": "es6", "target": "es6",
"strict": true,
"allowJs": true, "allowJs": true,
"noImplicitAny": true, "noImplicitAny": true,
"moduleResolution": "node", "moduleResolution": "node",