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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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